Я переносу векторизованный код, написанный с использованием встроенных функций SSE2, на встроенные функции AVX2.
К моему большому разочарованию, я обнаружил, что инструкции по смене _mm256_slli_si256 а также _mm256_srli_si256 работают только на две половины регистров AVX по отдельности, и между ними вводятся нули. (Это в отличие от _mm_slli_si128 а также _mm_srli_si128 которые обрабатывают целые регистры SSE.)
Можете ли вы порекомендовать мне короткую замену?
ОБНОВИТЬ:
_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
,
Из разных материалов я собрал эти решения. Ключом для пересечения межполосного барьера является инструкция выравнивания, _mm256_alignr_epi8
,
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)
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)
Вот функция сдвига бит влево в регистре 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;
}
//----------------------------------------------------------------------------
Если число сдвигов кратно 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
не решает проблему напрямую. Я указываю на это как на альтернативу, которая может работать в некоторых случаях, когда вы ищете полное смещение вектора.