2011-01-31 92 views
8

「Async.Parallel」結構是否真的有助於在多核系統上更快地進行計算? .NET TPL「任務」是否涉及這裏?F#異步並行加速計算?

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

隨着序列加速到60000000,它會在40秒左右的時間內運行10秒鐘,其映射和崩潰在pmap內存不足。兩個版本都只使用我的兩個內核之一。現在我需要一個F#專家來解釋這個:-) – TToni 2011-01-31 14:19:44

回答

9

pmap正在做的是創建一個包含300,000個任務對象的列表,並排運行它們,然後才能實際並行運行它們。換句話說,一個線程將在那裏創建300,000個對象並將它們排隊到線程池中。只有這樣他們纔會執行。由於你的任務是如此微不足道(一個乘法和一個除法),所以創建任務,調度它並處理其結果的開銷遠遠超過了運行計算的。這意味着async隱喻不適合這種操作。爲此,使用PLINQ要好得多。

3

通過這樣簡單的計算,更好的方法是隻創建一些異步線程(可能每個cpu都有一個線程),然後分別計算出答案的一部分。正如Gabe回答的那樣,你花費你所有的時間來創建任務對象。

使用這種類型的計劃的,我得到相當密切擴展到(我已經試過最多的是8 ...我知道這不會永遠比例)

編寫程序的CPU數量的加速因爲這樣做比起調用PLINQ更有效,但是一旦你有了一個pmap類型的工具,你可以輕鬆地重用它。

12

對於純CPU限制任務使用F#async僅在任務執行一些更復雜的操作時纔有效。如果您試圖對代碼進行並行化處理,那麼最好使用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模塊類似的功能SeqList:如果您想了解更多關於這

#r @"FSharp.PowerPack.Parallel.Seq.dll" 

data 
|> PSeq.map (fun a -> ...) 
|> PSeq.filter (fun a -> ...) 
|> PSeq.sort 
|> Array.ofSeq 

,我寫了一篇博客一系列關於parallel programming in F#最近。

+5

Tomas忘了提及他在這裏寫了一系列有關F#和並行性的博客文章:http://tomasp.net/blog/fsharp-parallel-samples.aspx – 2011-01-31 16:25:20