Bezdepbonusu7Программирование: статьи и ответы

Вопрос

Оптимизировать функцию для записи битов в файл

Автор вопроса: Danilo BrambillaОтветов: 6

Вот функция, которая записывает n битов в двоичный файл.

Параметры:

  • Данные: битовая последовательность для записи в файл (младший бит справа)
  • Длина: количество бит для записи
  • OutFile: файл назначения.

Первая версия функции:

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;
        }
    }
}

Вторая версия:

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);
    }
}

Оба они делают работу, но второй быстрее, чем первый. Любая идея сделать это еще быстрее?

Всем спасибо за помощь!

c++file-iobit-manipulation

Ответы

Ответ 1 · рейтинг 1 · Stas

  1. Я бы начал с удаления статических переменных из тела вашей функции. Они немного медленнее, так как должны проверять свое состояние (уже инициализировано или нет) при каждом вызове функции. Просто переместите их из области действия функции.

  2. Почему вы используете такой короткий буфер? Вы уверены, что вам нужно записывать в файл каждый unsigned long long? Я бы предложил использовать что-то вроде unsigned char buffer[1024].

  3. Тогда вам следует подумать, как избавиться от других операторов if.

Ответ 2 · рейтинг 1 · Nim

Вместо вызова write() попробуйте следующее:

OutFile.rdbuf()->sputn((char *) & BitBuffer, sizeof(BitBuffer));

Ответ 3 · Jay

Открытие файла, вероятно, будет намного медленнее, чем запись в него. В вашем дизайне вы минимизируете вызовы открытия файлов?

Ответ 4 · davka

если я правильно вас понял, вы хотите записать length младших битов длинного целого числа без знака, которое вы получаете. Вы можете сохранить цикл по входным битам, маскируя необходимые биты:

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

в качестве примечания, я не понимаю, почему ваш тест и запись находятся внутри цикла в первой версии.

Ответ 5 · user9876

Прежде всего, вам нужно написать код так, чтобы он был понятен. Я не могу легко понять ни один из ваших фрагментов кода. Вот попытка переформатировать и реорганизовать первый, чтобы сделать его проще, и добавить некоторые комментарии:

/**
 * @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;
      }
   }
}

Теперь, когда я понимаю, что вы пытаетесь сделать...

Ваша вторая версия выглядит намного лучше, потому что вы избегаете побитового цикла. Поскольку вы спрашиваете об оптимизации этого, я предполагаю, что у вас есть результаты профилирования, которые показывают, что это медленно? И я предполагаю, что вы пропускаете через это много данных?

Одной из возможных оптимизаций является запись в гораздо больший буфер (я бы предложил не менее 4 КБ). Это означает, что вам не нужно вызывать write() так часто. Вызов write() может быть относительно медленным.

Ответ 6 · AShelly

Вот один из способов сделать это с большим буфером. (на псевдо-C#)

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];
   }
}

Перевод вопроса и ответов сообщества Stack Overflow, лицензия CC BY-SA. Оригинал на Stack Overflow.

Похожие материалы