gcc究竟如何做优化?

时间:2016-03-01 10:42:07

标签: c gcc optimization compiler-optimization

为了知道gcc究竟如何进行优化,我编写了两个用-O2编译的程序,但汇编代码有一些不同之处。在我的程序中,我想输出" hello"在循环中,并在每个输出之间添加一些延迟。这两个程序仅用于说明我的问题,我知道我可以在程序1中使用volatile或asm来实现我的目的。

计划1

#include <stdio.h>

int main(int argc, char **argv)
{
    unsigned long i = 0;
    while (1) {
        if (++i > 0x1fffffffUL) {
            printf("hello\n");
            i = 0;
        }
    }
}

使用-O2编译,汇编代码为:

Disassembly of section .text.startup:

00000000 <_main>:
#include <stdio.h>

int main(int argc, char **argv)
{
   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:   66 90                   xchg   %ax,%ax
  10:   c7 04 24 00 00 00 00    movl   $0x0,(%esp)
  17:   e8 00 00 00 00          call   1c <_main+0x1c>
  1c:   eb f2                   jmp    10 <_main+0x10>
  1e:   90                      nop
  1f:   90                      nop

计划2

int main(int argc, char **argv)
{
    unsigned long i = 0;
    while (1) {
        if (i > 0x1fffffffUL) {
            printf("hello\n");
            i = 0;
        }
        i++;
    }
}

使用-O2编译,汇编代码为:

Disassembly of section .text.startup:

00000000 <_main>:
#include <stdio.h>

int main(int argc, char **argv)
{
   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:   31 c0                   xor    %eax,%eax
  10:   83 c0 01                add    $0x1,%eax
  13:   3d ff ff ff 1f          cmp    $0x1fffffff,%eax
  18:   76 f6                   jbe    10 <_main+0x10>
  1a:   c7 04 24 00 00 00 00    movl   $0x0,(%esp)
    while (1) {
        if (i > 0x1fffffffUL) {
            printf("hello\n");
            i = 0;
        }
        i++;
  21:   e8 00 00 00 00          call   26 <_main+0x26>

int main(int argc, char **argv)
{
    unsigned long i = 0;
    while (1) {
        if (i > 0x1fffffffUL) {
  26:   31 c0                   xor    %eax,%eax
  28:   eb e6                   jmp    10 <_main+0x10>
            printf("hello\n");
  2a:   90                      nop
  2b:   90                      nop
  2c:   90                      nop
  2d:   90                      nop
  2e:   90                      nop
  2f:   90                      nop

在程序1中,i的增加已经过优化,但它不在程序2中。为什么会发生这种情况?当使用-O2优化这两个程序时,gcc使用了哪些规则?

2 个答案:

答案 0 :(得分:9)

询问关于优化器的“原因”通常是浪费时间,因为 没有“规则”优化器运行 - 除了“好像”:优化器可能不会改变可观察量符合规范的行为。

你的程序的“可观察行为”是反复打印“你好”。

在您的第一个程序中,计数被优化掉,使得可观察的行为更快地发生。 这是优化器的工作。请高兴您的代码现在更高效!

在你的第二个程序中,计数优化了,因为不知何故优化器 - 在这个版本的这个编译器中< strong>这个设置 - 没有看到没有它可以做到。为什么?谁知道(除了编译器的优化器模块的维护者)?

如果所需的行为是在输出之间有延迟,请使用thrd_sleep()之类的内容。空计数循环是一种在C64上延迟BASIC 2.0程序的方法,但是它们不应该在C中使用,因为你刚刚观察到的确切原因:你永远不知道优化器的作用。

答案 1 :(得分:2)

if语句中的分支现在取决于循环的前一次迭代中发生的事情。特别是,编译器可以在程序1中轻松确定i在while循环的每次迭代中递增(因为它在右上方),而在程序2中则不是这样。

无论如何,编译器优化非常复杂。见下文:

gcc -O2是这些标志的快捷方式:(来自documentation

      -fauto-inc-dec 
      -fbranch-count-reg 
      -fcombine-stack-adjustments 
      -fcompare-elim 
      -fcprop-registers 
      -fdce 
      -fdefer-pop 
      -fdelayed-branch 
      -fdse 
      -fforward-propagate 
      -fguess-branch-probability 
      -fif-conversion2 
      -fif-conversion 
      -finline-functions-called-once 
      -fipa-pure-const 
      -fipa-profile 
      -fipa-reference 
      -fmerge-constants 
      -fmove-loop-invariants 
      -freorder-blocks 
      -fshrink-wrap 
      -fsplit-wide-types 
      -fssa-backprop 
      -fssa-phiopt 
      -ftree-bit-ccp 
      -ftree-ccp 
      -ftree-ch 
      -ftree-coalesce-vars 
      -ftree-copy-prop 
      -ftree-dce 
      -ftree-dominator-opts 
      -ftree-dse 
      -ftree-forwprop 
      -ftree-fre 
      -ftree-phiprop 
      -ftree-sink 
      -ftree-slsr 
      -ftree-sra 
      -ftree-pta 
      -ftree-ter 
      -funit-at-a-time
      -fthread-jumps 
      -falign-functions  -falign-jumps 
      -falign-loops  -falign-labels 
      -fcaller-saves 
      -fcrossjumping 
      -fcse-follow-jumps  -fcse-skip-blocks 
      -fdelete-null-pointer-checks 
      -fdevirtualize -fdevirtualize-speculatively 
      -fexpensive-optimizations 
      -fgcse  -fgcse-lm  
      -fhoist-adjacent-loads 
      -finline-small-functions 
      -findirect-inlining 
      -fipa-cp 
      -fipa-cp-alignment 
      -fipa-sra 
      -fipa-icf 
      -fisolate-erroneous-paths-dereference 
      -flra-remat 
      -foptimize-sibling-calls 
      -foptimize-strlen 
      -fpartial-inlining 
      -fpeephole2 
      -freorder-blocks-algorithm=stc 
      -freorder-blocks-and-partition -freorder-functions 
      -frerun-cse-after-loop  
      -fsched-interblock  -fsched-spec 
      -fschedule-insns  -fschedule-insns2 
      -fstrict-aliasing -fstrict-overflow 
      -ftree-builtin-call-dce 
      -ftree-switch-conversion -ftree-tail-merge 
      -ftree-pre 
      -ftree-vrp 
      -fipa-ra

这些标志中的每一个都对应于允许编译器进行的不同可能的优化。