Jan*_*rek 6 parallel-processing haskell
我偶然发现了Evalmonad和rpar StrategyHaskell 的问题.考虑以下代码:
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'
Run Code Online (Sandbox Code Playgroud)
我编译并运行它像这样:
ghc -O2 -Wall -threaded -rtsopts -fforce-recomp -eventlog eval.hs
./eval +RTS -N3 -ls -s
Run Code Online (Sandbox Code Playgroud)
我用的时候 inParallel函数并行工作时按预期工作.在输出运行时统计信息中,我看到:
SPARKS: 100000 (100000 converted, 0 overflowed, 0 dud, 0 GC'd, 0 fizzled)
Run Code Online (Sandbox Code Playgroud)
当我切换到inParallel2功能时,所有并行性都消失了:
SPARKS: 0 (0 converted, 0 overflowed, 0 dud, 0 GC'd, 0 fizzled)
Run Code Online (Sandbox Code Playgroud)
为什么元组的评估不能并行工作?在尝试将元组传递给rpar之前,我尝试强制它:
rpar $!! (x + 1, y + 1)
Run Code Online (Sandbox Code Playgroud)
但仍然没有结果.我究竟做错了什么?
kos*_*kus 11
该rpar策略并行地注释一个术语以进行可能的评估,但仅限于弱头部正常形式,这实际上意味着直到最外层的构造函数.因此对于整数或双精度,这意味着完全评估,但对于一对,只会对配对构造函数而不是其组件进行评估.
在将它们传递给它之前强迫它rpar不会有帮助.现在,您在对已经评估的元组进行注释以进行可能的并行评估之前,在本地评估该对.
您可能希望将策略rpar与rdeepseq策略结合起来,从而声明应该完全评估该术语,如果可能的话.你可以这样说
(rpar `dot` rdeepseq) (x + 1, y + 1)
Run Code Online (Sandbox Code Playgroud)
该dot运营商是构成战略.
但是,您的代码还有另一个问题:模式匹配会强制立即进行评估,因此对于带rpar注释的表达式使用模式匹配通常是个坏主意.特别是这条线
(x',y') <- (rpar `dot` rdeepseq) (x + 1, y + 1)
Run Code Online (Sandbox Code Playgroud)
将击败所有并行性,因为在另一个线程可以选择火花进行评估之前,本地线程已经开始对其进行评估以匹配模式.您可以通过使用延迟/无可辩驳的模式来防止这种情况:
~(x',y') <- (rpar `dot` rdeepseq) (x + 1, y + 1)
Run Code Online (Sandbox Code Playgroud)
或者替代地使用fst和snd访问该对的组件.
最后,如果你创建的火花和添加一个整数一样便宜,不要指望实际的加速.虽然火花本身相对便宜,但它们并不是免费的,所以如果你对并行评估进行注释的计算成本有些高,它们的效果会更好.
您可能希望阅读一些使用策略的教程,例如使用Haskell的 Simon Marlow 并行和并发编程或Haskell中我自己的确定性并行编程.