我在并行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)
答案 0 :(得分:3)
parBuffer
在概念上类似于循环缓冲区,其具有在输入上滚动的常量窗口大小并产生输出,在实现管道并行性或使用惰性流时非常有用。
它的实现内部取决于结果的评估方式 - 它的作用
使用延迟和图形共享(这解释了为什么不丢弃火花)来产生输出,因为输入被消耗,确保线程数限制为N
,因此使用了恒定空间(与{{1相对)在参数列表的长度中是线性的。)
parList
函数用于创建初始start
个火花,并将其余输入传递给N
unsparked。 ret
函数有两个列表(ret
和xs0
,但没有xs0
返回的初始N
元素并激发元素
每次线程完成时从第二个列表开始(结果中的start
;这实际上在用户请求结果时发生),直到没有剩余元素。