Key performance changes in .NET 7: an overview

With .NET 7 already here, you’re probably wondering what to expect from Microsoft’s new framework.  Let’s find out.  I started by creating and running an empty Web API project. I saw that .NET 7 requires around 40% less memory than its predecessor. To be honest, I was surprised. It took me back in time to […]

by Kristiyan Petrov

December 5, 2022

5 min read

shutterstock 1890767455 - Key performance changes in .NET 7: an overview

With .NET 7 already here, you’re probably wondering what to expect from Microsoft’s new framework. 

Let’s find out. 

I started by creating and running an empty Web API project. I saw that .NET 7 requires around 40% less memory than its predecessor. To be honest, I was surprised. It took me back in time to when apps were less resource intensive. This is going to change prices in the cloud a bit, offload all the docker pods – and many more wonderful things. 

Having seen this, I couldn’t wait to find out more about the other changes and new features. 

In this article, I’m going to tell you about them and explain which ones I believe are important. 

.Net Release Cycle & Support Policy

Ever since .NET 5, Microsoft has announced they’d be releasing a new version yearly.

Qwlcno4ki84h T1Ps4oQp08 uf0D17KrTVvbBp7bZatzPL6RlsBRR2fuKo - Key performance changes in .NET 7: an overview
  • LTS – Long-Term Support
  • STS – Short-Term Support

The even and odd version numbers have different support periods. As you can see, 6 and 8 are LTS versions, and they get 3 years of support. Versions 5 and 7 get around 18 months. By doing that, they’re giving us 6 months to update our application to the next version. 

Talking about updates, Microsoft has a tool that can help you migrate your app from .NET Framework – to .NET 6, for example. You can find the open-source code in this GitHub repository.

So this November, we’re waiting for the .NET 7 version. I was really impressed when I saw all the improvements it has to offer. Let’s go over them and dig into a few specific ones.

Reflection

As you already know, Reflection is a way to obtain information about already loaded assemblies and their type definitions. Most people have mixed feelings about this technology. As usual, the truth is in the middle.

If you aren’t familiar with it, I recommend you read Microsoft’s post on the topic before using it. For now, let’ compare the most widely used reflection methods and their behavior between .NET 6 and 7.

MethodRuntimeMeanRatio
MethodInvoke.NET 6.043.846 ns1.00
MethodInvoke.NET 7.08.078 ns0.18
GetUnderlyingType.NET 6.027.413 ns1.00
GetUnderlyingType.NET 7.05.115 ns0.19
CreateInstance.NET 6.03.827 ns1.00
CreateInstance.NET 7.02.276 ns0.60
IsByRefLike.NET 6.02.1322 ns1.000
IsByRefLike.NET 7.00.0000 ns0.000

Arrays and Strings

From my experience working in a custom software development company, I can say that .NET 7 introduces a lot of APIs, which helps us to write more optimized code. 

The first useful API which we will look at is MemoryExtension.CommonPrefixLength. It returns the number of elements that are the same at the beginning of each input. 

Other new APIs include the IndexOf variants: IndexOfAnyExcept and LastIndexOfAnyExcept. They do exactly what their names suggest. Of course, you can write your own methods for checking the elements in an array, but they won’t be so optimized. I`ll paste the Stephen Toub code samples below to show you how fast and useful the new methods are.

private byte[] _zeros = new byte[1024];

[Benchmark(Baseline = true)]
public bool OpenCoded()
{
    foreach (byte b in _zeros)
    {
        if (b != 0)
        {
            return false;
        }
    }

    return true;
}

[Benchmark]
public bool IndexOfAnyExcept() => _zeros.AsSpan().IndexOfAnyExcept((byte)0) < 0;
MethodMeanRatio
OpenCoded370.47 ns1.00
IndexOfAnyExcept23.84 ns0.06

I have good news for you. This new version also comes with changes in IndexOf and LastIndexOf. There are a lot of performance optimizations there, so it’s going to affect and improve the existing code, as well. 

For example, let’s search for the word ‘elementary’ in a text using indexOf. In the 6th version it’s going to take 1,063.67 us, compared to version 7 it’s going to take 56.04 us. It’s around 19 times faster than before. 

Let me show you some of the improved parts of StringBuilder. I was really impressed with the fact that they’ve changed the StringBuilder Replace method logic. Now it’s using IndexOf under the hood. The performance is, again, excellent – more than 20-fold improvement. AppendFormat, Append, and other functions have had many changes, but I won’t get into all of them here. 

LINQ

Microsoft has been analyzing a lot of real-world scenarios for use of Enumerable.Min and Max functions. The results show they’re mostly used with arrays. 

So now the functions check if the collection is an array. If it is, they execute the newly created implementation with Vector<T>. If not, then they continue with the old one. This check decreases the processing time when we’re working with an inappropriate collection(HashSet) because of these checks – but maybe this is going to be fixed in the next version. Also, the memory allocation in the newest version is 0. If we compare that to .NET 6, the memory there is 32B. 

nWajzU5 V4UCFzJ915rls0Rs2YDBn9 XjFjf80 DrfLwuGXTu4uSYHPZ QLh6pY26 aMyl87vQAZhe58bW02k0HxM45QEHNHe8MMIqUJ06dobMWkk1L9afOVXPEGx7q7 tUSvhmnNvVPOr0VK7aLE m0zsSKoy5PWlGJX84CyKzhIVHm3ng8HguDOK9wEA - Key performance changes in .NET 7: an overview

HashSet results:

0o9U3 5qN9maRQTY3EjP2 Ty2rIGMrUiDvE3blVco4btyGJnO5jdfRckkp6qc60XuUvKJgUxzYmuhThIJ6VkSQx91bp8MXeeVoN6aVTZn - Key performance changes in .NET 7: an overview

C# 11

Finally, we can format our string text however we want with the newly created string interpolations.

“””

As you can see

We can format our lines just like that

They just need to be wrapped in a (“””). 

If the string, for example, contains “”” , your interpolation has to start with one more double quote char

“””

There is one more wonderful thing. We can use switch statements in our string interpolations.

string message = $"The usage policy for {safetyScore} is {safetyScore switch
    {
        > 90 => "Unlimited usage",
        > 80 => "General usage, with daily safety check",
        > 70 => "Issues must be addressed within 1 week",
        > 50 => "Issues must be addressed within 1 day",
        _ => "Issues must be addressed before continued use",
    }}";

You can find a lot more interesting string interpolation features and examples in the Microsoft documentation.

List patterns are the second interesting thing. I’m not sure how often we are going to use this feature but it looks good.

Auto-default struct initialization is another good thing that can save some unnecessary code.

If we have a parameterless constructor the new version of c# is going to set default values to all properties.

A new way to create UTF-8 String literals for better productivity, resiliency and performance. When you add ‘u8’ to the string, this forces the type to be UTF-8. Ex: “example”u8.

Conclusion

More than 1000 PRs were merged into .NET 7. All the optimisations and improvements are simply too numerous to be described here. Every .NET version is getting better and better. I love the Microsoft migration strategy because every application is going to be migrated soon or later. Do not forget to review all the breaking changes before updating. Happy coding : )

Categories