x86乘法3:3:IMUL vs SHL + ADD

时间:2019-07-19 10:22:45

标签: assembly x86 x86-64 intel micro-optimization

我用x86-64程序集开发了一个程序,该程序需要通过相同的操作进行多次迭代:

IMUL rdx, 3   # rdx is always different

但是,我需要使运行时更快,因此我从上面想到了对该特定行的优化:

MOV rcx, rdx
SHL rdx, 1
ADD rdx, rcx

现在我问你们:这种修改会改善程序的运行时间(减少时钟),还是我应该坚持使用IMUL命令?

1 个答案:

答案 0 :(得分:5)

lea rdx, [rdx + rdx*2] 相比,两者都很糟糕,使用缩放索引寻址模式获得总计*3,这就是为什么如果您要求编译器始终使用LEA的原因他们来编译像这样的函数

long foo(long x){ return x * 3; }https://godbolt.org/z/6p4ynV


LEA是一种通过x86寻址模式馈送任意数字的方法,该方法使用结果进行加载或存储,只需将其放入寄存器即可。 Using LEA on values that aren't addresses / pointers?


在所有现代x86 CPU上,LEA都是单个uop。 唯一的问题是比其他方法好多少 imul也是1 uop,但是mov + shl + add的前端是3 。 (在所有仍然相关的主流和低功耗英特尔/ AMD上都是如此。请参见https://agner.org/optimize/。)64位imul在某些较旧的微体系结构(例如Bulldozer系列和Silvermont / Goldmont)上特别慢,或者特别是较旧的Atom。

在AMD CPU(Bulldozer / Ryzen)上,它具有缩放的索引,因此它是“复杂的” LEA,并且具有2个周期延迟(Ryzen上imul的延迟为3,或者在Bulldozer系列上更糟) 64位imul较慢且未完全流水线化)。在Ryzen上,此LEA仍具有每时钟2个吞吐量。

在Intel CPU上,它只有2个组件(一个+),因此它是“简单” LEA,具有 1个周期延迟,并且可以每2个运行一次。时钟吞吐量。因此,与一条shl指令所花费的成本大致相同,但运行在不同的端口上。

(或者在Ice Lake上,因为它们将LEA单元添加到其他2个整数ALU端口中,所以每4个小时一次。所以它的价格与Ice Lake上一个add一样便宜。)


您只想要movshl; subadd,当n > 3的乘数为2 ^ n + -1时。然后值得考虑imul在延迟和前端吞吐量成本之间进行权衡。

通过移位原始寄存器,即使没有mov消除的CPU(在IvyBridge和Ryzen之前)也可以以2个周期的延迟关键路径长度运行mov / shl / add序列。


也相关:C++ code for testing the Collatz conjecture faster than hand-written assembly - why?详细介绍了*3相对于LEA优化的问题。

其他相关: