оператор if/else в SSE intrinsics

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

мой исходный код:

unsigned long c;
unsigned long constant = 0x12345678;
unsigned long table[256];
int n, k;

for( n = 0; n < 256; n++ )
{
  c = n;
  for( k = 0; k < 8; k++ )
    {
      if( c & 1 ) c = constant ^ (c >> 1);
      else c >>= 1;
    }
  table[n] = c;
}

цель этого кода-вычислить таблицу crc (константа может быть любым полиномом, она здесь не играет роли),

Я полагаю, что мой оптимизированный код будет что-то вроде:

__m128 x;
__m128 y;
__m128 *table;

x = _mm_set_ps(3, 2, 1, 0);
y = _mm_set_ps(3, 2, 1, 0);
//offset for incrementation
offset = _mm_set1_ps(4);

for( n = 0; n < 64; n++ )
{
    y = x;
    for( k = 0; k < 8; k++ )
    {
        //if do something with y
        //else do something with y
    }
    table[n] = y;
    x = _mm_add_epi32 (x, offset);
}

Я понятия не имею, как пройти через if-else заявление, но я подозреваю, что это хитрый трюк. Кто-нибудь знает, как это сделать?

(помимо этого, моя оптимизация, вероятно, довольно плохая - любые советы или исправления по ней будут рассматриваться с наибольшей симпатией)

3 ответов


вы можете полностью избавиться от if/else. Еще в те дни, когда я создавал сборочный код MMX, это была обычная деятельность по программированию. Позвольте мне начать с серии преобразований на" ложном " утверждении:

c >>= 1;

c = c >> 1;

c = 0 ^ (c >> 1);

Почему я представил эксклюзив-или? Потому что exclusive-или также находится в" истинном " утверждении:

c = constant ^ (c >> 1);

обратите внимание на сходство? В "истинной" части мы xor с константой, а в ложной части мы xor с нулем.

теперь я собираюсь показать вам ряд преобразований по всей инструкции if/else:

if (c & 1)
    c = constant ^ (c >> 1);          // same as before
else
    c =        0 ^ (c >> 1);          // just different layout

if (c & 1)
    c =  constant      ^ (c >> 1);
else
    c = (constant & 0) ^ (c >> 1);    // 0 == x & 0

if (c & 1)
    c = (constant & -1) ^ (c >> 1);   // x == x & -1
else
    c = (constant &  0) ^ (c >> 1);

теперь две ветви отличаются только во втором аргументе от двоичного-и, который может быть вычислен тривиально из самого условия, что позволяет нам избавиться от if / else:

c = (constant & -(c & 1)) ^ (c >> 1);

отказ от ответственности: это решение работает только на дополнение архитектура, где -1 означает "все биты".


идея в SSE построить оба результата, а затем смешайте результаты.

Е. Г. :

__m128i mask = ...; // some way to build mask[n] = 0x1
__m128i constant = ...;

__m128i tmp_c = _mm_xor_si128( _mm_srli_epis32( c, 1 ), constant );
__m128i tmp_c2 = _mm_srli_epis32( c, 1 );

__m128i v = _mm_cmpeq_epi32( c, mask );
tmp_c = _mm_and_epi32( tmp_c, mask );
tmp_c2 = _mm_andnot_si128( mask, tmp_c2 );
c = _mm_or_si128( tmp_c, tmp_c2 );
// or in sse4_1
c = _mm_blendv_epi8( tmp_c, tmp_c2, mask );

Примечание рядом, это не полный код, только для демонстрации принципа.


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