如何使用SSE高效执行int8/int64转换?

pla*_*cel 6 c++ x86 sse simd intrinsics

我正在实现SSE类型之间的转换,我发现为SSE4.1之前的目标实现int8-> int64扩展转换非常麻烦.

简单的实施将是:

inline __m128i convert_i8_i64(__m128i a)
{
#ifdef __SSE4_1__
    return _mm_cvtepi8_epi64(a);
#else
    a = _mm_unpacklo_epi8(a, a);
    a = _mm_unpacklo_epi16(a, a);
    a = _mm_unpacklo_epi32(a, a);
    return _mm_srai_epi64(a, 56); // missing instrinsic!
#endif
}
Run Code Online (Sandbox Code Playgroud)

但是因为_mm_srai_epi64在AVX-512之前不存在,所以此时有两种选择:

  • 实施_mm_srai_epi64,或
  • convert_i8_i64以不同的方式实施.

我不确定哪一个是最有效的解决方案.任何的想法?

ana*_*lyg 4

这里以一种有趣的方式使用了拆包内在函数。他们“复制”数据,而不是像人们所期望的那样添加符号扩展。例如,在第一次迭代之前,您的寄存器中有以下内容

x x x x x x x x x x x x x x a b
Run Code Online (Sandbox Code Playgroud)

如果将a和转换b为 16 位,您应该得到:

x x x x x x x x x x x x A a B b
Run Code Online (Sandbox Code Playgroud)

这里A和是和B的符号扩展,即它们都不是 0 就是 -1。ab

相反,您的代码给出了

x x x x x x x x x x x x a a b b
Run Code Online (Sandbox Code Playgroud)

然后通过右移将其转换为正确的结果。

但是,您不必在“解包”内在函数中两次使用相同的操作数。如果您“解压”以下两个寄存器,您可以获得所需的结果:

x x x x x x x x x x x x x x a b
x x x x x x x x x x x x x x A B
Run Code Online (Sandbox Code Playgroud)

那是:

a = _mm_unpacklo_epi8(a, _mm_srai_epi8(a, 8));
Run Code Online (Sandbox Code Playgroud)

(如果该_mm_srai_epi8内在因素确实存在)


您可以将相同的想法应用于转换的最后阶段。您想要“解压”以下两个寄存器:

x x x x x x x x A A A a B B B b
x x x x x x x x A A A A B B B B
Run Code Online (Sandbox Code Playgroud)

要获得它们,请将 32 位数据右移:

_mm_srai_epi32(a, 24)
_mm_srai_epi32(a, 32)
Run Code Online (Sandbox Code Playgroud)

所以最后的“解压”是

_mm_unpacklo_epi32(_mm_srai_epi32(a, 24), _mm_srai_epi32(a, 32));
Run Code Online (Sandbox Code Playgroud)