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

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不会有帮助.现在,您在对已经评估的元组进行注释以进行可能的并行评估之前,在本地评估该对.

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

(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)

或者替代地使用fstsnd访问该对的组件.

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

您可能希望阅读一些使用策略的教程,例如使用Haskell的 Simon Marlow 并行和并发编程Haskell中我自己的确定性并行编程.