为什么mul指令不能实现常数乘法?

时间:2017-03-07 16:06:23

标签: linux assembly x86 x86-64

让我们考虑以下功能:

#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也需要两个周期。

1 个答案:

答案 0 :(得分:1)

您使用专用地址计算单元定位处理器。在地址计算器中计算小乘法的速度可能比在通用算术/逻辑单元(ALU)中快。

此外,根据您的处理器型号,ALU可能会与其他代码共享,这可能是由于超线程或同一线程内的推测或无序执行造成的。您的编译器正在很好地估计如何最好地利用可用资源来提供良好的执行吞吐量而不会停滞。

&#34; 每个汇编指令等于一个处理器时钟滴答&#34; (或者甚至是固定数量的周期)在最简单的处理器上才是真实的。