If Only I Had Switched To Branch-less Coding Sooner

One of the first lines of code you ever learn is a conditional statement, such as an “If Then” or “Switch” (aka “Select Case” in VB). But I didn’t find out that they can be expensive to use until I got into Shader code. A GPU has numerous cores to handle millions of pixels, so one branch statement is exponentially costly. In any case, the pitfall you want to avoid is branching inside a loop.

I experimented a bit with some ideas and benchmarked them to find out how expensive certain operations can be, so some of this might be unnecessary to use. But still, it’s a different way of thinking about things, which is always helpful somehow.

Mostly what I learned from all this is that the IIf() Function and the If() Operator are two very different things. In VB the Ternary Operator (bool ? true : false) is done using the “Inline If” Function or IIf(bool, true, false). It’s not quite the same thing as an If Statement or using If() as a Function, because it evaluates both Parameters, whereas the If(b,t,f) Operator only evaluates one branch based on the b Condition.

Writing branchless code can often involve turning a Condition, i.e. (x > 0), from a Boolean True or False into a Zero or One. In many languages True = -1, so negating it like -(x > 0) gives you the +1 you’re after. Other times you may want to turn a Boolean into either -1 or +1, in which case use (-(x > 0) * 2) - 1, or ((x > 0) * -2) - 1. I’m curious if there’s any difference between those on the assembly level.

Multiplying a number by either 1 or 0 will give you the same number or nothing, and a -1/+1 will flip a number’s polarity. So when deciding If you want one of two numbers, you can multiply each of them by opposite versions of the Boolean Condition from the If statement, and then add them together. One will zero out, and the other remains. The overall effect of the calculations might be no change, which is sometimes what you wanted the If Statement for.

A good example is a branch-less version of the Absolute Value function:

Public Function ABS(x As Single) As Single
    Return x * ((x > 0) * -1 + (x < 0))
End Function

The Value x is multiplied by the whole Expression, which becomes 1, 0, or -1 depending on the two Sub-Expressions (x > 0) and (x < 0). If x is above 0, the end result is (1) + (0) which is 1, so x stays positive. Otherwise it's (0) + (-1) which equals -1, so it will flip x to positive, giving you the Absolute Value. If x is zero, neither Sub-Expression is true and the result is zero.

This next function will increment a value, and wrap it around to 0 when it reaches Max.

Public Function WrapPlus(Value As Integer, Max As Integer,
                         Optional IncrementBy As Integer = 1) As Integer
    Value += IncrementBy
    '// "If Value >= Max Then Value = 0 * Value Else Value = 1 * Value"
    Return -(Value >= Max) * Value
End Function

That one was simpler as it only involves keeping it the same or canceling it to zero. To do the opposite, we have to either keep the same Value, or set it to the Max value when Value has been pushed below 0:

Public Function WrapMinus(Value As Integer, Max As Integer,
                          Optional DecrementBy As Integer = 1) As Integer
    Value -= DecrementBy
    Return If(Value < 0, Max, Value)
End Function

There is another big difference between an If Statement and a Ternary Operator though. A Ternary only involves two Expressions, whereas an If or Switch (Select Case) Statement can branch off to any number of other Statements.

I’m sure I will cover this particular subject again in the future, since I am just finally starting to learn it now. Any examples you might have would be helpful. It’s strange how something so fundamental could be feel new to me after a lifetime of this. But that’s coding for you, no matter how far you get, it keeps going.

Leave a Reply