Related to this answer: https://stackoverflow.com/a/11227902/4714970
In the above answer, it's mentioned how you can avoid branch prediction fails by avoiding branches.
The user demonstrates this by replacing:
if (data[c] >= 128) { sum += data[c]; }
With:
int t = (data[c] - 128) >> 31; sum += ~t & data[c];
How are these two equivalent (for the specific data set, not strictly equivalent)?
What are some general ways I can do similar things in similar situations? Would it always be by using >>
and ~
?
Branchless programming is a programming technique that eliminates the branches (if, switch, and other conditional statements) from the program. Although this is not much relevant these days with extremely powerful systems and usage of interpreted languages( especially dynamic typed ones).
Example usage of branchless if The branching version (not shown) suffers terribly from branch prediction failures, but the branchless version is very fast (at least with clang).
I believe the most common way to avoid branching is to leverage bit parallelism in reducing the total jumps present in your code. The longer the basic blocks, the less often the pipeline is flushed.
A branch instruction is not inherently slower than any other instruction. However, the reason you heard that branches should avoided is because modern CPUs follow a pipeline architecture. This means that there are multiple sequential instructions being executed simultaneously.
int t = (data[c] - 128) >> 31;
The trick here is that if data[c] >= 128
, then data[c] - 128
is nonnegative, otherwise it is negative. The highest bit in an int
, the sign bit, is 1 if and only if that number is negative. >>
is a shift that extends the sign bit, so shifting right by 31 makes the whole result 0 if it used to be nonnegative, and all 1 bits (which represents -1) if it used to be negative. So t
is 0
if data[c] >= 128
, and -1
otherwise. ~t
switches these possibilities, so ~t
is -1
if data[c] >= 128
, and 0
otherwise.
x & (-1)
is always equal to x
, and x & 0
is always equal to 0
. So sum += ~t & data[c]
increases sum
by 0
if data[c] < 128
, and by data[c]
otherwise.
Many of these tricks can be applied elsewhere. This trick can certainly be generally applied to have a number be 0
if and only if one value is greater than or equal to another value, and -1
otherwise, and you can mess with it some more to get <=
, <
, and so on. Bit twiddling like this is a common approach to making mathematical operations branch-free, though it's certainly not always going to be built out of the same operations; ^
(xor) and |
(or) also come into play sometimes.
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