让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
我明白了:
现在我运行以下内容:
gcc -O3 -o test_speed test_speed.c
然后:
time ./test_speed
我明白了:
第二轮怎么能这么快?它是否已在编译期间计算过?
答案 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 之间的区别很明显:
答案 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,交互或仅使用其值的情况下计算变量是不可观察的,因此当您的循环不执行任何操作时,优化器会完全抛弃它并直接指定最终值。