X86 SSE指令:字节+;短

X86 SSE指令:字节+;短,x86,sse,instructions,X86,Sse,Instructions,我有很长的字节数组,需要添加到类型为short(或int)的目标数组中。 是否存在此类SSE指令?或者它们的集合?您需要将每个8位值的向量解压为两个16位值的向量,然后将它们相加 __m128i v = _mm_set_epi8(15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0); __m128i vl = _mm_unpacklo_epi8(v, _mm_set1_epi8(0)); // vl = { 7, 6, 5, 4, 3, 2,

我有很长的字节数组,需要添加到类型为
short
(或
int
)的目标数组中。
是否存在此类SSE指令?或者它们的集合?

您需要将每个8位值的向量解压为两个16位值的向量,然后将它们相加

__m128i v = _mm_set_epi8(15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0);
__m128i vl = _mm_unpacklo_epi8(v, _mm_set1_epi8(0)); // vl = { 7, 6, 5, 4, 3, 2, 1, 0 }
__m128i vh = _mm_unpackhi_epi8(v, _mm_set1_epi8(0)); // vh = { 15, 14, 13, 12, 11, 10, 9, 8 }
其中,
v
是16 x 8位值的向量,
vl
vh
是8 x 16位值的两个未压缩向量

请注意,我假设8位值是无符号的,因此当解压缩到16位时,高字节设置为0(即无符号扩展)

如果你想对这些向量求和并得到一个32位的结果,那么一个有用的技巧就是使用乘法器为1的
\u mm\u madd\u epi16
,例如

__m128i vsuml = _mm_set1_epi32(0);
__m128i vsumh = _mm_set1_epi32(0);
__m128i vsum;
int sum;

for (int i = 0; i < N; i += 16)
{
    __m128i v = _mm_load_si128(&x[i]);
    __m128i vl = _mm_unpacklo_epi8(v, _mm_set1_epi8(0));
    __m128i vh = _mm_unpackhi_epi8(v, _mm_set1_epi8(0));
    vsuml = _mm_add_epi32(vsuml, _mm_madd_epi16(vl, _mm_set1_epi16(1)));
    vsumh = _mm_add_epi32(vsumh, _mm_madd_epi16(vh, _mm_set1_epi16(1)));
}
// do horizontal sum of 4 partial sums and store in scalar int
vsum = _mm_add_epi32(vsuml, vsumh);
vsum = _mm_add_epi32(vsum, _mm_srli_si128(vsum, 8));
vsum = _mm_add_epi32(vsum, _mm_srli_si128(vsum, 4));
sum = _mm_cvtsi128_si32(vsum);
\uuuum128i vsuml=\umm\uset1\uepi32(0);
__m128i vsumh=_mm_set1_epi32(0);
__m128i-vsum;
整数和;
对于(int i=0;i
如果需要对字节向量进行符号扩展而不是零扩展,请使用。与解包hi/lo指令不同,您只能从src寄存器的低位半/四分之一/八分之一执行pmovsx


不过,您可以直接从内存中创建pmovsx,尽管内部函数使其非常笨拙。由于在大多数CPU上,洗牌吞吐量比负载吞吐量更受限制,因此可能最好执行两次加载+pmovsx,而不是执行一次加载+三次洗牌。

请原谅我的无知,但您确定这是正确的吗?这个vsum=_mm_madd_epi16(vh,_mm_set1_epi16(1));“将删除vsum以前的值。@Alexandros:你说得对,我也看到其中至少还有一个错误-我想我写这个答案时一定很匆忙-我会很快修复代码,但我现在正在旅行。谢谢Paul,不要着急。”。你过去帮了我很多忙,所以任何时候你都可以把它修好。祝你旅途愉快!!事实上,这并不难修复-我现在无法测试它,但代码现在至少应该接近一个工作的解决方案。谢谢。我去看看