views:

409

answers:

3

Hello everyone,

Just a quick question.

I have a loop that looks like this:

for (int i = 0; i < dim * dim; i++)

Is the condition in a for loop re-evaluated on every loop?

If so, would it be more efficient to do something like this?:

int dimSquare = dim * dim;
for (int i = 0; i < dimSquare; i++)

Thanks

-Faken

A: 

the compiler will precompute the value of Dim * Dim before the Loop starts

Charles Bretana
Unless Dim can be modified inside the loop.
Mike Daniels
so what your saying is that in a for loop, even if the value of dim changes during the loop, the loop will run until the precompiled value is reached? or will the program re-evaluate it when dim changes midway through the loop?
Faken
@Faken, if there is any possibility whatsoever of Dim changing, the compiler will re-evaluate it _every_ loop.
bdonlan
But in this example, as the question is asked, it can't. So It will be precomputed before the loop just once.
Charles Bretana
+21  A: 

Yes, semantically it will be evaluated on every loop. In some cases, compilers may be able to remove the condition from the loop automatically - but not always. In particular:

void foo(const struct rect *r) {
  for (int i = 0; i < r->width * r->height; i++) {
    quux();
  }
}

The compiler will not be able to move the multiplication out in this case, as for all it knows quux() modifies r.

In general, usually only local variables are eligible for lifting expressions out of a loop (assuming you never take their address!). While under some conditions structure members may be eligible as well, there are so many things that may cause the compiler to assume everything in memory has changed - writing to just about any pointer, or calling virtually any function, for example. So if you're using any non-locals there, it's best to assume the optimization won't occur.

That said, in general, I'd only recommend proactively moving potentially expensive code out of the condition if it either:

  • Doesn't hurt readability to do so
  • Obviously will take a very long time (eg, network accesses)
  • Or shows up as a hotspot on profiling.
bdonlan
Note that r could be `const struct rect*` in your example, and the compiler *still* wouldn't be allowed to assume that quux() doesn't somehow modify its data members via an alias. It's commonly assumed that const activates all manner of compiler optimisations, but it's not quite as simple as one might first think.
Steve Jessop
@onebyone: Indeed. Restrict might be able to help here, however.
bdonlan
Yep, for C++ compilers which are also C99 compilers and therefore support restrict. And strict aliasing rules help with "writing to just about any pointer", but not with "calling virtually any function". Inlining should help with functions.
Steve Jessop
`quux()` couldn't possibly modify `r`, though, unless `r` was a global variable. Am I missing something?
GMan
Yes. 'r' could well be pointing to a global variable. Or something pointed to by a global variable. Etc.
bdonlan
Or a "global" with a non-global name, such as a static class member. Or even a function-static variable.
MSalters
+9  A: 

In general, if you would for example change the value of "dim" inside your loop, it would be re-evaluated every time. But since that is not the case in your example, a decent compiler would optimize your code and you wouldn't see any difference in performance.

Lucas
Sorry to the person with the very long thought out answer, but this answer explained everything to me in a clear and concise way.
Faken
But don't forget to take to heart bdonlan's suggestion about using local variables for loop control - locals make things easier for compilers to optimize since their visibility is limited (this can also help with reader comprehension, not just compiler optimization).
Michael Burr
I wouldn't apologise if I were you. This gives bdonlan a decent shot at the "populist" badge, with 9 more votes (at time of writing) and if Lulu gets another 8 :-)
Steve Jessop
@onebyone, well, if you put it that way I suppose I'll have to vote this one up too ;)
bdonlan
a decenet compiler would also hopefully turn your i++ into a ++i :-)
David Claridge
@David, I have to use a pretty awful compiler for $work, but it manages to get that right at least :)
bdonlan