Can';在并行Haskell代码上没有观察到加速,但分析表明情况并非如此
我写了一个最小的例子,试图用Haskell并行一些计算。我基本上是想通过Can';在并行Haskell代码上没有观察到加速,但分析表明情况并非如此,haskell,parallel-processing,profiling,ghc,Haskell,Parallel Processing,Profiling,Ghc,我写了一个最小的例子,试图用Haskell并行一些计算。我基本上是想通过 把名单一分为二 平行评估两半部分 连接它们 折叠列表 我的代码: 文件中 模块在哪里 进口管制 导入数据。列表 parmap f l=let halflen=地板$(realToFrac$长度l)/2 half1=取halflen l half2=下降半透镜l mapped1=mapfhalf1 mapped2=mapfhalf2 在mapped1`par`(mapped2`pseq`mapped1++mapped2)中
/par+RTS-p-N?
运行它,其中?
是处理器的数量
然后我查看了生成的par.prof
文件
下面是我得到的一些分析结果。我运行并分析了多次,所以我非常确定这些数字不是异常值 与
-N1
一起运行给了我:
Tue May 7 23:20 2019 Time and Allocation Profiling Report (Final)
par +RTS -N1 -p -RTS
total time = 1.20 secs (1200 ticks @ 1000 us, 1 processor)
total alloc = 1,936,132,144 bytes (excludes profiling overheads)
COST CENTRE MODULE SRC %time %alloc
forceEval Main par.hs:28:1-57 75.8 60.3
runMap Main par.hs:31:1-59 17.5 19.0
parmap' Main par.hs:(6,1)-(12,56) 3.2 14.9
parmap'.half1 Main par.hs:8:5-26 2.3 5.8
parmap'.half2 Main par.hs:9:5-26 1.1 0.0
使用-N2
运行(请注意配置文件如何指示超过2倍的加速比?)
使用-N4
运行(请注意,与-N2
相比,加速比稍高一些):
我希望在两个处理器上运行时会有一些加速,但如果在更多处理器上运行,则不会有额外的加速。但是我根本看不到任何加速,但是上面的GHC配置文件告诉我有一个加速——一个不切实际的好加速,如果我使用两个以上的处理器,还有额外的加速 事实上,在另一个我尝试并行计算的程序中,我使用堆栈编译和分析,并观察到类似的错误加速 如果有人能向我解释一下发生了什么,我将不胜感激:这是编写并行Haskell代码的正确方法吗?如果我只有两个部分要并行计算,为什么在运行时使用4个内核会有帮助?还是我只是误解了分析结果
提前感谢您的帮助。当
par
计算它的第一个参数时,它只是强制顶级节点进行计算,但它仍然可以引用一些延迟计算
如果你把它改成
import Control.Parallel
import Data.List
parmap f a l = let
halflen = floor $ (realToFrac $ length l) / 2
half1 = take halflen l
half2 = drop halflen l
mapped1 = map f half1
mapped2 = map f half2
agg1 = a mapped1
agg2 = a mapped2
in agg1 `par` agg2 `pseq` a [agg1, agg2]
forceEval x = sin x + cos x - tan x + sin(3*x) - cos(2*x)
runMap :: Double
runMap = parmap forceEval (foldl' (+) 0.0) [0.0..20000000.0]
main = do
putStrLn $ show runMap
当从一个线程切换时,您将看到加速
time ./f +RTS -N1
-4615093.834471449
real 0m13.077s
user 0m12.333s
sys 0m0.744s
两线
time ./f +RTS -N2
-4615093.834471449
real 0m9.057s
user 0m14.512s
sys 0m2.170s
在我的示例中,agg1
和agg2
是原始值,更容易强制计算
备选方案
您可以使用Control.DeepSeq
中的rnf
强制计算整个列表
import Control.Parallel
import Control.DeepSeq
import Data.List
parmap f l = let
halflen = floor $ (realToFrac $ length l) / 2
half1 = take halflen l
half2 = drop halflen l
mapped1 = map f half1
mapped2 = map f half2
in (rnf mapped1) `par` (rnf mapped2) `pseq` (mapped1 ++ mapped2)
forceEval x = sin x + cos x - tan x + sin(3*x) - cos(2*x)
runMap :: Double
runMap = foldl' (+) 0.0 $ parmap forceEval [0.0..20000000.0]
main = do
putStrLn $ show runMap
并看到性能改进
一根线
time ./f2 +RTS -N1
-4615093.83447202
real 0m15.241s
user 0m14.261s
sys 0m0.980s
两条线
time ./f2 +RTS -N2
-4615093.83447202
real 0m11.640s
user 0m17.092s
sys 0m3.178s
您如何确定没有任何加速?
time ./f2 +RTS -N1
-4615093.83447202
real 0m15.241s
user 0m14.261s
sys 0m0.980s
time ./f2 +RTS -N2
-4615093.83447202
real 0m11.640s
user 0m17.092s
sys 0m3.178s