rspeele / TaskBuilder.fs

F# computation expression builder for System.Threading.Tasks

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Any way we can execute some continuations with ExecuteSynchronously?

NinoFloris opened this issue · comments

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.

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.

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.

Thanks!