2016-12-26 36 views
6

我正在实现SSE类型之间的转换,并且发现对于SSE4.1之前的目标实现int8-> int64扩展转换非常麻烦。如何高效地使用SSE执行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 
} 

但由于_mm_srai_epi64不存在,直到AVX-512,也有在这一点上两个选项:

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

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

回答

4

开箱内部函数在这里以一种有趣的方式使用。他们“复制”数据,而不是像预期的那样添加符号扩展。例如,在第一次迭代之前,你有你的寄存器以下

x x x x x x x x x x x x x x a b 

如果转换ab到16位,你应该得到这样的:

x x x x x x x x x x x x A a B b 

这里AB是先签后换ab的扩展名,即它们都是0或-1。

取而代之的是,你的代码给

x x x x x x x x x x x x a a b b 

然后你通过右移把它转换成正确的结果。

但是,您没有义务在“解包”内部函数中两次使用相同的操作数。你可以得到想要的结果,如果你 “解包” 下面的两个寄存器:

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 

即:

a = _mm_unpacklo_epi8(a, _mm_srai_epi8(a, 8)); 

(如果_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 

为了得到他们,右移32位数据:

_mm_srai_epi32(a, 24) 
_mm_srai_epi32(a, 32) 

所以最后的“解压”是

_mm_unpacklo_epi32(_mm_srai_epi32(a, 24), _mm_srai_epi32(a, 32)); 
2

使用SSSE3,您可以使用pshufb来避免大部分解包。使用阿纳托利的a/A符号:

;; input in xmm0    ;; x x x x x x x x | x x x x x x a b 
pshufb xmm0, [low_to_upper] ;; a 0 0 0 0 0 0 0 | b 0 0 0 0 0 0 0 
psrad xmm0, 24    ;; A A A a 0 0 0 0 | B B B b 0 0 0 0 
pshufb xmm0, [bcast_signextend]; A A A A A A A a | B B B B B B B b 

没有SSSE3,我想你也许可以做PSHUFLW,PSHUFD的东西,也许POR,而不是一些PUNPCK步骤。但是我想到的没有任何东西比拆包更好,除非你使用的是Core2或其他缓慢洗牌的CPU,其中pshuflwpunpcklbw快。

相关问题