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

C++ 编译器会优化转义内部循环吗?,c++,optimization,compiler-construction,C++,Optimization,Compiler Construction,我的代码如下所示(显示了done的所有用法): booldone=false; for(int i=0;i

我的代码如下所示(显示了done的所有用法):

booldone=false;
for(int i=0;i
是否有编译器将其转换为:

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:;
for(int i=0;i

注意:虽然我最感兴趣的是
if(done)是否中断被绕过并作为死代码删除,我还对它和
done
是否被完全删除感兴趣。

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

也就是说,这是为数不多的情况之一,
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;
booldone=false;
对于(int i=0;i<10;i++)
{
对于(int j=0;j<10;j++)
{
如果(i==7&&j==3)
{
完成=正确;
打破
}
}
如果(完成)中断;
}
返回0;

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

其中.L14是您放置的标签的确切位置:


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

我已经尝试了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;
    }
}

***这是来自一个未链接的对象文件,
call34
实际上是对
foo
的调用,我不想自嘲,但……这有关系吗?总的来说,我认为最好让编译器完成他们的工作,而这项工作就是根据您的源代码生成“最佳”(注意,“最佳”可能根据您的需要而有所不同)编译代码。代码中的任何性能注意事项都应该通过分析器和算法复杂性的良好工作知识来确定


如果你只是好奇,那就不要理会这个评论。但是,如果您的目的是以某种方式优化代码,那么我认为还有更好的方法。

+1实用性。太多的人忽视了这样一个事实,即gotos、break、函数的多个返回点以及其他类似的东西,只有当它们使代码难以理解时才是不好的。明智地使用这些东西是好的。大胆地确保没有人错过它:)耶,去那里是合法的。OTOH一个带标签的break更好,如果它避免了我在代码审查中为它辩护,我会让编译器为我做一些魔术。我认为大多数编译器可以很容易地对它进行优化,因为代码的结尾类似:
set var,true;转到innerloopend(*)。。。innerloopend:cmp var,true;beq outerloopend
和“跟踪”变量很明显,在“转到innerloopend
(*)
将始终导致转到outerloopend”中,因为
cmp变量,从
(*)
到达时,true
将始终为true。对于大多数编译器来说,简单的优化好吧,我认为编译器找到这个并不是那么“容易”(你能告诉我怎么做),但是即使是msvc++也能找到它(即使它一般不能优化那么多)。SNC——至少,不是我们现在的版本。顺便说一句,你不应该定义任何以两个下划线开头的符号;这些符号是保留的。该符号将是优化过程的结果,即编译器生成的结果。我使用这个名字是因为它表示一个保留的/内部的名字。问题是:为什么这个名字不隐藏在函数中?您可以使用
返回
then;)带标签的break在C/C++中被称为GOTO语句,它适合您的情况,除非您想将代码分解成单独的函数。@fabspro:为了回答这个问题,假设禁止使用
GOTO
(例如,某些PHB)。将循环放入另一个函数也是如此。许多编译器做得不太好——至少不像我们想象的那样好。@Crashworks,对特定编译器进行微优化的问题在于,它是特定于编译器的。如果您主动阻止它进行一些新的优化,那么您的代码可能会在所述编译器的后续版本中显著倒退。最好在您知道编译器由于语言限制(过多的对象复制、别名等)而无法优化的地方进行优化。在这一点上,很难知道哪些优化没有完成,因为编译器没有,哪些优化没有完成,因为语言规范不允许这样做。我有点同意,崩溃,但我仍然认为最重要的是理解算法的复杂性。世界上最好的编译器无法修复一些劣质的线性搜索或糟糕的字符串串联代码。我发现将编译器视为一个黑匣子更有效,因为它迫使我们对我们最能控制的事情负责:我们自己的代码。@Crashworks,PS:算法和内存层次结构绝对是最重要的!值得记住的是,编译器并不完美,有时分析器会提供一些您真正可以改进的东西。因此,
3a
42
处直接分支到最终端?@BCS,如果AL为零(foo返回false),0x3a分支到循环的开始,否则它只会继续进行后期编译(弹出保存的寄存器,恢复上一个堆栈帧)3c,3f,…直到ret
call    _Z3fooii  // al = foo(i,j)
testb   %al, %al
jne .L14
...
// 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;
    }
}
  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