Performance Improvements in .NET 10

141 pointsposted 6 hours ago
by benaadams

27 Comments

throwaway13337

2 hours ago

C# 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/...

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

2 hours ago

Even way back when: 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

PaulHoule

4 hours ago

This 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/

SkiFire13

3 hours ago

> 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.

On the other side however if you don't write code that the borrow checker would accept you likely won't get these optimizations. And even if it was accepted there's a chance the analysis needs to be too deep or complex for the escape analysis to work. Ultimately this is a nice speed up in practice but not something I would rely on.

gpderetta

3 hours ago

GC is problematic for cross-language foundational libraries though (unless they run on the same VM of course).

rudedogg

3 hours ago

This is wishful thinking. It’s the same as other layers we have like auto-vectorization where you don’t know if it’s working without performance analysis. The complexity compounds and reasoning about performance gets harder because the interactions get more complex with abstractions like these.

Also, the more I work with this stuff the more I think trying to avoid memory management is foolish. You end up having to think about it, even at the highest of levels like a React app. It takes some experience, but I’d rather just manage the memory myself and confront the issue from the start. It’s slower at first, but leads to better designs. And it’s simpler, you just have to do more work upfront.

Edit:

> 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.

Should have noted that Zig solves this by making the convention be to pass an allocator in to any function that allocates. So the boundaries/responsibilities become very clear.

qingcharles

38 minutes ago

The 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.

jcmontx

2 hours ago

Native 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

andix

4 hours ago

Whenever 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.

verdie-g

3 hours ago

In my company running maybe 20K servers on .NET, we get a 10-20% CPU decrease every time we upgrade to the next major.

yread

6 hours ago

Amazing 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

4 hours 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.

cogman10

2 hours ago

This 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.

wiseowise

2 hours ago

This whole post alone deserves a big kudos. Very detailed, true engineering culture.

whalesalad

3 hours ago

This 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

2 hours ago

To be fair there is like 500 paragraphs of content after it

hvb2

an hour ago

Try 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...

Varelion

4 hours ago

If 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.

homebrewer

4 hours ago

TBH aspnet core has been the most stable web framework I've worked in my life. If you have good test coverage, upgrading projects between major versions often takes minutes — because nothing, or almost nothing, gets broken. Some things might get deprecated, but the old way keeps working for years, you can chip away at it slowly over the next year or two.

You still need to make sure that everything works, but that's what tests are for, and this has to be checked regardless of your tech stack.

Of course, they had a massive backwards compat break when moving from the regular aspnet to aspnet core, here's hoping nothing like that happens in the next 10-15 years..

PaulHoule

4 hours ago

I haven't written C# professionally since the early 2010s but back then the language had a big problem in that the old Container classes were not compatible with the Container<X> classes that were added when they added generics to C#. This created an ugly split in the ecosystem because if you were using Container<X> you could not pass it to an old API that expected a Container.

Java on the other hand had an implementation of generics that made Container<X> just a Container so you could mix your old containers with generic containers.

Now Java's approach used type erasure and had some limitations, but the C# incompatibility made me suffer every day, that's the cultural difference between Java and a lot of other languages.

It's funny because when I am coding Java and thinking just about Java I really enjoy the type system and rarely feel myself limited by type erasure and when I do I can unerase types easily by

- statically subclassing GenericType<X> to GenericType<ConcreteClass>

- dynamically by adding a type argument to the constructor

- mangling names (say you're writing out stubs to generate code to call a library, you can't use polymorphism to differentiate between

  Expression<Result> someMethod(Expression<Integer> x)
and

  Expression<Result> someMethod(Expression<Double> x)
since after erasure the signature is the same so you just gotta grit your teeth and mangle the method names)

but whenever I spend some time coding hard in a language that doesn't erase generic parameters I come back and I am not in my comfortable Java groove and it hurts.

CyanLite2

4 hours ago

lol, whut?

Microsoft created the ".NET Standard" for this. Literally anything that targets .NET Standard 1.0 should work from circa 2001 through modern day 2025. You still get the (perf) benefits up the runtime upgrade which is what the blog post is about.

giancarlostoro

2 hours ago

Can you provide more examples? I've taken a Win32 application from .NET 3.5, converted it to a .NET Console Application (it was a server with a GUI) to run on .NET 8 with minimal friction, a lot of it wound up me just importing .NET Framework packages anew from NuGet.

What are you looking for out of .NET? The staple packages don't go away as often as in languages like NodeJS

9cb14c1ec0

an hour ago

If you are not on the bleeding edge of whatever new framework Microsoft is promoting today, the ecosystem is incredibly stable.

porridgeraisin

4 hours ago

Yep. A breaking change that makes my code a gajillion times faster is still always just a dirty breaking change that I'll hate.

sidkshatriya

3 hours ago

Now 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

3 hours ago

What 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.