使用 monad 的并行策略
我经常看到与纯计算相关的 Haskell 并行策略的用法和解释(例如 fib
)。但是,我不经常看到它与一元结构一起使用:当应用于 ST
或 IO 时,是否有对
par
和相关函数的效果的合理解释?这样的用法会带来任何加速吗?
I often see the usage and explanation of Haskell's parallel strategies connected to pure computations (for example fib
). However, I do not often see it used with monadic constructions: is there a reasonable interpretation of the effect of par
and related functions when applied to ST s
or IO
? Would any speedup be gained from such a usage?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
IO monad 中的并行性更准确地称为“并发”,并且由
forkIO
和Control.Concurrent
模块中的朋友支持。并行化 ST monad 的困难在于 ST 必然是单线程的——这就是它的目的。 ST monad 有一个惰性变体,
Control.Monad.ST.Lazy
,原则上它可以支持并行计算,但我不知道有人尝试这样做。有一个用于并行评估的新 monad,名为 Eval,可以在最新版本的并行包。我建议将
Eval
monad 与rpar
和rseq
一起使用,而不是par
和pseq
如今,因为它可以带来更健壮和可读的代码。例如,通常的fib
示例可以写成Parallelism in the IO monad is more correctly called "Concurrency", and is supported by
forkIO
and friends in theControl.Concurrent
module.The difficulty with parallelising the ST monad is that ST is necessarily single-threaded - that's its purpose. There is a lazy variant of the ST monad,
Control.Monad.ST.Lazy
, which in principle could support parallel evaluation, but I'm not aware of anyone having tried to do this.There's a new monad for parallel evaluation called Eval, which can be found in recent versions of the parallel package. I recommend using the
Eval
monad withrpar
andrseq
instead ofpar
andpseq
these days, because it leads to more robust and readable code. For example, the usualfib
example can be written在某些情况下这是有意义的,但一般来说您不应该这样做。检查以下内容:
在
doPar
中,引发a
的计算,然后主线程计算b
。但是,主线程完成b
的计算后,可能也会开始计算a
。现在您有两个线程评估a
,这意味着某些 IO 操作将执行两次(或可能更多)。但是,如果一个线程完成对a
的评估,另一个线程就会放弃迄今为止所做的事情。为了确保这一点的安全,您需要满足以下几点:a
时改变了一些数据,那么另一个也在处理a
的线程会表现得明智吗?可能不会。如果您的
someIOCalc
看起来像这样,那么将其与
par
和一起使用绝对不安全>unsafePerformIO
。现在,这值得吗?或许。 Spark 很便宜,甚至比线程更便宜,所以理论上它应该是性能增益。实际上,也许没有那么多。 Roman Leschinsky 有一篇关于此的精彩博客文章。
就我个人而言,我发现 forkIO 的推理要简单得多。
There are some situations where this makes sense, but in general you shouldn't do it. Examine the following:
in
doPar
, a calculation fora
is sparked, then the main thread evaluatesb
. But, it's possible that after the main thread finishes the calculation ofb
it will begin to evaluatea
as well. Now you have two threads evaluatinga
, meaning that some of the IO actions will be performed twice (or possibly more). But if one thread finishes evaluatinga
, the other will just drop what it's done so far. In order for this to be safe, you need a few things to be true:a
, will the other thread also working ona
behave sensibly? Probably not.If your
someIOCalc
looks like thisit's absolutely not safe to use this with
par
andunsafePerformIO
.Now, is it ever worth it? Maybe. Sparks are cheap, even cheaper than threads, so in theory it should be a performance gain. In practice, perhaps not so much. Roman Leschinsky has a nice blog post about this.
Personally, I've found it much simpler to reason about
forkIO
.