Hi team,
I've implemented some logic attempting to reduce allocations when working with methods returning ValueTask.
I've used the ManualResetValueTaskSourceCore inside a type implementing IValueTaskSource.
My API has a non async method returning ValueTask
My question is if there is a simple way how to chain methods returning ValueTask while keeping allocations zero amortised?
Say i have public ValueTask
Both return ValueTasks from my custom IValueTaskSource.
Is there a simple way to do something like (this is prototype code):
var t1 = MethodA;
var t2 = MethodB;
await t1;
await t2;
, while achieving zero amortised allocs?
Tagging subscribers to this area: @tarekgh
Notify danmosemsft if you want to be subscribed.
CC @stephentoub
Is the idea that to achieve this natively we'd use .Net 5 with DOTNET_SYSTEM_THREADING_POOLASYNCVALUETASKS?
An async ValueTask that runs synchronously does not allocate anything. For the async case, you'll need some sort of state for this. You can certainly manage that yourself, or you can let DOTNET_SYSTEM_THREADING_POOLASYNCVALUETASKS do that for you.
Because ValueTask has some gotchas, the recommendation for success is to always await a ValueTask return directly rather than (as you have done in your example) store them in temp variables. This is the reason we don't have a built-in ValueTask.WhenAll.
Yes i understand the limitations of using ValueTask (double awaits etc). Tbh the interleaved execution in the above example is not even critical for the question. I was excited to be able to write allocation free async code, until it dawned on me that combining custom backed ValueTasks is still problematic (unless i'm missing something). I just want to clarify that it is not sufficient for both MethodA and MethodB to return IValueTaskSource backed ValueTasks, in order for the combination of them in some other method to be zero amortised allocation?
ValueTask
ValueTask
async ValueTask
=> await MethodA+ await MethodB
I just want to clarify that it is not sufficient for both MethodA and MethodB to return IValueTaskSource backed ValueTasks, in order for the combination of them in some other method to be zero amortised allocation?
Correct. An async operation that consumes two other async operations is still its own async operation; if the goal is to have that be an IValueTaskSource implementation, you would need to craft an implementation of IValueTaskSource which provides the relevant implementation. That's what DOTNET_SYSTEM_THREADING_POOLASYNCVALUETASKS does.
@stephentoub Thank you. I want to give you some details so you know of another use case where this makes a difference.
I have an application that sends Kafka messages and wait for another kafka message before finishing the call.
The flow of the method is:
1.Await a task from a TaskCompletionSource. The task of the TCS might handle the continuations of many concurrent calls
2.Send a Kafka message
3.Wait for a Kafka message
4.Complete request
Step 3 (the waiting) used to be based on another TaskCompletionSource. I replaced it with a IValueTaskSource backed VT. It improved throughput by 40%. The last allocation is the StateMachineBox (because i await the first TCS). I had a suspicion that removing allocations would improve throughput, and moving to IValueTaskSource made a difference. Am curious now what will happen if i use DOTNET_SYSTEM_THREADING_POOLASYNCVALUETASKS.
Most helpful comment
An
async ValueTaskthat runs synchronously does not allocate anything. For the async case, you'll need some sort of state for this. You can certainly manage that yourself, or you can letDOTNET_SYSTEM_THREADING_POOLASYNCVALUETASKSdo that for you.Because
ValueTaskhas some gotchas, the recommendation for success is to alwaysawaitaValueTaskreturn directly rather than (as you have done in your example) store them in temp variables. This is the reason we don't have a built-inValueTask.WhenAll.