2012-05-29 2 views
2

Я пытаюсь ускорить мой метод с помощью SSE (On Visual Studio). Я новичок в этом районе. Основными типами данных, с которыми я работаю в моем методе, являются биты размером 32 и логической операцией, которую я использую в основном, является операция И (с использованием _BitScanForward). Мне было интересно, можно ли использовать инструкции SSE для ускорения моих процедур.Реализация и выполнение использования битов с SSE

Это, как я делаю сейчас (я полностью сделан, и не могу сравнивать результаты напрямую):

загружает операнды (bitsets) с помощью _mm_set_ps. Я использую to_ulong() на bitsets, чтобы преобразовать их в неподписанные длинные целые числа:

__m128 v1 = _mm_set_ps(b1.to_ulong(),b2.to_ulong(),b3.to_ulong(),b4.to_ulong()); 
__m128 v2 = _mm_set1_ps(b.to_ulong()) 

Это следует собственно и операции:

__m128 v3 = _mm_and_ps(v1,v2); 

На данный момент, у меня есть два вопроса:

  1. Способ, которым я это делаю (преобразование битов в беззнаковые длинные целые числа с использованием to_ulong()) - хороший способ сделать это? Я подозреваю, что есть большие накладные расходы, которые могут убить потенциальное улучшение производительности, которое я могу извлечь из использования SSE.

  2. Каков наилучший способ сохранить v3 в памяти в виде 4 битов? Я планирую использовать встроенный _mm_storeu_ps.

ответ

3

Пара вещей:

  • если ваши битовые наборы в основном 32-битные Интс, то вы должны использовать подходящий целочисленный тип SIMD, т.е. __m128i, не с плавающей точкой (__m128)

  • _mm_set_XXX макросы относительно дороги - в отличие от обычных свойств SSE, они могут генерировать довольно много инструкций - если все, что вы делаете, это одна операция И, то любое преимущество производительности от _mm_and_XXX op чество будет более истреблена стоимости _mm_set_XXX операций

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

const int N = 1024; 

int32_t b1[N]; // 2 x arrays of input bit sets 
int32_t b2[N]; 
int32_t b3[N]; // 1 x array of output bit sets 

for (int i = 0; i < N; i += 4) 
{ 
    __m128i v1 = _mm_loadu_si128(&b1[i]); // load input bits sets 
    __m128i v2 = _mm_loadu_si128(&b2[i]); 
    __m128i v3 = _mm_and_si128(v1, v2); // do the bitwise AND 
    _mm_storeu_si128(&b3[i], v3);   // store the result 
} 

Если вы просто хотите и массив в месте с фиксированной маской, то это позволило бы упростить это:

const int N = 1024; 

int32_t b1[N]; // input/output array of bit sets 

const __m128i v2 = _mm_set1_epi32(0x12345678); // mask 

for (int i = 0; i < N; i += 4) 
{ 
    __m128i v1 = _mm_loadu_si128(&b1[i]); // load input bits sets 
    __m128i v3 = _mm_and_si128(v1, v2); // do the bitwise AND 
    _mm_storeu_si128(&b1[i], v3);   // store the result 
} 

Примечание: для повышения производительности убедитесь, что ваш вход/выход аг лучи выравниваются по 16 байт, а затем используют _mm_load_si128/_mm_store_si128, а не их равнозначные аналоги, как указано выше.

+1

Спасибо, Пол. Это прекрасно работает! – SMir

+0

Paul, _mm_set1_epi32 не работает с битами <32> экземпляров. Есть ли альтернатива, которая работает с реальными экземплярами битов? – SMir

+0

Я не эксперт на C++, но я ожидаю, что вы можете легко преобразовать битовый набор <32> в 32-битный int либо с использованием существующего метода, либо путем написания вспомогательной функции. –