C 仅使用SSE2提取SSE洗牌32位值

C 仅使用SSE2提取SSE洗牌32位值,c,optimization,sse,C,Optimization,Sse,我试图以有效的方式从128位寄存器中提取4个字节。问题是每个值都是32位的速率{120,0,0,0,0,55,0,0,0,0,0,42,0,0,0,0120,0,0,0}。我想把128位转换成32位,形式是{120,55,42120} “原始”代码如下所示: __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*)&

我试图以有效的方式从128位寄存器中提取4个字节。问题是每个值都是32位的速率
{120,0,0,0,0,55,0,0,0,0,0,42,0,0,0,0120,0,0,0}
。我想把128位转换成32位,形式是
{120,55,42120}

“原始”代码如下所示:

__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];  
             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 }
我的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有更好的版本吗

您可以查看此操作和反向操作的一些解决方案

特别是在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填充,因为否则它们将在饱和打包过程中被钳制。因此,操作如下:

__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];  
             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 }
如果最初未使用
0
s填充不相关的字节,则必须事先将其屏蔽:

byte_result_vec = _mm_and_si128(byte_result_vec, _mm_set1_epi32(0x000000FF));
byte_result_vec = _mm_srli_epi32(byte_result_vec, 24);
或者,如果interresting字节最初是高字节,则必须事先将其转换为低字节:

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

或者,如果你真的想要
{D,C,B,A}
(我对你的问题不太清楚),那么这相当于在赋值中切换数组索引(或者预先在初始SSE寄存器上交替执行32位洗牌(
\u mm\u shuffle\u epi32
)。

这是一个完美的答案。我怎么能投两次票呢?:)这对我帮助很大。你知道使用SSE4的更好方法吗?@martins:对于SSSE3和更高版本,你只需要一个PSHUFB(这是你现有的代码应该编译成的)。@martins我对SSE>2不是很精通,也许我会尝试研究一下。