Can';在并行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)中

我写了一个最小的例子,试图用Haskell并行一些计算。我基本上是想通过

  • 把名单一分为二
  • 平行评估两半部分
  • 连接它们
  • 折叠列表
  • 我的代码:

    <代码>文件中 模块在哪里 进口管制 导入数据。列表 parmap f l=let halflen=地板$(realToFrac$长度l)/2 half1=取halflen l half2=下降半透镜l mapped1=mapfhalf1 mapped2=mapfhalf2 在mapped1`par`(mapped2`pseq`mapped1++mapped2)中 强制力x=sinx+cox-tanx+sin(3*x)-cos(2*x) runMap::Double runMap=foldl'(+)0.0$parmap forceval[0.0..2000000.0] main=do putStrLn$显示运行图 我用<代码> GHC-PROF-FPROF-自动RTSopts线程-PAR。HS < /代码>

    我使用
    /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