.NET 8 has been officially released in November 2023. You can download it here. Designated as an LTS (Long Term Support) version, it guarantees ongoing support, updates, and bug fixes for a minimum of three years from its launch. This commitment empowers developers to embrace this new version with confidence. .NET 8 will become the default version for many organizations over the next years.
Building on the changes introduced in .NET 7, this release adds further enhancements. Let’s dive into the .NET 8 Top 10 New Features:
Performances, Performances, Performances
Microsoft once again has committed a substantial amount of resources to enhance the performance of its new .NET 8. As usual, Stephen Toub has compiled an exhaustive, hundreds-of-pages-long article summarizing all these improvements: Performance Improvements in .NET 8.
Here are three key performance improvements that stand out to me:
- The method
List<T>.AddRange(IEnumerable<T>)
has been rewritten to achieve better performance when the sequence is not aICollection<T>
.
Method | Job | Mean | Ratio |
---|---|---|---|
AddRange | .NET 7 | 6.365 us | 1.00 |
AddRange | .NET 8 w/o PGO | 4.396 us | 0.69 |
AddRange | .NET 8 | 2.445 us | 0.38 |
Int32.ToString()
performance has been improved by caching string values from0
to299
. Also, .NET engineers have cut the number of divisions in half. This reduction results from dividing by 100 to obtain two digits, as opposed to dividing by 10 to obtain a single digit.
Method | Runtime | i | Mean | Ratio | Allocated | Alloc Ratio |
---|---|---|---|---|---|---|
Int32ToString | .NET 7.0 | 12 | 16.253 ns | 1.00 | 32 B | 1.00 |
Int32ToString | .NET 8.0 | 12 | 1.985 ns | 0.12 | – | 0.00 |
Int32ToString | .NET 7.0 | 1234567890 | 26.964 ns | 1.00 | 48 B | 1.00 |
Int32ToString | .NET 8.0 | 1234567890 | 17.082 ns | 0.63 | 48 B | 1.00 |
- On Mac OS,
File.Copy()
has significantly improved its speed by relying on the OS’sclonefile
function, if it is available, for the copying process.
Method | Runtime | Mean | Ratio |
---|---|---|---|
FileCopy | .NET 7.0 | 1,624.8 us | 1.00 |
FileCopy | .NET 8.0 | 366.7 us | 0.23 |
New .NET 8 Garbage Collection DATAS Feature (Dynamic Adaptation To Application Sizes)
The .NET 8 GC introduces DATAS (Dynamic Adaptation To Application Sizes). It adjusts the application’s memory usage based on Live Data Size (LDS), which includes long-lived data and inflight data during a GC event. DATAS has two main use cases:
- It’s beneficial for bursty workloads in memory-constrained environments, like containerized applications with memory limits. DATAS shrinks or expands the heap size as needed.
- It’s useful for small workloads using Server GC, ensuring the heap size aligns with the application’s actual requirements.
While some initially called it “the Dynamic GC” DATAS’ key focus is on adapting to your application’s size. GC has always been dynamic, but DATAS fine-tunes it to better suit the workloads in .NET.
Explore an in-depth article on DATAS authored by the renowned .NET GC expert, Stephens Maoni, by clicking here: Dynamically Adapting To Application Sizes
System.Text.Json Serialization and Deserialization
In .NET 8, System.Text.Json brings an array of thrilling updates for developers. This release elevates the user experience in Native AOT (Ahead-Of-Time) applications thanks to many improvements on the source generator including:
- Supports serializing types with
required
andinit
properties. - JsonSourceGenerationOptionsAttribute: Aligns with JsonSerializerOptions, enabling compile-time serialization configuration.
- Improved Type Handling: Ignores inaccessible properties, allows nesting JsonSerializerContext declarations, and handles compiler-generated types dynamically.
- JsonStringEnumConverter<TEnum>: A new converter type simplifies enum serialization in Native AOT applications.
- Additionally, the JsonConverter.Type property helps retrieve the type of a non-generic JsonConverter instance, with nullable support for various scenarios.
Many other enhancements are packed including:
- JsonNamingPolicy now incorporates new naming policies for converting property names to
snake_case
(with underscores) andkebab-case
(with hyphens). - You can now deserialize data onto read-only fields or read-only properties, which are those without a set accessor.
- You can now opt to disable the reflection-based serializer as the default option. This capability is valuable for preventing the unintended inclusion of reflection components, particularly in trimmed and Native AOT applications.
More information about System.Text.Json improvements can be found in this official post: What’s new in System.Text.Json in .NET 8
Convenient New APIs for Managing Randomness
New Time Abstraction API
The newly introduced TimeProvider class and ITimer interface offer time abstraction functionality, facilitating the simulation of time in testing scenarios. The TimeProvider is an abstract class featuring numerous virtual functions, which makes it an ideal candidate for integration with mocking frameworks. This allows for seamless and comprehensive mocking of all its aspects. For example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
static DateTimeOffset AddNDaysToUtcNow(TimeProvider timeProvider, int nbDays) { return timeProvider.GetUtcNow().AddDays(nbDays); } [Test] public void Test_AddNDaysToNow() { var ttp = new TestTimeProvider(new DateTimeOffset(new DateTime(2023,10,30,0,0,0))); var result = AddNDaysToUtcNow(ttp, 5); Assert.IsTrue(result.Year == 2023); Assert.IsTrue(result.Month == 11); Assert.IsTrue(result.Day == 4); } class TestTimeProvider : TimeProvider { private readonly DateTimeOffset m_UtcNow; public TestTimeProvider(DateTimeOffset utcNow) { this.m_UtcNow = utcNow; } public override DateTimeOffset GetUtcNow() { return m_UtcNow; } } |
Let’s underline this remark from a Microsoft engineer:
“At the end of the day, we expect almost no one will use anything other than
TimeProvider.System
in production usage. Unlike many abstractions then, this one is special: it exists purely for testability”.
Notice that you can also harness time abstraction to simulate Task operations that depend on time progression, such as Task.Delay() and Task.WaitAsync().
These time abstractions were highly anticipated, following years of debates and discussions.
New Types that can Improve Performance in Various Situations
- .NET 8 offers the new System.Collections.Frozen namespace. It contains the new collection classes FrozenSet<T> and FrozenDictionary<TKey,TValue>. The Frozen qualifier means that the collections are immutable: it cannot be changed once created. Internally the implementation does harness this requirement to allows for faster enumeration and faster lookup operations like
Contains()
orTryGetValue()
. These new frozen collections prove particularly valuable in scenarios where collections are initially populated and subsequently endure for the entire lifecycle of a long-lived application.
1 2 3 4 5 6 7 8 |
List<int> list = [1, 2, 3, 4]; FrozenSet<int> frozenSet = list.ToFrozenSet(); Assert.IsTrue(frozenSet.Contains(4)); // Faster because the set is immutable HashSet<int> hashSet = list.ToHashSet(); hashSet.Add(5); Assert.IsTrue(hashSet.Contains(2)); // Slower because we can modify the set |
- The new class System.Buffers.SearchValues<T> is optimized for scenarios where a consistent set of values is frequently employed for runtime searches like for example in the implementation of
String.IndexOfAny(char[])
. When you create aSearchValues<T>
instance, all the essential data required to optimize future searches is computed in advance, streamlining the process. - The new class System.Text.CompositeFormat proves invaluable for optimizing format strings -like
"First Name:{0} Last Name: {1}"
– not known at compile time. While there is an initial overhead in tasks such as string parsing, this proactive approach significantly reduces the computational burden in subsequent uses, enhancing performance and efficiency.
UTF8 Formatting
To enable the generation of a string-like representation of your type into a destination span, implement the recently introduced IUtf8SpanFormattable interface for your type:
1 2 3 |
public interface IUtf8SpanFormattable { bool TryFormat(Span<byte> utf8Destination, out int bytesWritten, ReadOnlySpan<char> format, IFormatProvider? provider); } |
This interface is similar to ISpanFormattable. It is designed specifically for UTF-8 and Span<byte>
, as opposed to UTF-16 and Span<char>
.
In .NET 8 all primitive types (and more) implement this interface: Byte
, Complex
, Char
, DateOnly
, DateTime
, DateTimeOffset
, Decimal
, Double
, Guid
, Half
, IPAddress
, IPNetwork
, Int16
, Int32
, Int64
, Int128
, IntPtr
, NFloat
, SByte
, Single
, Rune
, TimeOnly
, TimeSpan
, UInt16
, UInt32
, UInt64
, UInt128
, UIntPtr
, and Version
.
Stream-based ZipFile methods
It is now feasible to compress files from a directory using a stream without the need to cache them in a temporary file. This allows for direct management of the compression result in memory. These new APIs prove beneficial in scenarios where disk space is limited, as they eliminate the need to utilize the disk as an intermediate step. Here are the new APIs:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
namespace System.IO.Compression; public static partial class ZipFile { public static void CreateFromDirectory(string sourceDirectoryName, Stream destination); public static void CreateFromDirectory(string sourceDirectoryName, Stream destination, CompressionLevel compressionLevel, bool includeBaseDirectory); public static void CreateFromDirectory(string sourceDirectoryName, Stream destination, CompressionLevel compressionLevel, bool includeBaseDirectory, Encoding? entryNameEncoding); public static void ExtractToDirectory(Stream source, string destinationDirectoryName) { } public static void ExtractToDirectory(Stream source, string destinationDirectoryName, bool overwriteFiles) { } public static void ExtractToDirectory(Stream source, string destinationDirectoryName, Encoding? entryNameEncoding) { } public static void ExtractToDirectory(Stream source, string destinationDirectoryName, Encoding? entryNameEncoding, bool overwriteFiles) { } } |
Support for the Intel AVX-512 instruction set
.NET Core 3.0 proposed support for SIMD by incorporating platform-specific hardware intrinsics APIs for x86/x64. Subsequently, .NET 5 extended this support to Arm64, and with the advent of .NET 7, cross-platform hardware intrinsics were introduced. .NET 8 further enhances SIMD capabilities by introducing Vector512<T> and extending support for Intel Advanced Vector Extensions 512 (AVX-512) instructions.
In particular, .NET 8 introduces support for the following key AVX-512 features:
- 512-bit vector operations.
- An additional 16 SIMD registers.
- Additional instructions are available for 128-bit, 256-bit, and 512-bit vectors.
Moreover, even without explicitly utilizing Vector512-specific or Avx512F-specific instructions in your code, you are likely to benefit from the improved AVX-512 support. The JIT compiler can leverage the extra registers and instructions implicitly when you use Vector128<T> or Vector256<T>.
Finally, note that if you have AVX-512 compatible hardware, Vector512.IsHardwareAccelerated will now return true.
Cryptography
SHA3_256
, SHA3_384
, and SHA3_512
for hashing; HashAlgorithmName.SHA3_256
, HashAlgorithmName.SHA3_384
, and HashAlgorithmName.SHA3_512
for hashing where the algorithm is configurable; HMACSHA3_256
, HMACSHA3_384
, and HMACSHA3_512
for HMAC; and RSAEncryptionPadding.OaepSHA3_256
, RSAEncryptionPadding.OaepSHA3_384
, and RSAEncryptionPadding.OaepSHA3_512
for RSA OAEP encryption.Conclusion
Summing it up, .NET 8 stands as a substantial leap forward, introducing a plethora of new features and enhancements. The new APIs and capabilities are well-suited to ensure the competitiveness and security of your code over the next three years.
.NET 8 is released along with C# 12. This new C# version introduces interesting new syntaxes explained in this post: Unveiling the Impressive Features of Upcoming C# 12
“This is an excellent contribution to the conversation. Your thoughtful analysis and practical advice make it a valuable resource. Keep up the fantastic work!”