简体   繁体   中英

F# AsyncSeq speed issue

I am trying out AsyncSeq and I am confused by a speed issue I am having. Here is a fiddle of the code below.

open System
open FSharpx.Control

let rec numbers n x = asyncSeq {
    yield n
    //printfn "%A" n
    do! Async.Sleep(1)
    if (n + 1 = x) then
        yield n + 1
    else
        yield! numbers (n + 1) x
}

Async.Sleep(300) |> Async.RunSynchronously
for i in [0..300] do printfn "%A" i

numbers 0 300
|> AsyncSeq.iter (fun x -> printfn "%A" x)
|> Async.RunSynchronously

The top loop finishes clearly in a shorter amount of time. While the bottom async sequence takes longer. Is this normal? or am I missing something?

Asynchronous sequences have been designed for writing computations that involve some asynchronous waiting such as disk or network I/O. For this reason, it is quite sensible to expect some overhead when using asyncSeq - in the typical use case, this is not very significant compared to the overhead of the asynchronous operations.

I had a quick look at your example and most of the overhead here is actually coming from Async.Sleep(1) - this uses System.Threading.Timer internally (to schedule the continuation to be called in a thread pool).

On my machine, the following code (with Async.Sleep ) takes about 4.6 seconds:

let rec numbers n x = asyncSeq {
    yield n
    do! Async.Sleep(1) // (sleep)
    if (n < x) then yield! numbers (n + 1) x }

numbers 0 300
|> AsyncSeq.iter (fun x -> printfn "%A" x)
|> Async.RunSynchronously

But when you drop the Async.Sleep call (line marked (sleep) ), the computation takes just 30ms, which is pretty much the same as the following for loop:

for i in [0..300] do 
  printfn "%A" i

Now, if you add asynchronous sleeping to the for loop, it takes 5 seconds too:

for i in [0..300] do 
  Async.Sleep(1) |> Async.RunSynchronously
  printfn "%A" i

This is not too surprising - if you replaced asynchronous sleeping with Thread.Sleep , then it would run faster (but synchronously). So, in summary:

  • There is certainly some overhead of asyncSeq itself, but it is not that big
  • Most of the overhead in your example comes from asynchronous sleeping using Async.Sleep
  • This is quite realistic model of typical uses of asynchronous sequences - they are designed for writing computations that do some asynchronous waiting
  • Measuring performance overhead using toy examples like Async.Sleep can be misleading :-)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM