Unleashing .NET 9: Key Performance Boosts and Pitfalls Compared to .NET 8

With the annual November release of new .NET versions, Stephen Toub, a Partner Software Engineer on the .NET team, publishes an in-depth analysis of performance improvements around September each year. Given the yearly release cycle, I have decided to update my performance book exclusively for the Long-Term Support (LTS) versions. To complement the book, I will publish articles covering the performance changes in Short-Term Support (STS) versions like .NET 9.

In this article, I will highlight the major performance differences between these two versions.

Performance Improvements & Slowdowns

Below is a brief overview of some key performance enhancements and potential slowdowns introduced in .NET 9. I found some of these results particularly surprising, especially in areas like character handling, integers, doubles, object creation, string comparison, and string compression.

CATEGORYDESCRIPTIONPERFORMANCE RESULT
ArrayAll() with a predicate for reference typesIncrease of 1.6 times
ArrayCount() with a predicate for reference typesIncrease of 2.28 times
Arrayforeach() for reference typesDecrease of 1.11 times
ArrayLongCount() for reference typesIncrease of 1.17 times
ArrayParallel.For() for value typesDecrease of 1.6 times
ArrayParallel.For(): + MaxDegreeOfParallelism for value typesDecrease of 1.56 times
ArrayParallel.ForEach() + MaxDegreeOfParallelism  for value typesDecrease of 1.6 times
ArrayParallel.ForEach() for value typesDecrease of 1.6 times
CharacterEndsWith()Decrease of 23.4 times
CharacterStartsWith()Increase of 36.75 times
CloningReference typeIncrease of 1.14 times
CloningValue typeIncrease of 1.12 times
CodeEnvironment.ProcessIdIncrease of 1.43 times
CodeNull coalescingIncrease of 1.16 times
CodeNull coalescing assignment using ??Increase of 1.2 times
DictionaryContainsKey()Increase of 1.1
DictionaryConvert to ImmutableDictionaryIncrease of 1.4 times
DictionaryIterating using for()Increase of 1.24
ExceptionsTrapping exceptions using the when clauseIncrease of 2.11 times
ExceptionsTrapping exceptions using try/catchIncrease of 2.2 times
ImmutableDictionaryIterating using for()Increase of 1.68
ImmutableSortedDictionaryIterating using for()Increase of 2.44
IQueryableAny() for reference typeIncrease of 2 times
JsonSerializerDeserialize with JsonSerializerContextIncrease of 1.28 times
ListAll() with a predicate for reference typeIncrease of 2 times and allocation decreased by half
ListAll() with a predicate for value typeIncrease of 4.19 times and allocations decreased by almost 100 bytes
ListAny() for reference typeIncrease of 1.44 times
ListAny() for value typeIncrease of 2.48 times
ListAny() with a predicate for reference typeIncrease of 3.14 times and allocations decreased by almost half
ListAny() with a predicate for value typeIncrease of 1.82 times
ListConvert to ImmutableDictionaryIncrease of 1.4 times
ListCount() for value typeIncrease of 2.36 times
ListCount() with a predicate for reference typeIncrease of 2.43 times and allocations decreased by almost half
ListCount() with a predicate for value typeIncrease of 4.39 times
Listforeach with CollectionsMarshall.AsSpan() for value typesIncrease of 1.23 times
ListForEach() for value typesIncrease of 1.31 times
Listforeach() for value typesIncrease of 1.35 times
ListIterating using for() for value typesIncrease of 1.28 times
ListLINQ First()Increase of 7.6 times
ListLongCount() for value typeIncrease of 2.44 times
Methodin paramsIncrease of 1.19 times and decrease of allocations by 15,559 bytes
Methodparams normalIncrease of 1.21 times and decrease of allocations by 16,119 bytes
Methodref readonly paramsIncrease of 1.19 times and decrease of allocations by 17,271 bytes
ObjectsAssigning to a variable for an integer returnIncrease of 8.33 times
ObjectsCreate value typeIncrease of 1.37 times
ObjectsCreateInstance() for a value typeIncrease of almost 2 times
ObjectsCreating a public classIncrease of 4 times
ObjectsCreating a public sealed classIncrease of 5 times
ObjectsCreating a value objectIncrease of 1.63 times
ObjectsCreating an internal classIncrease of 4 times
ObjectsCreating an internal sealed classIncrease of 4.44 times
ObjectsCreating AttributeIncrease of 3.7 times
ObjectsCreating sealed AttributeIncrease of 3.86 times
ObjectsDisposing of objects using the new style of usingIncrease of 1.2 times
ObjectsDisposing of objects using the using statementIncrease of 1.18 times
ObjectsDisposing of objects using try/finallyIncrease of 1.17 times
ObjectsGetUninitializedObject() for reference typeIncrease of 4.32 times
ObjectsGetUnitializedObject for reference typeIncrease of 4.32 times
ObjectsUsing discard for integer returnDecrease of 15.3 times
SpanFill()Decrease of 2.12 times
StringCombine 2 strings using Join()Increase of 1.45 times
StringComparison using AsSpan().Equals(OrdinalIgnoreCase)Increase of 22 times
StringComparison using Equals()Decrease of 6.32 times
StringComparison with EndsWith()Decrease of 1.2 times
StringContains()Increase of 5.27 times
StringEndsWith()Decrease of 1.29 times
StringReplace() with OrdinalIgnoreCaseIncrease of 1.22 times
String CompressionUsing DeflateStream async with CompressionLevel.FastestDecrease of 2.27 times
String CompressionUsing DeflateStream async with CompressionLevel.NoCompressionDecrease of 2 times
String CompressionUsing DeflateStream async with CompressionLevel.OptimalDecrease of 2.5 times
String CompressionUsing DeflateStream async with CompressionLevel.SmallestSizeDecrease of 2.55 times
String CompressionUsing GZipStream async with CompressionLevel.FastestDecrease of 2.25 times
String CompressionUsing GZipStream async with CompressionLevel.NoCompressionDecrease of 4 times
String CompressionUsing GZipStream async with CompressionLevel.OptimalDecrease of 2.5 times
String CompressionUsing GZipStream async with CompressionLevel.SmallestSizeDecrease of 2.54 times
String CompressionUsing ZLibStream async with CompressionLevel.FastestDecrease of 2.3times
String CompressionUsing ZLibStream async with CompressionLevel.NoCompressionDecrease of 4.1 times
String CompressionUsing ZLibStream async with CompressionLevel.OptimalDecrease of 2.5 times
String CompressionUsing ZLibStream async with CompressionLevel.SmallestSizeDecrease of 2.6 times
String DecodingUsing BigEndianUnicodeDecrease of 1.5 times
String EncodingUsing Encoding.UTF32Decrease of 1.16 times
String ValidationAny()Increase of 1.94 times
String ValidationUsing ? and string.LengthIncrease of 57 times
StringBuilderAppend()Increase of 1.35 times
StringBuilderAppendFormat()Increase of 1.19 times
TypeInitializing an integerDecrease of 7.25 times
TypesConsuming a constant that is a doubleDecrease of 733.75 times
TypesConsuming a readonly property that is a doubleIncrease of 4.69 times
TypesDouble to integerDecrease of 3.13 times
TypesDouble to integer with castingDecrease of 3.5 times
TypesInitialize reference typeDecrease of 1.31 times and increase of allocation by 3,644 bytes
TypesUsing readonly property that is a doubleDecrease of 7.36 times

Summary

This article highlights only a portion of the performance changes I discovered using the thousands of benchmark tests I routinely run to track .NET improvements. I have focused on performance changes of 1.1 or higher. Overall, based on all the tests I conducted; .NET 9 is approximately 1.11 times faster than .NET 8—a notable improvement for an interim release!

This guide is intended to help you decide whether to upgrade to .NET 9. I strongly recommend upgrading as soon as possible to leverage the enhanced performance. Your users will benefit from the increased speed without any need for changes to your existing code.

Feel free to share any questions or feedback in the comments below.

Pick up any books by David McCarter by going to Amazon.com: http://bit.ly/RockYourCodeBooks

One-Time
Monthly
Yearly

Make a one-time donation

Make a monthly donation

Make a yearly donation

Choose an amount

$5.00
$15.00
$100.00
$5.00
$15.00
$100.00
$5.00
$15.00
$100.00

Or enter a custom amount

$

Your contribution is appreciated.

Your contribution is appreciated.

Your contribution is appreciated.

DonateDonate monthlyDonate yearly

If you liked this article, please buy David a cup of Coffee by going here: https://www.buymeacoffee.com/dotnetdave

© The information in this article is copywritten and cannot be preproduced in any way without express permission from David McCarter.


Discover more from dotNetTips.com

Subscribe to get the latest posts sent to your email.

Leave a comment

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