使用monad的并行策略

时间:2011-01-04 10:17:32

标签: haskell concurrency parallel-processing monads

我经常看到Haskell与纯计算相关的并行策略的用法和解释(例如fib)。但是,我并不经常看到它与monadic结构一起使用:par和相关函数在应用于ST sIO时是否有合理的解释?是否可以通过这种用途获得任何加速?

2 个答案:

答案 0 :(得分:12)

IO monad中的并行性更准确地称为“并发”,并由forkIO模块中的Control.Concurrent和朋友支持。

并行ST monad的困难在于ST必然是单线程的 - 这就是它的目的。有一个ST monad的惰性变体Control.Monad.ST.Lazy,它原则上可以支持并行评估,但我不知道有人试图这样做。

有一个名为Eval的并行评估新monad,可以在最新版本的parallel package中找到。我建议Eval monad使用rparrseq代替parpseq这些天,因为它会带来更强大和可读的代码。例如,可以编写通常的fib示例

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

答案 1 :(得分:1)

在某些情况下这是有道理的,但一般情况下你不应该这样做。检查以下内容:

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

,引发a的计算,然后主线程评估b。但是,在主线程完成b的计算之后,它可能也会开始评估a。现在您有两个线程评估a,这意味着某些IO操作将执行两次(或可能更多)。但是如果一个线程完成了对a的评估,那么另一个线程将会放弃到目前为止所做的事情。为了使其安全,您需要做一些事情:

  1. 多次执行IO操作是安全的。
  2. 只执行某些IO操作是安全的(例如,没有清理)
  3. IO动作没有任何竞争条件。如果一个线程在评估a时改变某些数据,那么另一个在a上工作的线程是否会表现得合理?可能不会。
  4. 任何外来电话都是可重入的(当然,你需要这一点用于并发)
  5. 如果您的someIOCalc看起来像这样

    someIOCalc n = do
      prelaunchMissiles
      threadDelay n
      launchMissiles
    

    使用parunsafePerformIO绝对不安全。

    现在,它值得吗?也许。火花很便宜,甚至比螺纹便宜,因此理论上它应该是性能提升。在实践中,也许不是那么多。 Roman Leschinsky有一个很好的blog post about this

    就个人而言,我发现理由更简单forkIO