如何在Haskell中使用rpar策略并行评估元组?

时间:2012-11-12 08:35:49

标签: haskell parallel-processing

我偶然发现了Haskell中Eval monad和rpar Strategy的问题。请考虑以下代码:

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)

但仍然没有结果。我做错了什么?

1 个答案:

答案 0 :(得分:11)

rpar策略并行注释一个术语以进行可能的评估,但仅限于弱头正常形式,这实际上意味着直到最外层的构造函数。因此,对于整数或双精度,这意味着完全评估,但对于一对,只会对配对构造函数而不是其组件进行评估。

在将对传递给rpar之前强制该对没有帮助。现在,您在对已经评估的元组进行注释以进行可能的并行评估之前,在本地评估该对。

您可能希望将rparrdeepseq策略结合使用,从而声明应该完全评估该术语,如果可能的话。你可以这样说

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

dot运算符用于撰写策略。

然而,您的代码还有另一个问题:模式匹配强制立即评估,因此使用rpar的模式匹配 - 带注释的表达式通常是一个坏主意。特别是,行

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

将击败所有并行性,因为在另一个线程可以选择火花进行评估之前,本地线程已经开始对其进行评估以匹配模式。您可以使用惰性/无可辩驳的模式来阻止这种情况:

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

或者使用fstsnd访问该对的组件。

最后,如果您创建的火花与向整数添加一个火花一样便宜,请不要指望实际加速。虽然火花本身相对便宜,但它们并不是免费的,所以如果你对并行评估进行注释的计算成本有些高,它们的效果会更好。

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