Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Performance of async in F# vs C# (is there a better way to write async { ... })

Tags:

async-await

f#

FWIW I think that the issues detailed here just comes down to the c# compiler being smarter, and making an efficient state machine based model to handle async code, whereas the F# compiler creates a myriad of objects and function calls that are just generally less efficient.

Anyway, if I have the c# function below:

public async static Task<IReadOnlyList<T>> CSharpAsyncRead<T>(
         SqlCommand cmd,
         Func<SqlDataReader, T> createDatum)
{
    var result = new List<T>();
    var reader = await cmd.ExecuteReaderAsync();

    while (await reader.ReadAsync())
    {
        var datum = createDatum(reader);
        result.Add(datum);
    }

    return result.AsReadOnly();
}

And then convert this to F# as follows:

let fsharpAsyncRead1 (cmd:SqlCommand) createDatum = async {
    let! reader =
        Async.AwaitTask (cmd.ExecuteReaderAsync ())

    let rec readRows (results:ResizeArray<_>) = async {
        let! readAsyncResult = Async.AwaitTask (reader.ReadAsync ())
        if readAsyncResult then
            let datum = createDatum reader
            results.Add datum
            return! readRows results
        else
            return results.AsReadOnly() :> IReadOnlyList<_>
    }

    return! readRows (ResizeArray ())
}

Then I find that the performance of the f# code is significantly slower, and more CPU hungry, than the c# version. I was wondering if there better was to compose it. I tried removing the recursive function (which appeared a bit ugly with the no while! and no mutable let!s) as follows:

let fsharpAsyncRead2 (cmd:SqlCommand) createDatum = async {
    let result = ResizeArray () 

    let! reader =
        Async.AwaitTask (cmd.ExecuteReaderAsync ())

    let! moreData = Async.AwaitTask (reader.ReadAsync ())
    let mutable isMoreData = moreData
    while isMoreData do
        let datum = createDatum reader

        result.Add datum

        let! moreData = Async.AwaitTask (reader.ReadAsync ())
        isMoreData <- moreData

    return result.AsReadOnly() :> IReadOnlyList<_>
}

But the performance was basically the same.

As an example of the performance, when I was loading a bar of market data such as:

type OHLC = {
    Time  : DateTime
    Open  : float
    High  : float
    Low   : float
    Close : float
}

On my machine, the F# async version took ~ twice as long, and consumed ~ twice as much CPU resources for the whole time it ran - thus taking about 4x as many resources (i.e. internally it must be spinning up more threads?).

(Possibly it is somewhat dubious to be doing a read of such a trivial structure? I'm really just poking the machine to see what it does. In comparison to the non-async version (i.e. just straight Reads) the c# one completes in ~ same time, but consumes > twice as much CPU. i.e straight Read() consumes < 1/8 of the f# resources)

So my question is, as I doing the F# async the "right" way (this was my first attempted usage)?

(...and if I am, then do I just need to go and modify the compiler to add a state machine based implementation for compiled Asyncs... how hard could that be :-) )

like image 936
Paul Westcott Avatar asked Mar 03 '16 03:03

Paul Westcott


People also ask

Does async await improve performance?

The main benefits of asynchronous programming using async / await include the following: Increase the performance and responsiveness of your application, particularly when you have long-running operations that do not require to block the execution.

Why asynchronous is slow?

Answer. The asynchronous version will always be slower than the synchronous version when there is no concurrency. It's doing all of the same work as the non-async version, but with a small amount of overhead added to manage the asynchrony.

What is the benefit of using async?

Benefits of Asynchronous ProgrammingImprove your application's performance and responsiveness, especially if you have long-running operations that don't require blocking the execution. In this case, you can do other things while you wait for the long-running Task to finish.

What is the advantage of async await in C#?

The biggest advantage of using async and await is, it is very simple and the asynchronous method looks very similar to a normal synchronous methods. It does not change programming structure like the old models (APM and EAP) and the resultant asynchronous method look similar to synchronous methods.


1 Answers

F#'s Async and TPL boundary (Async.AwaitTask/Async.StartAsTask) is the slowest thing. But in general, F# Async is slower itself and should be used for IO bound not CPU bound tasks. You may find this repo interesting: https://github.com/buybackoff/FSharpAsyncVsTPL

Basically, I benchmarked the two and also a task builder computation expression, that is originally from FSharpx project. Task builder is much faster when used together with TPL. I use this approach in my Spreads library - which is written in F# but leverages TPL. On this line is highly optimized bind of computation expression which effectively does the same thing as C#'s async/await behind the scenes. I benchmarked every use of task{} computation expression in the library and it is very fast (the gotcha is not to use for/while of computation expression, but recursion). Also, it makes the code interoperable with C#, while F#'s async cannot be consumed from C#.

like image 88
V.B. Avatar answered Sep 19 '22 18:09

V.B.