X86 为什么adox和adcx不';我们不能在Ryzen一起玩得好吗?

X86 为什么adox和adcx不';我们不能在Ryzen一起玩得好吗?,x86,cpu-architecture,gmp,amd-processor,adx,X86,Cpu Architecture,Gmp,Amd Processor,Adx,我花了很多时间手工优化低级整数算法,并取得了一些成功。例如,我的6x6乘法子程序花费了66个滴答声,而Skylake上的mpn\u mul\u basecase(6,6)则花费了82个滴答声。我的代码发布在 我目前正在为AMD Ryzen进行8x8乘法运算。我使用Ryzen 7 3800X进行基准测试。我尽量避免迟到。我研究过Agner Fog的“指令表”和Torbjörn Granlund的“指令延迟…”。没有任何迹象表明Ryzen上的adox/adcx存在重大问题;关于adox/adcx,R

我花了很多时间手工优化低级整数算法,并取得了一些成功。例如,我的6x6乘法子程序花费了66个滴答声,而Skylake上的mpn\u mul\u basecase(6,6)则花费了82个滴答声。我的代码发布在

我目前正在为AMD Ryzen进行8x8乘法运算。我使用Ryzen 7 3800X进行基准测试。我尽量避免迟到。我研究过Agner Fog的“指令表”和Torbjörn Granlund的“指令延迟…”。没有任何迹象表明Ryzen上的adox/adcx存在重大问题;关于adox/adcx,Ryzen和Skylake之间应该没有太大区别。我已经使用mulx和adcq、adox或adcx中的一个对乘法8x1子例程进行了基准测试;子程序的所有三个变体在Skylake和Ryzen上都运行得很快(18-19个刻度)

然而,当我尝试混合使用adox和adcx时,我的代码在Ryzen上运行得非常慢。例如,在Skylake i7-6700上花费34个蜱虫,在Ryzen 7 3800X上花费293个蜱虫(相差8倍)


任何关于为什么mulx/adox/adcx代码在Ryzen上执行速度慢8倍的建议?

摆脱了大量使用xmm/ymm的习惯,解决了这个问题

只需42滴答

看起来Ryzen在adox/adcx方面没有问题。Ryzen显然存在vmovdqu mem注册和/或vpextrq和/或vperm2i128的问题

这个问题很愚蠢


@NateEldredge你的暗示很有帮助。谢谢。

Zen是否有部分标志合并暂停,使两个独立的dep链(CF和OF)比单个dep链的延迟瓶颈更糟糕?我不记得Agner Fog在他的Zen Microach指南中提到了这方面的细节,但指令表通常只测试相同的指令,而不是交替使用adcx/adox。Zen是否有任何相关事件的性能计数器?比如暂停,或者后端UOP,或者甚至是专门合并UOP?似乎acdx和adox的全部目的是将它们作为两个独立的链使用。如果AMD在没有微体系结构功能的情况下实现这些指令,这将是一种肮脏的伎俩,而微体系结构功能实际上会给它们提供可接受的性能。@NateEldredge:同意。AMD使用慢速的pdep实现BMI2是有理由的——设置CPUID功能位可以让代码使用其他几种快速的有用指令,特别是
shlx
,等等。(与AMD上的shl速度相同,但比Intel上的shl速度快)。但ADX功能位只提供这两条指令,因此,如果在几乎所有情况下使用它们而不是普通的
adc
,它们都会导致严重的暂停,那么最好根本不提供它们。它们不使用VEX编码或任何使它们在单dep链情况下更有用的东西,因为Haswell,BMI2
mulx
没有ADX是一个有用的代码路径,无论如何,对于bigint乘法。英特尔在本白皮书中展示了这两种方式。如果AMD在所有3种情况下都有相应的版本,那么去掉该功能位将使代码使用该版本。看起来代码使用xmm寄存器作为暂存空间,并将它们移动到整数寄存器之间进行计算,这似乎有点不寻常。有可能是这个原因吗?你还在使用XMM注册表。Zen 2具有零延迟存储转发功能,因此,如果您无法对操作进行重新排序,从而不需要在体系结构寄存器中同时存在这么多值,那么使用存储/重新加载而不是ALU
movq
来发送XMM regs可能是有意义的。(请记住,所有x86 CPU都有无序执行,因此在开始下一个dep链之前,在一个dep链中执行多个操作是可以的,如果这样可以使用一个值完成操作,那么您就不需要将其保存到以后。但是,当您有足够的REG时,安排混合多个dep链或先执行关键路径的指令也是很好的。),您的函数看起来像是R15、RAX等中已有值的代码的助手。将
vzeropper
置于该函数的顶部。或者根本不要使用它:标准的方法是在调用可能不支持AVX的函数之前,以及在返回之前,将YMM upper函数脏到用户
vzeroupper
。在不接受任何YMM参数的正常函数的顶部,您可以假设CPU处于干净的上半部分状态。@Peter Cordes我试图将中间值存储在目标内存中,而不是暂存xmm寄存器。这有时会导致加速,有时会导致减速。例如,在代码的前半部分使用xmm暂存(乘以v[0..3]),在后半部分使用内存暂存(乘以v[4..7])。用xmm scratch替换内存scratch或反之亦然,会导致此代码的速度减慢。@Peter Cordes I只有movq r64、xmm和movq xmm、r64,没有其他向量指令。我听从了你的建议,删除了你的建议。这使速度变慢(从106个滴答声变为110个滴答声)是的,我看到您使用的唯一指令是
movq
,但是您有很多这样的指令。内存的一个优点是,您可以使用内存源操作数,如
adc-16(%rsp),%rax
,而不是单独的
movq
重新加载。正如我所说,在Zen 2上,存储/重新加载可以有效地实现零延迟。此外,Zen2的前端比integer后端更宽,因此我希望一些存储而不是ALU movq可以为有用的工作留下更多的执行单元。在可以使用红色区域的非窗口上,移动RSP不需要任何说明。