编译器会优化转义内循环吗?

时间:2010-06-16 01:18:47

标签: c++ optimization compiler-construction

我的代码看起来像这样(显示完成的所有用法):

bool done = false;
for(int i = 0; i < big; i++)
{
  ...
  for(int j = 0; j < wow; j++)
  {
    ...
    if(foo(i,j))
    {
       done = true;
       break;
    }
    ...
  }
  if(done) break;
  ...
}

任何编译器都会将其转换为:

for(int i = 0; i < big; i++)
{
  ...
  for(int j = 0; j < wow; j++)
  {
    ...
    if(foo(i,j))
      goto __done; // same as a labeled break if we had it
    ...
  }
  ...
}
__done:;

注意:虽然我最感兴趣的是if(done)break;被绕过并作为死代码删除,但我也对它是否被删除done感兴趣共

4 个答案:

答案 0 :(得分:14)

显然这取决于编译器。当您不确定时,最好的办法是查看编译器的汇编输出(所有流行的编译器都有一个开关)。即使您不熟悉汇编,也至少可以将调试版本与优化版本进行比较。

话虽如此,这是goto不是一个坏主意的少数情况之一。随意使用它来打破内循环。

修改

在VS2010中尝试了以下内容,确实优化了外部条件:

bool done = false;
for(int i = 0; i < 10; i++)
{
    for(int j = 0; j < 10; j++)
    {
        if(i == 7 && j == 3)
        {
            done = true;
            break;
        }
    }
    if(done) break;
}
return 0;

答案 1 :(得分:7)

GNU编译器就是这样做的,从优化级别-O1开始(我在x86_64上使用gcc 4.5.1)

call    _Z3fooii  // al = foo(i,j)
testb   %al, %al
jne .L14
...

其中.L14是放置在__done:

的确切位置的标签

更好的问题可能是:哪个现代编译器执行此优化?

答案 2 :(得分:4)

我不是想偷偷摸摸,但是......这有关系吗?一般来说,我认为最好让编译器完成他们的工作,并且这项工作是根据您的源代码生成“最佳”(注意“最佳”可能根据您的需要而变化)编译代码。您应该使用分析器和算法复杂性的良好工作知识来识别代码中的任何性能注意事项。

如果你只是好奇,那么请忽略这个评论。但如果你的意图是以某种方式优化你的代码,那么我认为有更好的途径。

答案 3 :(得分:1)

我已经尝试了以下GCC 4.2.1:

// Prevent optimizing out calls to foo and loop unrolling:
extern int big, wow;
bool foo(int,int);

void
bar()
{
    int done = false;
    for(int i = 0; i < big; i++)
    {
        for(int j = 0; j < wow; j++)
        {
            if(foo(i,j))
            {
                done = true;
                break;
            }
        }
        if(done)
            break;
    }
}

...它会直接与-O3

同时发生
  33:   e8 fc ff ff ff          call   34 <bar()+0x34> ; call to foo*
  38:   84 c0                   test   %al,%al
  3a:   74 e5                   je     21 <bar()+0x21> ; next loop iteration
  3c:   83 c4 10                add    $0x10,%esp
  3f:   5b                      pop    %ebx
  40:   5e                      pop    %esi
  41:   5d                      pop    %ebp
  42:   c3                      ret

***这是来自未关联的目标文件,call 34实际上是对foo的调用。