Fast-path with ValueTask slower than Task #56849
-
Hi, I'm trying to understand why synchronous/non-blocking fast-path with I have two variations. The "ultra fast" is just a simple chain of calls without any
Same code, but with explicit
|
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 1 reply
-
Allocation and gen0 collection is relatively cheap, so the allocations occurring for the tasks here is likely not significantly negatively impacting those throughput numbers. The impact of allocation ends up being higher when there's more going on than in a microbenchmark. For a serialized benchmark, I expect Try changing your benchmarks to actually stress things in parallel to make the GC impact more visible, e.g. [Benchmark]
public int UltraFastTask()
{
Parallel.For(0, 1_000_000, i =>
{
_a.DoTask(_b, _c, _d, _e).GetAwaiter().GetResult();
});
return 0;
}
[Benchmark]
public int UltraFastValueTask()
{
Parallel.For(0, 1_000_000, i =>
{
_a.DoValueTask(_b, _c, _d, _e).GetAwaiter().GetResult();
});
return 0;
} I expect you'll see a stark difference. This is what I got when I tried:
|
Beta Was this translation helpful? Give feedback.
Allocation and gen0 collection is relatively cheap, so the allocations occurring for the tasks here is likely not significantly negatively impacting those throughput numbers. The impact of allocation ends up being higher when there's more going on than in a microbenchmark. For a serialized benchmark, I expect
Task<T>
to be faster. AValueTask<T>
is a discriminated union of aTask<T>
and aT
, which means it has fields for both, which means copying one is more expensive than copying aTask<T>
(that also means aValueTask<T>
takes up more space in a state machine for an async method for which it's awaited). A reference to aTask<T>
could be passed back in a register, but aValueTask<decimal>
…