我经常看到Haskell与纯计算相关的并行策略的用法和解释(例如fib
)。但是,我并不经常看到它与monadic结构一起使用:par
和相关函数在应用于ST s
或IO
时是否有合理的解释?是否可以通过这种用途获得任何加速?
答案 0 :(得分:12)
IO monad中的并行性更准确地称为“并发”,并由forkIO
模块中的Control.Concurrent
和朋友支持。
并行ST monad的困难在于ST必然是单线程的 - 这就是它的目的。有一个ST monad的惰性变体Control.Monad.ST.Lazy
,它原则上可以支持并行评估,但我不知道有人试图这样做。
有一个名为Eval的并行评估新monad,可以在最新版本的parallel package中找到。我建议Eval
monad使用rpar
和rseq
代替par
和pseq
这些天,因为它会带来更强大和可读的代码。例如,可以编写通常的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
的评估,那么另一个线程将会放弃到目前为止所做的事情。为了使其安全,您需要做一些事情:
a
时改变某些数据,那么另一个在a
上工作的线程是否会表现得合理?可能不会。如果您的someIOCalc
看起来像这样
someIOCalc n = do
prelaunchMissiles
threadDelay n
launchMissiles
使用par
和unsafePerformIO
绝对不安全。
现在,它值得吗?也许。火花很便宜,甚至比螺纹便宜,因此理论上它应该是性能提升。在实践中,也许不是那么多。 Roman Leschinsky有一个很好的blog post about this。
就个人而言,我发现理由更简单forkIO
。