gcc -O3如何让运行如此之快?

时间:2018-04-03 15:17:46

标签: c gcc

让test_speed.c成为以下C代码:

#include <stdio.h>
int main(){
    int i;
    for(i=0; i < 1000000000; i++) {}
    printf("%d", i);
}

我在终端跑:

gcc -o test_speed test_speed.c 

然后:

time ./test_speed

我明白了:

enter image description here

现在我运行以下内容:

gcc -O3 -o test_speed test_speed.c

然后:

time ./test_speed

我明白了:

enter image description here

第二轮怎么能这么快?它是否已在编译期间计算过?

4 个答案:

答案 0 :(得分:7)

那是因为-O3积极优化假设

for(i=0; i < 1000000000; i++) {}

没有副作用(i的值除外)并完全删除循环(直接将i设置为1000000000)。

反汇编(x86):

00000000 <_main>:
   0:   55                      push   %ebp
   1:   89 e5                   mov    %esp,%ebp
   3:   83 e4 f0                and    $0xfffffff0,%esp
   6:   83 ec 10                sub    $0x10,%esp
   9:   e8 00 00 00 00          call   e <_main+0xe>
   e:   c7 44 24 04 00 ca 9a    movl   $0x3b9aca00,0x4(%esp)  <== 1000000000 in hex, no loop
  15:   3b
  16:   c7 04 24 00 00 00 00    movl   $0x0,(%esp)
  1d:   e8 00 00 00 00          call   22 <_main+0x22>
  22:   31 c0                   xor    %eax,%eax
  24:   c9                      leave
  25:   c3                      ret

如您所见,优化级别不适用于校准的活动CPU循环(结果与-O2相同,但仅使用-O循环仍未优化)

答案 1 :(得分:3)

gcc“知道”循环中没有正文,并且不依赖于任何结果,临时或真实 - 所以它删除循环。

这样一个很好的分析工具是godbolt.org,它会显示生成的程序集。 根本没有优化 -O3 optmization 之间的区别很明显:

无优化

enter image description here

使用-O3

enter image description here

答案 2 :(得分:2)

编译器识别出循环什么都不做,并且删除它不会改变程序的输出,所以循环被完全优化了。

这是带-O0的程序集:

.L3:
    .loc 1 4 0 is_stmt 0 discriminator 3
    addl    $1, -4(%rbp)
.L2:
    .loc 1 4 0 discriminator 1
    cmpl    $999999999, -4(%rbp)        # loop 
    jle .L3 
    .loc 1 5 0 is_stmt 1
    movl    -4(%rbp), %eax
    movl    %eax, %esi
    movl    $.LC0, %edi
    movl    $0, %eax
    call    printf
    movl    $0, %eax
    .loc 1 6 0
    leave   
    .cfi_def_cfa 7, 8
    ret

使用-O3

main:
.LFB23:
    .file 1 "x1.c"
    .loc 1 2 0
    .cfi_startproc
.LVL0:
    subq    $8, %rsp
    .cfi_def_cfa_offset 16
.LBB4:
.LBB5:
    .file 2 "/usr/include/x86_64-linux-gnu/bits/stdio2.h"
    .loc 2 104 0
    movl    $1000000000, %edx      # stored value, no loop
    movl    $.LC0, %esi
    movl    $1, %edi
    xorl    %eax, %eax
    call    __printf_chk
.LVL1:
.LBE5:
.LBE4:
    .loc 1 6 0
    xorl    %eax, %eax
    addq    $8, %rsp
    .cfi_def_cfa_offset 8
    ret

您可以看到,在-O3情况下,完全删除了循环,并且直接存储了i的最终值1000000000。

答案 3 :(得分:2)

编译器只需要保持程序的可观察行为。在没有任何I / O,交互或仅使用其值的情况下计算变量是不可观察的,因此当您的循环不执行任何操作时,优化器会完全抛弃它并直接指定最终值。