SSE:shuffle (permutevar) 4x32 整数

标签 sse simd intrinsics avx

我有一些使用 AVX2 内在的代码 _mm256_permutevar8x32_epi32 又名 vpermd通过索引向量从输入向量中选择整数。现在我需要同样的东西,但需要 4x32 而不是 8x32。 _mm_permutevar_ps是为浮点数做的,但我使用的是整数。

一个想法是 _mm_shuffle_epi32 ,但我首先需要将我的 4x32 索引值转换为单个整数,即:

imm[1:0] := idx[31:0]
imm[3:2] := idx[63:32]
imm[5:4] := idx[95:64]
imm[7:6] := idx[127:96]

我不确定什么是最好的方法,而且我不确定这是最好的方法。我正在寻找 Broadwell/Haswell 上最有效的方法来模拟“失踪”_mm_permutevar_epi32(__m128i a, __m128i idx) .如果可能的话,我宁愿使用 128 位指令而不是 256 位指令(即我不想扩大 128 位输入然后缩小结果)。

最佳答案

尽管 Peter Cordes 说 AVX 指令 vpermilps 是正确的及其内在 _mm_permutevar_ps()如果您使用的是比 Sandy Bridge 更旧的机器(使用 pshufb 的 SSE4.1 变体),则可能会完成这项工作。效果也很好。

AVX 变体

归功于@PeterCordes

#include <stdio.h>
#include <immintrin.h>


__m128i vperm(__m128i a, __m128i idx){
    return _mm_castps_si128(_mm_permutevar_ps(_mm_castsi128_ps(a), idx));
}


int main(int argc, char* argv[]){
    __m128i a   = _mm_set_epi32(0xDEAD, 0xBEEF, 0xCAFE, 0x0000);
    __m128i idx = _mm_set_epi32(1,0,3,2);
    __m128i shu = vperm(a, idx);
    printf("%04x %04x %04x %04x\n", ((unsigned*)(&shu))[3],
                                    ((unsigned*)(&shu))[2],
                                    ((unsigned*)(&shu))[1],
                                    ((unsigned*)(&shu))[0]);
    return 0;
}

SSE4.1 变体
#include <stdio.h>
#include <immintrin.h>


__m128i vperm(__m128i a, __m128i idx){
    idx = _mm_and_si128  (idx, _mm_set1_epi32(0x00000003));
    idx = _mm_mullo_epi32(idx, _mm_set1_epi32(0x04040404));
    idx = _mm_or_si128   (idx, _mm_set1_epi32(0x03020100));
    return _mm_shuffle_epi8(a, idx);
}


int main(int argc, char* argv[]){
    __m128i a   = _mm_set_epi32(0xDEAD, 0xBEEF, 0xCAFE, 0x0000);
    __m128i idx = _mm_set_epi32(1,0,3,2);
    __m128i shu = vperm(a, idx);
    printf("%04x %04x %04x %04x\n", ((unsigned*)(&shu))[3],
                                    ((unsigned*)(&shu))[2],
                                    ((unsigned*)(&shu))[1],
                                    ((unsigned*)(&shu))[0]);
    return 0;
}

这编译成清脆的
0000000000400550 <vperm>:
  400550:       c5 f1 db 0d b8 00 00 00         vpand  0xb8(%rip),%xmm1,%xmm1        # 400610 <_IO_stdin_used+0x20>
  400558:       c4 e2 71 40 0d bf 00 00 00      vpmulld 0xbf(%rip),%xmm1,%xmm1        # 400620 <_IO_stdin_used+0x30>
  400561:       c5 f1 eb 0d c7 00 00 00         vpor   0xc7(%rip),%xmm1,%xmm1        # 400630 <_IO_stdin_used+0x40>
  400569:       c4 e2 79 00 c1                  vpshufb %xmm1,%xmm0,%xmm0
  40056e:       c3                              retq

如果您可以保证控制索引始终是 32 位整数 0、1、2 或 3,则 AND 掩码是可选的。

关于SSE:shuffle (permutevar) 4x32 整数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56033329/

相关文章:

c - 用于整个 256 位寄存器的 AVX unpackhipd/unpacklopd 模拟

c++ - 如何使用没有运行时库的 VC++ 内部函数

c++ - SIMD 内部函数和指针

c - 与 SSE 的并行前缀(累积)总和

c++ - 应该为每个操作传递或创建 SSE 数据类型吗?

x86 - 将8个16位SSE寄存器转换成8位数据

x86 - AVX 和 AVX2 的区别

c++ - 如何加快积分图像的计算?

c# - 使用 C# Vector<T> SIMD 查找匹配元素的索引

c++ - 优化位 vector 检查是否是另一个位 vector 的真子集?