Quick Tip: Branchless Programming

Branchless programming, in a general sense, refers to the use of mathematical operations utilizing conditional bools (i.e. true or false) in place of if/else statements.

Here’s a good primer that speaks to how it’s used and why it matters. The short version is this: if/else statements take a little bit of excess processing power because both branches of the if/else (that is, what happens when your condition is met and what happens when it’s not) are temporarily committed to memory. The stuff that’s associated with the ‘wrong’ branch is just tossed out when the expression figures out if the condition is met. It’s like the After Effects expression engine is an eager personification of a calculator, shivering, sweaty with excitement, getting both answers ready before you’ve even asked it to do that.

Here’s a quick, simple After Effects example that illustrates the concept. Let’s say you want to flip the opacity of a layer completely off when the scale falls below 20%.

This is an if/else statement to accomplish that task:

if (scale[0] >= 20){ // x-scale greater than or equal to 20
value;
} else {
0;
}

There’s absolutely nothing wrong with this expression. It will serve you well. What I’d like to draw your attention to, though, is the fact that the condition statement in the if/else [in this case, (scale[0] >= 20)], for all intents and purposes, works out to a 0 when false and a 1 when true. With this in mind, take a gander at this execution of the operation above:

value * (scale[0] >= 20);

It accomplishes effectively the same thing as the if/else statement. When the scale (the x-dimension of the scale, that is) is less than 20, the condition works out to 0.

The above example is kind of a cherry-picked best-case-scenario for the savings on typing. Calculating a 0 for your final expression result with this method is very easy.

Here’s another example of an if/else that we can accomplish without branches. Let’s say we want to control the size of a Rectangle Shape layer based on the y-position relative to a null object’s y-position. Let’s say our rectangle size is keyframed, and we want it to be half the size when the layer drops below the null (which we’ll call “groundNull“):

nPos = thisComp.layer("groundNull").position;
pos = position;
if (pos[1] <= nPos[1]){
value;
} else {
value*.5;
}

Again. This expression is perfectly valid. It will accomplish what you need it to. Let’s take a look at the branchless version. We just need to keep in mind how to write the inverse of the condition.

nPos = thisComp.layer("groundNull").position;
pos = position;
value * (pos[1] <= nPos[1]) + (value*.5)*(pos[1] > nPos[1]);

What you’ll notice is that things seem to get a little convoluted and redundant. First, take note that we need to explicitly write the inverse of the original condition to catch the rest of the cases when the original condition is not true.

  • (pos[1] <= nPos[1]) [y-position is less than or equal to groundNull y-position]
  • (pos[1] > nPos[1]) [y-position is greater than groundNull y-position]

We’re using a simple addition operation to make sure both possibilities of the conditions make their way to the final result.

Now– the astute among you might holler at me…”But Steve, doesn’t this expression have to calculate both results just like an if/else statement does?! Is there really any time savings at all with this example?!” You’re right. At the scope of these simple example expressions, you’re likely not going to notice any change in how fast your frames are calculated. I think this branchless workflow is best used if you have nested if/else statements that can be accomplished using simple math. (The point of this post, really, is to get you comfortable with the fact that you can use the bool resulting from a condition as a 0 or a 1 in a math operation.) Let me rewrite the rectangle size expression from above to see if I can consolidate some of the logic and refactor the math.

nPos = thisComp.layer("groundNull").position;
pos = position;
value * (1 - .5*(pos[1] > nPos[1]));

You’ll notice that I’m using the inverse of my original condition in the example. The reason for doing that will make sense in a second. (Spoiler alert: it just makes the math look prettier.) It’s easiest to understand the example above by testing out what happens when the condition is replaced with a 0 or a 1.

In the event that the y-position of the layer is greater than the y-position of the groundNull:

nPos = thisComp.layer("groundNull").position;
pos = position;
value * (1 - .5*(1));

The stuff in the parentheses works out, in this case, to (1 - .5)… or, yes, you got it… (.5). The calculated result is value*(.5).

In the event that the y-position of the layer is less than or equal to the y-position of the groundNull:

nPos = thisComp.layer("groundNull").position;
pos = position;
value * (1 - .5*(0));

The parenthesized result works out to (1 - 0)… and yeah… I see you understanding this jam…. I can smell it…. it’s (1). The result of value * (1) is value. Wow.

I can accomplish the same thing, if I wanted to use the original condition (pos[1] <= nPos[1]) (less than or equal to), just by altering the math.

nPos = thisComp.layer("groundNull").position;
pos = position;
value * (.5 + .5*(pos[1] <= nPos[1]));

I’ll leave you to internalize how that one works on your own. Hint: the math is just a slightly different route to getting the result of a .5 or a 1 in the parentheses.

Wrapping up

Because I don’t have a thorough understanding of how the Javascript or Legacy expression engines in After Effects work under the hood, I can’t tell you for sure if the introduction of branchless programming into your expressions is going to be a life-changing improvement to your frame calculation times. You should see it as simply a tool for the tool-belt. I personally prefer expressions that are more concise as opposed to verbose. If/else statements make for nice expressions because are easy to read and understand. The logic is separated and put into compartments. The drawback, though, is that they have the potential to be redundant– you might have lots of the same code copy/pasted between the two conditions just to get something working.

I like to think that any step to simplify my expressions (reducing the number of operations the expression has to do, that is) is a step that will help my preview/frame-calculation/render times… however slight. It’s not out of the ordinary for me to work on a composition that has hundreds of layers with identical expressions pasted on them. I have to believe that across those hundreds of layers, in a comp that’s hundreds of frames long, I have some control over how fast a preview or render happens– just by the way I write my expressions.

Published by thatsmadden

I'm an animation artist with an interest in code-driven motion.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: