parBuffer是如何工作的?

use*_*679 5 parallel-processing haskell

我在并行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 ]
Run Code Online (Sandbox Code Playgroud)

我可以看到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)
Run Code Online (Sandbox Code Playgroud)

jev*_*jev 6

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

它的实现在内部取决于结果的评估方式——它利用惰性和图形共享(这解释了为什么不丢弃火花)在输入被消耗时产生输出,确保线程数被限制为N恒定空间使用(而不是parList在参数列表的长度上是线性的)。

start函数用于创建初始N火花并将其余输入传递给retunsparked。该ret函数有两个列表(xs0以及xs0但没有初始N元素,如通过返回start),并从所述第二列表中的每个时间火花的元素的线程完成时(x在结果;这实际发生一旦用户要求的结果),直到没有元素离开。