Эмуляция сдвигов на 32 байта с помощью AVX

Я переношу векторизованный код, написанный с использованием встроенных свойств SSE2, для встроенных AVX2.

К моему разочарованию, я обнаружил, что инструкции сдвига _mm256_slli_si256 и _mm256_srli_si256 работают только по двум половинам регистров AVX отдельно, а нули вводятся между ними. (Это в отличие от _mm_slli_si128 и _mm_srli_si128, которые обрабатывают все регистры SSE.)

Можете ли вы порекомендовать мне короткую замену?

UPDATE:

_mm256_slli_si256 достигается с помощью

_mm256_alignr_epi8(A, _mm256_permute2x128_si256(A, A, _MM_SHUFFLE(0, 0, 3, 0)), N)

или

_mm256_slli_si256(_mm256_permute2x128_si256(A, A, _MM_SHUFFLE(0, 0, 3, 0)), N)

для сдвигов более 16 байт.

Но вопрос остается за _mm256_srli_si256.

Ответ 1

С разных входов я собрал эти решения. Ключом к пересечению межполосного барьера является команда align, _mm256_alignr_epi8.

_mm256_slli_si256 (A, N)

0 < N < 16

_mm256_alignr_epi8(A, _mm256_permute2x128_si256(A, A, _MM_SHUFFLE(0, 0, 2, 0)), 16 - N)

N = 16

_mm256_permute2x128_si256(A, A, _MM_SHUFFLE(0, 0, 2, 0))

16 < N < 32

_mm256_slli_si256(_mm256_permute2x128_si256(A, A, _MM_SHUFFLE(0, 0, 2, 0)), N - 16)

_mm256_srli_si256 (A, N)

0 < N < 16

_mm256_alignr_epi8(_mm256_permute2x128_si256(A, A, _MM_SHUFFLE(2, 0, 0, 1)), A, N)

N = 16

_mm256_permute2x128_si256(A, A, _MM_SHUFFLE(2, 0, 0, 1))

16 < N < 32

_mm256_srli_si256(_mm256_permute2x128_si256(A, A, _MM_SHUFFLE(2, 0, 0, 1)), N - 16)

Ответ 2

Вот функция битового сдвига слева от регистра ymm с использованием avx2. Я использую его для сдвига влево на один, хотя похоже, что он работает до 63 бит сдвигов.

//----------------------------------------------------------------------------
// bit shift left a 256-bit value using ymm registers
//          __m256i *data - data to shift
//          int count     - number of bits to shift
// return:  __m256i       - carry out bit(s)

static __m256i bitShiftLeft256ymm (__m256i *data, int count)
   {
   __m256i innerCarry, carryOut, rotate;

   innerCarry = _mm256_srli_epi64 (*data, 64 - count);                        // carry outs in bit 0 of each qword
   rotate     = _mm256_permute4x64_epi64 (innerCarry, 0x93);                  // rotate ymm left 64 bits
   innerCarry = _mm256_blend_epi32 (_mm256_setzero_si256 (), rotate, 0xFC);   // clear lower qword
   *data      = _mm256_slli_epi64 (*data, count);                             // shift all qwords left
   *data      = _mm256_or_si256 (*data, innerCarry);                          // propagate carrys from low qwords
   carryOut   = _mm256_xor_si256 (innerCarry, rotate);                        // clear all except lower qword
   return carryOut;
   }

//----------------------------------------------------------------------------

Ответ 3

Если количество сдвигов кратно 4 байтам, vpermd (_mm256_permutevar8x32_epi32) с правой маской перетасовки будет делать трюк с одной инструкцией (или больше, если вам действительно нужно обнулить сдвинутые байты вместо копирования другого элемента над ними). ​​

Чтобы поддерживать подсчет сдвига переменной (множественный-4B), вы можете загрузить маску управления из окна в массив 0 0 0 0 0 0 0 1 2 3 4 5 6 7 0 0 0 0 0 0 0 или что-то еще, за исключением того, что 0 является только нижним элементом и не выполняет нулевые вещи. Подробнее об этой идее создания маски из скользящего окна см. мой ответ по другому вопросу.

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