27
if (x == null) x = new X();

versus

x = x ?? new X();

which of these two is actually more performant? once compiled do they effectively wind up as the same thing (would x = x; be a NO-OP as a result)?

6
  • I'd say the second one would be faster because the ?? operator has a specific purpose, but you can compile those two snippets and compare the generated IL and/or run a test to see which one is faster. Commented Feb 6, 2011 at 19:59
  • @MasterMax1313 the IL difference is not that much irrelevent that you downvote.. still i have updated my answer Commented Feb 6, 2011 at 20:26
  • Note that neither is thread-safe. And one usually expects getters to be thread-safe. I recommend using Lazy<T> if you really need lazy initialization. It is a bit slower but avoids several difficulties. Commented Feb 6, 2011 at 20:46
  • 1
    What is the definition of "performant"? ;-) Commented Feb 6, 2011 at 21:33
  • 1
    @Shekhar_Pro - I didn't down vote, your answer was already down voted when I noticed, which is partly why I said something, noticing that I'd made the initial mistake. Commented Feb 6, 2011 at 21:41

4 Answers 4

40

Looking at the intermediate language code there is a difference:

.method private hidebysig instance void Method1() cil managed
{
    .maxstack 8
    L_0000: ldarg.0 
    L_0001: ldfld class X Program::x
    L_0006: brtrue.s L_0013
    L_0008: ldarg.0 
    L_0009: newobj instance void X::.ctor()
    L_000e: stfld class X Program::x
    L_0013: ret 
}

.method private hidebysig instance void Method2() cil managed
{
    .maxstack 8
    L_0000: ldarg.0 
    L_0001: ldarg.0 
    L_0002: ldfld class X Program::x
    L_0007: dup 
    L_0008: brtrue.s L_0010
    L_000a: pop 
    L_000b: newobj instance void X::.ctor()
    L_0010: stfld class X Program::x
    L_0015: ret 
}

Here's the code I compiled to get this:

void Method1()
{
    if (x == null) x = new X();
}

void Method2()
{
    x = x ?? new X();
}

To be sure which is faster you should time both.

Method     Initial condition   Iterations per second
---------------------------------------------------
NullCheck  x is null           33 million  
Coalesce   x is null           33 million
NullCheck  x is not null       40 million
Coalesce   x is not null       33 million

Conclusion:

  • They are about the same in this case where the value is initially null.
  • The method using an if statement is considerably faster than the null coalescing operator when x is already not null.

The difference when x is not null looks like it might be due to the null coalescing operator assigning the value of x back to x (stfld in IL), whereas the null check jumps over the stfld instruction when x is not null.

Both are so fast that you'd have to have a very tight loop to notice the difference. You should only make these sorts of performance optimizations if you have profiled your code with your data. Different situations, different versions of .NET, different compilers, etc. may produce different results.

In case someone wants to know how I got these results or reproduce them, here's the code I used:

using System;

class X { }

class Program
{
    private X x;

    private X xNull = null;
    private X xNotNull = new X();

    private void Method1Null()
    {
        x = xNull;
        if (x == null) x = xNotNull;
    }

    private void Method2Null()
    {
        x = xNull;
        x = x ?? xNotNull;
    }

    private void Method1NotNull()
    {
        x = xNotNull;
        if (x == null) x = xNotNull;
    }

    private void Method2NotNull()
    {
        x = xNotNull;
        x = x ?? xNotNull;
    }

    private const int repetitions = 1000000000;

    private void Time(Action action)
    {
        DateTime start = DateTime.UtcNow;
        for (int i = 0; i < repetitions; ++i)
        {
            action();
        }
        DateTime end = DateTime.UtcNow;
        Console.WriteLine(repetitions / (end - start).TotalSeconds);
    }

    private void Run()
    {
        Time(() => { Method1Null(); });
        Time(() => { Method2Null(); });
        Time(() => { Method1NotNull(); });
        Time(() => { Method2NotNull(); });
        Console.WriteLine("Finished");
        Console.ReadLine();
    }

    private static void Main()
    {
        new Program().Run();
    }
}

Disclaimer: No benchmark is perfect, and this bechmark is far from perfect, mainly to keep things simple. I've run numerous different tests e.g. with the methods in a different order, with and without "warming up" first, over different lengths of time, etc. I get roughly the same results each time. I didn't have anything to prove one way or the other so any bias favoring one method or the other is accidental.

Sign up to request clarification or add additional context in comments.

3 Comments

Different IL doesn't necessarily mean the JIT translates them into two different pieces of machine code.
Bottom line - if you're calling this 20+ million times in your code in a 1-second interval, you'll notice a difference. Otherwise, it won't make any performance difference whatsoever.
Awesome :) Though you should consider using Stopwatch class in your Time method: msdn.microsoft.com/en-us/library/…
19

I wouldn't worry about this premature optimization. I'm sure the designers of the C# compiler were smart enough to do this for you.

Comments

5

There is no interresting performance difference at all. The most important thing is that if (x == null) x = new X(); is much more readable.

To answer your original question: a smart, optimizing compiler would compile x = x ?? new X() in the same way as if (x == null) x = new X();.

Hence leave micro-optimizations on the compiler and concentrate on code readability and clarity.


Update: To learn more about optimization practices read this article on wikipedia and then, in respect to the nature of your question, Google for “premature optimization is the root of all evil”.

9 Comments

That's a little bit subjective - Should be "is much more readable to me"
I completely agree; you should design for maintainability given that the performance difference is nil.
@PostMan From my point of view, and I'd bet most people feel it the same, any construct similar to x = x smells with side effects and causes the reader to actually have to think about it much more in contrast to the plain if.
I find the coalesce to be "much more readable" -- then again, I also find if-then-as-expressions "much more readable" (assuming same quality code) than if-then-as-statements-and-side-effects ;-) In any case, be consistent (whichever method is chosen), and follow project guidelines.
@Postman, @pst, most usages I've seen of null coalesce were added simply because majority of C# developers don't know about it (ie showing off). The majority of C# developers will see the simple if check as more readable than the null coalesce.
|
4

As others have mentioned, the performance difference between the two is not that different, however there is an important difference between the two, although it's not so apparent from your example.

if (x == null) x = new X(); creates a potential race condition where x can be newed up by another thread between the check for null and the creation, thus losing an object.

x = x ?? new X(); creates a copy of the variable first, thus no potential race condition.

This is more of a problem in situations where you are referencing the object. For instance:

if (x != null) x.DoSomething();  // this can cause a null reference exception
                                 // if another thread nulls x between the check
                                 // and the call to DoSomething()

(x = x ?? new X()).DoSomething()     // this should be fine.

4 Comments

This doesn't prevent race conditions in general (e.g. it should not be taken as any form of implicit "threading protection" without a very refined and analyzed context) -- it merely ensures that the latter case is always invoked upon a non-null object (whatever it may be). I would up-vote, but I'm out today :(
-1 your reasoning is wrong — 1) mentioning a variable just once in an expression does not mean the memory location will be accesses just once during evaluation of that expression, 2) thread-safety / variable read/write atomicity (race condition prevention) hasn't nothing to do with this case.
@Ondrej Tucny - I said nothing about mentioning a variable once. I was referring to the IL posted by others, where the coalescing version makes a copy of the variable before it's used, while the if version does not. and I also never said anything about thread safety or atomicity.. I was refering only to the fact that it's possible tog et a null reference exception with the if version, while the ?? version you wouldn't. Whether or not the state is correct is a different story. I'd vote you down for making assumptions, especially ones that aren't valid... if I could.
Saying that x = x ?? … avoids a race condition is simply wrong. Making a presumption that a certain IL gets generated by an arbitrary C# compiler (i.e. without making a reference to a specific compiler version and platform) or that the IL gets translated into a race-condition-free machine code is just plain risky. Anyway, you are free to down vote my own answer, if that helps :-)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.