LEA或ADD指令?

当我手写汇编,我通常select的forms

lea eax, [eax+4] 

在forms上

 add eax, 4 

我听说lea是一个“0时钟”指令(如NOP),而“add”则不是。 但是,当我看编译器生成的Assembly时,我经常会看到后面的表单而不是第一个。 我足够聪明地信任编译器,那么谁能说明哪一个更好? 哪一个更快? 为什么编译器select后者的forms?

LEAADD的x86 CPU之间的一个显着的区别是执行单元实际执行指令。 现代的x86 CPU是超标量的,并且有多个并行运行的执行单元,pipe线将它们像循环(酒吧摊位)一样供给它们。 事情是, LEA由处理(处理stream水线的早期阶段)处理单元(之一)处理,而ADD则转到ALU(算术/逻辑单元),最后处理pipe道。 这意味着超标量x86 CPU可以同时执行LEA和算术/逻辑指令。

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

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

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

我足够聪明地信任编译器,那么谁能说明哪一个更好?

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

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

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

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

然而,在稍后的消息中,它是在AMD芯片上测得的,performance不佳。

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

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

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

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

你可以在同一个时钟周期中执行一条加法指令,就像一个加法操作一样,但是如果你使用lea并加在一起,你只需要在一个周期内增加三个操作数! 如果您使用两个只能在2个时钟周期内执行的添加操作:

 mov eax, [esp+4] ; get a from stack mov edx, [esp+8] ; get b from stack mov ecx, [esp+12] ; get c from stack lea eax, [eax+edx] ; add a and b in the adress decoding/fetch stage of the pipeline add eax, ecx ; Add c + eax in the execution stage of the pipeline ret 12