How to evaluate tuples in parallel with using rpar Strategy in Haskell?

I came across a problem with Eval monad and rpar Strategy in Haskell. Consider the following code:

 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' 

I compile and run it like this:

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

When I use inParallel , the parallelism function works as expected. In runtime statistics, I see:

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

When I switch to inParallel2 , all parallelism disappears:

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

Why is there no parallel evaluation of tuples? I tried to force the tuple before passing it to rpar:

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

but no result yet. What am I doing wrong?

+6
source share
1 answer

The rpar strategy annotates the term for possible evaluation in parallel, but only to the weak normal shape of the head , which essentially means to the outermost constructor. Thus, for an integer or double value, this means a full estimate, but for a pair only the constructor of the pair will be evaluated, not its components.

Forcing a couple before passing it to rpar will not help. Now you evaluate the pair locally before annotating the already-estimated tuple for a possible parallel evaluation.

You probably want to combine rpar with the rpar strategy, thereby stating that the term should be fully evaluated, if possible, in parallel. You can do it by saying

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

The dot operator is used to write strategies.

However, there is another problem with your code: pattern matching makes you evaluate right away, and so using pattern matching for rpar named expressions is usually bad. In particular, the line

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

all parallelism will win, because before the spark can be raised for evaluation by another thread, the local thread will already start evaluating it to match the pattern. You can prevent this by using a lazy / incontrovertible pattern:

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

Or, conversely, use fst and snd to access the components of the pair.

Finally, don't expect actual acceleration if you create sparks that are as cheap as adding one to the whole. While the sparks themselves are relatively cheap, they are not free, so they work better if the calculation that you comment on for a parallel evaluation is somewhat expensive.

You might want to read some tutorials on using strategies, such as Simon Marlow's Parallel and Parallel Programming using Haskell or my own Deterministic Parallel Programming in Haskell .

+11
source

All Articles