# Deep dive into .NET 11 Performance Improvements #2

In this series of posts, I am going to explore some of the most interesting .NET 11 performance improvements I learned about in Stephen Toub's blog post, [Performance Improvements in .NET 11](https://devblogs.microsoft.com/dotnet/performance-improvements-in-net-11).

Each of my post focuses on one example from the original article, but digs a little deeper into one additional aspect that was not covered there. The series covers the following topics:

1. [Allocation free numbers](https://blog.ladeak.net/posts/net11-perf-imp-01.md)
1. *Current post: [Virtual generic method calls](https://blog.ladeak.net/posts/net11-perf-imp-02.md)*
1. Vector zero equality
1. Guid creation on Linux
1. JIT generating implicit-else assembly instructions
1. Guid equality
1. Copy-on-write locking strategy

## Virtual generic method calls

One of the early examples in the [Deabstraction](https://devblogs.microsoft.com/dotnet/performance-improvements-in-net-11/#deabstraction) section discusses how JIT code generation optimizes virtual dispatch for generic methods.

```csharp
public class Benchmarks
{
    [Benchmark]
    public int NonShared() => ((IProcessor)new Processor()).SizeOf(new object());

    private interface IProcessor
    {
        int SizeOf<T>(T value);
    }

    private sealed class Processor : IProcessor
    {
        public int SizeOf<T>(T value) => Unsafe.SizeOf<T>();
    }
}
```

The original post details the shared and non-shared cases, referring to whether the generic type parameter is a reference type (shared, or 'canon') or a value type (non-shared). It explains that the JIT can turn the interface call into a devirtualized method call on the actual `Processor` type, because argument setup is moved to a later point in the analyis. This enables further optimizations: in this example, the `Processor` object can be stack allocated, and the `SizeOf` method can be inlined.

It is interesting to see how the JITted assembly code changes from .NET 10 to .NET 11. In .NET 10, the JITted code is `291 B` and includes two heap allocations: `new Processor` and `new object()`.

In .NET 11, after all optimizations, the code is `6 B` with no heap allocations. The method is folded and inlined into a move that places the value 8 (the size of a reference type on x64) into the result register:

```assembly
; Benchmarks.NonShared()
       mov       eax,8
       ret
; Total bytes of code 6
```

In practice, .NET 11 can completely skip the `System.Runtime.CompilerServices.VirtualDispatchHelpers.VirtualFunctionPointer` function call when possible.

## Conclusion

This post focused on the second of many interesting nuances I learned from the [Performance Improvements in .NET 11](https://devblogs.microsoft.com/dotnet/performance-improvements-in-net-11) article. It explored how the .NET 11 JIT optimizes virtual generic method calls through devirtualization, inlining, and stack allocation. Compared with .NET 10, the example's generated code shrinks from 291 bytes with two heap allocations to just 6 bytes with no allocations.
