Sometimes a tiny amount of code costs a huge amount of performance. This is especially true of built-in language features, which many programmers assume to be extremely cheap if not free. Today we’ll look at if and see just how much performance it can cost your app. Read on to see!

Today’s article is a follow-up to an article about AS3 from 2014. AS3 had a lot of issues related to performance, so it’s good to validate with another language to make sure it wasn’t some weird quirk related to Flash. In this case, we’ll port that test to C# and see if we can replicate the results.

To check it out, let’s run two very similar loops. Both of them just compute a simple sum, but one of them has an if. Here’s how those loops look:

// No "if"
int sum = 0;
for (var i = 0; i < REPS; ++i)
{
	int cur = i & 1;
	sum += cur;
}
 
// Includes an "if"
sum = 0;
for (int i = 0; i < REPS; ++i)
{
	int cur = i & 1;
	if (cur > 0)
	{
		sum += cur;
	}
	else
	{
		sum += cur;
	}
}

Notice that both the if and else cases do the same work: adding to the sum. So the result will be the same for both loops.

Now let’s check the timing for those loops with a very large REPS value to see how long they take. Here’s the full script:

using UnityEngine;
 
public class TestScript : MonoBehaviour
{
	private string report;
 
	void Start()
	{
		const int REPS = 1000000000;
 
		var sw = System.Diagnostics.Stopwatch.StartNew();
		int sum = 0;
		for (var i = 0; i < REPS; ++i)
		{
			int cur = i & 1;
			sum += cur;
		}
		var noBranchTime = sw.ElapsedMilliseconds;
 
		sw.Reset();
		sw.Start();
		sum = 0;
		for (int i = 0; i < REPS; ++i)
		{
			int cur = i & 1;
			if (cur > 0)
			{
				sum += cur;
			}
			else
			{
				sum += cur;
			}
		}
		var branchTime = sw.ElapsedMilliseconds;
 
		report =
			"Operation,Time\n" +
			"No Branch," + noBranchTime + "\n" +
			"Branch," + branchTime;
	}
 
	void OnGUI()
	{
		GUI.TextArea(new Rect(0, 0, Screen.width, Screen.height), report);
	}
}

If you want to try out the test yourself, simply paste the above code into a TestScript.cs file in your Unity project’s Assets directory and attach it to the main camera game object in a new, empty project. Then build in non-development mode for 64-bit processors and run it windowed at 640×480 with fastest graphics. I ran it that way on this machine:

  • 2.3 Ghz Intel Core i7-3615QM
  • Mac OS X 10.11.3
  • Unity 5.3.4f1, Mac OS X Standalone, x86_64, non-development
  • 640×480, Fastest, Windowed

And here are the results I got:

Operation Time
No Branch 696
Branch 873

Branching Performance Chart

The version with the if-else block took 25% longer than the version without one. Again, both versions result in the same sum, so the if-else block isn’t adding anything to the output.

There are multiple potential reasons for this performance discrepancy. First, there is an actual cost to do the if: comparisons and jumps. Second, modern CPUs attempt to predict which instructions will be executed and the presence of an if means that it’ll be wrong sometimes. In the case of this test, it’s wrong half the time and that can diminish the performance of the version with if.

The simple takeaway from this test is to remember that two simple letters—if—can carry a huge performance cost. Avoid branching with if-else or switch in performance-critical code if you can manage it. Let me know in the comments if you’ve found any performance boosts like this, such as by removing an if!