Haskell-parMap与并行性
我有一个康威生活游戏的实现。如果可能的话,我想通过使用并行来加速它Haskell-parMap与并行性,haskell,parallel-processing,conways-game-of-life,Haskell,Parallel Processing,Conways Game Of Life,我有一个康威生活游戏的实现。如果可能的话,我想通过使用并行来加速它 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 &
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]
并将并行版本编译为
ghc --make -O2 -threaded life.hs
并将其作为
./life +RTS -N3
事实证明,并行版本的速度较慢。我在这里使用parMap有误吗?这甚至是可以使用并行性的情况吗?我认为您的测量不正确。你的
parLife
确实比life
快一点。事实上,在我的机器(Phenom X4,4核)上,前者只需要后者92.5%的时间,考虑到你所说的你只期望6%的改进是相当好的
您的基准测试设置是什么?你试过使用吗?以下是我所做的:
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运行
.首先,您的计算机中是否至少有3个内核?其次,并行总是伴随着一些开销,因此如果每个线程所做的工作非常小,那么额外的开销将超过任何速度提升。我有一个i5-2500k,因此肯定有多达4个内核可用。注意,改进算法比并行化可以获得更大的加速。大部分时间用于排序
和元素
。使用单元格列表已排序的事实(并更改fPent
使其排序),您可以大致将时间减半。@DanielFischer:如果fPent已排序,则列表不必排序。freq将与活动单元相邻的每个单元的列表作为其输入,同一个单元可能是许多不同活动单元的邻居,并且在列表中分散显示。如果有一种方法能够比排序更快地找到列表中每个唯一元素的出现总数,这确实会改进算法chris,您可以在步骤中对列表进行排序:freq=map(length&&head)。小组。排序
,这样下一代的单元格总是被排序的。嗯,奇怪。我还得到了parLife
比criteria更快的结果,但是当我独立运行时,parLife
始终比life
慢很多。啊,只有线程运行时,而不是非线程运行时!我认为这与进程的寿命有关…例如,初始化线程池等比我们从并行化中获得的(公认的较小)收益更昂贵。可能。但我运行了500次迭代以获得更可靠的计时。这足够长了,初始化线程池等应该无关紧要。可能线程运行时的触发开销更高。哦,等等!非线程运行时甚至不支持并行,没有火花!
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 ]