2016-12-26 117 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快。

相關問題