“Async.Parallel”结构真的有助于在多核系统上更快地进行计算吗? .NET TPL“任务”是否以某种方式涉及到这里?
open System;
let key = Console.ReadKey(true);
let start = System.DateTime.Now
let pmap f l = seq { for a in l do yield async {return f a} } |> Async.Parallel |> Async.RunSynchronously
let map f l = seq {for a in l do yield f a}
let work f l =
match key.KeyChar with
| '1' -> pmap f l
| '2' -> Seq.toArray (map f l)
| _ -> [||]
let result = work (fun x -> (x * x) / 75) (seq { 1 .. 100000*3})
let endtime = DateTime.Now - start
printfn "%A"endtime;
let pause = Console.ReadKey(true);
我想你们中的一些人会在理论上解释它,但我也会欣赏一些现实世界的测试。
答案 0 :(得分:12)
仅当任务执行更复杂的操作时,才使用F#async
进行纯粹的CPU绑定任务。如果您正在尝试并行执行非常简单的代码,那么最好使用PLINQ(和任务并行库),它们针对这类问题进行了更优化。
然而,即便如此,在一个微不足道的情况下获得加速是很困难的。如果您想再尝试一下,可以试试这个:
// Turn on timing in F# interactive
#time
let data = [| 1 .. 5000000*3 |]
// Use standard 'map' function for arrays
let result = Array.map (fun x -> (x * x) / 75) data
// Use optimized parallel version
let result = Array.Parallel.map (fun x -> (x * x) / 75) data
请注意,使用Array.map
本身比使用序列表达式然后将结果转换为数组要快得多。如果您想使用比映射更复杂的操作,那么F#PowerPack包含PSeq
模块,其功能类似于Seq
或List
中的功能:
#r @"FSharp.PowerPack.Parallel.Seq.dll"
data
|> PSeq.map (fun a -> ...)
|> PSeq.filter (fun a -> ...)
|> PSeq.sort
|> Array.ofSeq
如果您想了解更多相关信息,我最近写了一篇关于parallel programming in F#的博客系列。
答案 1 :(得分:9)
pmap
正在做的是创建一个包含300,000个任务对象的列表,安排它们并行运行,然后才实际并行运行它们。换句话说,一个线程将在那里创建300,000个对象并将它们排队到线程池中。只有这样他们才能执行。
由于你的任务是如此微不足道(乘法和除法),创建任务,安排任务和处理结果的开销远不仅仅是运行计算。这意味着async
隐喻不适合此操作。为此使用PLINQ要好得多。
答案 2 :(得分:3)
通过计算这么简单,更好的方法是只创建一些异步线程(可能每个cpu一个),然后分别计算答案的一部分。正如Gabe回答的那样,您将花费所有时间来创建任务对象。
使用这种类型的计划,我得到的速度与CPU的数量非常接近(我尝试的最多是8 ......我意识到它不会永远扩展)
编写一个实用程序来执行此操作比调用PLINQ更重要,我想,但是一旦有了pmap
类型实用程序,就可以轻松地重用它。