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)?
An int can't be null .
You should initialize your variables at the top of the class or withing a method if it is a method-local variable. You can initialize to null if you expect to have a setter method called to initialize a reference from another class.
you cannot assign null to a var type. If you assign null the compiler cannot find the variable you wanted in var place. You can assign some empty values.
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:
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With