Micro-optimization #1: Setting a `done` flag using bitwise operators

I’m planning a series of very brief micro-optimization notes, both for my own records and to help anyone else who may be looking at some optimizations. I plan to provide minimal code, results, and brief explanations.

In this case, I came across a bitwise |= for setting a done flag in one of Penny de Byl’s Udemy courses. I was curious and decided to see if it was an optimization. It felt like it would be, but I didn’t expect by much. Sure enough, it is, but not by much. Still, if it’s a function that you have a lot of while-loops in your code checking against a boolean value, it could be handy.

The code:

static void Main(string[] args)
{
	for (int i = 0; i < 10; i++)
	{
		DoneTest1();
		DoneTest2();
	}
	Console.Read();

	Environment.Exit(0);
}

static void DoneTest1()
{
	bool done = false;
	int x = 0;
	int xSize = 100_000_000;

	while (!done)
	{
		x++;
		done |= (x < 0 || x >= xSize);
	}
}

static void DoneTest2()
{
	bool done = false;
	int x = 0;
	int xSize = 100_000_000;

	while (!done)
	{
		x++;
		if (x < 0 || x >= xSize)
			done = true;
	}
}

The results:

Using done |=  : 112ms  (1122354 ticks).
Using if  : 151ms  (1518356 ticks).
Using done |=  : 107ms  (1073112 ticks).
Using if  : 129ms  (1298421 ticks).
Using done |=  : 127ms  (1275415 ticks).
Using if  : 141ms  (1414998 ticks).
Using done |=  : 111ms  (1112100 ticks).
Using if  : 127ms  (1273705 ticks).
Using done |=  : 108ms  (1086612 ticks).
Using if  : 140ms  (1400030 ticks).
Using done |=  : 127ms  (1271739 ticks).
Using if  : 128ms  (1282120 ticks).
Using done |=  : 108ms  (1089749 ticks).
Using if  : 111ms  (1118823 ticks).
Using done |=  : 108ms  (1086191 ticks).
Using if  : 110ms  (1100477 ticks).
Using done |=  : 100ms  (1002949 ticks).
Using if  : 113ms  (1131274 ticks).
Using done |=  : 104ms  (1040928 ticks).
Using if  : 110ms  (1101986 ticks).

Each iteration shows a better performance using the bitwise |= compare and set rather than the if-statement. Across the ten iterations, the bitwise averaged 111.2ms while the if-statement averaged 126.0ms which amounts to a ~11.75% increase in performance. The bulk of the time in each set is, of course, the computer counting to 100,000,000, but given that the only difference in the two methods is the check for setting the flag, the variance is accounted for by that difference.

The Reason:

Bitwise operations on most architectures are almost always faster than other calculations, and branching statements are typically computationally heavier. When bitwise operations are an option, they usually result in more performant if less readable code.

Methodology:

For those of you not familiar with benchmarking in C#, I typically use the .NET Stopwatch class (System.Diagnostics.Stopwatch). I’ve removed the Stopwatch() code for brevity in the code example above. So long as you Start, Stop, read, and Reset your stopwatch in appropriate locations, you don’t need to worry about setup and other functionality as the only portion times is what is wrapped between Start and Stop.

I also run the program (usually a .NET Core console application) as a release executable rather than a debug executable to ensure performance isn’t being bogged down by the debugger for any reason.

Lastly, I try to run ten (or more) iterations of each thing that I’m testing. As you can see in the results, timing can vary for all manner of reasons. Sometimes the first execution of a code block is slower than subsequent executions. I also try to interleave each method or function being tested (e.g.: 1, 2, 1, 2, 1, 2, 1, 2 rather than 1, 1, 1, 1, 2, 2, 2, 2) to help ensure the code block isn’t being cached and repeated. Running only a single iteration is often misleading. In this case, all ten iterations of the bitwise comparison were faster, but it’s often the case the the slower of two methods might have a small percentage of faster executions and running single iteration may provide incorrect information about which is typically faster.

What to do, what to do…?

Still playing with some new Unity 2020 features, still dabbling on Labyrintheer as well as a few other projects. Learning a bit of Machine Learning just for fun, figuring out the ins and outs of HDRP and RT, and generally using this lovely COVID pandemic as time to reset a bit and figure out what I actually want to develop sooner rather than later.

For whatever it may be worth to you, if you are interested in Machine Learning, either specific to Unity, or more generally, I cannot recommend Penny deByl’s courses on Udemy highly enough. All of her courses are great, and the ML and AI courses are no different.

https://www.udemy.com/course/machine-learning-with-unity/

While the course is ostensibly about Unity and the development takes place within Unity, it isn’t specific to the Unity ML-Agents (though there are sections for that). The bulk of the course is geared toward developing your own agents and brains in C#, which is fantastic whether you want to use Unity’s ML-Agents or not.

In the immediate now, I’ve been working on some Unity code to create a system of CCTVs and monitors to show them. It’s a core component of a potential game idea I’m futzing with at the moment. I expect to have some pictures or videos in the next week or two to show off. Until then, Happy Thanksgiving 2020.