C#: The Order of Interfaces Is Important for Casting Performance

Recently there was a discussion in a Pull Request for the .NET runtime on GitHub. In the discussion it was mentioned by Jan Kotas from Microsoft that the order of interfaces is important for the casting performance.

I never heard of that, and looks I’m not alone:

So, let’s see this in action with a little, simple example in .NET Core.

First, let’s define a few interfaces:

public interface IPersonFirst { }
public interface IPerson0 { }
public interface IPerson1 { }
public interface IPerson2 { }
public interface IPerson3 { }
public interface IPerson4 { }
public interface IPerson5 { }
public interface IPerson6 { }
public interface IPerson7 { }
public interface IPerson8 { }
public interface IPerson9 { }
public interface IPerson10 { }
public interface IPersonLast { }

Now let’s create a Person class that implements these interfaces

public class Person : IPersonFirst,
  IPerson0,
  IPerson1, 
  IPerson2,
  IPerson3,
  IPerson4, 
  IPerson5, 
  IPerson6, 
  IPerson7, 
  IPerson8, 
  IPerson9, 
  IPerson10,
  IPersonLast
{
}

As you can see in the code snippet above, the Person class implements all the interfaces.

The IPersonFirst interface is the first defined interface on the Person class. That means that casting an object to IPersonFirst should be faster than casting an object to any other IPerson interface. Casting to IPersonLast should have the slowest casting performance.

But why should casting to the first interface be faster?

In the CLR, class definitions have an array of implemented interfaces. That means that if you cast to the last interface defined on a class, the runtime needs to walk through the full array to do the cast. If you cast to the first interface, it doesn’t have to walk through the full array.

Of course, in both cases it is very fast for a few casts. But if you do millions of casts, for example in a server-side application, then it can be important!

Let’s see it in action!

To see this effect in action, I’ve written a simple .NET Core console app.
(Note: You could use also Unit Testing and Benchmarking tools/packages, but let’s keep it here very simple)

Below you see the Program class of that console app. In the Main method a person variable of type object is initialized with a new Person. Then the RunAction method is called. A lambda is passed to that method: First with a cast to IPersonFirst, then another time with a cast to IPersonLast. As we don’t care about the result of the casts, I just use discards (underscores) as assignment targets.

class Program
{
  const int castTimes = 10_000_000;
  static void Main(string[] args)
  {
    Console.WriteLine($"Casting {castTimes} times!");
    Console.WriteLine();

    object person = new Person();

    Console.WriteLine($"IPersonFirst:");
    RunAction(() => { _ = (IPersonFirst)person; });

    Console.WriteLine($"IPersonLast:");
    RunAction(() => { _ = (IPersonLast)person; });
  }

  private static void RunAction(Action action)
  {
    var watch = Stopwatch.StartNew();
    for (int i = 0; i < castTimes; i++)
    {
      action();
    }
    watch.Stop();
    Console.WriteLine($"{watch.ElapsedTicks} ticks");
    Console.WriteLine($"{watch.ElapsedMilliseconds} ms");
    Console.WriteLine();
  }
}

In the RunAction method above you can see that a Stopwatch is started at the beginning. Then the action, which is in our code the corresponding cast, is executed in a for loop for the number that is specified in the castTimes constant. After that, the Stopwatch is stopped, and ticks and milliseconds are written to the console.

As you can see in the code above, the castTimes constant has the value 10.000.000. The output in the console is this (depending on your processor, you might see different values):

Casting 10000000 times!

IPersonFirst:
437084 ticks
43 ms

IPersonLast:
1022823 ticks
102 ms

Wow, casting to the IPersonFirst interface is a lot faster. It looks like it’s in this case more than twice as fast.

Let’s increase the castTimes variable to 1.000.000.000. This is the output:

Casting 1000000000 times!

IPersonFirst:
41143822 ticks
4114 ms

IPersonLast:
83499796 ticks
8349 ms

Wow, incredible, again, we see the same, casting is more than twice as fast for the IPersonFirst interface.

What would be the difference if the Person class has just two interfaces like below:

public class Person : IPersonFirst, IPersonLast { }

Let’s try it, and let’s cast again 1.000.000.000 times. This is the output:

Casting 1000000000 times!

IPersonFirst:
41096526 ticks
4109 ms

IPersonLast:
44485942 ticks
4448 ms

Ok, looks with just two interfaces, the difference is not as big, but still, you want to define the most important interface first. Casting to the first interface is still a bit faster.

Now let’s define the Person class with 22 interfaces like this:

public class Person : IPersonFirst,
    IPerson0,
    IPerson1,
    IPerson2,
    IPerson3,
    IPerson4,
    IPerson5,
    IPerson6,
    IPerson7,
    IPerson8,
    IPerson9,
    IPerson10,
    IPerson11,
    IPerson12,
    IPerson13,
    IPerson14,
    IPerson15,
    IPerson16,
    IPerson17,
    IPerson18,
    IPerson19,
    IPerson20,
    IPersonLast
{

}

Let’s run it again with 1.000.000.000 casts, and now this is the output:

Casting 1000000000 times!

IPersonFirst:
44831688 ticks
4483 ms

IPersonLast:
132791946 ticks
13279 ms

Wow, 4 seconds vs. 13 seconds, that’s three times as fast for the first interface compared to the last interface defined on the Person class.

What does this mean for you?

Now you’ve learned that there’s a difference in performance. The first interface defined on a class has the best performance when it comes to casting.

If you have an application that processes for example a lot of data, you could reach that number of casts shown in this post really quickly.

So, just put the most important interface first, and order all interfaces that a class implements by importance/performance.

Happy coding,
Thomas

Share this post

Comments (6)

  • Dew Drop – March 6, 2020 (#3148) | Morning Dew Reply

    […] C#: The Order of Interfaces Is Important for Casting Performance (Thomas Claudius Huber) […]

    March 6, 2020 at 1:31 pm
  • Jason Bock Reply

    Why didn’t you use Benchmark.NET to gather the metrics? It’s really not that hard, and in fact it makes the code easier to write.

    By the way, if you create the Person object like this:

    var person = new Person();

    Your results will be very different. The difference between the cast times will go down significantly. Here’s what I got with person typed as an object:

    | Method | Mean | Error | StdDev | Ratio | RatioSD |
    |———— |———:|———-:|———-:|——:|——–:|
    | CastToFirst | 1.489 ns | 0.1100 ns | 0.1029 ns | 1.00 | 0.00 |
    | CastTo6 | 3.777 ns | 0.1365 ns | 0.1210 ns | 2.55 | 0.20 |
    | CastToLast | 4.054 ns | 0.1619 ns | 0.1435 ns | 2.74 | 0.21 |

    In this cast, there is a difference. But when person is typed as a Person…

    | Method | Mean | Error | StdDev | Median | Ratio | RatioSD |
    |———— |———-:|———-:|———-:|——-:|——:|——–:|
    | CastToFirst | 0.0058 ns | 0.0166 ns | 0.0163 ns | 0.0 ns | ? | ? |
    | CastTo6 | 0.0000 ns | 0.0000 ns | 0.0000 ns | 0.0 ns | ? | ? |
    | CastToLast | 0.0000 ns | 0.0000 ns | 0.0000 ns | 0.0 ns | ? | ? |

    The time to execute the cast is so fast Benchmark.NET can’t even distinguish the difference. BTW here’s the code I wrote:

    public class CastingInterfaces
    {
    private readonly Person person = new Person();

    [Benchmark(Baseline = true)]
    public IPersonFirst CastToFirst() => (IPersonFirst)this.person;

    [Benchmark]
    public IPerson6 CastTo6() => (IPerson6)this.person;

    [Benchmark]
    public IPersonLast CastToLast() => (IPersonLast)this.person;
    }

    March 6, 2020 at 3:02 pm
    • Thomas Claudius Huber Reply

      Hey Jason, thanks for these insights. Yes, I noticed that var p = new Person(); produces a totally different output than object p = new Person();. That’s why I wrote it explicitly like this with an object variable. I think if you have already a variable of type Person, you don’t have a need to cast it anyway. Only need would be to cast to explicitly implemented interfaces.

      Why didn’t I use Benchmark.NET: I like it, and yes, it’s not too hard. As I wrote in the post, I thought about using benchmarking tools like Benchmark.NET, but most developers are not so familiar with it. That was the reason I went with a very simple console app that everybody can understand and try out without adding any additional tools/packages. The main idea was: Go to file new project and just paste in the code and try it. I had in mind to do eventually a follow up post in the future with benchmarking tools to get people started. So, your comment comes in a good time. :-)

      Thanks for the code, looks good!

      Have a great weekend!

      Cheers,
      Thomas

      March 6, 2020 at 7:47 pm
  • The Morning Brew - Chris Alcock » The Morning Brew #2948 Reply

    […] C#: The Order of Interfaces Is Important for Casting Performance – Thomas Claudius Huber […]

    March 9, 2020 at 7:03 am
  • Andy Clark Reply

    Thanks Thomas, great experiment. For me the key lessons learn from this are not about the order of interfaces but reducing the number of interfaces and avoid casting from object.

    March 14, 2020 at 1:25 pm

Leave a Reply

Your email address will not be published. Required fields are marked *

*

This site uses Akismet to reduce spam. Learn how your comment data is processed.