从/向xmm / ymm寄存器加载/存储通用寄存器的最佳方法

时间:2016-11-16 03:52:32

标签: assembly x86 simd sse2 avx2

从SIMD寄存器加载和存储生成目的寄存器的最佳方法是什么?到目前为止,我一直在使用堆栈作为临时。例如,

mov [rsp + 0x00], r8
mov [rsp + 0x08], r9
mov [rsp + 0x10], r10
mov [rsp + 0x18], r11
vmovdqa ymm0, [rsp] ; stack is properly aligned first.

我不认为任何指令可以直接(或另一个方向)执行此操作,因为它意味着具有五个操作数的指令。但是,上面的代码对我来说似乎很愚蠢。有没有更好的方法呢?我只能想到一个替代方案,使用pinsrd和相关说明。但它似乎没有任何好转。

动机是,有时候在AVX2中做某些事情会更快,而其他用于通用目的的则更快。例如,在一小段代码中,有四个64位无符号整数,我需要四个xor,两个来自BMI2的mulx。使用xor执行vpxor会更快,但mulx没有AVX2等效项。由于打包和拆包的过程,vpxor与4 xor的任何增益都会丢失。

1 个答案:

答案 0 :(得分:5)

您的瓶颈延迟,吞吐量或融合域uops?如果它是延迟,那么存储/重新加载是可怕的,因为存储转发从窄存储到大范围的停顿。

对于吞吐量和融合域uops,它并不可怕:只有5个融合域uops,商店端口上的瓶颈。如果周围的代码主要是ALU uops,那么值得考虑。

对于您建议使用的用例:

在整数和向量寄存器之间花费大量指令/ uop来移动数据通常是一个坏主意。 PMULUDQ确实提供了相当于32位mulx的功能,但你确实在AVX2中不能直接使用64位乘法器。 (AVX512有它们)。

您可以使用PMULUDQ的常用扩展精度技术进行64位向量乘法。我对Fastest way to multiply an array of int64_t?的回答发现,矢量化为64 x 64 =>使用AVX2 256b矢量64b乘法是值得的,但不是128b矢量。但这与内存中的数据有关,而不是数据在向量寄存器中开始和结束。

在这种情况下,可能值得构建64x64 => 128b完全乘以多个32x32 => 64位向量相乘,但它可能需要很多指令,因此它不值得。如果你确实需要上半部分的结果,那么解压缩到标量(或者做整个标量)可能是最好的。

整数XOR非常便宜,具有出色的ILP(延迟= 1,吞吐量=每时钟4个)。如果你没有其他任何对矢量友好的东西,那么将你的数据移动到向量寄存器中绝对不值得。有关效果链接,请参阅 tag wiki

延迟的最佳方式可能是:

vmovq   xmm0, r8
vmovq   xmm1, r10            # 1uop for p5 (SKL), 1c latency
vpinsrq xmm0, r9, 1          # 2uops for p5 (SKL), 3c latency
vpinsrq xmm1, r11, 1
vinserti128 ymm0, ymm0, ymm1, 1    # 1uop for p5 (SKL), 3c latency

总计:p5为7 uop,有足够的ILP来运行它们几乎所有背靠背。因为大概r8将比r10更快地准备一个或两个周期,你不会损失太多。

另外值得考虑的是:无论你做什么来生产r8..r11,都要使用向量整数指令,这样你的数据已经在XMM regs中。然后你仍然需要将它们混合在一起,使用2x PUNPCKLQDQ和VINSERTI128。