9

私はコンウェイのライフゲームを実装しています。可能であれば、並列処理を使用して速度を上げたいと思います。

life :: [(Int, Int)] -> [(Int, Int)]
life cells = map snd . filter rules . freq $ concatMap neighbours cells
    where rules (n, c) = n == 3 || (n == 2 && c `elem` cells)
          freq = map (length &&& head) . group . sort

parLife :: [(Int, Int)] -> [(Int, Int)]
parLife cells = parMap rseq snd . filter rules . freq . concat $ parMap rseq neighbours cells
    where rules (n, c) = n == 3 || (n == 2 && c `elem` cells)
          freq = map (length &&& head) . group . sort

neigbours :: (Int, Int) -> [(Int, Int)]
neighbours (x, y) = [(x + dx, y + dy) | dx <- [-1..1], dy <- [-1..1], dx /= 0 || dy /= 0]

プロファイリングでは、ネイバーが費やした時間の6.3%を占めるため、小さいながらも、並列にマッピングすることで顕著なスピードアップを期待していました。

簡単な関数でテストしました

main = print $ last $ take 200 $ iterate life fPent
    where fPent = [(1, 2), (2, 2), (2, 1), (2, 3), (3, 3)]

並列バージョンを次のようにコンパイルしました

ghc --make -O2 -threaded life.hs

として実行しました

./life +RTS -N3

並列バージョンの方が遅いことがわかります。ここでparMapを誤って使用していますか?これは、並列処理を使用できる場合でもありますか?

4

1 に答える 1

2

私はあなたが正しく測定しているとは思わない。あなたparLifeは確かにより少し速いですlife。実際、私のマシン(Phenom X4、4コア)では、前者は後者の約92.5%の時間しかかかりません。これは、6%の改善しか期待できないと言っていることを考えるとかなり良いことです。

ベンチマークの設定は何ですか?使ってみましたcriterionか?これが私がしたことです:

import Criterion
import Criterion.Main

-- your code, minus main

runGame f n = last $ take n $ iterate f fPent
    where fPent = [(1, 2), (2, 2), (2, 1), (2, 3), (3, 3)]

main = defaultMain
    [ bench "No parallelism 200" $ whnf (runGame life)    200
    , bench "Parallelism 200"    $ whnf (runGame parLife) 200 ]

でコンパイルされghc --make -O2 -o bench、実行されました./bench -o bencht.hmtl +RTS -N3

レポートの詳細な結果は次のとおりです。

于 2012-09-01T13:57:09.963 に答える