F#AsyncSeq速度问题

时间:2015-03-28 04:04:27

标签: .net asynchronous f#

我正在尝试使用AsyncSeq,我对我遇到的速度问题感到困惑。以下是fiddle代码。

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

顶部循环在较短的时间内完成。底部异步序列需要更长时间。这是正常的吗?或者我错过了什么?

1 个答案:

答案 0 :(得分:2)

异步序列设计用于编写涉及某些异步等待的计算,例如磁盘或网络I / O.出于这个原因,在使用asyncSeq时期望一些开销是非常明智的 - 在典型的用例中,与异步操作的开销相比,这并不是非常重要。

我快速查看了您的示例,此处的大部分开销实际上来自Async.Sleep(1) - 这在内部使用System.Threading.Timer(计划在线程池中调用继续)。 / p>

在我的机器上,以下代码(Async.Sleep)大约需要4.6秒:

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

但是当您放弃Async.Sleep调用(标记为(sleep)的行)时,计算只需30ms,这与以下for循环几乎相同:

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

现在,如果向for循环添加异步休眠,则需要5秒钟:

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

这并不太令人惊讶 - 如果用Thread.Sleep替换异步休眠,那么它会运行得更快(但同步)。所以,总结一下:

  • asyncSeq本身肯定会有一些开销,但它不是那么大
  • 您示例中的大部分开销来自使用Async.Sleep
  • 的异步休眠
  • 这是异步序列典型用法的非常现实的模型 - 它们被设计用于编写进行异步等待的计算
  • 使用Async.Sleep等玩具示例衡量性能开销可能会产生误导: - )