2012-09-01 5 views
9

私はConwayのGame of Lifeを実装しています。私は、並列性を使用することによって可能な限り高速化したいと思います。プロファイリングでHaskellのparMapと並列処理

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] 

小さなI並列にそれをマッピングすることにより、顕著な高速化を期待しつつ、隣人は、費やされる時間の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を間違って使用していますか?これは並列処理が使用できる場合でもそうですか?

+0

まず、コンピュータに少なくとも3つのコアがありますか?第2に、並列処理には常にオーバーヘッドが発生するため、各スレッドで実行される作業が非常に小さい場合、余分なオーバーヘッドがすべてのスピードアップよりも重要です。 – huon

+0

私はi5-2500kを持っていますので、最大4つのコアが利用可能です。 – cdk

+0

並列化よりもアルゴリズムの改善からはるかに高速化できます。大部分の時間は 'sort'と' elem'で費やされます。セルのリストがソートされている(そしてソートされるように 'fPent'を変更するという事実を利用して)、あなたはおおよそ時間を半分にすることができます。 –

答えて

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で走りました。

Here's the detailed result of the report

+0

うん、奇妙な。私はまた、 'parLife'が基準より速いという結果を得るが、私がスタンドアロンでそれを実行すると、' parLife'は一貫して 'life'よりかなり遅いです。 –

+0

ああ、スレッド化されたランタイムでのみ、非スレッド化ではありません! –

+0

私はそれはプロセスの長寿と関係があると思います...私は。スレッドプールなどを初期化することは、並列化によって得られる(明らかにマイナーな)利益よりも高価です。 –