Сдвиг 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 (и он не проверен). Изменить (как объяснил Пол Р. этот код не будет работать).

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?

Редактировать:

Пол Р. сообщил мне, что мой код AVX2 не будет работать и что код AVX, вероятно, не стоит этого. Вместо AVX2 я должен использовать _mm256_permutevar8x32_ps вместе с _mm256_and_ps, У меня нет системы с AVX2 (Haswell), так что это сложно проверить.

Редактировать: Основываясь на ответе Феликса Вайсса, я придумал несколько решений для 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);

2 ответа

Решение

Ваша реализация 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.

Вы можете сделать сдвиг прямо с _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, Сдвиг / поворот влево и более крупные сдвиги / вращения могут быть выполнены аналогичным образом путем изменения байтов управления перестановкой и смешиванием.

Другие вопросы по тегам