我正在尝试并行化光线追踪器。这意味着我有很长的小型计算清单。Vanilla程序在特定场景上运行需要67.98秒,总内存使用量为13 MB,生产率为99.2%。
在我的第一次尝试中,我使用parBuffer
了缓冲区大小为50 的并行策略。我之所以选择parBuffer
它,是因为它仅在消耗火花时才通过列表,并且不会像这样强行parList
使用列表的主干,因为它将消耗大量内存。因为列表很长。使用-N2
,它的运行时间为100.46秒,总内存使用量为14 MB,生产率为97.8%。火花信息是:SPARKS: 480000 (476469 converted, 0 overflowed, 0 dud, 161 GC'd, 3370 fizzled)
大量的冒泡火花表示火花的粒度太小,因此接下来我尝试使用策略parListChunk
,该策略将列表分成多个块并为每个块创建一个火花。块大小为时,我得到了最好的结果0.25 * imageWidth
。该程序耗时93.43秒,总内存使用量为236 MB,生产率为97.3%。火花信息为:SPARKS: 2400 (2400 converted, 0 overflowed, 0 dud, 0 GC'd, 0 fizzled)
。我相信更多的内存使用是因为parListChunk
强制列表的脊椎。
然后,我尝试编写自己的策略,将列表懒散地划分为多个块,然后将这些块传递给parBuffer
并连接结果。
concat $ withStrategy (parBuffer 40 rdeepseq) (chunksOf 100 (map colorPixel pixels))
耗时95.99秒,总内存使用量为22MB,生产率为98.8%。在所有火花都已转换并且内存使用率低得多的意义上说,这是成功的,但是速度并没有提高。这是事件日志配置文件的一部分的图像。
如您所见,由于堆溢出,线程正在停止。我尝试添加+RTS -M1G
,这将默认堆大小一直增加到1Gb。结果没有改变。我读到Haskell主线程会在堆栈溢出时使用堆中的内存,因此我也尝试增加默认堆栈大小,+RTS -M1G -K1G
但这也没有影响。
还有什么我可以尝试的吗?我可以根据需要发布有关内存使用情况或事件日志的更详细的分析信息,我没有全部包括在内,因为这是很多信息,我认为没有必要包括所有信息。
编辑:我正在阅读有关Haskell RTS多核支持的文章,它谈到每个内核都有一个HEC(Haskell执行上下文)。每个HEC都包含一个分配区域(这是单个共享堆的一部分)。每当任何HEC的分配区域都用尽时,必须执行垃圾收集。似乎是一个用于控制它的RTS选项 -A。我试过-A32M,但没什么区别。
EDIT2: 这是专用于此问题的github存储库的链接。我已将分析结果包含在分析文件夹中。
EDIT3:这是相关的代码位:
render :: [([(Float,Float)],[(Float,Float)])] -> World -> [Color]
render grids world = cs where
ps = [ (i,j) | j <- reverse [0..wImgHt world - 1] , i <- [0..wImgWd world - 1] ]
cs = map (colorPixel world) (zip ps grids)
--cs = withStrategy (parListChunk (round (wImgWd world)) rdeepseq) (map (colorPixel world) (zip ps grids))
--cs = withStrategy (parBuffer 16 rdeepseq) (map (colorPixel world) (zip ps grids))
--cs = concat $ withStrategy (parBuffer 40 rdeepseq) (chunksOf 100 (map (colorPixel world) (zip ps grids)))
网格是被预先计算和colorPixel.The类型的使用随机彩车colorPixel
为:
colorPixel :: World -> ((Float,Float),([(Float,Float)],[(Float,Float)])) -> Color
Strategy
。应该选一个更好的词。此外,堆溢出问题与发生parListChunk
和parBuffer
太。
concat $ withStrategy …
吗?我无法在6008010
,这是与您的修改最接近的提交。