Here's a One-Stop Shop for .NET 5 Improvements
The sprawling .NET 5 ecosystem can be hard to get a handle on. Going live in November 2020, .NET 5 was a milestone release in Microsoft's effort to move off the old Windows-only .NET Framework to a one-size-fits-all, open source, cross-platform alternative.
As part of that, .NET 5 serves as an umbrella for disparate components including desktop (WPF, WinForms, UWP), web (ASP.NET, Blazor), mobile (Xamarin), gaming (Unity), IoT (ARM32, ARM64) and AI (ML.NET, .NET for Apache Spark).
With Microsoft devoting separate dev teams for those offerings and more -- not to mention community contributions coming in from all over -- the number of changes introduced in the migration process is, well, unknowable.
Luckily, however, Microsoft developers have been painstakingly documenting changes to those components, with an emphasis on improvements, for much of last year continuing into this year. Here's a one-stop resource that provides a high-level view of those changes in different areas -- networking, diagnostics, general performance, code interop, regex and more -- along with links for those wanting to dig into the nitty-gritty coding details.
Microsoft detailed general performance improvements in .NET 5 in a mid-year post that covered the following:
Garbage Collection: "Lots of effort goes into reducing allocation, not because the act of allocating is itself particularly expensive, but because of the follow-on costs in cleaning up after those allocations via the garbage collector (GC)," Microsoft says. "No matter how much work goes into reducing allocations, however, the vast majority of workloads will incur them, and thus it's important to continually push the boundaries of what the GC is able to accomplish, and how quickly."
Specific improvements include:
- Optimize decommitting GC heap memory pages
- Improve the GC's scalability on machines with higher core counts by reducing lock contention involved in the GC's scanning of statics
- Avoid costly memory resets
- Vectorize sorting employed in the GC
- Reduce the time it takes for the GC to suspend threads
For more on GC, go here.
Just-In-Time (JIT) Compiler:
".NET 5 is an exciting version for the Just-In-Time (JIT) compiler, too, with many improvements of all manner finding their way into the release. As with any compiler, improvements made to the JIT can have wide-reaching effects. Often individual changes have a small impact on an individual piece of code, but such changes are then magnified by the sheer number of places they apply."
As far as specific JIT improvements, there are way too many to list here, but they run a gamut from Span<T> functionality to intrinsics. They also get pretty technical, with pull requests featuring titles like "Use xmm for stack prolog zeroing rather than rep stos."
A few other PRs are described like this:
- Cause the JIT to see an array's length as unsigned, which results in it being able to use better instructions for some mathematical operations
- Enables= the JIT to use faster 0-based comparisons for some unsigned integer operations
- Allow the JIT to recognize “constantString”.Length as a constant value
- Reduce the size of ReadyToRun images by removing nop padding
- Improve the code generated for the Math.FusedMultiplyAdd intrinsic
For more on JIT, go here. Intrinsics are covered in more detail here.
Many more general performance improvements -- along with testing numbers and benchmarks to back up the claims -- are explained in great detail for:
While the above information comes from a huge July 2020 post, Microsoft also published several other blog posts devoted to performance improvements in more targeted areas, some of which are included above. Here's a look at some of those.
Networking Improvements in .NET 5
These cover HTTP, Sockets, networking-related security, and other networking primitives. Here are a few of the main improvements highlighted by Microsoft, with links to visit for more information:
Here, Microsoft build upon diagnostic improvements made in .NET Core 3.0, which are outlined here. For newer goodies, the company pointed to:
Diagnostics tool are available without the .NET SDK: Microsoft now provides a single-file distribution mechanism that only requires a runtime (3.1+) to be available on the target machine. Previously the .NET diagnostics tools were available only as .NET SDK global tools, providing a convenient way to acquire and update the tools, but making it difficult to acquire them in environments where the full SDK was not present. Go here for more.
Analyze Linux memory dumps on Windows: Debugging managed code requires special knowledge of managed objects and constructs. The Data Access Component (DAC) is a subset of the runtime execution engine that has knowledge of these constructs and can access these managed objects without a runtime. In .NET Core 3.1.8+ and in .NET 5+, Microsoft started to compile the Linux DAC against Windows. .NET Core process dumps collected on Linux can now be analyzed on Windows using WinDBG, dotnet dump analyze, and Visual Studio 2019 16.8. More on that is available here.
Startup tracing: In .NET Core 3.1, it wasn't possible to perform startup tracing (via EventPipe; ETW was still possible) since events emitted before the tools could connect to the runtime would be lost. In .NET 5, it is now possible to configure the runtime to suspend itself during startup until a tool has connected (or have the runtime connect to the tool). Go here for more on that.
Assembly load diagnostics: The runtime now emits events for assembly binds via EventPipe, providing information that can be used to diagnose why the runtime cannot locate an assembly at runtime. This replaces the Fusion Log Viewer (fuslogvw.exe) present in the .NET Framework. More details are here.
Native Code Interop
A bunch of improvements here include:
Function pointers: These are coming in C# 9.0, to enable the declaration of function pointers to both managed and unmanaged functions. Specifically, Microsoft delves in the details about
UnmanagedCallersOnly and unmanaged calling convention. For the latter, the company said C# function pointers will allow declaration with an unmanaged calling convention using the unmanaged keyword (this syntax is not yet shipped, but will be in the final release, Microsoft said in September). Go here for more.
Low-level APIs for interaction with the built-in interop system: An underlying theme for interop in .NET 5 has been providing low-level building blocks that enable components outside of the runtime itself to better integrate with the built-in interop system. In .NET 5, Microsoft added some APIs that allow for more control over the interop system used in the runtime, which has a built-in system to handle interop support such as P/Invokes, marshalling, and COM interactions. Details on SuppressGCTransition, ComWrappers and IDynamicInterfaceCastable are available here.
Support for WinRT: Via APIs previously discussed, Microsoft provided support for WinRT APIs while de-coupling the WinRT interop system from the .NET runtime itself. More on that and other details about WinRT are available here.
For more on all of the above, along with coverage of the new ability to use COM objects with the dynamic keyword, marshalling of blittable generics and what's in store for native interop beyond .NET 5 (including code analyzers and source generators), see this post.
Various optimizations that have been made for Regex (regular expressions) in .NET 5, with Microsoft discussing the details around:
- "Character classes," which define the set of characters that an input character should or should not match in order for that position to be considered a match (see more)
- Codgegen like a dev might write (see more)
- Span-based searching with vectorized methods (see more)
- Backtracking elimination (see more)
- Regex.* static methods and concurrency (see more)
- Other overhead reductions (see more)
Stay tuned for more coverage as Microsoft turns .NET 5 into .NET 6, coming in November.