让我们考虑以下功能:
#include <stdint.h>
uint64_t foo(uint64_t x) { return x * 3; }
如果我要写它,我会做
.global foo
.text
foo:
imul %rax, %rdi, $0x3
ret
另一方面,编译器使用-O0
:
0: 55 push %rbp
1: 48 89 e5 mov %rsp,%rbp
4: 48 89 7d f8 mov %rdi,-0x8(%rbp)
8: 48 8b 55 f8 mov -0x8(%rbp),%rdx
c: 48 89 d0 mov %rdx,%rax
f: 48 01 c0 add %rax,%rax
12: 48 01 d0 add %rdx,%rax
15: 5d pop %rbp
16: c3 retq
或lea
与-O2
:
0000000000000000 <foo>:
0: 48 8d 04 7f lea (%rdi,%rdi,2),%rax
4: c3 retq
为什么呢?由于每个汇编指令等于一个处理器时钟周期,我的版本应该在2个CPU时钟周期内运行(因为它有两个指令),在-O0
我们需要4个周期来执行加法,因为它可以被重写为
mov %rdi,%rax
add %rax,%rax
add %rdi,%rax
retq
并且lea
也需要两个周期。
答案 0 :(得分:1)
您使用专用地址计算单元定位处理器。在地址计算器中计算小乘法的速度可能比在通用算术/逻辑单元(ALU)中快。
此外,根据您的处理器型号,ALU可能会与其他代码共享,这可能是由于超线程或同一线程内的推测或无序执行造成的。您的编译器正在很好地估计如何最好地利用可用资源来提供良好的执行吞吐量而不会停滞。
&#34; 每个汇编指令等于一个处理器时钟滴答&#34; (或者甚至是固定数量的周期)在最简单的处理器上才是真实的。