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

Yan*_*hou 7 x86 assembly 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.
Run Code Online (Sandbox Code Playgroud)

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

动机是,有时候在AVX2中做一些事情会更快,而其他用于通用的注册事项.例如,在一小段代码中,有四个64位无符号整数,我将需要四个xor,两个mulx来自BMI2.这将是更快的做xorvpxor,但是,mulx没有一个AVX2等同.由于包装和拆包过程,任何vpxor对比4 的增益xor都会丢失.

Pet*_*des 5

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

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


对于用例,您建议:

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

您可以使用PMULUDQ的常用扩展精度技术进行64位向量乘法.我的答案是最快的方法来乘以一个int64_t数组?发现矢量化64 x 64 => 64b乘法值得用AVX2 256b矢量,但不是128b矢量.但那是内存中的数据,而不是数据在向量regs中开始和结束.

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

整数XOR非常便宜,具有出色的ILP(延迟= 1,吞吐量=每个时钟4个).如果你没有其他任何对矢量友好的东西,那么将你的数据移动到向量寄存器中绝对不值得.有关性能链接,请参阅 标记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
Run Code Online (Sandbox Code Playgroud)

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


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