Faster .NET? Monster post by Microsoft software engineer shows serious improvements
- Reference: 1629287467
- News link: https://www.theregister.co.uk/2021/08/18/faster_dotnet_6/
- Source link:
Toub's [1]45,000-word post follows similar ones for earlier versions since .NET Core 2.0, and is based on an analysis of all merged pull requests (PRs – changes to the code) that were likely to affect performance.
Around 400 PRs are covered, out of 550 candidates, compared to 250 for .NET 5. These posts are always worth investigating for .NET developers since they include performance tips as well as a deep dive into the inner workings of Microsoft's cross-platform runtime.
[2]
There is no single answer to the question "how much faster is .NET 6.0?" (and there may be cases where it is slower). What Toub supplies is benchmark code that compares both performance and the size of the generated code from .NET 6.0, currently in late preview, with .NET Framework 4.8 (the final version of old-style Windows-only .NET) and .NET 5.0 (the current release).
[3]
[4]
The .NET 6.0 runtime is particularly significant since it is a long-term support release. Looking at the benchmarks Toub posts for each case, the results vary though around 25 per cent is common.
[5]
Significant speed-up in .NET 6.0. In this example, .NET 6.0 is around 25% faster than older versions, but generates slightly larger code
In certain cases, .NET 6.0 reduces the time taken to almost nothing. An example is the new Environment.ProcessPath API for getting the path of the current process, which is 72,000 times faster and generates no code, compared to the previous approach using Process.GetCurrentProcess().MainModule.FileName . Similarly, there is an entertaining case where the new optimizer spots that a function in the code always has the same inputs, which are defined as constants, and is therefore able to replace the entire function with a constant, reducing both the time taken and the size to almost zero.
Zero Trust anti-ransomware Rubrik deal
Microsoft has sunk some money into equity in data management firm Rubrik and struck a deal with the backup bods to develop Zero Trust anti-ransomware Azure services.
Over on our sister site, Blocks and Files, storage editor Chris Mellor explained the software hookup was based on the idea that Azure migration by customers would be sped up by an assurance they'd be protected against ransomware attacks, especially against the backdrop of a global rise in criminals attacking firms whose platforms allow access to multiple endpoints.
The amount of Microsoft's equity investment in Rubrik wasn't disclosed. A Bloomberg [6]report suggested it was in the low tens of millions of dollars, at a $4bn valuation.
B&F has said the Rubrik deal with Microsoft could be seen as its response to Cohesity's relationship with AWS. More on the deal [7]here .
This latter case uses a technique called dynamic profile-guided optimization (PGO) which is new in .NET 6.0. The idea is that the compiler does a first compile with instrumentation and then uses the results to optimize a second compilation.
Note that Toub said: "In .NET 6, dynamic PGO is off by default. To enable it, you need to set the DOTNET_TieredPGO environment variable."
Toub also said that the Filestream code, used for reading and writing files, has been rewritten in .NET 6.0. "FileStream has also been plagued over the years by numerous performance-related issues, most of which are part of its asynchronous I/O implementation on Windows," he said, claiming that in the new version "all of these issues are resolved."
[8]
Another change is that over 2,300 "internal and private" classes in the .NET 6.0 code have been newly marked as sealed. A sealed class cannot be inherited, which means the compiler can deduce that a virtual method (which if inherited can be overridden with different code) is not in fact overridden and therefore there is no need to look for this different code.
In networking, data sent via websockets can now be compressed per-message. This is a trade-off since the compression takes more processing time, but the smaller message is faster to transmit. "That could be a good tradeoff if communicating over a real network with longer latencies," said Toub.
[9]Microsoft revamps Visual Studio JavaScript projects in forthcoming version
[10]Microsoft emits last preview of .NET 6 and C# 10, but is C# becoming as complex as C++?
[11]Microsoft wonders if disabling just-in-time compilation of JavaScript improves browser security
[12]Stack Overflow survey: Microsoft IDEs dominate, GCP and Azure battle behind AWS
A recent framework called [13]Blazor means that .NET code can run in a web browser as WebAssembly. Microsoft has put a lot of effort into optimizing this for .NET 6.0.
With Blazor, "an entire .NET application, inclusive of the runtime, is downloaded to and executed in a browser," Toub explained. The size of a Hello World Blazor application in .NET 5.0 is around 2.10MB. In .NET 6.0 this falls to 1.88MB, for reasons which he detailed in the post. Using additional tools based on [14]Emscripten the size falls to 1.82MB.
At this point Toub adds a handy hint: there is a ton of code in .NET's Globalization routines for formatting output for different languages and cultures. If this is not needed, there is a switch developers can set which further reduces the size, to just 1.07MB. Unfortunately, this is for Hello World and a real-world Blazor application will likely be much larger, but this is still an impressive result.
[15]
Toub also noted that the Blazor runtime is based on Mono. "There are actually two runtime implementations in dotnet/runtime: coreclr and mono... Blazor WebAssembly relies on mono, which has been honed over the years to be small and agile for these kinds of scenarios, and has also received a lot of performance investment in .NET 6."
Other performance techniques include smarter code inlining (copying code to where it is called), optimizing for value types such as structs, [16]loop alignment which moves compiled code so the CPU can fetch it more efficiently, and better performance for code which checks whether a type implements an interface, and a new faster implementation of the algorithm for pseudo-random numbers (though the old algorithm is used for compatibility reasons if a seed is supplied).
Impressive, but why is there not a native code compiler for .NET that can compile single-file executables? The answer is in [17]this post about .NET Runtime Form Factors in which the .NET Team explained:
At one point, the goal of .NET Native and CoreRT projects was to replace the established .NET runtime implementation in its entirety. We even had a project for that called Rover – 'Runtime over RedHawk'. This goal was proven to be unrealistic. Re-architecting half of the .NET features built over 20 years (with a large team) to run on the nice clean runtime is prohibitively expensive. Executing this endeavor would require slowing down the investment into the mainstream .NET runtime to a trickle. The vast majority of customers would not see any material improvements for number of years. We consider that direction unacceptable.
Microsoft chose instead to bring over smaller improvements piece by piece to the .NET Runtime so that all .NET code benefits – though there is still an experimental [18]NativeAOT project for those who consider the trade-off worth pursuing. ®
Get our [19]Tech Resources
[1] https://devblogs.microsoft.com/dotnet/performance-improvements-in-net-6/
[2] https://pubads.g.doubleclick.net/gampad/jump?co=1&iu=/6978/reg_software/front&sz=300x50%7C300x100%7C300x250%7C300x251%7C300x252%7C300x600%7C300x601&tile=2&c=2YR0uuYqNa1iZ0UmOwEjxlAAAAMo&t=ct%3Dns%26unitnum%3D2%26raptor%3Dcondor%26pos%3Dtop%26test%3D0
[3] https://pubads.g.doubleclick.net/gampad/jump?co=1&iu=/6978/reg_software/front&sz=300x50%7C300x100%7C300x250%7C300x251%7C300x252%7C300x600%7C300x601&tile=4&c=44YR0uuYqNa1iZ0UmOwEjxlAAAAMo&t=ct%3Dns%26unitnum%3D4%26raptor%3Dfalcon%26pos%3Dmid%26test%3D0
[4] https://pubads.g.doubleclick.net/gampad/jump?co=1&iu=/6978/reg_software/front&sz=300x50%7C300x100%7C300x250%7C300x251%7C300x252%7C300x600%7C300x601&tile=3&c=33YR0uuYqNa1iZ0UmOwEjxlAAAAMo&t=ct%3Dns%26unitnum%3D3%26raptor%3Deagle%26pos%3Dmid%26test%3D0
[5] https://regmedia.co.uk/2021/08/18/benchmark.png
[6] https://www.bloomberg.com/news/articles/2021-08-17/microsoft-invests-in-rubrik-to-protect-against-cyberattacks
[7] https://blocksandfiles.com/2021/08/17/rubrik-gets-microsoft-equity-investment-as-two-build-zero-trust-anti-ransomware-azure-services/
[8] https://pubads.g.doubleclick.net/gampad/jump?co=1&iu=/6978/reg_software/front&sz=300x50%7C300x100%7C300x250%7C300x251%7C300x252%7C300x600%7C300x601&tile=4&c=44YR0uuYqNa1iZ0UmOwEjxlAAAAMo&t=ct%3Dns%26unitnum%3D4%26raptor%3Dfalcon%26pos%3Dmid%26test%3D0
[9] https://www.theregister.com/2021/08/16/microsoft_visual_studio_javascript_revamp/
[10] https://www.theregister.com/2021/08/12/dotnet_6_preview_7/
[11] https://www.theregister.com/2021/08/06/edge_super_duper_security_mode/
[12] https://www.theregister.com/2021/08/03/stackoverflow_developer_survey/
[13] https://www.theregister.com/2020/11/06/blazor_full_stack_c_and/
[14] https://emscripten.org/
[15] https://pubads.g.doubleclick.net/gampad/jump?co=1&iu=/6978/reg_software/front&sz=300x50%7C300x100%7C300x250%7C300x251%7C300x252%7C300x600%7C300x601&tile=3&c=33YR0uuYqNa1iZ0UmOwEjxlAAAAMo&t=ct%3Dns%26unitnum%3D3%26raptor%3Deagle%26pos%3Dmid%26test%3D0
[16] https://devblogs.microsoft.com/dotnet/loop-alignment-in-net-6/
[17] https://github.com/dotnet/designs/blob/main/accepted/2020/form-factors.md
[18] https://github.com/dotnet/runtimelab/tree/feature/NativeAOT
[19] https://whitepapers.theregister.com/
I have c# code running on Windows, Linux, Android, iOS and serverless Functions (on Linux). All the same c# code with no changes. It is not running on a web server but it could do that as well.
Tell me again how it is tied to one OS.
Arguably, it's the long-standing Windows users who have most to complain about. As a consequence of reinventing .NET for multiple platforms (including macOS) it's they who have work to do.
Not necessarily one OS but certainly a limited selection of architectures:
https://github.com/dotnet/runtime/tree/main/src/coreclr/vm
You have ARM*, s390x and x86*
Admittedly Mono has a lot more (almost catching up to Java) but many of these are in no way as well tested as the tier 1 architectures and I possibly wouldn't use them in production.
So is this a problem? Not really, .NET developers are mainly focused on x86* anyway. However I do prefer the "comfort" of the free architecture support offered by the native tools (almost always C and C++).
The .NET native stuff is closer but the GC still makes assumptions (growing / shrinking stack?) and thus is still architecture specific.
The best example I can refer to is Unity3D getting WebAssembly / ASM.js support (for Pluginless web) a full 3 years after Unreal Engine 4 did. Porting .NET (Mono) was a PITA!
1.07 MB for a "Hello World"?
And in the best case... Am I the only one thinking it's a little wasteful?
Okay these days the emphasis is on reducing developers' time instead of resource consumption, but for such a size the program better write itself in under a millisecond.
Re: 1.07 MB for a "Hello World"?
org 100h
mov dx, message
mov ah, 9
int 21h
sub ah, ah
int 21h
message: db "Hello world",13,10,"$"
Re: 1.07 MB for a "Hello World"?
Not a complete implementation. Text should be "Hello, world!"
Re: 1.07 MB for a "Hello World"?
It might surprise you to learn - it certainly opened my eyes - that the [1]average web "page" requires 2MB of total download.
The actual HTML is negligible. About half the traffic relates to images and the remainder is roughly 50% JavaScript.
In that context, the 1.07MB isn't as horrendous as first appears.
Of course, it might be better to download the complete runtime if you could cache it and use it on multiple pages, but I'm not sure all the bits are there yet.
Or do the work on the server, like in the old days...
[1] https://httparchive.org/reports/page-weight?start=2020_01_01&end=latest&view=list
Here's a hint : use NoScript and an ad blocker and that page size is down to the text and the images that have been included by the author.
Makes the web a lot faster. Try it, you'll like it.
Beware of Benchmarks
I've been doing a fair bit of optimization work on a set of libraries lately and so have been writing a lot of benchmarks. One major problem you can run into is one that is mentioned in the story. Optimizing compilers can break the benchmark. A lot of benchmarks do no useful work beyond exercising the CPU. The compiler may spot this and decide to be "helpful" by deleting the benchmark. You end up thinking you are getting amazing performance improvements when in fact you didn't, it's just that your benchmark is no longer working. Writing a good benchmark can be as much work as creating the optimization you are trying to measure.
"Microsoft chose instead to bring over smaller improvements piece by piece to the .NET Runtime"
Thereby creating the equivalent of the old "DLL hell" for anyone who doesn't keep permanently and automatically "updated". I can't count the applications that fail to install or run because they can't find "dot net runtime version xxxx" or some component of it.
Any organisation with a serious security posture (i.e. one that doesn't just posture about security) requires their systems to be stable and known. Constant change (even supposedly for the better) results in permanent unknowns which are a nightmare for risk management.
In the face of constant change, all one can do is reactively and blindly trust to updates from vendors who so far haven't even managed to get any application or OS completely debugged before it's superseded by the next major version.
It's good stuff and when I've had need to use it I have been impressed how easy it is to get your head round.
But I still don't like stuff that is mostly tied to one OS. Mono or official linux version or not. I'm less inclined to invest, same with the Swift and Objective-C. Just my personal 2p for what its worth. Probably less than 2p.