Using parallel strategies with monads

I often see the use and explanation of Haskell parallel strategies related to pure computing (e.g. fib ). However, I do not often see that it is used with monadic constructions: is there a reasonable interpretation of the effect of par and related functions when applied to ST s or IO ? Is it possible to expedite the receipt of such use?

+6
concurrency parallel-processing haskell monads
source share
2 answers

Parallelism in the IO monad is more correctly called "Concurrency" and is supported by forkIO and friends in the Control.Concurrent module.

The difficulty with parallelizing the ST monad is that ST is necessarily single-threaded - this is its goal. There is a lazy version of the ST monad, Control.Monad.ST.Lazy , which in principle can support parallel evaluation, but I do not know whoever tries to do this.

There appeared a new monad for parallel evaluation of Eval , which can be found in the latest versions of the parallel package . I recommend using the Eval monad with rpar and rseq instead of par and pseq these days because it leads to more reliable and readable code. For example, a regular fib example can be written

 fib n = if n < 2 then 1 else runEval $ do x <- rpar (fib (n-1)) y <- rseq (fib (n-2)) return (x+y) 
+12
source share

There are situations where it makes sense, but overall you should not do this. Learn the following:

 doPar = let a = unsafePerformIO $ someIOCalc 1 b = unsafePerformIO $ someIOCalc 2 in a `par` b `pseq` a+b 

in doPar , the calculation for a calculated, then the main thread evaluates b . But it is possible that after the main thread completes the calculation of b , it will also begin to evaluate a . You now have two threads evaluating a , which means that some of the I / O will be performed twice (or possibly more). But if one thread completes the evaluation of a , the other will simply abandon what it has done so far. To be safe, you need a few things to be true:

  • It is safe that I / O actions are performed multiple times.
  • Safe only for some of the I / O operations (e.g. no cleanup)
  • IO actions are free from any race conditions. If one thread mutates some data when evaluating a , will the other thread working on a behave intelligently? Probably not.
  • Any external calls are repetitive (you need it for concurrency in general, of course)

If your someIOCalc looks like this

 someIOCalc n = do prelaunchMissiles threadDelay n launchMissiles 

it is absolutely safe to use this with par and unsafePerformIO .

Now, is it worth it? May be. Sparks are cheap, even cheaper than streams, so theoretically this should be a performance boost. In practice, perhaps not so much. Roman Leshchinsky has a nice blog post about this .

Personally, I found it much easier to talk about forkIO .

+1
source share

All Articles