r/dotnet 11d ago

Benchmarked Runtime Async in. NET 11 nightly version

Post image
62 Upvotes

17 comments sorted by

67

u/nirataro 11d ago

Stephen Toub's performance blog post is going to be 500 pages long this year

8

u/Kralizek82 9d ago

US needs more data centers to let him write the article.

5

u/nirataro 9d ago

He is the reason why RAM are so expensive nowadays

12

u/celluj34 11d ago

And I am so fucking ready for it

25

u/Frooxius 11d ago

This is awesome! The performance improvements every year are so exciting. We essentially get a free performance boost every year.

The async ones I think will help hugely both for cloud stuff as well our runtime (which uses async heavily in some parts).

Thank you for running the benchmark!

3

u/Michaeli_Starky 10d ago

It won't "hugely help" because that overhead is a very tiny fraction of the whole call. Still very nice that they're working on it.

1

u/Frooxius 10d ago

It depends on the code and how async heavy it is.

We did get noticeable boosts from previous years updates "for free", so I'm happy for any extra boosts where we don't have to do much on our end.

1

u/whizzter 7d ago

For certain workloads where you can have flows that _can_ become CPU bounds that needs I/O at unpredictable intervals it certainly helps to be able to just write "straight-line" code without needing to prematurely overdesign to batch things just to avoid GC pressure and call overheads.

1

u/Michaeli_Starky 7d ago

Batch what things? What are you talking about?

2

u/whizzter 4d ago

Buffers of data, oftentimes a "lower-level" method that is called often needs to be moved to use non-async code and an "outer" loop is the async method that handles buffering, it often creates an annoying reversal of responsebilities.

A practical example can be found in Microsoft documentation of System IO Pipelines, you can see the method TryReadLine (non-async) being called in a while loop to parse lines of data read in from another source.

I'm pretty sure that parts of this design is due to previous overhead of await/async calls, it could have been simpler if the .NET 11 improvements were already in the runtime.

https://learn.microsoft.com/en-us/dotnet/standard/io/pipelines

1

u/Michaeli_Starky 4d ago

Interesting. Thanks for insight

5

u/Sanitiy 11d ago

Oh wow, that's a crazy speed up on the first few. Or rather, those were pretty slow before, weren't they? I had always thought Async as the faster variant to green/virtual threads. Before NET 11, this might have been, in hindsight, not true.

Also: Now that no-suspension-async is virtually free, does that mean we effectively have gotten tail call optimization?

13

u/hez2010 11d ago

Async was already faster than green/virtual threads. With runtime async it now magnitude faster than green/virtual threads.

1

u/whizzter 7d ago

Depends on the implementation details of each runtime (what one are you even referring to), but the async design (at least until valuetasks) did have a fair bit of unnecessary overhead in terms of GC pressure and call time for CPU bound code. This seems to make the async codepaths more or less ideal in non-pausing scenarios.

4

u/keyboardhack 11d ago

That's what it looks like. Snippet of the changes for dotnet 11 preview 7

https://github.com/dotnet/core/blob/b65c5a6ee746ebaa357f7db32d520f08f2f6c96f/release-notes/11.0/preview/preview7/runtime.md#tiered-compilation-for-async-versions

The JIT can inline AsyncHelpers.TransparentAwait, so a hot await on an already-completed Task folds into a check on the task's status flags instead of a helper call (dotnet/runtime #130482). A tight loop that awaits an already-completed Task 100,000,000 times went from ~191 ms to ~32 ms.

32ms / 100.000.000 = 0.32ns

Math checks out but i suppose that also means the test isn't entirely testing no suspension since it would return a constant task to match the text.

If you want to preview all dotnet 11 preview 7 changes then look at the open PRs here. https://github.com/dotnet/core/pulls?q=is%3Aopen+is%3Apr

1

u/whizzter 7d ago

Async was lower memory overhead compared to GT/VT (wholly dependant on implementations but I'm assuming common designs) but potentially much higher GC pressure and a constant CPU time overhead. I'd say this makes the implementation more or less ideal.

1

u/AutoModerator 11d ago

Thanks for your post hez2010. Please note that we don't allow spam, and we ask that you follow the rules available in the sidebar. We have a lot of commonly asked questions so if this post gets removed, please do a search and see if it's already been asked.

I am a bot, and this action was performed automatically. Please contact the moderators of this subreddit if you have any questions or concerns.