Смещение SSE/AVX регистрирует 32 бита влево и вправо при сдвиге в нулях
Я хочу переместить регистры SSE/AVX кратным 32 битам влево или вправо при сдвиге в нулях.
Позвольте мне уточнить изменения, которые меня интересуют. Для SSE я хочу сделать следующие смены четырех 32-битных поплавков:
shift1_SSE: [1, 2, 3, 4] -> [0, 1, 2, 3]
shift2_SSE: [1, 2, 3, 4] -> [0, 0, 1, 2]
Для AVX я хочу сдвинуть следующие сдвиги:
shift1_AVX: [1, 2, 3, 4, 5, 6, 7, 8] -> [0, 1, 2, 3, 4, 5, 6, 7]
shift2_AVX: [1, 2, 3, 4, 5, 6, 7, 8] -> [0, 0, 1, 2, 3, 4, 5, 6]
shift3_AVX: [1, 2, 3, 4 ,5 ,6, 7, 8] -> [0, 0, 0, 0, 1, 2, 3, 4]
Для SSE я придумал следующий код
shift1_SSE = _mm_castsi128_ps(_mm_slli_si128(_mm_castps_si128(x), 4));
shift2_SSE = _mm_shuffle_ps(_mm_setzero_ps(), x, 0x40);
//shift2_SSE = _mm_castsi128_ps(_mm_slli_si128(_mm_castps_si128(x), 8));
Есть ли лучший способ сделать это с помощью SSE?
Для AVX я придумал следующий код, который нуждается в AVX2 (и он не тестировался). Изменить (как объяснил Paul R, этот код не будет работать).
shift1_AVX2 =_mm256_castsi256_ps(_mm256_slli_si256(_mm256_castps_si256(x), 4)));
shift2_AVX2 =_mm256_castsi256_ps(_mm256_slli_si256(_mm256_castps_si256(x), 8)));
shift3_AVX2 =_mm256_castsi256_ps(_mm256_slli_si256(_mm256_castps_si256(x), 12)));
Как я могу сделать это лучше всего с AVX не AVX2 (например, с помощью _mm256_permute
или _mm256_shuffle`)? Есть ли лучший способ сделать это с помощью AVX2?
Edit:
Пол R сообщил мне, что мой код AVX2 не будет работать и что код AVX, вероятно, не стоит. Вместо этого для AVX2 я должен использовать _mm256_permutevar8x32_ps
вместе с _mm256_and_ps
. У меня нет системы с AVX2 (Haswell), поэтому это трудно проверить.
Изменить:
На основе ответа Felix Wyss я придумал некоторые решения для AVX, для которых требуется только 3 интригники для shift1_AVX и shift2_AVX и только одна встроенная для shift3_AVX. Это связано с тем, что _mm256_permutef128Ps
имеет функцию обнуления.
shift1_AVX
__m256 t0 = _mm256_permute_ps(x, _MM_SHUFFLE(2, 1, 0, 3));
__m256 t1 = _mm256_permute2f128_ps(t0, t0, 41);
__m256 y = _mm256_blend_ps(t0, t1, 0x11);
shift2_AVX
__m256 t0 = _mm256_permute_ps(x, _MM_SHUFFLE(1, 0, 3, 2));
__m256 t1 = _mm256_permute2f128_ps(t0, t0, 41);
__m256 y = _mm256_blend_ps(t0, t1, 0x33);
shift3_AVX
x = _mm256_permute2f128_ps(x, x, 41);
Ответы
Ответ 1
Ваша реализация SSE прекрасна, но я предлагаю вам использовать реализацию _mm_slli_si128
для обеих сдвигов - приведения делают ее сложной, но она действительно сводится к одной команде для каждой смены.
Реализация AVX2 не будет работать, к сожалению. Практически все инструкции AVX - это фактически две инструкции SSE, которые работают параллельно на двух соседних 128-битных дорожках. Итак, для первого примера shift_AVX2 вы получите:
0, 0, 1, 2, 0, 4, 5, 6
----------- ----------
LS lane MS lane
Все не потеряно: одна из немногих инструкций, которые работают на дорогах на AVX, _ mm256_permutevar8x32_ps. Обратите внимание, что вам нужно использовать _mm256_and_ps
в сочетании с этим, чтобы обнулить смещенные элементы. Также обратите внимание, что это AVX2-решение - AVX сам по себе очень ограничен для чего-либо другого, кроме основных арифметических/логических операций, поэтому я думаю, что вам будет трудно это делать эффективно без AVX2.
Ответ 2
Вы можете сделать сдвиг вправо с помощью _mm256_permute_ps
, _mm256_permute2f128_ps
и _mm256_blend_ps
следующим образом:
__m256 t0 = _mm256_permute_ps(x, 0x39); // [x4 x7 x6 x5 x0 x3 x2 x1]
__m256 t1 = _mm256_permute2f128_ps(t0, t0, 0x81); // [ 0 0 0 0 x4 x7 x6 x5]
__m256 y = _mm256_blend_ps(t0, t1, 0x88); // [ 0 x7 x6 x5 x4 x3 x2 x1]
Результат находится в y
. Чтобы сделать поворот вправо, установите маску перестановок на 0x01
вместо 0x81
. Сдвиг/поворот влево и влево, а также большие сдвиги/поворота можно сделать аналогичным образом, изменив байты управления перестановкой и смешением.