# Deep dive into .NET 11 Performance Improvements

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. *Current post: [Allocation free numbers](https://blog.ladeak.net/posts/net11-perf-imp-01.md)*
1. Virtual generic method calls
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

## Allocation free numbers

One of the early examples in the [Deabstraction](https://devblogs.microsoft.com/dotnet/performance-improvements-in-net-11/#deabstraction) section discusses how the JIT avoids heap allocations through improved escape analysis.

```csharp
[SimpleJob]
[DisassemblyDiagnoser]
public class Benchmarks
{
    private int? _nullableValue = 10;

    [Benchmark]
    public string? FormatNullableInt() => Format(_nullableValue);

    private static string? Format<T>(T value)
    {
        if (value is IFormattable formattable)
            return formattable.ToString(null, null);

        return null;
    }
}
```

When the `Format` method executes the type check, it has to box the `T value`. However, the JIT sees that the `formattable` reference does not escape from the `Format` method, so it can avoid allocating it on the heap. This results in net-zero heap allocations.

But how does this benchmark result in zero heap allocations when it still has to create a `string` that is allocated on the heap?

The trick is hidden in the actual `IFormattable` [implementation](https://source.dot.net/#System.Private.CoreLib/src/runtime/src/libraries/System.Private.CoreLib/src/System/Number.Formatting.cs,2393), which caches numbers smaller than 300.

This can be validated by changing the `_nullableValue` field to 300, which produces a heap allocation:

```
| Method            | Mean     | Code Size | Allocated |
|------------------ |---------:|----------:|----------:|
| FormatNullableInt | 15.15 ns |     481 B |      32 B |
```

This caching is, of course, limited by several factors: format string, value, negative sign, input type, and so on.

Creating a string still results in an allocation, but when the value falls within a cacheable range, the value is cached for the lifetime of the application. That makes the overall allocation profile effectively amortized to zero in the original example.

## Conclusion

This post focused on the first 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 a `string` return type may result in zero heap allocations. In the next post, I’ll look at the optimized assembly code for generic virtual methods.