为什么在Haskell中与'par'一起需要使用'seq'或'pseq'?

32

我试图理解为什么我们需要标准示例代码中的所有部分:

a `par` b `pseq` a+b

以下为什么不足以满足要求?

a `par` b `par` a+b

上述表达似乎非常描述性:尝试并行评估ab,并返回结果a+b。这样做的原因仅仅是为了效率吗:因为第二个版本会触发两次而不是一次?

以下更简洁的版本呢?

a `par` a+b

为什么我们需要确保在原始标准代码中先计算b,然后再计算a+b

3个回答

31

好的。我认为下面这篇论文回答了我的问题:http://community.haskell.org/~simonmar/papers/threadscope.pdf

总之,问题出在

a `par` b `par` a+b 
并且
a `par` a+b

问题在于没有指定评估的顺序。 在两个版本中,主线程立即开始处理 a(有时是 b),导致火花立即“熄灭”,因为不再需要启动线程来评估主线程已经开始评估的内容。

原始版本

a `par` b `pseq` a+b

确保主线程在对a+b进行评估之前,先处理b,否则会开始评估a,从而为并行评估创建一个Spark a线程的机会。


6
这是正确的,也解释了为什么seq对于这个问题不够用。 seq不能保证评估的顺序。在seq b (a+b)中,主线程可以在(a+b)被求值时先求值b,只要b在WHNF中。 - John L
2
我不明白那个论点如何描述 par a (par b (a + b)) 的问题 - 当然,ab 中的一个将立即被评估,并且相应的火花将熄灭,但另一个火花应该仍然非常活跃,产生并行性。当然,创建然后熄灭火花可能不是最有效的方法,但它可以工作,并将评估顺序问题留给编译器。 - gereeter
par a (a + b) 的情况下,如果运行时首先选择 b,仍然有可能获得“幸运”的并行化。那么 a 火花就不会被熄灭。这在 PDF 中提到:community.haskell.org/~simonmar/papers/threadscope.pdf(第2页)。 - CMCDragonkai

16
a `par` b `par` a+b 

会同时并行地评估a和b,然后返回a+b

然而,pseq确保在评估a+b之前,两者都被评估了

有关该主题的更多详细信息,请参见此链接


7

a `par` b `par` a+b 会为 ab 都创建火花,但是 a+b 会立即被执行,因此其中一个火花会消失(即在主线程中计算)。这个做法的问题在于效率不高,因为我们创建了一个不必要的火花。如果你使用它来实现并行分治,则开销将限制你的加速比。

相对较好的做法是 a `par` a+b,因为它只会创建一个单独的火花。然而,尝试在计算 b 之前计算 a 会使 a 的火花消失,而且由于 b 没有火花,这将导致 a+b 的顺序计算。将顺序切换为 b+a 可以解决这个问题,但是作为代码,这并不强制执行顺序,Haskell 仍然可以将其计算为 a+b

因此,我们使用 a `par` b `pseq` a+b 来强制在尝试计算 a+b 之前在主线程中计算 b。这给了 a 火花的机会在我们尝试计算 a+b 之前出现,并且我们没有创建任何不必要的火花。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接