# Null-coalescing Operator vs. Equals - Equals

I have been asked the following question over-and-over: which solution is the faster and better to use in the condition part of the ```if``` keyword, to branch based on a property which might be defined on a nullable reference type:
* to use the null-coalescing operator (??)
* or the equals operator (==)

To give some context let's assume that we branch our code based on a reference type's bool property:

```csharp
public class SomeClass
{
    public bool Condition { get; set; }
}
```

We might not be sure if the reference type has a value. To express this in a [nullable reference type](https://docs.microsoft.com/en-us/dotnet/csharp/nullable-references) semantics, we could say that we are testing against a property defined as:

```csharp
public SomeClass? Instance { get; set; }
```

## Benchmarkig the code

To perform a benchmark comparison, I use the [BenchmarkDotNet](https://benchmarkdotnet.org/) library from NuGet.

I have two set of tests, one where the *Instance* property has an actual object assigned to its backing field, and one where it returns null.

The actual initial benchmark methods:

```csharp
[Benchmark]
public int EqualsEquals()
{
    if (Instance?.Condition == true)
        return Param1;
    return Param2;
}

[Benchmark]
public int QuestionmarkQuestionmark()
{
    if (Instance?.Condition ?? false)
        return Param1;
    return Param2;
}
```
In the ```EqualsEquals```  method I am using a comparison with the ```==``` operator against ```true```, so if the *Instance* is not null and the Condition property is ```true```, *Param1* is returned, otherwise *Param2*.

In the ```QuestionmarkQuestionmark``` method I am using the ```??``` operator and substitute the ```null``` case with ```false```. When the *Instance* is not null and Condition property is ```true```, *Param1* is returned, otherwise *Param2*. 


Both *Param1* and *Param2* are parameterized by BenchmarkDotNet ```[Params]``` attribute, to avoid hardcoded values and shortcuts by the compiler.
These tests also have a return value to avoid dangling code optimizations.

I am using .net core 2.2 to perform the benchmarks.

```
BenchmarkDotNet=v0.11.5, OS=Windows 10.0.17134.950 (1803/April2018Update/Redstone4), VM=Hyper-V
Intel Xeon CPU E5-2673 v3 2.40GHz, 1 CPU, 2 logical and 2 physical cores
.NET Core SDK=2.2.203
  [Host]     : .NET Core 2.2.4 (CoreCLR 4.6.27521.02, CoreFX 4.6.27521.01), 64bit RyuJIT
  DefaultJob : .NET Core 2.2.4 (CoreCLR 4.6.27521.02, CoreFX 4.6.27521.01), 64bit RyuJIT
```

|                       Method | Param1 | Param2 |      Mean |     Error |    StdDev |    Median |
|----------------------------- |------- |------- |----------:|----------:|----------:|----------:|
|                 EqualsEquals |     18 |     12 | 0.0074 ns | 0.0139 ns | 0.0116 ns | 0.0000 ns |
|     QuestionmarkQuestionmark |     18 |     12 | 0.0688 ns | 0.0425 ns | 0.0650 ns | 0.0613 ns |
|             EqualsEqualsNull |     18 |     12 | 0.4651 ns | 0.0533 ns | 0.1065 ns | 0.4506 ns |
| QuestionmarkQuestionmarkNull |     18 |     12 | 0.4673 ns | 0.0548 ns | 0.1167 ns | 0.4452 ns |

> *MethodName* suffixed with 'Null' indicates when the instance is null.

The initial benchmarks show significant difference in the case when the *Instance* property was not null. Comparison with the equals operator is significantly faster. Having a second look, we may notice that the Error column shows a larger value in case of the null-coalescing operator. Is this just some negligible difference caused by measurement errors?

Repeating the tests confirms that measurement errors may occur for the ```EqualsEquals``` testcase as well, having the results in opposite ratio. To mitigate the measurement error, I increase the test length with an iterator and scale the results with ```OperationsPerInvoke``` attribute:

```csharp
[Benchmark(OperationsPerInvoke = 10000)]
public int QuestionmarkQuestionmark()
{
    int result = 0;
    for (int i = 0; i < 10000; i++)
    {
        if (Instance?.Condition ?? false)
            result = Param1;
        else
            result = Param2;
    }
    return result;
}
```
Repeating the benchmarks now show a more balanced result:

|                       Method | Param1 | Param2 |      Mean |     Error |    StdDev |
|----------------------------- |------- |------- |----------:|----------:|----------:|
|                 EqualsEquals |     18 |     12 | 0.8224 ns | 0.0162 ns | 0.0373 ns |
|     QuestionmarkQuestionmark |     18 |     12 | 0.7757 ns | 0.0150 ns | 0.0220 ns |
|             EqualsEqualsNull |     18 |     12 | 0.8161 ns | 0.0173 ns | 0.0337 ns |
| QuestionmarkQuestionmarkNull |     18 |     12 | 0.7992 ns | 0.0160 ns | 0.0323 ns |

## What is inside the box?

Are these solution equivalent? Do they produce the same IL and machine code? These are the questions I am answering in the following sections.

First, let's compare the generated IL for both solutions.

The ```EqualsEquals``` method - without the for loop - has the following IL code. For the better understanding I add some description as comments after each line.

```IL
.method public hidebysig 
	instance int32 EqualsEquals () cil managed 
{
	// Method begins at RVA 0x226d
	// Code size 34 (0x22)
	.maxstack 8

	// SomeClass instance = this.Instance;
	IL_0000: ldarg.0 // Loads 'this' 
	IL_0001: call instance class IfNullTestBenchmark.SomeClass IfNullTestBenchmark.Program::get_Instance()
	// if (instance != null && instance.Condition)
	IL_0006: dup // Duplicates topmost value on the evaluation stack, at this point the value of Instance property
	IL_0007: brtrue.s IL_000d // If the value is not null, jumps to IL_000d

	// (no C# code)
	IL_0009: pop // If the value is null, pop the top of eval stack (Instance property)
	IL_000a: ldc.i4.0 // Pushes the integer value of 0 onto the evaluation stack 
	IL_000b: br.s IL_0012 // Jumps to IL_0012

	IL_000d: call instance bool IfNullTestBenchmark.SomeClass::get_Condition()

	IL_0012: brfalse.s IL_001b // Transfers control to a target instruction if value is false, a null reference, or zero. If the code jumped here from IL_000b, this condition will always be false, so it jumps ahead to IL_001b. Otherwise it branches based on the value of the Condition property.

	// return this.Param1;
	IL_0014: ldarg.0
	IL_0015: call instance int32 IfNullTestBenchmark.Program::get_Param1()
	// (no C# code)
	IL_001a: ret

	// return this.Param2;
	IL_001b: ldarg.0
	IL_001c: call instance int32 IfNullTestBenchmark.Program::get_Param2()
	// (no C# code)
	IL_0021: ret
} // end of method Program::EqualsEquals
```

Comparing this with the ```QuestionmarkQuestionmark```, the only difference we may notice is the name of the method.

The final question I have, if there are any differences (optimizations) made by the JIT compiler to the above logic of the IL code. To check the machine code, I will use [WinDBG](https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/debugger-download-tools) with some inserted breakpoints, where I may examine the machine code.

* Fire up WinDBG
* Load [SOS extension](https://docs.microsoft.com/en-us/dotnet/framework/tools/sos-dll-sos-debugging-extension) for the coreclr
* Find the method in the method table with *!name2ee* command
* Proint the Jitted code with !u

```
0:008> .loadby sos coreclr
0:008> !name2ee IfNullTestBenchmark!Program.EqualsEquals
Module:      00007ff99f674590
Assembly:    IfNullTestBenchmark.dll
Token:       000000000600001a
MethodDesc:  00007ff99f6755c8
Name:        IfNullTestBenchmark.Program.EqualsEquals()
JITTED Code Address: 00007ff99f791590
0:008> !U /d 00007ff99f791590

[path]\IfNullTestBenchmark\Program.cs @ 27:
>>> 00007ff9`9f791590 488b4108        mov     rax,qword ptr [rcx+8]
00007ff9`9f791594 4885c0          test    rax,rax
00007ff9`9f791597 740c            je      00007ff9`9f7915a5
00007ff9`9f791599 0fb64008        movzx   eax,byte ptr [rax+8]
00007ff9`9f79159d 85c0            test    eax,eax
00007ff9`9f79159f 7404            je      00007ff9`9f7915a5
00007ff9`9f7915a1 8b4110          mov     eax,dword ptr [rcx+10h]
00007ff9`9f7915a4 c3              ret
00007ff9`9f7915a5 8b4114          mov     eax,dword ptr [rcx+14h]
00007ff9`9f7915a8 c3              ret
```

Looking at the code, we may notice that the additional jump (when *Instance* was null in the IL code) has been optimized away, and the logic stands a lot closer to the logic of the C# method. It can be also confirmed that the ```QuestionmarkQuestionmark``` compiles exactly to the same code.

So are there any differences between the two approaches? It seems that the current C# compiler and RyuJIT compiles our code exactly to the same machine code, leaving us only with the textual difference in the source code.
So which one shall I use? I think it is up to the developer, whichever is easier to read and understand in the given context. I would choose the one which has a smaller mental weight in the given context.