我试图理解为什么我们需要标准示例代码中的所有部分:
a `par` b `pseq` a+b
以下为什么不足以满足要求?
a `par` b `par` a+b
上述表达似乎非常描述性:尝试并行评估a
和b
,并返回结果a+b
。这样做的原因仅仅是为了效率吗:因为第二个版本会触发两次而不是一次?
以下更简洁的版本呢?
a `par` a+b
为什么我们需要确保在原始标准代码中先计算b
,然后再计算a+b
?
我试图理解为什么我们需要标准示例代码中的所有部分:
a `par` b `pseq` a+b
以下为什么不足以满足要求?
a `par` b `par` a+b
上述表达似乎非常描述性:尝试并行评估a
和b
,并返回结果a+b
。这样做的原因仅仅是为了效率吗:因为第二个版本会触发两次而不是一次?
以下更简洁的版本呢?
a `par` a+b
为什么我们需要确保在原始标准代码中先计算b
,然后再计算a+b
?
好的。我认为下面这篇论文回答了我的问题: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
线程的机会。
a `par` b `par` a+b
会为 a
和 b
都创建火花,但是 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
之前出现,并且我们没有创建任何不必要的火花。
seq
对于这个问题不够用。seq
不能保证评估的顺序。在seq b (a+b)
中,主线程可以在(a+b)
被求值时先求值b
,只要b
在WHNF中。 - John Lpar a (par b (a + b))
的问题 - 当然,a
或b
中的一个将立即被评估,并且相应的火花将熄灭,但另一个火花应该仍然非常活跃,产生并行性。当然,创建然后熄灭火花可能不是最有效的方法,但它可以工作,并将评估顺序问题留给编译器。 - gereeterpar a (a + b)
的情况下,如果运行时首先选择b
,仍然有可能获得“幸运”的并行化。那么a
火花就不会被熄灭。这在 PDF 中提到:community.haskell.org/~simonmar/papers/threadscope.pdf(第2页)。 - CMCDragonkai