【发布时间】:2018-07-17 23:52:31
【问题描述】:
我正在比较运行相同计算的两个 haskell 程序的性能。
第一个是顺序的:
main :: IO()
main = putStr $ unlines . map (show . solve) $ [100..107]
where solve x = pow x (10^7) (982451653)
第二个使用Control.Parallel.Strategies:
import Control.Parallel.Strategies
main :: IO()
main = putStr $ unlines . parMap rdeepseq (show . solve) $ [100..107]
where solve x = pow x (10^7) (982451653)
在这两种情况下,pow 是 modular exponentiation 天真地实现为:
pow :: Int -> Int -> Int -> Int
pow a 0 m = 1
pow a b m = a * (pow a (b-1) m) `mod` m
按预期使用 100% 的 CPU,顺序程序在大约 3 秒内运行。
$ stack ghc seq.hs -- -O2
$ \time -f "%e s - %P" ./seq > /dev/null
2.96 s - 100%
当限制为单核时,并行程序在 100% CPU 的情况下也可以在大约 3 秒内运行。
$ stack ghc par.hs -- -O2 -threaded
$ \time -f "%e s - %P" ./par +RTS -N1 > /dev/null
3.14 s - 99%
但是当我在 4 核上运行它时,并没有观察到预期的性能提升:
$ \time -f "%e s - %P" ./par +RTS -N4 > /dev/null
3.31 s - 235%
更令人惊讶的是,顺序程序在多个内核上运行时使用超过 100% 的 CPU:
$ stack ghc seq.hs -- -O2 -threaded
$ \time -f "%e s - %P" ./seq +RTS -N4 > /dev/null
3.26 s - 232%
如何解释这些结果?
编辑 - 根据@RobertK 和@Yuras 的建议,我将rdeeseq 替换为rpar,它确实解决了最初的问题。但是,性能仍然比我预期的要差很多:
$ stack ghc par.hs -- -O2 -threaded
$ \time -f "%e s - %P" ./par +RTS -N1 > /dev/null
3.12 s - 99%
$ \time -f "%e s - %P" ./par +RTS -N4 > /dev/null
1.91 s - 368%
即使 4 个内核平均运行超过 90% 的时间,执行时间也几乎没有除以 2。
【问题讨论】:
标签: haskell parallel-processing