rspeele / TaskBuilder.fs

F# computation expression builder for System.Threading.Tasks
Creative Commons Zero v1.0 Universal
235 stars 27 forks source link

Any way we can execute some continuations with ExecuteSynchronously? #5

Closed NinoFloris closed 6 years ago

NinoFloris commented 6 years ago

https://blogs.msdn.microsoft.com/pfxteam/2010/05/24/why-is-taskcontinuationsoptions-executesynchronously-opt-in/

I've also been working with getting F#'s TPL usage up to the perf level that C# has for a few of our ASP.NET Core middlewares and judiciously adding TaskContinuationOptions.ExecuteSynchronously for small function bodies like exception handlers etcetera really made a huge difference in that code's performance profile.

The hard part about this however is that there is no way of accessing these levers while working with a CE. As I see you have done some really nice work on writing a performant CE I hope you might have some good ideas we, the F# community, are able to pursue.

I think it's a pity the TPL is natively so badly supported as it's crucial in working with many of the libraries written in C# these days.

rspeele commented 6 years ago

I don't think this is possible (or necessary? not sure) since Task.ContinueWith is not used at all in the TaskBuilder, so we have nowhere to pass this option.

The implementation is designed to mimic the state machine generated by the C# compiler for async methods. It has a bit more overhead than the C# version because on each MoveNext() call it must call a closure to run the next chunk of synchronous code (to get to the next awaitable or return), instead of just switching on a state ID to jump straight to the code. Other than that unavoidable(?) performance cost, it should function identically.

In my experience so far, Task.ContinueWith is only suitable for chaining a fixed, relatively small number of continuations onto a task. It doesn't make a very good all-purpose monadic Bind due to this issue.

NinoFloris commented 6 years ago

Yeah saw that a while after posting this issue too, I think that is all the room you have there then. Never hurts to confirm though :)

Bottom line it's just a bit slower in stepping and probably more allocation hungry, that doesn't sound that bad actually ^_^. Our hotpaths are mostly using ContinueWIth + ExecSync at the right places, minimizing the time we're in a TPL continuation and evading the CE builder allocs. Most other places just use Async or our own Task CE.

All in all it's a very pretty implementation you created, really nice work, and I'm absolutely including this in our codebase.

rspeele commented 6 years ago

Thanks!