16 ms·
Performance Improvements in .NET 10
- yread 1y agoAmazing progress, some LINQ constructions were made 300 times faster. But reasoning over what code does heap allocations is getting more and more complicated. I guess intuition has to give even more way to benchmarking
- kg 1y ago(Disclosure: I get paid to work on .NET) The good thing is that the older techniques to minimize allocations still work, and tools like refs and ref structs make it easier to write zero-allocation code than it used to be. But it's definitely harder to reason about whether optimizations will 'light up' for code that uses heap-allocated classes, closures, etc. than it was in the past, even if it's harder for a nice reason. BenchmarkDotNet is a fantastic piece of tech at least, I have found it easier to benchmark in C# than in most other ecosystems I work with.
- jsmith45 1y agoMy view, which I suspect even Toub would agree with is that if being allocation free or even just extremely low allocation is critical to you, then go ahead and use structure and stackalloc, etc that guarentee no allocations. It is far more guarenteed that that will work in all circumstances than these JIT optimizations, which could have some edge cases where they won't function as expected. If stopwatch allocations were a major concern (as opposed to just feeling like a possible perf bottleneck) then a modern ValueStopwatch struct that consists of two longs (accumulatedDuration, and startTimestamp, which if non-zero means the watch is running) plus calling into the stopwatch static methods is still simple and unambiguous. But in cases where being low/no allocation is less critical, but your are still concerned about the impacts of the allocations, then these sort of optimizations certainly do help. Plus they even help when you don't really care about allocations, just raw perf, since the optimizations improve raw performance too.
- Varelion 1y agoIf only they could fix the ecosystem's stability; I feel like anything written with C#'s staple packages becomes outdated considerably faster than any other options.
- porridgeraisin 1y agoYep. A breaking change that makes my code a gajillion times faster is still always just a dirty breaking change that I'll hate.
- CyanLite2 1y agoThe breaking changes are very well documented and are esoteric in nature. https://learn.microsoft.com/en-us/dotnet/core/compatibility/10.0 https://learn.microsoft.com/en-us/dotnet/core/compatibility/... Scott Hanselman has a very short blog on how 20 year old code is upgraded to the latest .NET in just a few short minutes: https://www.hanselman.com/blog/upgrading-a-20-year-old-university-project-to-net-6-with-dotnetupgradeassistant https://www.hanselman.com/blog/upgrading-a-20-year-old-unive...
- AtNightWeCode 1y agoBs, basically, the docs about upgrading between frameworks and what works with what is actually pretty current and often disappears after some years. Especially anything about edge cases. Several upgrades also demands that you do the upgrade version by version. It is tedious work if you don't have a full understanding of the app. Nuget has also become a complete dependency hell. Today you often have to point out what to use to get your legacy apps to build with newer .NET versions. You can't just go with latest.
- GreymanTheGrey 1y ago"Several upgrades also demands that you do the upgrade version by version" This seems unlikely. Do you have a source?
- deleted 1y ago[deleted]
- PaulHoule 1y agoThis kinda stuff brings languages like C# and Java closer to Rust in performance, thinking like the "borrow checker" it understands the scope of some objects and puts them on the stack and avoids garbage collection and allocation overhead. It keeps the unsung benefit of garbage collection for "programming in the large" in which memory allocation is treated as a global concern independent of everything else instead of a global concern that has to be managed locally in every line of code. Rust's strategy is problematic for code reuse just as C/C++'s strategy is problematic. Without garbage collection a library has to know how it fits into the memory allocation strategies of the application as a whole. In general a library doesn't know if the application still needs a buffer and the application doesn't know if the library needs it, but... the garbage collector does. Sure you can "RC all the things" but then you might as well have a garbage collector. In the Java world we are still waiting for https://openjdk.org/projects/valhalla/ https://openjdk.org/projects/valhalla/
- gpderetta 1y agoGC is problematic for cross-language foundational libraries though (unless they run on the same VM of course).
- PaulHoule 1y agoBut what’s so bad about that Clojure and Java make a great team.
- Grikbdl 1y agoYeah but now try to make your Java library useful to a C#, Go or Python application.
- PaulHoule 1y agoIn the case of Python I think you could produce something like Jython that runs inside the Java runtime and lines up https://openjdk.org/jeps/454 https://openjdk.org/jeps/454 with the Python FFA so you could run things like numpy inside of it. Would be transformative for my side projects.
- andix 1y agoWhenever I read about those yearly performance improvements, I wonder if there are some real world benchmarks. Some applications that were benchmarked on all .NET versions up from Framework 4.7 as a baseline. And all the .NET versions since as a comparison.
- deleted 1y ago[deleted]
- andyayers 1y agoHere is one that has some historical comparison, though it does not show perf on Framework, and no .NET 10 yet. https://endjin.com/blog/2024/11/how-dotnet-9-boosted-ais-dotnet-performance-by-9-percent-for-free https://endjin.com/blog/2024/11/how-dotnet-9-boosted-ais-dot...
- verdie-g 1y agoIn my company running maybe 20K servers on .NET, we get a 10-20% CPU decrease every time we upgrade to the next major.
- andix 1y agoThat's impressive, considering a major release is sniped every year. I've never thought it would be that much of an improvement outside of synthetic benchmarks.
- cjbgkagh 1y agoManaged devs have been begging for such fixes since the start, so many important perf gains were just left on the table at the same time we were being given the message that C++ and Javascript was the future. My exposure to it at MS was during the Win vs Dev Div conflict (the Steve Sinofsky / Steve Balmer era) and the message that managed software was slow was a core part of that battle.
- andix 1y ago
- whalesalad 1y agoThis reads like one of those recipe blogs where you first need to hear about great grandpappy's migration during the potato famine before you can get to the details on how to make cupcakes. First 5 paragraphs are just noise.
- yread 1y agoTo be fair there is like 500 paragraphs of content after it
- hvb2 1y agoTry to imagine the hours going into a post like this. These posts are among the very best, digging into details explaining why things work and why changes were made. Every time they get released I'm happy because no one killed it...
- jiggawatts 1y agoThe first five paragraphs tell a very relevant story to drive a key point home: performance is often about many small things shaved down, not one giant silver bullet. I’ve lost count of the number of times I’ve seen customers immediately “double down” on the size of their servers as a quick fix… and achieving nothing other than increasing their cloud provider’s revenue. Performance comes from a long series of individually small fixes.
- deleted 1y ago[deleted]
- sidkshatriya 1y agoNow if only they could bring their focus on performance to windows 11 as a whole. It’s just shocking how much faster vanilla Linux is compared to vanilla windows 11. Edit: by vanilla Linux I mean out of the box installation of your typical distribution e.g. Ubuntu without any explicit optimisation or tuning for performance
- tracker1 1y agoWhat is "vanilla Linux"? Ubuntu+Gnome, Mint+Cinnamon, Fedora+KDE, Arch+COSMIC ..? Each distro, platform and desktop manager and related apps are relatively different, though all work pretty well on modern hardware. I'm currently running PopOS COSMIC alpha with the 6.16-4 kernel via mainline. It's been pretty good, though there have been many rough edges regarding keyboard navigation/support in the new apps.
- throwaway13337 1y agoC# is definitely fast. There are some benchmark games that I relied on in the past as a quick check and saw it as underwelming vs rust/c++. For example: https://benchmarksgame-team.pages.debian.net/benchmarksgame/performance/binarytrees.html https://benchmarksgame-team.pages.debian.net/benchmarksgame/... We see that the fastest C# version is 6 times slower than the rust/c++ implementation. But that's super deceiving because those versions use arena allocators. Doing the same (wrote this morning, actually) yielded a ~20% difference vs the fastest rust implementation. This was with dotnet 9. I think the model of using GC by default and managing the memory when it's important is the sanest approach. Requiring everything to be manually managed seems like a waste of time. C# is perfect for managing memory when you need it only. I like rust syntactically. I think C# is too object-oriented. But with a very solid standard lib, practical design, good tools, and speed when you need it, C# remains super underrated.
- anonymars 1y agoEven way back when: https://devblogs.microsoft.com/oldnewthing/20060731-15/?p=30293 https://devblogs.microsoft.com/oldnewthing/20060731-15/?p=30... > The fact that Rico Mariani was able to do a literal translation of the original C++ version into C# and blow the socks off it is a testament to the power and performance of managed code. It took me several days of painful optimization to catch up, including one optimization that introduced a bug, and then Rico simply had to do a little tweaking with one hand tied behind his back to regain the lead. Sure, I eventually won but look at the cost of that victory
- duncans 1y agoUnrelated, but Microsoft should be ashamed that most of the links in that blog no longer work.
- tialaramex 1y agoI was actually surprised much of the material still exists - though the links don't work. Microsoft performs so much needless self-vandalism and I know some things I care about are gone. Which just reminded that yeah, all the links I'd made to Raymond Chen's "The poor man's way of identifying memory leaks" no longer work. The Rust implementation is less than four years old, but its link (which worked) now does not. -sigh- Tempting to go reconstruct that performance improvement "fight" in Rust too. Maybe another day.
- cogman10 1y agoThis sort of post really makes me appreciate what's gone into the JVM. A lot of these optimizations are things that the JVM has long implemented. That's not a knock on C# either. Besides the JVM the only other place you'll see these sorts of optimizations are the likes of V8. It makes me happy to see MS investing in C# like this. I love the notion of having competing VMed languages.
- markjgx 1y agoI was wondering how the JVM and V8 stack up. Do you have a source for that claim? Genuinely curious. Coming from the game dev world, I’ve grown more and more convinced that managed languages are the right move for most code. My reasoning is simple: most game developers don’t have the time or patience to deeply understand allocation strategy, span usage, and memory access patterns, even though those are some of the most performance-critical and time-consuming parts of programming to get right. Managed languages hide a lot of that complexity. Instead of explaining to someone, “you were supposed to use this specialized allocator for your array and make sure your functions were array-view compatible”—something that’s notoriously tedious to guarantee in game engines given how few developers even think about array views—you just let developers write code and most of those problems go away. I’m not saying everything should be managed. Core engine code should still live in the predictable, statically compiled world. But history shows it can work: projects like Jak and Daxter were written primarily in a custom LISPy scripting language, and even Ryujinx (RIP), the excellent Nintendo Switch emulator, is written entirely in C#. Another strong technical reason is that managed JIT languages can profile at runtime and keep optimizing call sites based on actual usage patterns. Normally, developers would have to do this by hand or rely on PGO, which works but is painful to set up. Industry standards make this harder to adopt since platforms like Sony still block JIT, but I think this is the direction we should be moving.
- cogman10 1y ago> Do you have a source for that claim? Genuinely curious. Hmm, no real source for the claim, just a general interest in the two VMs. JVM for work and V8 partially for work. AFAIK, those are two of the VMs that are getting the highest amount of research in due to how they are positioned. If you want more general information on V8, I suggest reading about the turbofan design docs [1]. The JVM first started doing a lot of these optimizations with Hotspot. Google poached the engineers that did Hotspot and put them to work on V8. That's why the two VMs tend to share similar optimizations. > I’ve grown more and more convinced that managed languages are the right move for most code. I tend to agree, to an extent. The JVM is very fast, but it's also memory hungry and doesn't give up the memory it claims easily. That's due to the nature of the GC algorithms it employs and some historical constraints which bloat object size. One thing you get out of non-gced languages is much lower memory usage and much better live memory density (when done correctly). [1] https://docs.google.com/presentation/d/1sOEF4MlF7LeO7uq-uThJSulJlTh--wgLeaVibsbb3tc/edit?slide=id.g5499b9c42_0983#slide=id.g5499b9c42_0983 https://docs.google.com/presentation/d/1sOEF4MlF7LeO7uq-uThJ...
- wiseowise 1y agoThis whole post alone deserves a big kudos. Very detailed, true engineering culture.
- jcmontx 1y agoNative AOT brings C# closer to Go, pretty nice feature. BTW, Upgrading .NET apps for the last few years has been such a breeze. It won't take more than a few minutes + adjusting a couple build pipelines
- qingcharles 1y agoThe big benefit of this for my work is being able to run sites on smaller and cheaper boxes every year. It's not just the speed, but they've made huge strides in the last couple of versions in massively reducing the memory use of different object types. This is only being compared to last year's v9, but if you compare against v7 from a couple of years ago, the changes are huge. And this only reflects the changes to the underlying framework compilation, and doesn't factor in changes to say the Kestrel web server and static asset delivery that have taken a ton of load away. Intel are also regularly checking in changes before they release new CPUs now so that the framework is ahead of their releases and takes advantage of new features.
- kristianp 1y agoInteresting that they've introduced LeftJoin and RightJoin into Linq with this version, 18 years after the 1st version of LinqToSQL with .net 3.5. Left and right joins aren't a particularly obscure thing.
- jitbit 1y agoWe run a (pretty) big multi-tenant SaaS app on dotnet and I was literally able to downgrade our production servers from 4-core-16GB vms to 2-core-8GB on AWS when going from .NET 6 to .NET 8 (we only use LTS releases b/c compliance, don't ask). Super excited to try .NET 10. Also, almost zero breaking changes when bumping versions, which is very refreshing compared to the front-end world. That said, while C# (and the dotnet runtime) are awesome, MS is doing it a disservice lately (poor tooling, Cursor/VSCode controversy etc. etc.) C# could've been so much bigger...