レイトレーサーを並列化しようとしています。これは、小さな計算の非常に長いリストがあることを意味します。バニラプログラムは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 Execution Context)があることについて話しています。各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によって使用されるランダムな浮動小数点colorPixel
です。タイプは次のとおりです。
colorPixel :: World -> ((Float,Float),([(Float,Float)],[(Float,Float)])) -> Color
Strategy
。もっと良い言葉を選ぶべきだった。また、ヒープオーバーフローの問題がで発生parListChunk
し、parBuffer
あまりにも。
concat $ withStrategy …
か?この動作をで再現することはできません6008010
。これは、編集に最も近いコミットです。