我正在寻找一个SSE指令,它在__m128i
中接受四个32位整数的两个参数,计算相应对的总和,并将结果作为__m128i
中的两个64位整数返回。
有没有这方面的指示?
答案 0 :(得分:6)
没有携带的SSE操作。执行此操作的方法是首先使用全零辅助向量将32位整数(punpckldq
/ punpckhdq
)解压缩为4组64位整数,然后使用64位成对添加。
答案 1 :(得分:2)
SSE仅对byte-> word和word-> dword具有此功能。 (pmaddubsw
(SSSE3)和pmaddwd
(MMX / SSE2),垂直乘以v1 * v2,然后水平添加相邻对。)
我不清楚你想要的输出是什么。你有8个输入整数(两个向量为4)和2个输出整数(一个向量为2)。因为没有任何类型的32 + 32 - >> 64b向量加法,让我们看看如何将向量的低两个32b元素零扩展或符号扩展到64b。您可以将其组合到您需要的任何内容中,但请注意,没有添加水平对phaddq
,只有垂直paddq
。
phaddd
与您想要的类似,但没有扩展:结果的低半部分是第一个操作数中水平对的总和,高半部分是第二个操作数中水平对的总和。如果您需要所有这些结果,那么它几乎是值得使用的,并且您不会进一步将它们组合在一起。 (即它通常更快地进行洗牌和垂直添加而不是运行phadd
以在减少结束时水平地对矢量累加器求和。如果你要将所有内容归结为一个结果,做到正常的垂直和,直到你归入一个寄存器。)phaddd
可以在硬件中实现,与paddd
一样快(单周期延迟和吞吐量) ),但它不在任何AMD或Intel CPU中。
与Mysticial评论一样,SSE4.1 pmovzxdq
/ pmovsxdq
正是您所需要的,甚至可以在64b内存位置(包含两个32b整数)的加载中动态执行)。
SSE4.1与英特尔Penryn,第二代Core2(45纳米芯片缩芯2)一起推出,这是Nehalem之前的一代。回到比这更早的CPU上的非向量代码路径可能没问题,这取决于你对已经老旧的CPU的缓慢程度。
没有SSE4.1:
无符号零扩展很容易。就像pmdj回答一样,只需使用punpck*
lo和hi来解压缩零。
如果您的整数已签名,则您必须手动执行符号扩展。
没有psraq
,只有psrad
(打包右移算术双字)和psraw
。如果有,你可以自己解包然后算术右移32b。
相反,我们可能需要生成一个向量,其中每个元素都转换为其符号位。然后将其与解压缩的矢量混合(但pblendw
也是SSE4.1,因此我们必须使用por
)。
或者更好的是,使用符号掩码向量解压缩原始向量。
# input in xmm0
movdqa xmm1, xmm0
movdqa xmm2, xmm0
psrad xmm0, 31 ; xmm0 = all-ones or all-zeros depending on sign of input elements. xmm1=orig ; xmm2=orig
; xmm0 = signmask; xmm1=orig ; xmm2=orig
punpckldq xmm1, xmm0 ; xmm1 = sign-extend(lo64(orig))
punpckhdq xmm2, xmm0 ; xmm2 = sign-extend(hi64(orig))
对于Intel SnB或IvB上的两个结果,这应该以2周期延迟运行。 Haswell和后来只有一个shuffle端口(因此他们不能并行执行两个punpck
insn),因此xmm2将在那里延迟另一个周期。 Pre-SnB Intel CPU通常会使用向量指令对前端(解码器等)造成瓶颈,因为它们的平均值通常超过4B。
对于没有移动消除的CPU(在寄存器重命名阶段处理xmm0
指令),移动原始而不是复制缩短了生成mov
的依赖链,因此它们为零延迟。仅限英特尔,仅适用于IvB及更高版本。)使用3操作数AVX指令,您不需要movdqa
或第3个寄存器,但是您可以使用{{1}无论如何,对于low64。要对高64进行符号扩展,您可能vpmovsx
将高64字节移位到低64位。
或psrldq
或movhlps
使用较短的编码指令。 (或AVX2 punpckhqdq self,self
到256b reg,然后vpmovsx
高128,只用两条指令得到128b结果。)
与GP寄存器移位(例如vextracti128
)不同,向量移位使计数饱和而不是屏蔽。将原始符号位保留为LSB(移位31)而不是它的副本(移位32)也可以正常工作。它的优势在于,对于那些在看到sar eax, 31
时会担心的人,不需要对代码进行大量评论。