F# AsyncSeq 速度问题

F# AsyncSeq speed issue

我正在试用 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

最上面的循环显然在更短的时间内完成了。而底部的异步序列需要更长的时间。这是正常的吗?还是我遗漏了什么?

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

我快速查看了您的示例,这里的大部分开销实际上来自 Async.Sleep(1) - 这在内部使用 System.Threading.Timer(安排在线程池中调用的延续) .

在我的机器上,以下代码(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) 的行),计算只需要 30 毫秒,这与下面的 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 这样的玩具示例来衡量性能开销可能会产生误导:-)