Par函数底层逻辑

时间:2014-05-30 12:27:58

标签: haskell parallel-processing

par功能如何运作?它的签名是:

par :: a -> b -> b.

但这很奇怪。为什么不呢:

par :: (a -> b) -> a -> b

(获取函数,在新线程中执行并返回结果)?

另一个问题,这是正常的haskell多线程吗?

enter image description here

3 个答案:

答案 0 :(得分:10)

par用于推测并行,并依赖于懒惰。

您推测,在您忙于处理a时,应计算未评估的b

稍后在您的计划中,您可能会再次引用a,它就会准备就绪。

这是一个例子。我们希望将3个数字加在一起。每个数字的计算成本都很高。我们可以并行计算它们,然后将它们加在一起:

main = a `par` b `par` c `pseq` print (a + b + c)
    where
        a = ack 3 10
        b = fac 42
        c = fib 34

fac 0 = 1
fac n = n * fac (n-1)

ack 0 n = n+1
ack m 0 = ack (m-1) 1
ack m n = ack (m-1) (ack m (n-1))

fib 0 = 0
fib 1 = 1
fib n = fib (n-1) + fib (n-2)

答案 1 :(得分:4)

因为我们不执行功能"在哈斯克尔。我们评估价值观,这是我们控制处理器活动的方式。 par x y基本上做了什么:在评估结果y时,运行时也已经预先评估x,尽管还没有要求它。

请注意,这不一定是现在编写并行代码的最佳方式。查看更新的替代方案,例如Eval monad。您可能需要阅读Simon Marlow's book

答案 2 :(得分:1)

除了之前的答案之外,值得指出的是ab将仅被评估为弱头正常形式(WHNF)(即应用最外层的缩减或构造函数),因此使用deepseq强制进行评估可能很有用。

在操作语义方面par创建一个 spark ,它是一个指向thunk(未评估计算)的指针并添加到spark池中。这非常便宜,可能有数百万的火花。线程创建是建议性的,运行时系统可以决定不将a转换为线程并通过忽略spark或通过在父级中包含子spark来修剪superfluos parallelism。

您显示的图片可能表示您的代码存在问题,其中在CPU2上执行的线程执行的工作量明显减少(负载不平衡)。