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
这样的玩具示例来衡量性能开销可能会产生误导:-)
我正在试用 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
这样的玩具示例来衡量性能开销可能会产生误导:-)