views:

251

answers:

4

The code I have looks like this (all uses of done shown):

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;
  ...
}

will any compilers convert it to this:

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:;

Note: While I'm mostly interested in if the if(done)break; gets bypassed, I'm also interested in if it and done gets removed altogether.

+13  A: 

Obviously this depends on the compiler. The best thing to do when you're unsure is to view the compiler's assembly output (all popular compilers have a switch for this). Even if you aren't familiar with assembly, you can at least compare the debug version with the optimized version.

That being said, this is one of the few situations where goto is NOT a bad idea. Feel free to use it to break out of inner loops.

Edit

Just tried the following in VS2010 and it does indeed optimize the outer conditional:

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;
Cogwheel - Matthew Orlando
+1 for pragmatism. Far too many people have lost sight of the fact that gotos, breaks, multiple return points from functions, and other such things, are bad _only when they make the code hard to understand._ Judicious use of such things is fine.
paxdiablo
Bolded to make sure no one misses it :)
Cogwheel - Matthew Orlando
Yah, a goto is legit there. OTOH a labeled break is even better and if it avoids me having to defend it in a code review, I'll live with the compiler doing some magic for me.
BCS
I think that most compilers can optimize it quite easily since the code ends to resemble: `set var, true; goto innerloopend (*); ... innerloopend: cmp var, true; beq outerloopend` and "tracking" var it is clear that in "goto innerloopend `(*)` will result always in a goto outerloopend" since `cmp var, true` will be always true when reached from `(*)`. Easy optimization for most compilers
ShinTakezou
well, i think its not that "easy" for the compiler to find this (can you please tell me how) but its good that even msvc++ finds it (even if it can't optimize that much in general).
Quonux
+6  A: 

GNU compiler does just that, starting with optimization level -O1 (I am using gcc 4.5.1 on x86_64)

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

where .L14 is the label placed exactly where you placed __done:

A better question might be: which modern compiler does not perform this optimization?

Cubbi
SNC -- at least, not the version we have.
Crashworks
+1  A: 

I've tried GCC 4.2.1 with the following:

// 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;
    }
}

...and it falls through straight to postamble with -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

* This is from an unlinked object file, call 34 is actually call to foo.

Alex B
so `3a` branches directly to the ultimate end at `42`?
BCS
@BCS, 0x3a branches to the beginning of the loop if AL is zero (foo returned false), else it just continues to postamble (pops saved registers, restores previous stack frame) 3c, 3f, .. until ret
Alex B
+4  A: 

I'm not trying to be snarky, but...does it matter? In general, I think it's best to let compilers to their job, and that job is to produce the "best" (note that "best" may vary depending on your needs) compiled code given your source code. Any performance considerations in your code should be identified with a profiler and good working knowledge of algorithmic complexity.

If you're just curious, then disregard this comment. But if your intention is to somehow optimize your code, then I think there are much better avenues.

kidjan
A lot of compilers don't do that job very well -- at least, not as well as we assume they do.
Crashworks
@Crashworks, the problem with micro-optimizing to a particular compiler is just that, it's compiler-specific. Your code may regress significantly ob subsequent versions of said compiler if you actively prevent it from doing some new optimizations. It's better to optimize where you know compiler can't optimize due to language constraints (excessive object copying, aliasing, etc). On that tangent, it's hard to know which optimizations aren't done because the compiler is lacking, and which, because the language spec does not allow that.
Alex B
I somewhat agree, Crash, but I still think what's fundamentally important is understanding algorithmic complexity. The best compiler in the world can't fix some shoddy linear search, or poor string concatenation code. I find it more productive to think of compilers as a black box because it forces us to take accountability for things we have the most control over: our own code.
kidjan
@Crashworks, PS: http://www.linux-kongress.org/2009/slides/compiler_survey_felix_von_leitner.pdf
Alex B
Algorithms and memory hierarchy are definitely paramount! It's just worth remembering that compilers aren't perfect, and sometimes the profiler will turn up things that you really can improve on.
Crashworks
In this case the optimization is so trivial (and arguably easier to understand) that the question is it worth having to defend it re knee jerk reactions?
BCS