编译器可以优化不必要的比较吗?

时间:2018-02-03 16:50:03

标签: c++ c optimization

让我们考虑用C编写的以下代码块:

void foo(int i) {
  if(i > 0) {
    bar(i);
  }
}

static void bar(int i) {
  if(i > 0) {
    //Do something useful
    printf("FOO-BAR");
  }
}

一个好的主流C编译器可以优化不必要的 i> 0 与foo(int)内部调用的bar(int)的比较?

如果是,那么在什么情况下如果没有那么为什么不呢?

3 个答案:

答案 0 :(得分:2)

它主要适用于程序的输出控制流程不依赖于运行时事件的情况(这是最常用的技术之一)。例如,这里不会进行优化,因为您永远不知道i的价值是多少。但这肯定是,

static void bar(int i) {
  i = 1;
  if(i > 0) {
    //Do something useful
    printf("FOO-BAR");
  }
}

此处无论下一次i>0检查多余都是如此。而且,你总是应该生成组件并寻找它。编译器在这方面的优化可能非常具有攻击性。

此处还有另一件事需要注意 - 例如,编译器不会优化此代码 - 即使它总是用于大于0值的参数。编译器无法预测这一点。

代码优化中最常见的技术涉及 - 检查数据流图,消除死代码,循环展开(在您的小例子中肯定不能非侵略性地应用)。

答案 1 :(得分:2)

标准允许它。

对于C语言,草案n1570在5.1.2.3程序执行中说明(强调我的)

  

1本国际标准中的语义描述描述了一个人的行为   优化问题无关的抽象机器。

  4在抽象机器中,所有表达式都按语义指定进行计算。 <强>一种   实际实现不需要评估表达式的一部分,如果它可以推导出它   没有使用价值,也没有产生所需的副作用

对于C ++草案,n1659包含4.6程序执行[intro.execution]

  

1本国际标准中的语义描述定义了参数化的非确定性摘要   机。本国际标准对符合实施的结构没有要求。   特别是,它们不需要复制或模拟抽象机器的结构。相反,符合   实现需要模拟(仅)抽象机器的可观察行为,如上所述   below.6   
...
  5 执行格式良好的程序的符合实现应产生与之相同的可观察行为   具有相同程序的抽象机器的相应实例的可能执行之一   和相同的输入。但是,如果任何此类执行包含未定义的操作,则此国际   标准不要求使用该输入执行该程序的实现(甚至不是   关于第一次未定义操作之前的操作。)

答案 2 :(得分:2)

C ++编译器可以自由地进行任何不可观察的优化。优化器比大多数人想象的要切碎得多。 What Has My Compiler Done For Me Lately?在给定的情况下可能会发生的事情是foo将被转换为:

void foo(int i) {
  if(i > 0) {
     printf("FOO-BAR");
  }
}

这就是VC ++ 2017的功能。事实上,它优化了foobar,并且在给定以下MCVE时只执行一次测试。

static void bar(int i) {
    if (i > 0) {
        //Do something useful
        printf("FOO-BAR");
    }
}

void foo(int i) {
    if (i > 0) {
        bar(i);
    }
}

int main() {
    int i;
    scanf("%d", &i);
    foo(i);
}

以下是生成的程序集。请注意,从stdin读取int i后,下一条指令是cmp(比较),后跟jle(如果小于或等于则跳转)。没有foobar在优化程序中幸存下来。

  int main() {
000000013F401000  sub         rsp,38h  
000000013F401004  mov         rax,qword ptr [__security_cookie (013F403000h)]  
000000013F40100B  xor         rax,rsp  
000000013F40100E  mov         qword ptr [rsp+28h],rax  
    int i;
    scanf("%d", &i);
000000013F401013  lea         rdx,[i]  
000000013F401018  lea         rcx,[string "%d" (013F402228h)]  
000000013F40101F  call        scanf (013F401060h)  
    foo(i);
000000013F401024  cmp         dword ptr [i],0  
000000013F401029  jle         main+37h (013F401037h)  
000000013F40102B  lea         rcx,[string "FOO-BAR" (013F402220h)]  
000000013F401032  call        printf (013F4010D0h)  
}
000000013F401037  xor         eax,eax  
000000013F401039  mov         rcx,qword ptr [rsp+28h]  
000000013F40103E  xor         rcx,rsp  
000000013F401041  call        __security_check_cookie (013F401140h)  
000000013F401046  add         rsp,38h  
000000013F40104A  ret