好的,所以我正在做 ProjectEuler 问题 #14,我正在摆弄优化以感受 f#。
在以下代码中:
let evenrule n = n / 2L
let oddrule n = 3L * n + 1L
let applyRule n =
if n % 2L = 0L then evenrule n
else oddrule n
let runRules n =
let rec loop a final =
if a = 1L then final
else loop (applyRule a) (final + 1L)
n, loop (int64 n) 1L
let testlist = seq {for i in 3 .. 2 .. 1000000 do yield i }
let getAns sq = sq |> Seq.head
let seqfil (a,acc) (b,curr) = if acc = curr then (a,acc) else if acc < curr then (b,curr) else (a,acc)
let pmap f l =
seq { for a in l do yield async {return f a} }
|> Seq.map Async.RunSynchronously
let pmap2 f l =
seq { for a in l do yield async {return f a} }
|> Async.Parallel
|> Async.RunSynchronously
let procseq f l = l
|> f runRules
|> Seq.reduce seqfil
|> fst
let timer = System.Diagnostics.Stopwatch()
timer.Start()
let ans1 = testlist |> procseq Seq.map // 837799 00:00:08.6251990
printfn "%A\t%A" ans1 timer.Elapsed
timer.Reset()
timer.Start()
let ans2 = testlist |> procseq pmap
printfn "%A\t%A" ans2 timer.Elapsed // 837799 00:00:12.3010250
timer.Reset()
timer.Start()
let ans3 = testlist |> procseq pmap2
printfn "%A\t%A" ans3 timer.Elapsed // 837799 00:00:58.2413990
timer.Reset()
为什么 Async.Parallel 代码与直接映射相比运行速度非常慢?我知道我不应该看到那么大的影响,因为我只使用双核 mac。
请注意,我不需要帮助解决问题 #14,我只想知道我的并行代码是怎么回事。
最佳答案
Async.Parallel
的使用似乎是正确的。这些数字看起来确实很可疑,但我没有立即看出这里可能有什么问题。
无论如何,异步工作流确实更适合涉及一些异步操作(例如 I/O、通信、等待事件等)的计算。对于 CPU 密集型任务,最好使用 .NET 并行扩展(现在是 .NET 4.0 的一部分;不幸的是,没有 .NET 2.0 版本)。
要从 F# 执行此操作,您需要 F# PowerPack和 FSharp.PowerPack.Parallel.Seq.dll
汇编,其中包含用于处理序列的高阶函数的并行版本(例如 map
:-))
这些函数返回 pseq<'a>
类型的值(在 C# 中称为 ParallelQuery<T>
),表示并行运行的延迟计算(当您在管道中使用多个操作时,这可以实现更好的优化)。还有PSeq.reduce
函数,所以你可能也想在你的处理中使用它(除了 PSeq.map
)。
关于F#:告诉我我对使用 Async.Parallel 的遗漏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2678850/