Null references D:

Checking for null references in Unity «There's no reference, only NULL»

Early, when learning how to develop games in Unity, programmers come across with the need to check for null references. Null checks are necessary to avoid exceptions when trying to access or make use of an empty position in memory.

The problem is that there are several ways to check for them and (surprise, surprise) they aren’t equal in terms of performance. To answer the ultimate question of which is the best way to check for null references in Unity (using C#), I decided to do some tests and determine once and for all who’s boss.

About the tests

The experiments were made using the guidelines presented in Jackson Dunstan’s article, Unity Script Performance Testing, where he recommends the System.Diagnostics.Stopwatch class to measure the elapsed time between a set of instructions.

I also used ODU (Osom Debugger for Unity), a plugin made by Jorge Palacios to output debug messages inside the game screen, for two reasons:

  1. The test involves running the code, first inside the Unity editor and later as built binary.
  2. It was quicker for me than checking the game’s log.
ODU in action
ODU in action

Several methods to check for null references were tested, each one over a loop with different levels of iteration. All methods followed this structure:

All the tested methods were native to either Unity or C#, except for IsNull() which was an extension method I created, defined as:

Also, the tests were performed using the following specs and were run in both the Unity editor and as standalone binary executables:

OS Windows 10 (64-bit)
Engine Unity 5.1.3f1 (64-bit)
Processor 2.2GHz Intel Core i7-2670QM
Standalone config. 640×480, Fastest, Windowed

The results were registered on a table that holds the execution time (in milliseconds) after 1, 10 and 100 million iterations. Also, a column labeled as Correctness holds whether the current method reported the expected value given the initial input.

It’s worth to notice that, in all tests, a variable go of type GameObject was used but never initialized (i.e. they are implicitely initialized to null by Unity), and the scene only had the script with the tests (plus the default camera).

Results inside Unity

# of Millions of Iterations
(results in milliseconds)
Method Correctness 1M 10M 100M
(object) go == null false 5 58 575
Object.ReferenceEquals(go, null) false 12 130 1342
object.ReferenceEquals(go, null) false 12 126 1345
go.IsNull() false 13 129 1225
GameObject.Equals(go, null)) false 17 179 1879
!go true 90 913 9068
go == (GameObject) null true 92 913 9004
go == null true 91 919 9087

Results outside Unity

# of Millions of Iterations
(results in milliseconds)
Method Correctness 1M 10M 100M
(object) go == null true 0 3 36
Object.ReferenceEquals(go, null) true 0 7 71
object.ReferenceEquals(go, null) true 0 7 72
go.IsNull() true 0 7 75
GameObject.Equals(go, null)) true 3 40 366
!go true 7 75 730
go == (GameObject) null true 7 76 792
go == null true 8 79 783

Analysis of the results

There are two things to keep in mind: execution time and the Is Null? field (which I haven’t explained yet).

First things first, the execution time. Both experiments resulted in (object) go == null being the fastest of them all, executing at less than 5% of the time that other popular solutions like go == null needed. It also needed half the time it took IsNull(), an extension method that internally used (object) go == null. It’s lightning fast.

Why is it faster? I guess it’s because it doesn’t need to perform dynamic casting, unboxing operations or change of contexts from calling a function. Although I still think it looks cleaner to write go.IsNull() than using an explicit ugly cast such as (object) go.

Now, regarding that Is Null? field, there’s this thing you should know about Unity. After doing some research over the internet, I found a post on Unity’s blog, titled CUSTOM == OPERATOR, SHOULD WE KEEP IT?, that reveals a dirty little secret of theirs.

Long story short, Unity works with C/C++ under the hood, with C# being just a wrapper. As they mention, null objects are not really null in the C# meaning of the word and the == operator is overridden to make advantage of such design decision. That allows them to provide a better debugging experience to the developers, inside the Unity editor.

In other words, only inside the Unity editor, (object) go == null fails because it performs a C# null check while go == null doesn’t because it uses Unity’s custom checker. Sadly, this design decision means a slower execution. In fact, the results from within the editor show that the three alternatives that work (i.e. return true when it really is true) are around 7 times slower than the unaltered C# methods.

tl;dr

The best way to check for null references is by casting the variable to object, using C#’s native means in the process.

The caveat, however, is that such solution reports nulls correctly only when it runs outside the Unity editor. As a developer, you either need to use pre-processor directives with the best solution when running outside the editor and the traditional go == null approach inside the editor, or just stick with the traditional approach all the way down.

There’s also a third possibility: explicitly assign null to a reference whenever the variable is defined or after the component destroyed. In those cases, the true C# null value is used, instead of the one provided by Unity. In fact, if the tests inside the editor had been performed by explicitly saying go = null; instead of leaving the variable uninitialized, the program would have behaved as it should have.

In conclusion

As a sidenote, this issue only happens with objects that derive from UnityEngine.Objects as they are the ones that have the custom null checkers. Other types of objects, like stringListDictionary, etc. don’t suffer from this and they can be tested directly using the == null instruction without any kind of negative impact on the performance.

Also, despite the magnitude of difference in the results, between all the methods, there’s a silver lining to this. It took around 1 million iterations for the traditional go == null instruction to show lag compared to the fastest one (about 8 milliseconds of difference). That scenario is far away from a typical game run. Nevertheless, it’s important to keep these things in mind when trying to optimize your code; it may not seem a big deal, but those things can pile up very quickly resulting in a perplexing thing for the uninformed programmer.

2 thoughts on “Checking for null references in Unity «There's no reference, only NULL»

  1. I make more tests.

    In editor if we set variable to null in function we will get correct results. For example”:
    void Awake ()
    {
    go= null;
    }

    Wrong results we get if we not set nothing to variable or if we set null to variable in declaration. For example:
    GameObject go;
    or
    GameObject go=null;

  2. So if I understand properly, the best solution working in both editor and standalone build would be something like this?

    public static bool IsNull(this GameObject go)
    {
    #if UNITY_EDITOR
    return go == null;
    #else
    return (object) go == null;
    #endif
    }

Leave a Reply