August 22nd, 2024

Async2 – The .NET Runtime Async experiment concludes

The .NET team's async2 experiment aims to enhance async/await efficiency by shifting management to the runtime, improving performance and exception handling, but may take years to become production-ready.

Read original articleLink Icon
Async2 – The .NET Runtime Async experiment concludes

The .NET team has concluded its async2 experiment, which aimed to enhance the async/await pattern's efficiency and flexibility by transitioning it from a compiler feature to a runtime feature. Initially, the team explored using green threads, which are lightweight user-space threads, but abandoned this approach due to complexities in interaction with the existing async model, performance issues with native code interop, and compatibility challenges with security measures. The async2 experiment focuses on allowing the runtime to manage async and await, which could lead to improved performance, especially in deep call stacks, and better exception handling. Unlike the current implementation, async2 would not revert changes in the ExecutionContext and SynchronizationContext at function boundaries, allowing for more consistent data flow. The experiment has shown that async2 could outperform the current async model in various scenarios, although it may take years before it is production-ready. The transition will require interoperation between the existing async and the new async2.

- The async2 experiment aimed to improve the efficiency of the async/await pattern in .NET.

- Green threads were initially considered but were abandoned due to various complexities.

- Async2 allows the runtime to manage async/await, potentially improving performance and exception handling.

- The new model will not revert context changes, leading to more consistent data flow.

- Production readiness for async2 may take several years, requiring interop with the current async model.

Link Icon 8 comments
By @cube2222 - 6 months
This all makes sense but

> Green threads are different. The memory of a green thread is allocated on the heap. But all of this comes with a cost: As they aren't managed by the OS, they can't take advantage of multiple cores inherently. But for I/O-bound operations, they are a good fit.

this is clearly not true? Am I missing some nuance here, as I'm sure the author knows what they're talking about?

Green threads can totally use a multi-threaded runtime, like e.g. Go does, and it works just fine. The main hurdle with them is arguably FFI.

By @neonsunset - 6 months
For everyone reading this blog post I caution to take the conclusions there with a grain of salt as they are an interpretation of the notes written down here: https://github.com/dotnet/runtimelab/blob/feature/async2-exp...

It is difficult to draw conclusions at the present moment on e.g. memory consumption until the work on this, which is underway, makes it into mainline runtime. It's important to understand that the experiment was first and foremost a research to look into modernizing async implementation, and was a massive success. Now once that is proven, the tuned and polished implementation will be made.

Once it is done and makes into a release (it could even be as early as .NET 10), then further review will be possible.

With that said, thank you for writing it, .NET tends to be criminally underrated and slept on by the most of the industry, and these help to alleviate it even if just a bit.

By @bob1029 - 6 months
I don't know if this proposal makes a lot of sense.

The existing async1/TPL path is stable & predictable. If you find yourself needing more performance, you can reach for hardware Thread instances and use whatever locking/waiting/sharing/context strategies you desire. Anything else is a weird blend of compromises that is going to have caveats that are not immediately obvious for your specific use case.

For example, async2 w/ runtime JIT appears to have some tradeoffs with regard to GC & memory usage and the experiment writeup leaves some open-ended questions here[0].

[0]: https://github.com/dotnet/runtimelab/blob/feature/async2-exp...

By @kodablah - 6 months
> And for the transition phase, there has to be interop for async ↔ async2

For those like me who weren't clear whether `async2` was expected to be a real keyword in the final language, it's not[0].

0 - https://github.com/dotnet/runtime/issues/94620#issuecomment-...

By @jayd16 - 6 months
I'm encouraged by the talk of improved exception handling and a recognition of how much garbage it creates. It makes it hard to do things in an idiomatic way and keep GC pressure low.

I hope they also revisit task state tracking and TaskCanceledException. It feels out of place to use an exception (and unroll the stack) as control flow in the same API they added ValueTask to keep Task handles on the stack.

By @ikekkdcjkfke - 6 months
What is the fundemental problem we are trying to solve here? My pet problem with async is that it takes so much syntax, and i wonder why we need to do that. As i understand it it is all about blocking calls taking up OS threads.

So i will try to naievly solve it here, and maybe i end up with the same conclusions.

When doing a blocking call, the OS thread could just start executing something else transparently and not 'yield' anything to the code, it just stops executing the code and comes back and executes further, no await, no tasks no nothing. Ok, but how did we end up in a thread? The http listener started 4 threads and it's just putting a stack of function pointers and context memory for the threads to eat when they want. There is a separate engine on another OS thread that handles where the threads can start executing ready code again. No Task or await keywords show up in this code. I have no idea how stack traces work but i guess that can just be saved to memory and loaded back again when the threads feel like executing some ready code

By @xeromal - 6 months
It's a bummer green threads didn't work out
By @giancarlostoro - 6 months
I still remember first hearing about Rust and how it was using Green Threads, then out of the blue a year later passes or months, and I'm reading that it doesn't do any of that anymore, it's basically C++ on steroids mixed with functional programming. I never did look up why they gave up on Green threads and other things, I wonder if they faced similar challenges?