Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Removing branches via bitwise select

I was told that the branches in the code

int value = //some number;
if(value > some_other_value)
   value *= 23;
else
   value -= 5; 

can be eliminated via bitwise masking (in order to enable SIMD optimization for the code):

const int Mask = (some_other_value-value)>>31;
value =      ((value * 23)&Mask)|((value-5)&~Mask);

However, I do not understand how this works (even though I understand what operations are being used here and how the results will look in binary). Furthermore, how generally applicable is this? What if the original code was instead something like

if(value & 1 == 1)
   value *= 23;
else
   value -= 5;

Would the branch-removed code still be the same? Otherwise, what is the purpose of the mask and how should I go about creating it? What is happening here?

like image 325
TravisG Avatar asked Oct 06 '22 05:10

TravisG


1 Answers

This works:

const int Mask = (some_other_value-value)>>31;
value =      ((value * 23)&Mask)|((value-5)&~Mask);

Mask becomes the sign bit of some_other_value - value - similar to:

if (value > some_other_value) mask = -1; else mask = 0; 

You could achieve the same thing with your second example, using:

mask = -(value & 1);

So, -0 = 0, -1 = all ones.

Edit: I would also bear in mind that if the calculation gets too complicated, you are not gaining anything over the branching version, particularly not if the branches are reasonably predictable.

like image 97
Mats Petersson Avatar answered Oct 07 '22 17:10

Mats Petersson