将 SSE/AVX 寄存器左移和右移 32 位,同时移入零

问题描述 投票:0回答:3

我想将 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 有更好的方法吗?

编辑:

Paul R 告诉我,我的 AVX2 代码将无法工作,并且 AVX 代码可能不值得。 对于 AVX2,我应该使用

_mm256_permutevar8x32_ps
_mm256_and_ps
。我没有带有 AVX2 (Haswell) 的系统,所以很难测试。

编辑: 根据 Felix Wyss 的回答,我提出了一些 AVX 解决方案,其中 shift1_AVX 和 shift2_AVX 只需要 3 个内在函数,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);
x86 sse simd avx avx2
3个回答
9
投票

您可以使用

_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
。 通过更改排列和混合控制字节,可以类似地完成向左移位/旋转和更大的移位/旋转。


6
投票

您的 SSE 实现很好,但我建议您对两个班次都使用

_mm_slli_si128
实现 - 强制转换使其看起来很复杂,但实际上可以归结为每个班次只有一条指令。

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

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

然而,一切并没有丢失:在 AVX 上
确实
跨通道工作的少数指令之一是

_mm256_permutevar8x32_ps。请注意,您需要结合使用 _mm256_and_ps 来将移位的元素归零。另请注意,这是一个 AVX2 解决方案 - AVX 本身对于基本算术/逻辑运算以外的任何操作都非常有限,因此我认为如果没有 AVX2,您将很难有效地完成此操作。


对于 SSE 实现,可以简单地使用

0
投票
,AVX2 会出现问题,因为它需要三个指令:

_mm256_permutevar8x32_epi32

 移动所需索引,
_mm256_set_epi32
 创建掩码,
_mm256_and_si256
 按位使用并将该掩码应用于前导零的向量。
示例:

x = _mm256_set_epi32(8,7,6,5,4,3,2,1); // x = [1,2,3,4,5,6,7,8] tmp = _mm256_permutevar8x32_epi32(tmp1, _mm256_set_epi32(5,4,3,2,1,0,0,0)); // shifting indexes 2 position right // tmp = [1,1,1,2,3,4,5,6] __m256i mask = _mm256_set_epi32(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000); // mask to make first 2 indexes zero result = _mm256_and_si256(tmp2, mask); // result = [0,0,1,2,3,4,5,6]


© www.soinside.com 2019 - 2024. All rights reserved.