2012-09-01 27 views
9

我有一个Conway的生命游戏的实现。如果可能的话,我想通过使用并行性来加速它。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] 
在仿形

,邻居占所用的时间的6.3%,因此,虽然小我期望的noticable加速通过并联映射它。

我用一个简单的函数

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个内核?其次,并行性总是会带来一些开销,所以如果每个线程所做的工作都非常小,额外的开销将超过任何加速。 – 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

我认为这与这个过程的长寿有关......也就是说,初始化线程池等比我们从并行化中获得的收益(固然微不足道)要昂贵。 –