【发布时间】:2015-11-12 00:41:07
【问题描述】:
我正在寻找一条 SSE 指令,它在 __m128i 中接受四个 32 位整数的两个参数,计算相应对的总和并将结果作为 __m128i 中的两个 64 位整数返回。
有这方面的说明吗?
【问题讨论】:
-
为什么要这样做?我明白你为什么想要 64b+64b+carry 而不是 32b+32b+carry。
我正在寻找一条 SSE 指令,它在 __m128i 中接受四个 32 位整数的两个参数,计算相应对的总和并将结果作为 __m128i 中的两个 64 位整数返回。
有这方面的说明吗?
【问题讨论】:
没有带有进位的 SSE 操作。这样做的方法是首先使用全零辅助向量将 32 位整数 (punpckldq/punpckhdq) 解压缩为 4 组 64 位整数,然后使用 64 位成对加法。 /p>
【讨论】:
pmovsx 实际上很多更容易和更快。它并没有我一开始想的那么大,因为我在写我的解包问题的答案时有一个很好的主意,而不是解包并然后混合一个标志掩码。但是pmovsx 如果你从内存中加载是非常好的,否则你必须努力让上半部分转移到准备符号扩展它。
SSE 仅对 byte->word 和 word->dword 具有此功能。 (pmaddubsw (SSSE3) 和 pmaddwd (MMX/SSE2),垂直相乘 v1 * v2,然后水平相加相邻对。)
我不清楚您希望输出是什么。您有 8 个输入整数(两个 4 向量)和 2 个输出整数(一个两个向量)。由于没有任何类型的 32+32 -> 64b 向量加法的 insn,让我们看看如何将向量的低两个 32b 元素零扩展或符号扩展为 64b。你可以将它组合成你需要的任何东西,但请记住,没有添加水平对phaddq,只有垂直paddq。
phaddd 与您想要的类似,但没有加宽:结果的下半部分是第一个操作数中水平对的总和,高半部分是第二个操作数中水平对的总和。如果您需要所有这些结果,并且您不会进一步组合它们,那么它几乎只值得使用。 (即,通常更快地随机和垂直添加,而不是运行phadd 在减少结束时对向量累加器进行水平求和。如果您要将所有内容求和为一个结果,请进行正常的垂直求和,直到您重新减少到一个寄存器。)phaddd 可以在硬件中实现与paddd 一样快(单周期延迟和吞吐量),但它不在任何 AMD 或 Intel CPU 中。
就像 Mysticial 评论的那样,SSE4.1 pmovzxdq / pmovsxdq 正是您所需要的,甚至可以作为从 64b 内存位置(包含两个 32b 整数)加载的一部分即时执行。
SSE4.1 是在 Nehalem 之前的一代 Intel Penryn 中引入的,第二代 Core2(45nm 芯片收缩核心 2)。在比这更早的 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))
对于英特尔 SnB 或 IvB 上的两个结果,这应该以 2 个周期延迟运行。 Haswell 和以后只有一个 shuffle 端口(所以他们不能同时执行 punpck insns ),所以 xmm2 将在那里延迟另一个周期。 Pre-SnB Intel CPU 通常在前端(解码器等)上遇到向量指令的瓶颈,因为它们通常平均每个 insn 超过 4B。
对于没有移动消除的 CPU(在寄存器重命名阶段处理 mov 指令,因此它们是零延迟。仅英特尔,并且仅适用于 IvB 及更高版本。)使用 3 操作数 AVX 指令,您不需要 movdqa 或第 3 个寄存器,但无论如何您都可以将 vpmovsx 用于 low64。要对高位 64 进行符号扩展,您可能需要将 psrldq 字节移位高位 64 到低位 64。
或movhlps 或punpckhqdq self,self 使用更短的编码指令。 (或 AVX2 vpmovsx 到 256b reg,然后 vextracti128 上 128,只需两条指令即可获得 128b 结果。)
与 GP 寄存器移位(例如 sar eax, 31)不同,向量移位使计数饱和而不是屏蔽。将原始符号位保留为 LSB(移位 31)而不是它的副本(移位 32)也可以正常工作。它的优点是不需要在代码中添加大注释来解释这一点,以便人们看到psrad xmm0, 32 时会担心。
【讨论】: