A trivial example is actually written with a branch in C/C++, but relies on compiler optimizations to kick in. If you compile a ternary operator in C/C++ (and probably rust, C# and other languages) such as in:
int min_branchless(int a, int b) {
return a < b ? a : b; // Often emits cmov with -O2
}
With gcc/clang a -O2, one would expect the compiler to emit the following assembly: cmp edi, esi
cmovle eax, edi ; select a if a <= b
ret
There's numerical tricks for other operations/comparisons, and compilers know a lot of them. But, I just suggest compiling your code and configuring your compiler to emit the generated assembly with references to the code it was generated from (you should be able to get it to emit source line references in the assembly). You'll likely be surprised at the optimizations applied at -02, and utterly confused by what you find at -03.edit: Also, it doesn't mean to never branch, but to minimize branching, especially in tight loops. Branch outside loops, not inside, for instance.
e.g. don't do:
for (...) {
if (condition independent of loop variable) {
...
} else {
...
}
}
do: if (condition independent of loop variable) {
for (...) {
...
}
} else {
for (...) {
...
}
}