

Key performance changes in .NET Core 7: an overview
With .NET Core 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 Core 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 Core 5, Microsoft has announced they’d be releasing a new version yearly.
- 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 Core 6, for example. You can find the open-source code in this GitHub repository.
So this November, we’re waiting for the .NET Core 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 Core 6 and 7.
Method | Runtime | Mean | Ratio |
MethodInvoke | .NET 6.0 | 43.846 ns | 1.00 |
MethodInvoke | .NET 7.0 | 8.078 ns | 0.18 |
GetUnderlyingType | .NET 6.0 | 27.413 ns | 1.00 |
GetUnderlyingType | .NET 7.0 | 5.115 ns | 0.19 |
CreateInstance | .NET 6.0 | 3.827 ns | 1.00 |
CreateInstance | .NET 7.0 | 2.276 ns | 0.60 |
IsByRefLike | .NET 6.0 | 2.1322 ns | 1.000 |
IsByRefLike | .NET 7.0 | 0.0000 ns | 0.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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
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; |
Method | Mean | Ratio |
OpenCoded | 370.47 ns | 1.00 |
IndexOfAnyExcept | 23.84 ns | 0.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.
HashSet results:
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.
1 2 3 4 5 6 7 8 9 10 |
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 Core 7. All the optimisations and improvements are simply too numerous to be described here. Every .NET Core 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 : )