我正在看并行3.2.0.4中的parBuffer的代码,但是我缺少关于它如何工作的信息。除了最初的火花外,我看不到如何产生新的火花。
据我所知,它使用parBufferWHNF中的start强制第一个n被par激发,然后经过ret它再次在相同的条目上使用par(这不应该只是丢弃y并且没有冒火花的风险) GC'd?),同时返回相应的结果?然后它直接返回xs,而没有任何额外的火花创建,因为rdeepseq只是调用pseq。

但是清楚地测试这样的代码

withStrategy (parBuffer 10 rdeepseq) $ take 100 [ expensive stuff ]

我可以在ghc RTS信息中看到所有100个火花,但是其他90个火花在哪里创建?

这是我正在查看的代码:
parBufferWHNF :: Int -> Strategy [a]
parBufferWHNF n0 xs0 = return (ret xs0 (start n0 xs0))
  where -- ret :: [a] -> [a] -> [a]
      ret (x:xs) (y:ys) = y `par` (x : ret xs ys)
      ret xs     _      = xs

    -- start :: Int -> [a] -> [a]
       start 0   ys     = ys
       start !_n []     = []
       start !n  (y:ys) = y `par` start (n-1) ys


-- | Like 'evalBuffer' but evaluates the list elements in parallel when
-- pushing them into the buffer.
parBuffer :: Int -> Strategy a -> Strategy [a]
parBuffer n strat = parBufferWHNF n . map (withStrategy strat)

最佳答案

parBuffer在概念上类似于具有恒定窗口大小的循环缓冲区,该缓冲区在输入上滚动并生成输出,并且在实现管道并行性或使用惰性流时很有用。

它的实现内部取决于结果的评估方式-
使用惰性和图共享(这解释了为什么不丢弃火花的原因)来消耗输入来产生输出,从而确保将线程数限制为N,因此使用了恒定的空间(而不是parList,它的长度是线性的参数列表)。
start函数用于创建初始的N火花,并将其余的输入传递给未闪烁的retret函数接受两个列表(xs0xs0,但没有由N返回的初始start元素),并触发一个元素
每次线程完成时从第二个列表开始(结果中的x;这实际上是在用户需要结果后才发生),直到没有剩余元素为止。

关于haskell - parBuffer如何工作?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/24155737/

10-12 17:08