当我手写组装时,我通常会选择表格
lea eax, [eax+4]
Run Code Online (Sandbox Code Playgroud)
在表格上..
add eax, 4
Run Code Online (Sandbox Code Playgroud)
我听说lea是一个"0时钟"指令(如NOP),而'add'则不是.但是,当我看到编译器生成的程序集时,我经常看到后一种形式而不是第一种.我足够聪明地相信编译器,所以任何人都可以了解哪一个更好?哪一个更快?为什么编译器选择后一种形式呢?
Fra*_*kH. 55
x86 CPU 之间LEA和之间的一个显着区别ADD是实际执行指令的执行单元.现代的x86 CPU是超标量的,并且有多个并行运行的执行单元,管道供给它们有点像循环(bar stalls).事情是LEA由处理寻址的单元(其中一个)处理(在管道的早期阶段发生),然后ADD进入ALU(算术/逻辑单元),并且在管道的后期处理.这意味着超标量x86 CPU可以同时执行LEA和算术/逻辑指令.
LEA通过地址生成逻辑而不是算术单元的事实也是它曾经被称为"零时钟"的原因; 它没有时间执行,因为地址生成已经发生在它执行时.
它不是免费的,因为地址生成是执行管道中的一个步骤,但它没有执行开销.并且它不占用ALU管道中的插槽.
编辑:要澄清一下,LEA是不是免费的.即使在没有通过算术单元实现它的CPU上,由于指令解码/调度/退出和/或所有指令经过的其他流水线阶段,执行也需要时间.所需的时间LEA恰好发生在通过地址生成实现它的CPU 的管道的不同阶段.
小智 15
我足够聪明地相信编译器,所以任何人都可以了解哪一个更好?
对,一点.首先,我从以下消息中获取此信息: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, sizepair 更有效.但是,我不确定这是否是仅仅测试不同指令的结果.你不得不问.
但是,在稍后的消息中,它是在AMD芯片上测量的,并且表现不佳.
我还了解不同的操作在不同的处理器上执行不同的操作.我知道,例如,在GMP项目检测处理器使用cpuid,并通过基于不同的架构,例如,不同的汇编程序core2,nehalem.
您必须问自己的问题是您的编译器是否为您的cpu架构生成优化输出?例如,英特尔编译器就是这样做的,因此可能值得测量性能并查看它产生的输出.
LEA并不比ADD指令快,执行速度相同.
但LEA有时提供的不仅仅是ADD.如果我们需要简单快速的加法/乘法与第二个寄存器相结合,那么LEA可以加速程序执行.从另一方面来看,LEA不会影响CPU标志,因此不存在溢出检测的可能性.