Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does the compiler optimize the Null Coalescing operator when used for self assignment?

Tags:

.net

c#-4.0

These two code blocks are functionally the same

if (myObj == null)
{
    myObj = new MyObj();
}

and

myObj = myObj ?? new MyObj();

However, the one using the null coalescing operator does an unnecessary assignment in the case where myObj is not null. But then I thought maybe the compiler optimizes these self assignments. Does anyone know if the compiler will notice what is going on and essentially convert the bottom snippet into the top one?

like image 931
JeremyWeir Avatar asked Jun 23 '11 00:06

JeremyWeir


2 Answers

For comparison purposes, I tried compiling both

object myObj = null;
myObj = myObj ?? new object();

and

object myObj = null;
if(myObject == null)
{
  myObj = new object();
}

inside of the Main method. (I am using MonoDevelop 2.4 on Mono 2.6.7)

If the code were optimized as expected, we should see similar IL being generated. Here is the IL for the first version of Main:

.method public static  hidebysig 
       default void Main (string[] args)  cil managed 
{
    .entrypoint
    .maxstack 3
    .locals init (
            object  V_0)
    IL_0000:  ldnull 
    IL_0001:  stloc.0 
    IL_0002:  ldloc.0 
    IL_0003:  dup 
    IL_0004:  brtrue IL_000f

    IL_0009:  pop 
    IL_000a:  newobj instance void object::'.ctor'()
    IL_000f:  stloc.0 
    IL_0010:  ret 
}

and for the second version:

.method public static  hidebysig 
       default void Main (string[] args)  cil managed 
{
    .entrypoint
    .maxstack 1
    .locals init (
            object  V_0)
    IL_0000:  ldnull 
    IL_0001:  stloc.0 
    IL_0002:  ldloc.0 
    IL_0003:  brtrue IL_000e

    IL_0008:  newobj instance void object::'.ctor'()
    IL_000d:  stloc.0 
    IL_000e:  ret 
}

So the first version (using the null-coalescing operator) has generated slightly more IL.

But there are two things to note about this:

  1. This IL is what I got using MoveDevelop - if you compile it in Visual Studio, it could very well have been a different story. Perhaps it is optimized in Microsoft's with compiler. And even if it's the same with them, another person's compiler could easily optimize this. Just because something holds for some compilers, doesn't mean you would expect it from everybody's compiler.
  2. The CLR makes use of a JIT. Even if this wasn't optimized at compile-time, it probably would have been optimized at run-time. In fact, this may very well be why the compile isn't so concerned about such a micro-optimization.
like image 96
Ken Wayne VanderLinde Avatar answered Oct 20 '22 09:10

Ken Wayne VanderLinde


Just tried it in LinqPad:

void Main()
{
    MyObj myObj = null;
    myObj = myObj ?? new MyObj();
}

This gives the following IL:

IL_0000:  ldnull      
IL_0001:  stloc.0     
IL_0002:  ldloc.0     
IL_0003:  dup         
IL_0004:  brtrue.s    IL_000C
IL_0006:  pop         
IL_0007:  newobj      UserQuery+MyObj..ctor
IL_000C:  stloc.0     

So it seems the assignment (stloc.0 at IL_000C) is done whether or not myObj was null... but perhaps the JIT optimizes this later.

like image 37
Thomas Levesque Avatar answered Oct 20 '22 09:10

Thomas Levesque