2012-11-20 51 views
6

我試圖以有效的方式從128位寄存器中提取4個字節。問題是每個值都是一個32bit的{120,0,0,0,55,0,0,0,42,0,0,0,120,0,0,0}。我想將128位轉換爲32位,格式爲{120,55,42,120}只用SSE2提取SSE混洗32位值

「原始」 的代碼如下所示:

__m128i byte_result_vec={120,0,0,0,55,0,0,0,42,0,0,0,120,0,0,0}; 
unsigned char * byte_result_array=(unsigned char*)&byte_result_vec; 
result_array[x]=byte_result_array[0]; 
result_array[x+1]=byte_result_array[4]; 
result_array[x+2]=byte_result_array[8]; 
result_array[x+3]=byte_result_array[12]; 

我SSSE3代碼:

unsigned int * byte_result_array=...; 
__m128i byte_result_vec={120,0,0,0,55,0,0,0,42,0,0,0,120,0,0,0}; 
const __m128i eight_bit_shuffle_mask=_mm_set_epi8(1,1,1,1,1,1,1,1,1,1,1,1,0,4,8,12);  
byte_result_vec=_mm_shuffle_epi8(byte_result_vec,eight_bit_shuffle_mask); 
unsigned int * byte_result_array=(unsigned int*)&byte_result_vec; 
result_array[x]=byte_result_array[0]; 

我怎樣才能做到這一點有效地SSE2。 SSSE3或SSE4有更好的版本嗎?

回答

9

您可以查看a previous answer of mine以獲取針對此操作和相反操作的一些解決方案。

尤其是在SSE2,你可以先打包的32位整數爲16位有符號整數和飽和做到這一點:

byte_result_vec = _mm_packs_epi32(byte_result_vec, byte_result_vec); 

然後我們包中的16位值轉換爲無符號的8位值使用無符號飽和:

byte_result_vec = _mm_packus_epi16(byte_result_vec, byte_result_vec); 

然後,我們可以採取終於從寄存器的低32位我們的價值觀:

int int_result = _mm_cvtsi128_si32(byte_result_vec); 
unsigned char* byte_result_array = (unsigned char*)&int_result; 
result_array[x] = byte_result_array[0]; 
result_array[x+1] = byte_result_array[1]; 
result_array[x+2] = byte_result_array[2]; 
result_array[x+3] = byte_result_array[3]; 

編輯:上面假定8位字開始在其各自的32位字的低字節中,其餘填充0 s,因爲否則它們將在飽和打包過程中被鉗位。因此,操作如下:

   byte 15        0 
        0 0 0 D 0 0 0 C 0 0 0 B 0 0 0 A 

_mm_packs_epi32 -> 0 D 0 C 0 B 0 A 0 D 0 C 0 B 0 A 

_mm_packus_epi16 -> D C B A D C B A D C B A D C B A 
               ^^^^^^^ 

_mm_cvtsi128_si32 -> int DCBA, laid out in x86 memory as bytes A B C D 

-> reinterpreted as unsigned char array { A, B, C, D } 

如果uninterresting字節沒有充滿0小號開始,你要掩蓋他們離開事先:

byte_result_vec = _mm_and_si128(byte_result_vec, _mm_set1_epi32(0x000000FF)); 

或者如果interresting字節是最初在高字節,你必須將它們轉移到低字節事先:

byte_result_vec = _mm_srli_epi32(byte_result_vec, 24); 

或者,如果你真的想{ D, C, B, A }(不併發症從你的問題中清楚地知道),那麼這相當於只是在分配中切換陣列索引(或者預先在初始SSE寄存器中交替執行32位隨機播放(_mm_shuffle_epi32))。

+0

這是一個完美的答案。我怎樣才能使它兩次上升? :)這幫了我很多。你碰巧知道SSE4更好的方法嗎? –

+0

@martins:對於SSSE3和更高版本,您只需要一個PSHUFB(這是您現有的代碼應該編譯的內容)。 –

+0

@martins我對SSE> 2不太熟悉,也許我會試着去研究它。 –