向左和向右移位SSE/AVX寄存器,同时以零移位

Z b*_*son 8 x86 sse simd avx avx2

我希望左移或右移32位的SSE/AVX寄存器,同时移位为零.

让我对我感兴趣的转变更加准确.对于SSE,我想做四个32位浮点数的以下转换:

shift1_SSE: [1, 2, 3, 4] -> [0, 1, 2, 3]
shift2_SSE: [1, 2, 3, 4] -> [0, 0, 1, 2]
Run Code Online (Sandbox Code Playgroud)

对于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]
Run Code Online (Sandbox Code Playgroud)

对于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));
Run Code Online (Sandbox Code Playgroud)

使用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))); 
Run Code Online (Sandbox Code Playgroud)

如何使用AVX而不是AVX2(例如使用_mm256_permute或_mm256_shuffle`)做到最好?AVX2有更好的方法吗?

编辑:

Paul 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);
Run Code Online (Sandbox Code Playgroud)

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);
Run Code Online (Sandbox Code Playgroud)

shift3_AVX

x = _mm256_permute2f128_ps(x, x, 41);
Run Code Online (Sandbox Code Playgroud)

Fel*_*yss 6

你可以做一个右移带_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]
Run Code Online (Sandbox Code Playgroud)

结果是y.要向右旋转,请将置换遮罩设置为0x01而不是0x81.通过更改置换和混合控制字节,可以类似地完成左移和左移和更大的移位/旋转.

  • 我想出了一种在一个内在中做shift3_AVX的方法.`_mm256_permute2f128_ps`有[归零选项](http://software.intel.com/sites/products/documentation/doclib/iss/2013/compiler/cpp-lin/GUID-B45DFF8A-A71A-4DDB-B77C-BF48A17AFCCE. HTM).所以shift3_AVX =`_ mm256_permute2f128_ps(x,x,41);` (2认同)

Pau*_*l R 5

你的SSE实现很好,但我建议你使用_mm_slli_si128两种转换的实现 - 转换使它看起来很复杂,但它实际上归结为每个班次只有一条指令.

不幸的是,您的AVX2实现将无法正常工作.几乎所有AVX指令实际上只是在两个相邻的128位通道上并行运行的两条SSE指令.所以对于你的第一个shift_AVX2例子你会得到:

0, 0, 1, 2, 0, 4, 5, 6
----------- ----------
 LS lane     MS lane
Run Code Online (Sandbox Code Playgroud)

所有不丢失但是:其中的几条指令一个跨越车道上工作AVX是_mm256_permutevar8x32_ps.请注意,您需要_mm256_and_ps与此结合使用以将移位的元素归零.另请注意,这是一个AVX2解决方案 - 除了基本的算术/逻辑运算之外,AVX本身非常有限,所以我认为如果没有AVX2,你将很难有效地完成这项工作.