X86 将SSE/AVX寄存器向左和向右移位32位,同时进行零移位

X86 将SSE/AVX寄存器向左和向右移位32位,同时进行零移位,x86,sse,simd,avx,avx2,X86,Sse,Simd,Avx,Avx2,我想将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]

我想将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));
有没有更好的方法与苏格兰和南方能源公司合作

对于AVX,我提出了以下需要AVX2的代码(它还没有经过测试)。编辑(如Paul R所解释的,此代码不起作用)

如果使用AVX而不是AVX2(例如使用
\u mm256\u permute
或\u mm256\u shuffle`),我如何才能做到最好?使用AVX2有更好的方法吗?

编辑:

Paul R告诉我,我的AVX2代码不起作用,AVX代码可能不值得。对于AVX2,我应该使用
\u mm256\u permutevar8x32\u ps
以及
\u mm256\u和\u ps
。我没有一个带有AVX2(Haswell)的系统,所以这很难测试

编辑: 根据Felix Wyss的回答,我为AVX提出了一些解决方案,其中shift1_AVX和shift2_AVX只需要3个内部电路,shift3_AVX只需要一个内部电路。这是因为
\u mm256\u permutef128Ps
具有

移位

__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);
移位2_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);
移位

x = _mm256_permute2f128_ps(x, x, 41);

您的SSE实现很好,但我建议您对两个移位都使用
\u mm\u slli\u si128
实现-强制转换使它看起来很复杂,但实际上每个移位只能使用一条指令

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

0, 0, 1, 2, 0, 4, 5, 6
----------- ----------
 LS lane     MS lane

然而,一切都没有丢失:在AVX上跨车道工作的为数不多的指令之一是。请注意,您需要结合使用
\u mm256\u和_ps
将移位的元素归零。还要注意的是,这是一个AVX2解决方案-AVX本身对于基本算术/逻辑运算以外的任何操作都非常有限,因此我认为如果没有AVX2,您将很难有效地执行此操作。

您可以使用
\u mm256\u permute\u ps
\u mm256\u permute2f128\u ps
,,和
\u mm256\u blend\u 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
。通过更改排列和混合控制字节,可以类似地执行向左移位/旋转和更大的移位/旋转

我如何使用mm_slli_si128而不使用内部类型转换?当我尝试它时,它会说没有合适的转换来将_m128转换为_m128i,反之亦然。强制转换只是为了让编译器满意(我猜是MSVC?),它们实际上不会生成任何代码。因此,您的代码很好,我只是说对两个移位都使用
\u mm\u slli\u si128
实现,而不是对第二个移位使用
\u mm\u shuffle\u ps
替代方案。您在32位模式下只有8个SSE寄存器,在64位模式下只有16个寄存器。编译器可以在寄存器中保存的临时变量越多,性能就可能越好。如果您的代码需要太多的寄存器,那么编译器必须将寄存器“溢出”到内存中。因此,当您有两个备选解决方案,其中一个需要更少的临时寄存器时,如果没有其他因素需要考虑,那么这就是应该采用的解决方案。我最终对代码进行了基准测试,SSE和AVX代码的速度大约是顺序代码的两倍!我没有料到。在我的4芯常春藤桥系统上,我的整体提升大约是7倍。我把答案中的代码贴在了是的,那得等到我找到哈斯韦尔系统。我对代码进行了测试。我不知道这是什么系统。它没有AVX,因为我必须删除AVX代码才能运行。线程的数量是4,但我认为它只有两个核心,因为OpenMP的结果并不令人印象深刻。在任何情况下,该系统的增益都超过3倍,而在我的系统上则超过7倍。不要担心错误。这是由于浮点精度。我把计数的数字相加,并与精确的公式进行比较。这比我预期的要多。对于SSE,它只能使用一条指令/内部指令(
\u mm\u slli\u si128
)完成。我想用AVX2我可以用两个内部函数
\u mm256\u permute2f128\u ps
\u mm256\u和\u ps
来实现。我刚刚意识到使用blend有一个更简单的解决方案。我编辑了答案。这是一个更好的解决方案。我误解了,虽然这是AVX2代码。这是AVX代码。我认为使用AVX可以在两个指令中完成
shift3\u AVX
。我使用您的解决方案编辑了我的问题。你的解决方案让他们走错了方向,但想法是正确的。非常感谢。我想出了一个方法,在一个固有的时间内完成移位3_AVX<代码>\u mm256\u permute2f128\u ps具有[调零选项]()。所以移位3\u AVX=
\u mm256\u permute2f128\u ps(x,x,41)
__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]