If-else statements in for loops

  • Context:
  • Thread starter Thread starter ChrisVer
  • Start date Start date
  • Tags Tags
    For loops Loops
Click For Summary

Discussion Overview

The discussion revolves around the performance implications of using if-else statements within for loops in programming, particularly in C and C++. Participants explore concepts such as loop hoisting, branch prediction, and compiler optimizations, while considering how these factors affect execution speed.

Discussion Character

  • Technical explanation
  • Debate/contested
  • Mathematical reasoning

Main Points Raised

  • Some participants mention that if-else statements can slow down loops due to their impact on instruction pipelines and branch prediction.
  • There is a discussion about whether all C++ compilers can perform optimizations like loop hoisting, with some arguing that blanket statements about compiler capabilities are unreasonable.
  • A participant shares a personal experience of improving code performance by restructuring if-else statements outside of loops.
  • Another participant notes that certain expressions can be optimized out of loops if they are constant, referencing loop-invariant expressions.
  • Branch prediction is identified as a key factor in performance, with some participants discussing low-level optimizations and their practical applications.
  • References to external resources, such as Ulrich Drepper's white paper on CPU memory efficiency, are provided for further reading on improving code performance.
  • Participants emphasize the programmer's control over various factors affecting performance, including algorithm choice, data locality, and branching strategies.

Areas of Agreement / Disagreement

Participants express differing views on the extent to which compilers can optimize code and the impact of if-else statements on performance. There is no consensus on the effectiveness of specific optimizations across all compilers.

Contextual Notes

Limitations include the variability of compiler optimizations, the dependence on specific programming practices, and the influence of external factors like data locality and algorithm choice on performance.

ChrisVer
Science Advisor
Messages
3,372
Reaction score
465
I've come across a rumor that states that if-else statements can make the loops go slower. Well, it's not rumors since the writer offered numbers (times) to support the statement.
http://blogs.sas.com/content/iml/2012/02/13/avoid-unnecessary-if-then-statements-in-loops.html
I was wondering though: it says "For some compiled programming languages such as FORTRAN and C/C++, an optimizing compiler can sometimes determine that an expression is constant and move it outside of the loop for you."
What does "sometimes determine that an expression is constant" and can you know it beforehand?
Thanks
 
  • Like
Likes   Reactions: OmCheeto
Technology news on Phys.org
The sentence just before the one you quoted:
This is sometimes called "loop hoisting."
In the link, you have an example of loop-invariant (i.e. constant) expression.
 
jack action said:
In the link, you have an example of loop-invariant (i.e. constant) expression.
So does it mean that all C++ compilers can do this optimization by themselves?
 
ChrisVer said:
So does it mean that all C++ compilers can do this optimization by themselves?
I don't think it's reasonable to make blanket statements about "all C++ compilers" as far as what optimizations they can or cannot do.

A major reason the if ... else statements inside loops slow things down is that they disrupt the pipeline of instructions. Modern processors are fast, in part, because instructions pass through a multi-stage pipeline, with up to 14 stages. (I believe this number is accurate.) A program that executes sequentially, with one instruction executing after another, keeps the pipeline full and everything chugging along. If a branch instruction is hit (i.e., such as an if ... else), a modern processor can make an educated guess as to which branch will be taken. If its prediction is correct, all is good. If the guess is incorrect, the pipeline is invalidated, and must be filled again, slowing things down.
 
  • Like
Likes   Reactions: DrClaude
ChrisVer said:
So does it mean that all C++ compilers can do this optimization by themselves?
Mark44 said:
I don't think it's reasonable to make blanket statements about "all C++ compilers" as far as what optimizations they can or cannot do.
Especially since it is usually the programmer that controls the level of optimization!

For gcc, you can see here the optimizations performed at the different levels. At the lowest level of optimization, -O or -O1, you already find
-fmove-loop-invariants
 
  • Like
Likes   Reactions: jim mcnamara and ChrisVer
I have found that if statements inside of loops can slow things dramatically, for the reason Mark44 explained. Sometimes you have no choice, but in one instance I sped my code up substantially by changing the sequence from:
for (i...)
if (test) do A
else do B

to:
if (test)
for (i...)
do A
else
for (i...)
do B
 
Something like this:

Code:
float theta = 0;
for(unsigned int i = 0; i < 255; ++i){
     callFunction(theta + pi);
}
It'll pull that theta + pi out because it'll always be the same while it's in a loop.

if switches don't really have much to do with performance. There is some low level optimization that allows you to do branch prediction, but almost nobody uses it. I only used it in a high speed parsing library, and even then, I left it as a compile option. If you're really into speed, you can do something like this.

Code:
if (likely(a > b)){
     //This block of code will immediately follow the jump, this is faster because it's almost certainly already in processor cache
} else {
    //This block of code may be any number of bytes away
}
 
  • Like
Likes   Reactions: ChrisVer
The technical term is branch prediction; some compilers have features that let the programmer tell the compiler which branch of an if statement is expected to happen the most frequently.

The reason this is important is that memory is a lot slower than modern cpu's. Which is why prefetch of instructions helps improve execution speed. So having to go out and refetch new instructions can be a real bottleneck.

Ulrich Drepper has an older white paper, still very valid. If you want to improve your code efficiency consider reading:
https://www.akkadia.org/drepper/cpumemory.pdf
 
  • Like
Likes   Reactions: ChrisVer
jim mcnamara said:
Ulrich Drepper has an older white paper, still very valid. If you want to improve your code efficiency consider reading:
https://www.akkadia.org/drepper/cpumemory.pdf
that's a long paper but interesting, I'll have a look in it :)
though in general, in order to see how the compiler compiles the code you have to look at its features (as in DrClaude's reference for gcc) and you have control over it.
 
  • #10
Under gcc's control? No, not always. Example: Algorithm choice has a massive impact. Locality (of data) is under your control, as you will see in the article. It can have a big impact as well. Branching too, as case statements provide an interesting challenge. Nested if then else clauses have impact too. Threading. I/O buffer cache size. The list goes on...

You control all these fully. I would suggest you read known good C/C++ code -- like the source for GNU grep or GNU C library code:
https://www.gnu.org/software/libc/sources.html
 

Similar threads

  • · Replies 3 ·
Replies
3
Views
5K
Replies
11
Views
2K
  • · Replies 1 ·
Replies
1
Views
2K
  • · Replies 1 ·
Replies
1
Views
2K
Replies
4
Views
4K
  • · Replies 2 ·
Replies
2
Views
6K
  • · Replies 5 ·
Replies
5
Views
2K
  • · Replies 28 ·
Replies
28
Views
6K
  • · Replies 4 ·
Replies
4
Views
2K