Runtime: Please consider ReusableTask and ReusableValueTask

Created on 3 Dec 2017  路  25Comments  路  Source: dotnet/runtime

What makes reference type important is its reusability, but Task as a reference type is not designed to be reused.
Considering REST Api or RPC, the most frequent functions are to be called millions of times from Internet requests, if task or valueTask would be able to be reused, the time for heap allocation or stack initialization(deep copy does cost time) could be saved at somewhat degree.
Reusable must take some disadvantage, but just like valueTask, it could bring some goodness.

area-System.Threading.Tasks question

Most helpful comment

i do compare Task.Delay with thread.Sleep

No, you do not. You are comparing the system timer resolution with the system's thread sleep resolution. It happens so that, at least on Windows, Sleep can sometimes offer better resolution that timers.

All 25 comments

  1. The common wisdom is that a remote request takes a fairly long time, so adding to that the time required for GC of the Task will not affect the overall performance much. Are you sure a reusable Task would help you? If you have data showing that, could you share it?
  2. You can write your own reusable awaitable, Stephen Toub has an article describing how to do that.
  3. I don't understand what would be the point of ReusableValueTask. In what situation would it help, if you had both ValueTask and ReusableTask?

@svick, it's not about task's death(GC),it's about its birth, allocating consumes resources. The idea is out of hint of golang's goroutine vs .net's task comparison, where goroutine is reusable and bring some benefits. Wpf and x.forms's dependency system also inspire me a bit.

allocating consumes resources

Sure, but do you have anything showing it's a significant amount of resources? Everything consumes resources, and almost everything could consume less resources. The question is, what do you have to pay for that, in terms of programmer time, code complexity, increased API surface and tradeoffs?

bring some benefits

Can you describe and ideally quantify what benefits are those?

As for Go's channels, .Net types that are somewhat comparable are Rx IObservable<T> and the proposed IAsyncEnumerable<T> (https://github.com/dotnet/csharplang/issues/43).

@svick ,consider the code below, which cost 15 seconds, where eache Task.Delay delayed 15 ms more than 1. That's too much, bro. If there is a ResuableTask.Delay, it is possible that only some thread switch delay could be added.
static async Task loopAsync() { var wt = new Stopwatch(); wt.Start(); for (int i = 0; i < 1000; i++) { await Task.Delay(1); //Thread.Sleep(1); } wt.Stop(); Console.WriteLine($"Time:{ wt.ElapsedMilliseconds}"); }

@juepiezhongren Task.Delay(1) taking more than 1ms (e.g. 15ms) is nothing to do with processing overhead in the framework - it's just an effect of Windows scheduling granularity. If you could travel back in time and run it on a single-core Win2000 machine it would take ~10ms, and if you really pushed back to Win98 then it would probably take 55ms.

As a rule of thumb, if you're looking for the overhead of a single allocation you should be looking for microseconds, not 10+ms.

@willdean 锛宨 do compare Task.Delay with thread.Sleep, while later cost around 5ms. So, not just system's business.

i do compare Task.Delay with thread.Sleep

No, you do not. You are comparing the system timer resolution with the system's thread sleep resolution. It happens so that, at least on Windows, Sleep can sometimes offer better resolution that timers.

For very high throughput, low latency situations, where all memory is preallocated and we're leaning heavily on cpu prefetch patterns, having a ReusableTask which one could push/pop into a stack/cache would be really useful. @stephentoub

@unicomp21 , task, valueTask, taskCompletionSource..... there r quite a few to be reusable.

ReusableValueTask doesn't make much sense. It's a value type..

How would you possibly know it's safe to reset a ReusableTask instance? Consuming code may have been doing other things for an arbitrarily long time before needing to .ContinueWith or .GetAwaiter on the original (reusable) task. If the (reusable) task instance is reset, you'd have logic errors and deadlocks.

It would need to be special usage. You cant replace all instances of Task with reusable task. It鈥檚 really a new custom awaitable that can be reset. Having something like this in the framework would be nice (there are many places where the BCL does this today internally). Task as it exists today is too big and has too many features to do something like this safely

To replace every Task is a catastrophe, just treat Task the most comprehensive IPromise imp, ValueTask and ReusableTask as supplementary ones

mistake

@davidfowl, considering an rpc method is a vTask async one, if there is a ReusableValueTask pool(native heap or managed heap), i guess that there still will be some performance improvement

@jnm2, it's just a more option just like vTask

I think this would be a great idea to prevent GC pressure, but also recognize that IObservable subscriptions are perhaps more suited for many of the areas where a reusable Task might fit.

There are lots of APIs where some sort of ResettableTask capability would valuable. Stream read/write is the obvious example. In the typical pattern, you don't have more than one outstanding read/write at a time. So being able to reuse the same Task-like thing for each read/write avoids GC allocations. This is part of why SocketAsyncEventArgs exists, because it avoids this allocation.

In 2.1, we are introducing new read/write overloads on Stream for Memory. Since these are new APIs, they can return any sort of awaitable they want. So it would be very nice to allow these to return some sort of resettable awaitable. Otherwise, we'd need to introduce new APIs in the future to support this.

I believe @stephentoub has a proposal for how to do this.

@stephentoub after so long time, i realized and understood what u means! I think valueTask with iValueTaskSource could perfectly solve my consideration! Good work and great thanks to .net core team!
btw, .net standard is a bit lagging behind, we want this and together with span for our xamarin apps!

@kouvel i think this issue could be closed.
but there r more to be required
https://github.com/dotnet/corefx/issues/31253

Does it make sense to have a reusable TCS build on top of this infrastructure? (there are several versions of that inside of corefx) Should we expose one?

As I've mentioned previously elsewhere, it has been and continues to be the plan to expose ManualResetValueTaskSource{Logic}, and/or some derivation thereof.

Ah, I didn't see that. I know the compiler prototype for IAsyncEnumerable uses it but I didn't see any API proposal.

ManualResetValueTaskSourceCore is shipping in .NET Core 3.0.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

chunseoklee picture chunseoklee  路  3Comments

GitAntoinee picture GitAntoinee  路  3Comments

nalywa picture nalywa  路  3Comments

Timovzl picture Timovzl  路  3Comments

EgorBo picture EgorBo  路  3Comments