The Is, As and the Is-As

C# has recently introduced with C# 7 a couple of new syntax sugars. I this post, I will investigate the new is keyword from IL standpoint and from performance standpoint. I will investigate by analyzing the generated IL Code and by micro benchmarking the instructions.

In my tests I repeat and measure the time it takes to run these instructions in a loop of 1000000000. I elimininate the best and worst case, and calculate an average on the rest.

The book Pro .Net Performance has a chapter on micro benchmarking the is and as keywords. It starts with a false assumption that the 'as' keyword is faster compared to the 'is', while the real conclusion of the chapter would be to consider all details of micro benchmarking to avoid making false assumption.I will have a simple 'My' class created for the tests. Note, the NoInlining attribute on the Work method to avoid any optimization when invoking the method.

public class My
{
  public int a = 0;
  
  [MethodImpl(MethodImplOptions.NoInlining)]
  public void Work() {}
}

The is as it was

First of all, let's see what we can do with the is keyword. Before pattern matching the is keyword was used only to test type compatibility.

if(a is My)
{
  ((My)a).Work();
}

The average of running this is 3445.2ms. Let's see the IL Code generated:

old-is

Let's change the Work method call to incrementing a field of a, (a++), this results an average of 2577.6 ms. As seen, this is faster.

old-is2

The as keyword

As I earlier believed, the as keyword is faster. Let's see if it really is. I run and measure the following code:

var b = a as My;
if(b != null)
{
  b.Work();
}

When I test this code, the average is 2617.8ms which is definitely faster compared to the is counterpart. Though the IL code shows no sign or reason for this to be faster:

as1

It uses the same isinst and callvirt instructions as in the case of is. I am going to avoid further investigation int this post (will do in the following), as it is well-detailed in the book above mentioned. So can we make a generalized statement that 'as' is better because it is faster. Well, let's see the other use case, where we increment the field of a.

as2

In this case the average is 2824.8ms. Interesting enough, this case is not faster compared to the 'is'. When we look at the IL, it is pretty much the same, and does not give us an explanation, it is all up to the JIT compiler.

Pattern matching with is

The point of this post is to compare the is when pattern matched.

if(a is My b)
{
  b.Work();
}

This given code runs in an average of 2591ms. The related IL code looks a bit more interesting:

is-new

It uses the same isinst but instead of storing once and loading twice (as incase of of 'as') it simply uses dup, but, generally, from IL it looks pretty much similar.

Repeating the same with incrementing a field, the average is 2861.4ms, and the IL code:

is-new

So the performance of the pattern matched is keyword is rather similar to the as. Also note, how ILSpy is actually reflecting back to as and a null check in C# syntax what we have previously written as a is My b.

In the next post, I will investigate the same code, but through the lense of WinDBG.