load/store from/to 通用寄存器的最佳方式 to/from xmm/ymm 寄存器

Best way to load/store from/to general purpose registers to/from xmm/ymm register

加载和存储生成目的寄存器的最佳方法是什么to/from 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,两个 mulx 来自 BMI2。使用 vpxor 执行 xor 会更快,但是,mulx 没有 AVX2 等效项。 vpxor vs 4 xor 的增益性能因打包和解包过程而丢失。

您的瓶颈是延迟、吞吐量还是融合域微指令?如果它是延迟,那么 store/reload 是可怕的,因为从狭窄的存储到广泛的负载的存储转发停顿。

对于吞吐量和融合域微指令,这并不可怕:只有 5 个融合域微指令,存储端口出现瓶颈。如果周边的代码大多是ALU uops,值得考虑。


对于您提出的用例:

花费大量 instructions/uops 在整数和向量 reg 之间移动数据通常不是一个好主意。 PMULUDQ 确实为您提供了 32 位乘法的等价物,但您是对的,AVX2 中不能直接使用 64 位乘法。 (AVX512 有)。

您可以通过 PMULUDQ 使用常用的扩展精度技术进行 64 位向量乘法运算。我对 的回答发现,向量化 64 x 64 => 64b 乘法对于 AVX2 256b 向量是值得的,但对于 128b 向量则不值得。但那是内存中的数据,而不是在向量 regs 中开始和结束的数据。

在这种情况下,可能值得构建一个 64x64 => 128b 的完全乘法运算,而不是多个 32x32 => 64 位向量乘法运算,但它可能需要很多指令这是不值得的。如果你确实需要上半部分的结果,解包成标量(或者做你所有的事情标量)可能是最好的。

整数 XOR 非常便宜,具有出色的 ILP(延迟 = 1,吞吐量 = 每个时钟 4)。绝对不值得将您的数据移动到向量 regs 中只是为了对它进行 XOR,如果您没有其他任何向量友好的事情可以在那里做。请参阅 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 微指令,具有足够的 ILP 运行 它们几乎都是背靠背的。因为大概 r8 无论如何都会比 r10 早一两个周期准备好,所以你不会损失太多。


还值得考虑:无论您为生成 r8..r11 做了什么,请使用向量整数指令进行操作,以便您的数据已经在 XMM regs 中。不过,您仍然需要使用 2x PUNPCKLQDQ 和 VINSERTI128 将它们洗牌。