Dave Dave - 4 months ago 18
C Question

How far does GCC's __builtin_expect go?

While answering another question I got curious about this. I'm well aware that

if( __builtin_expect( !!a, 0 ) ) {
// not likely
} else {
// quite likely
}


will make the "quite likely" branch faster (in general) by doing something along the lines of hinting to the processor / changing the assembly code order / some kind of magic. (if anyone can clarify that magic that would also be great).

But does this work for a) inline ifs, b) variables and c) values other than 0 and 1? i.e. will

__builtin_expect( !!a, 0 ) ? /* unlikely */ : /* likely */;


or

int x = __builtin_expect( t / 10, 7 );
if( x == 7 ) {
// likely
} else {
// unlikely
}


or

if( __builtin_expect( a, 3 ) ) {
// likely
// uh-oh, what happens if a is 2?
} else {
// unlikely
}


have any effect? And does all of this depend on the architecture being targeted?

Answer

But does this work for a) inline ifs, b) variables and c) values other than 0 and 1?

It works for an expression context that is used to determine branching.

So, a) Yes. b) No. c) Yes.

And does all of this depend on the architecture being targeted?

Yep!

It leverages architectures that use instruction pipelining, which allow a CPU to begin working on upcoming instructions before the current instruction has been completed.

(if anyone can clarify that magic that would also be great).

("Branch prediction" complicates this description, so I'm intentionally omitting it)

Any code resembling an if statement implies that an expression may result in the CPU jumping to a different location in the program. These jumps invalidate what's in the CPU's instruction pipeline.

__builtin_expect allows (without guarantee) gcc to try to assemble the code so the likely scenario involves fewer jumps than the alternate.