2012-11-12 32 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只是一个例子,我不想用一些精心设计的计算来复杂化示例代码。 –