2012-11-12 37 views
6

我偶然發現了一個存在於Haskell中的Eval monad和rparStrategy的問題。請考慮下面的代碼:如何在Haskell中使用rpar策略並行評估元組?

module Main where 

import Control.Parallel.Strategies 

main :: IO() 
main = print . sum . inParallel2 $ [1..10000] 

inParallel :: [Double] -> [Double] 
inParallel xss = runEval . go $ xss 
    where 
     go [] = return [] 
     go (x:xs) = do 
     x' <- rpar $ x + 1 
     xs' <- go xs 
     return (x':xs') 

inParallel2 :: [Double] -> [Double] 
inParallel2 xss = runEval . go $ xss 
    where 
     go [] = return [] 
     go [x] = return $ [x + 1] 
     go (x:y:xs) = do 
     (x',y') <- rpar $ (x + 1, y + 1) 
     xs'  <- go xs 
     return (x':y':xs' 

我編譯,這樣運行:

ghc -O2 -Wall -threaded -rtsopts -fforce-recomp -eventlog eval.hs 
./eval +RTS -N3 -ls -s 

當我使用inParallel功能並行工作正常。在運行時輸出統計資料中看到:

SPARKS: 100000 (100000 converted, 0 overflowed, 0 dud, 0 GC'd, 0 fizzled) 

當我切換到inParallel2功能所有的並行機制消失了:

SPARKS: 0 (0 converted, 0 overflowed, 0 dud, 0 GC'd, 0 fizzled) 

爲什麼沒有並行元組工作的評價是什麼?我試圖強制元組之前傳遞給rpar:

rpar $!! (x + 1, y + 1) 

但仍然沒有結果。我究竟做錯了什麼?

回答

11

rpar策略註釋用於並行可能的評估的術語,但只到弱頭正常形式,這基本上意味着,直至最外側的構造。因此,對於整數或雙精度,這意味着完整的評估,但對於一對,只有對構造函數,而不是它的組件,將得到評估。

在將它傳遞給rpar之前強制這對不會有幫助。現在您在本地評估這對,然後註釋已經評估過的元組,以便進行可能的並行評估。

您可能希望將rparrdeepseq戰略相結合,從而聲明該術語應該完全評估(如果可能的話)並行。您可以通過說

(rpar `dot` rdeepseq) (x + 1, y + 1) 

dot運算符用於撰寫策略。

但是,您的代碼還有另一個問題:模式匹配強制立即評估,因此使用模式匹配rpar -annotated表達式通常是一個壞主意。特別是,行

(x',y') <- (rpar `dot` rdeepseq) (x + 1, y + 1) 

將打敗所有的並行機制,因爲火花可以被另一個線程回升後進行評價時,本地線程就已經開始,以匹配圖案評估它。您可以通過使用一個懶惰/無可辯駁的方式避免這種情況:

~(x',y') <- (rpar `dot` rdeepseq) (x + 1, y + 1) 

或者選擇使用fstsnd訪問對的組成部分。

最後,如果您創建的火花與向整數加1一樣便宜,不要指望實際加速。雖然火花本身相對便宜,但它們不是免費的,所以如果您爲並行評估註釋的計算花費有些昂貴,它們會更好地工作。

您可能想閱讀一些使用策略的教程,例如Simon Marlow的 Parallel and Concurrent Programming using Haskell或我自己的Deterministic Parallel Programming in Haskell

+0

謝謝!當我閱讀Marlow的教程並做一些自己的練習時,實際上出現了這個問題。加1只是一個例子,我不想用一些精心設計的計算來複雜化示例代碼。 –