LEA或ADD指令?

当我手写汇编时,我通常选择表格

lea eax, [eax+4]

在形式上..

add eax, 4

我听说lea是一个“0时钟”指令(如NOP),而'add'则不是。 但是,当我看着编译器生成的Assembly时,我经常会看到使用后一种形式而不是第一种。 我足够聪明地相信编译器,所以任何人都可以对哪一种更好? 哪一个更快? 为什么编译器会选择前者的后一种形式?


之间的一个差异显著LEAADD上x86 CPU的是实际执行指令的执行单元。 现代x86 CPU是超标量的,并且有多个并行运行的执行单元,管线有点像循环(酒吧停车)。 事情是, LEA由处理寻址的单元(其中之一)处理(发生在流水线的早期阶段),而ADD则转到ALU(算术/逻辑单元),最后处理管道。 这意味着超标量x86 CPU可以同时执行LEA和算术/逻辑指令。

LEA通过地址生成逻辑而不是算术单元的事实也是它以前被称为“零时钟”的原因; 它不需要执行任何时间,因为地址生成已经发生了,直到它被执行。

这不是免费的,因为地址生成是执行管道中的一个步骤,但它没有执行开销。 并且它不占用ALU管线中的插槽。

编辑:澄清, LEA 不是免费的 。 即使在没有通过算术单元实现的CPU上,由于指令解码/调度/退休和/或所有指令经过的其他流水线阶段,执行都需要时间。 执行LEA的时间恰好发生在通过地址生成实现它的CPU的管道的不同阶段。


我足够聪明地相信编译器,所以任何人都可以对哪一种更好?

对,一点。 首先,我从以下消息中获取:https://groups.google.com/group/bsdnt-devel/msg/23a48bb18571b9a6

在这个消息中,开发人员优化了一些我写得非常糟糕的程序集,以便在Intel Core 2处理器中快速运行。 作为这个项目的背景,它是我和其他一些开发人员参与的bsd bignum库。

在这种情况下,所有正在优化的是添加两个看起来像这样的数组: uint64_t* x, uint64_t* y 。 阵列中的每个“肢体”或成员代表了该bignum的一部分; 基本过程是从最不重要的肢体开始迭代它,添加对并继续向上,每次传递进位(任何溢出)。 adc在处理器上为你做这件事(不可能从CI访问进位标志不认为)。

在这段代码中,使用了lea something, [something+1]jrcxz的组合,这显然比我们之前使用过的jnz / add something, size pair更有效。 但是,我不确定这是否是因简单测试不同指令而发现的。 你不得不问。

然而,在稍后的消息中,它是在AMD芯片上测量的,并且表现不佳。

我也被告知理解不同的操作在不同的处理器上执行不同的操作。 例如,我知道GMP项目使用cpuid检测处理器,并根据不同的体系结构传递不同的汇编程序,例如core2nehalem

你必须问自己的问题是你的编译器是否为你的CPU架构产生了优化的输出? 例如,英特尔编译器已知这样做,因此可能值得衡量性能并查看它产生的输出。


LEA不会比ADD指令快,执行速度相同。

但LEA有时候提供的不仅仅是ADD。 如果我们需要简单快速的加法/乘法与第二个寄存器的结合,LEA可以加速程序的执行。 另一方面,LEA不影响CPU标志,所以没有溢出检测的可能性。

链接地址: http://www.djcxy.com/p/12555.html

上一篇: LEA or ADD instruction?

下一篇: What's the purpose of the rotate instructions (ROL, RCL on x86)?