C++20 introduced the attributes [[likely]]
and [[unlikely]]
to the language, which can be used to allow the compiler to optimize for the case where one execution path is either much more likely or much less likely than the other ones.
Given the cost of an incorrect branch prediction, this seems like a feature that's potentially extremely useful in performance-critical sections of code, but I don't know what it would actually cause the compiler to do.
Is there a simple piece of code for which adding [[likely]]
and [[unlikely]]
attributes changes the assembly output by the compiler? And perhaps more importantly, what do these changes do?
I created a simple example for my own understanding to see if there was any difference in the assembly, but it appears that this example is too simple to actually show any changes to the assembly:
void true_path();
void false_path();
void foo(int i) {
if(i) {
true_path();
} else {
false_path();
}
}
void bar(int i) {
if(i) [[likely]] {
true_path();
} else [[unlikely]] {
false_path();
}
}
View the compiled assembly here.
Assisting the compiler in optimizing if conditions A look at the Linux kernel code will show many if conditions enclosed in likely and unlikely macros. These macros invoke compiler directives that give the compiler a hint on the code leg that should be optimized for performance.
You can use the __builtin_expect built-in function to indicate that an expression is likely to evaluate to a specified value. The compiler can use this knowledge to direct optimizations. This built-in function is portable with the GNU C/C++ __builtin_expect function.
Assembly Programming Tutorial. Assembly language is a low-level programming language for a computer or other programmable device specific to a particular computer architecture in contrast to most high-level programming languages, which are generally portable across multiple systems.
Assembly - Conditions. Conditional execution in assembly language is accomplished by several looping and branching instructions. These instructions can change the flow of control in a program.
Assembly language is converted into executable machine code by a utility program referred to as an assembler like NASM, MASM, etc.
likely and unlikely attributes is our way of saying to the compiler that which code is most likely to be executed so compiler can schedule accordingly and result in a overall faster code execution. We should be cautious using likely and unlikely if we are not very sure because otherwise we would have serious performance degradations.
As it seems, there is a bug in gcc. If you have two functions which are the same, besides [[likely]]
attributes, gcc folds them incorrectly.
But if you use just one function, and switch between [[likely]]
/[[unlikely]]
, assembly changes.
So, this function:
void bar(int i) {
if(i) [[unlikely]] {
true_path();
} else [[likely]] {
false_path();
}
}
compiles to:
bar(int):
test edi, edi
jne .L4
jmp false_path()
.L4:
jmp true_path()
And this:
void bar(int i) {
if(i) [[likely]] {
true_path();
} else [[unlikely]] {
false_path();
}
}
compiles to:
bar(int):
test edi, edi
je .L2
jmp true_path()
.L2:
jmp false_path()
Notice, that the condition has changed: the first version jumps, if i
is non-zero, while the second one jumps if i
is zero.
This is in agreement with the attributes: gcc generates code, where the conditional jump happens in the unlikely path.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With