Какой самый быстрый способ (ы) перебирать большой фрагмент данных в разбивке по битам

StackOverflow https://stackoverflow.com/questions/418266

Вопрос

Я просматриваю блок памяти с двоичными данными по байтам.

В настоящее время я делаю что-то вроде этого:

for (i = 0; i < data->Count; i++)
{   
    byte = &data->Data[i];
    ((*byte & Masks[0]) == Masks[0]) ? Stats.FreqOf1++; // syntax incorrect but you get the point.
    ((*byte & Masks[1]) == Masks[1]) ? Stats.FreqOf1++;
    ((*byte & Masks[2]) == Masks[2]) ? Stats.FreqOf1++;
    ((*byte & Masks[3]) == Masks[3]) ? Stats.FreqOf1++;
    ((*byte & Masks[4]) == Masks[4]) ? Stats.FreqOf1++;
    ((*byte & Masks[5]) == Masks[5]) ? Stats.FreqOf1++;
    ((*byte & Masks[6]) == Masks[6]) ? Stats.FreqOf1++;
    ((*byte & Masks[7]) == Masks[7]) ? Stats.FreqOf1++;
}

Где находится Masks:

for (i = 0; i < 8; i++)
{
    Masks[i] = 1 << i;
}

(Мне почему-то не удалось сделать это так быстро в цикле или во встроенной функции, поэтому я записал это.)

Есть ли у кого-нибудь какие-либо предложения о том, как улучшить этот первый цикл?Я довольно неопытен в том, чтобы разбираться по частям.

Это может показаться глупым поступком.Но я нахожусь в процессе реализации алгоритма сжатия.Я просто хочу, чтобы часть доступа к биту была расположена справа.

Спасибо!

PS:Это находится в компиляторе Visual Studio 2008.Так что было бы неплохо, если бы предложения применялись к этому компилятору.

ППС:Я только что понял, что мне не нужно увеличивать два счета.Одного было бы достаточно.Затем вычислите разницу с общим количеством битов в конце.Но это было бы специфично только для подсчета голосов.Что я действительно хочу сделать быстро, так это извлечение бита.

Редактировать:Идея таблицы подстановки, которая была выдвинута, хороша.Однако я понимаю, что неправильно поставил вопрос в названии.Потому что, в конце концов, то, что я хочу сделать, это не считать биты, а получить доступ к каждому биту как можно быстрее.

ЕЩЕ ОДНА ПРАВКА:Возможно ли продвинуть указатель всего на один бит в данных?

ЕЩЕ ОДНА ПРАВКА:Спасибо вам за все ваши ответы на данный момент.

То, что я хочу реализовать на следующих шагах, - это несложный двоичный арифметический кодер, который не анализирует контекст.Так что пока меня интересуют только отдельные биты.В конце концов это станет контекстно-адаптивным BAC, но я оставлю это на потом.

Одним из вариантов может быть обработка 4 байт вместо 1 байта.Но цикл более 32 бит также является дорогостоящим, не так ли?

Это было полезно?

Решение

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

#include <stddef.h>

_Bool isbitset(unsigned char * bitmap, size_t idx)
{
    return bitmap[idx / 8] & (1 << (idx % 8)) ? 1 : 0;
}

void setbit(unsigned char * bitmap, size_t idx)
{
    bitmap[idx / 8] |= (1 << (idx % 8));
}

void unsetbit(unsigned char * bitmap, size_t idx)
{
    bitmap[idx / 8] &= ~(1 << (idx % 8));
}

void togglebit(unsigned char * bitmap, size_t idx)
{
    bitmap[idx / 8] ^= (1 << (idx % 8));
}

Редактировать: Хорошо, я подумай Я понимаю, что ты хочешь сделать:Быстрая итерация по последовательности битов.Поэтому мы не хотим использовать функции произвольного доступа, описанные выше, а читаем сразу целое слово данных.

Вы можете использовать любой целочисленный тип без знака, который вам нравится, но вы должны выбрать тот, который, вероятно, будет соответствовать размеру слова вашей архитектуры.Я пойду с uint_fast32_t От stdint.h:

uint_fast32_t * data = __data_source__;
for(; __condition__; ++data)
{
    uint_fast32_t mask = 1;
    uint_fast32_t current = *data;
    for(; mask; mask <<= 1)
    {
        if(current & mask)
        {
            // bit is set
        }
        else
        {
            // bit is not set
        }
    }
}

Из внутреннего цикла вы можете установить бит с помощью

*data |= mask;

сбросьте бит с помощью

*data &= ~mask;

и переключите бит с помощью

*data ^= mask;

Предупреждение: Код может вести себя неожиданно на архитектурах с большим расширением!

Другие советы

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

Смотрите следующую ссылку для получения десятка материалов, связанных с bit: Немного Подкручивающие Хаки

Используйте таблицу, которая сопоставляет значение каждого байта (256) с числом единиц в нем.(Число 0 - это всего лишь (8 - число 1 -х)).Затем выполните итерацию по байтам и выполните один поиск для каждого байта вместо нескольких поисков и сравнений.Например:

int onesCount = 0;
for (i = 0; i < data->Count; i++)
{   
    byte = &data->Data[i];
    onesCount += NumOnes[byte];
}
Stats.FreqOf1 += onesCount;
Stats.FreqOf0 += (data->Count * 8) - onesCount;

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

static int bitcount_lookup[256] = { ..... } ; /* or make it a global and compute the values in code */

...

for( ... ) 
   byte = ... 
   Stats.FreqOf1 += bitcount_lookup[byte];

Вот метод, как подсчитать 1 бит 32-битного целого числа (основанный на Java Integer.bitCount(i) способ):

unsigned bitCount(unsigned i) {
    i = i - ((i >> 1) & 0x55555555);
    i = (i & 0x33333333) + ((i >> 2) & 0x33333333);
    i = (i + (i >> 4)) & 0x0f0f0f0f;
    i = i + (i >> 8);
    i = i + (i >> 16);
    return i & 0x3f;
}

Таким образом, вы можете преобразовать свои данные в int и продвигаться вперед с шагом в 4 байта.

Вот простой вариант, который я придумал всего для одного 32-битного значения, но вы можете видеть, что адаптировать его к любому количеству битов не составит труда....

int ones = 0;
int x = 0xdeadbeef;
for(int y = 0;y < 32;y++)
{
    if((x & 0x1) == 0x1) ones++;
    x = (x >> 1);
}

printf("%x contains %d ones and %d zeros.\n", x, ones, 32-ones);

Обратите внимание, однако, что это изменяет значение в процессе.Если вы делаете это с данными, которые вам нужно сохранить, то сначала вам нужно сделать их копию.

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

С каждым решением, которое вы рассматриваете, у каждого из них будут недостатки.Таблица подстановки или битовый сдвиг (как у меня), у обоих есть недостатки.

Ларри

ttobiass - Имейте в виду, что ваши встроенные функции важны в приложениях, о которых вы говорите, но есть вещи, которые вам нужно иметь в виду.Ты МОЖЕТ получите максимальную производительность от встроенного кода, просто запомните пару вещей.

  • встроенный в режиме отладки не существует.(Если вы не заставите это сделать силой)
  • компилятор будет встроять функции так, как он считает нужным.Часто, если вы прикажете ему встроить функцию, он может вообще этого не делать.Даже если вы используете __forceinline.Проверьте MSDN для получения дополнительной информации о встраивании.
  • Только определенные функции могут быть даже встроены.Например, вы не можете встроить рекурсивную функцию.

Вы получите максимальную производительность благодаря настройкам вашего проекта для языка C / C ++ и тому, как вы создаете свой код.На этом этапе важно понимать, что куча противОперации со стеком, соглашения о вызовах, выравнивание памяти и т.д.

Я знаю, что это не совсем ответ на ваш вопрос, но вы упомянули производительность и как добиться наилучшей производительности, и эти вещи являются ключевыми.

Чтобы присоединиться к link wagon:подсчет битов

Если это не случай преждевременной оптимизации и вам действительно нужно выжимать каждую последнюю фемтосекунду, то вам, вероятно, лучше использовать статический массив из 256 элементов, который вы заполняете один раз количеством битов каждого байтового значения, тогда

Статистика.FreqOf1 += количество битов[байт]

и когда цикл будет завершен:

Статистика.FreqOf0 = ((данные-> Количество * 8) - Статистика.FreqOf1)

В книге есть целая глава, посвященная различным техникам для этого Красивый Код.Вы можете прочитать (большую часть) его в Google books начиная с этого.

Более быстрый способ извлечения битов - это использовать:

bitmask= data->Data[i];

while (bitmask)
{
    bit_set_as_power_of_two= bitmask & -bitmask;
    bitmask&= bitmask - 1;
}

Если вы просто хотите посчитать установленные биты, вычисление LUT в кэше per было бы быстрым, но вы также можете делать это за постоянное время с помощью метода подсчета чередующихся битов в ссылка в этом ответе.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top