The .NET 4.5 framework libraries integrate C#-style Task-based async fairly extensively. In many cases, they also continue to expose APM-style Begin/End method pairs. F# can easily adapt either method to F#-style asynchronous computations.
My question is, given an IO-bound operation that’s implemented in the framework as both Begin/End and Task-based async, is there a performance or memory advantage to choosing one over the other when adapting to F# async?
For example, in .NET 4.5, System.IO.Stream has both BeginRead and ReadAsync. That means I can do this…
type System.IO.Stream with
member x.AsyncRead(buffer, offset, count) =
Async.FromBeginEnd(buffer, offset, count, x.BeginRead, x.EndRead)
Or I can do this…
type System.IO.Stream with
member x.AsyncRead(buffer, offset, count) =
x.ReadAsync(buffer, offset, count) |> Async.AwaitTask
Is there any reason to prefer one over the other? The main difference that I can think of is that the read operation will have already started when the second extension method returns, but not so with the first extension method.
An
AsyncReadextension method (implemented in terms ofFromBeginEnd) is already defined in FSharp.Core.AwaitTaskis just a thin wrapper overTask.ContinueWith. So it boils down to a comparison ofTaskandasync–which is more efficient, or right for the job. Since you’re working withasyncs, the only relevant difference would be performance. I’m not an expert on this, but I thinkasyncandTaskaddress the same issue, withTaskhaving an edge for CPU-bound operations.EDIT
I didn’t read your question carefully enough. I don’t know the definitive answer, but given that
Taskandasyncare roughly equivalent, I don’t see any reason to wrap aTaskwith anasyncunless it’s your only option.Begin/Endmethods are a lower-level, more lightweight abstraction, and therefore seem like better building blocks forasyncs.An ancillary thought: the fact that
AsyncReadwasn’t changed to useTaskcould be instructive.