Bitlarni faylga yozish funksiyasini optimallashtirish

Mana ikkilik faylga n bitni yozuvchi funksiya.

Parametrlar:

  • Ma'lumotlar: faylga yoziladigan bit ketma-ketligi (o'ngda lsb)
  • Uzunlik: yozish uchun bitlar soni
  • OutFile: maqsad fayl.

Funksiyaning birinchi versiyasi:

void WriteBitsToFile(unsigned long long Data, unsigned Length, std::ofstream & OutFile) {
    static unsigned long long BitBuffer = 0;
    static unsigned BitCounter = 0;

    for (unsigned i = Length; i --; ) {
        (BitBuffer <<= 1) |= ((Data >> i) & 0x1);
        BitCounter ++;

        if (BitCounter == 64) {
            OutFile.write((char *) & BitBuffer, sizeof(BitBuffer));
            BitCounter = 0;
        }
    }
}

Ikkinchi versiya:

void WriteBitsToFile(unsigned long long Data, unsigned Length, std::ofstream & OutFile) {
    static unsigned long long BitBuffer = 0;
    static unsigned FreeBitCounter = sizeof(BitBuffer) << 3;

    Data &= (1 << Length) - 1;

    if (FreeBitCounter > Length) {
        BitBuffer |= (Data << (FreeBitCounter -= Length));
    } else if (FreeBitCounter < Length) {
        BitBuffer |= (Data >> (Length -= FreeBitCounter));
        OutFile.write((char *) & BitBuffer, sizeof(BitBuffer));
        BitBuffer = Data << ((sizeof(BitBuffer) << 3) - Length);
        FreeBitCounter = (sizeof(BitBuffer) << 3) - Length;
    } else {
        BitBuffer |= Data;
        OutFile.write((char *) & BitBuffer, sizeof(BitBuffer));
        BitBuffer = 0; FreeBitCounter = (sizeof(BitBuffer) << 3);
    }
}

Ikkalasi ham ishni bajaradi, lekin ikkinchisi birinchisidan tezroq. Buni tezroq qilish uchun biron bir fikr bormi?

Yordam uchun barchangizga rahmat!


person Danilo Brambilla    schedule 05.12.2010    source manba


Javoblar (6)


  1. Men sizning funktsiyangizdan statik o'zgaruvchilarni olib tashlashdan boshlardim. Ular biroz sekinroq, chunki har bir funktsiya chaqiruvida ularning holatini (allaqachon ishga tushirilgan yoki yo'q) sinab ko'rish kerak. Faqat ularni funktsiya doirasidan tashqariga ko'chiring.

  2. Nima uchun bunday qisqa buferdan foydalanasiz? Har bir imzosiz uzunlikni faylga yozishingiz kerakligiga ishonchingiz komilmi? Men unsigned char buffer[1024] kabi biror narsadan foydalanishni tavsiya qilaman.

  3. Keyin boshqa "if" iboralaridan qanday qutulish haqida o'ylashingiz kerak.

person Stas    schedule 05.12.2010
comment
Javobingiz uchun rahmat. Qo'ng'iroqlar orasida BitBuffer qiymatini saqlab qolish uchun menga statik o'zgaruvchilar kerak. BitBuffer faylga faqat to'lganida yozilishi kerak. Men uzoqroq buferdan foydalanishga harakat qilaman. Buning uchun menga ko'proq kod kerak bo'ladi. Unsigned long long bufer [1024] dan foydalanishda muammo bormi? Bundan tashqari, ish tugagach, buferni tozalash uchun menga kod kerak bo'ladi, lekin men 1024 ga yetmadim. Bu "agar (Uzunlik == 0) keyin tozalash" kabi bo'lishi mumkin. - person Danilo Brambilla; 05.12.2010
comment
Re: funktsiya tanangizdan statik o'zgaruvchilarni olib tashlash. Ular biroz sekinroq, chunki har bir funktsiya chaqiruvida ularning holatini (allaqachon ishga tushirilgan yoki yo'q) sinab ko'rish kerak. Bu faqat murakkab boshlang'ichga ega bo'lgan statik o'zgaruvchilar uchun to'g'ri keladi (masalan, funktsiya chaqiruvi). Ammo berilgan misolda kompilyator kompilyatsiya vaqtida dastlabki qiymatlarni hisoblashi mumkin, shuning uchun ularni ishga tushirilgan ma'lumotlar segmentiga qo'yadi. Shunday qilib, allaqachon boshlangan tekshiruv yo'q. - person user9876; 05.12.2010
comment
@Foydalanuvchi izohini to'ldirish uchun: va bu xarajat write qo'ng'iroqqa nisbatan hech narsa emas. @Danilo kattaroq char buferdan foydalanadi (va long emas, chunki oxirgi flush operatsiyada uni boshqarish qiyin bo'ladi). - person ruslik; 05.12.2010

write() qo'ng'iroq o'rniga quyidagilarni sinab ko'ring:

OutFile.rdbuf()->sputn((char *) & BitBuffer, sizeof(BitBuffer));
person Nim    schedule 05.12.2010

Faylni ochish, unga yozishdan ancha sekinroq bo'lishi mumkin. Dizayningizda siz ochiq qo'ng'iroqlarni minimallashtirasizmi?

person Jay    schedule 05.12.2010
comment
Javobingiz uchun rahmat. Fayl butun dasturda faqat bir marta ochiladi va yopiladi. - person Danilo Brambilla; 05.12.2010

Agar sizni to'g'ri tushunsam, siz olgan belgisiz uzun butun sonning length pastki bitini yozmoqchisiz. Kerakli bitlarni maskalash orqali kirish bitlari orqali aylanishni saqlashingiz mumkin:

unsigned long long mask = (1ull << length) - 1; // creates a mask of 'length' 1's
BitBuffer = Data & mask;

Izoh sifatida, nima uchun testingiz va yozishingiz birinchi versiyada loop ichida ekanligini tushunmayapman.

person davka    schedule 05.12.2010
comment
Javobingiz uchun rahmat. Funktsiya nima qilishi haqida siz haqsiz. Loop ichida test va yozish kerak, chunki BitBuffer faqat to'lgan bo'lsa yozilishi kerak - person Danilo Brambilla; 05.12.2010
comment
@Danilo Brambilla: Oh, tushundimki, siz ushbu funktsiyani bir necha marta chaqirasiz va buferni faqat to'lganida faylga o'tkazasizmi? Agar shunday bo'lsa, haqiqatan ham, @user9876 ta'kidlaganidek, agar shunday desangiz foydali bo'lardi. Bufer hali to'lmagan bo'lsa, oxirgi bo'lak yozilishiga qanday ishonch hosil qilasiz? - person davka; 05.12.2010
comment
Dastur oxirida men oxirgi qismni 0 ketma-ketligi bilan tugaydigan yozishni majburlash uchun Data = 0 va Length = 64 funksiyasini chaqiraman :-) - person Danilo Brambilla; 05.12.2010

Avvalo, kodni tushunarli bo'lishi uchun yozishingiz kerak. Kod bo'laklaringizning ikkalasini ham oson tushuna olmayman. Mana, birinchisini qayta formatlash va qayta tiklashga urinish soddaroq bo'lishi va ba'zi izohlar qo'shish:

/**
 * @brief Write some bits to a file.
 *
 * The bits are written MSB-first to a temporary 64-bit integer, which is
 * then written to the file in host byte-order.
 *
 * @param Data The data to write to the file.  Only the least-significant
 *             Length bits are written.  Of the bits that are written, the
 *             most significant bit is written first.
 * @param Length The length of the data to write, in bits.  Must be <= 64.
 * @param OutFile The file to write to
 *
 * @note This function is not thread-safe
 * @note This function stores some state in a static variable.  You must
 *       ensure that the total data written is a multiple of 64 bits, or
 *       some data will be lost.  You can only switch from one OutFile to
 *       another on a 64-bit boundry.
 */
void WriteBitsToFile(unsigned long long Data,
                     unsigned Length,
                     std::ofstream & OutFile)
{
   static unsigned long long BitBuffer = 0;
   static unsigned BitCounter = 0; 

   // Loop through input bits, one bit at a time
   for (int i = (int)Length; i >= 0; --i)
   {
      // Get the input bit
      unsigned long long NextBit = ((Data >> i) & 1);

      // Add it to the temporary buffer
      BitBuffer = ((BitBuffer << 1) | NextBit);
      BitCounter++;

      // If the temporary buffer is full, write it out
      if (BitCounter == 64)
      {
         OutFile.write((char *) & BitBuffer, sizeof(BitBuffer));
         BitCounter = 0;
      }
   }
}

Endi men nima qilmoqchi ekanligingizni tushundim ...

Sizning ikkinchi versiyangiz ancha yaxshi ko'rinadi, chunki siz bitli tsikldan qochasiz. Buni optimallashtirish haqida so'raganingiz uchun, menimcha, sizda bu sekin ekanligini ko'rsatadigan profillash natijalari bormi? Va menimcha, siz bu orqali juda ko'p ma'lumotlarni joylashtiryapsizmi?

Mumkin bo'lgan optimallashtirishlardan biri bu juda katta buferga yozish (men kamida 4 kB taklif qilaman). Bu shuni anglatadiki, write() ga tez-tez qo'ng'iroq qilishingiz shart emas. write() ni chaqirish nisbatan sekin bo'lishi mumkin.

person user9876    schedule 05.12.2010
comment
Sizning taklif qilgan optimallashtirishingiz umuman olganda juda to'g'ri, lekin bu erda kirish ma'lumotlari bitta unsigned long long butun son, shuning uchun chiqish ma'lumotlarini to'plashning hojati yo'q (shuningdek, OPda bajarilgan buferni tiklash uchun) - person davka; 05.12.2010
comment
Kod izohlanmagani uchun uzr. Men aslida ba'zan 40 MB dan ortiq faylni shu tarzda joylashtiraman. Men kattaroq bufer bilan sinab ko'raman. - person Danilo Brambilla; 05.12.2010

Buni kattaroq bufer bilan qilishning bir usuli. (Pseudo-C# tilida)

const int wordSz= sizeof(unsigned long long)*8;

void WriteBitsToFile(unsigned long long Data, unsigned Length, std::ofstream & OutFile) { 
   static unsigned long long BitBuffer[BUFSZ+1] ={0};  
   static unsigned bitsSoFar = 0;

   Data &= (1 << Length) - 1; 
   int index = bitsSoFar/wordSz;
   int offset = bitsSoFar - (index*wordSz);
   BitBuffer[index]|=Data<<offset;
   int remainder = offset+length-wordSz;
   if (remainder > 0)
   {
      index++;
      BitBuffer[index]=Data>>(length-remainder);
   }
   bitsSoFar+=length;
   if (bitsPacked > BUFSZ*wordSz)
   {
      OutFile.write((char*)BitBuffer, BUFSZ*sizeof(unsigned long long));
      bitsSoFar-=BUFSZ*wordSz;
      BitBuffer[0]=BitBuffer[BUFSZ];
   }
}
person AShelly    schedule 06.12.2010