并行Haskell – GHC GC’ing火花

前端之家收集整理的这篇文章主要介绍了并行Haskell – GHC GC’ing火花前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我有一个程序,我正在尝试并行化(完全粘贴与可运行代码 here).

我已经分析过并发现大部分时间都花在了findNearest上,它基本上是一个大型Data.Map的简单折叠器.

findNearest :: RGB -> M.Map k RGB -> (k,Word32)
findNearest rgb m0 =
    M.foldrWithKey' minDistance (k0,distance rgb r0) m0
    where (k0,r0) = M.findMin m0
          minDistance k r x@(_,d1) =
            -- Euclidean distance in RGB-space
            let d0 = distance rgb r
            in if d0 < d1 then (k,d0) else x

parFindNearest应该在较大Map的子树上并行执行findNearest.

parFindNearest :: NFData k => RGB -> M.Map k RGB -> (k,Word32)
parFindNearest rgb = minimumBy (comparing snd)
                   . parMap rdeepseq (findNearest rgb)
                   . M.splitRoot

不幸的是,GHC GC在转换为有用的并行性之前是我最大的火花.

这是使用ghc -O2 -thaded编译并使用RTS -s -N2运行的结果

839,892,616 bytes allocated in the heap
 123,999,464 bytes copied during GC
   5,320,184 bytes maximum residency (19 sample(s))
   3,214,200 bytes maximum slop
          16 MB total memory in use (0 MB lost due to fragmentation)

                                    Tot time (elapsed)  Avg pause  Max pause
  Gen  0      1550 colls,1550 par    0.23s    0.11s     0.0001s    0.0004s
  Gen  1        19 colls,18 par    0.11s    0.06s     0.0030s    0.0052s

  Parallel GC work balance: 16.48% (serial 0%,perfect 100%)

  TASKS: 6 (1 bound,5 peak workers (5 total),using -N2)

  SPARKS: 215623 (1318 converted,0 overflowed,0 dud,198111 GC'd,16194 fizzled)

  INIT    time    0.00s  (  0.00s elapsed)
  MUT     time    3.72s  (  3.66s elapsed)
  GC      time    0.34s  (  0.17s elapsed)
  EXIT    time    0.00s  (  0.00s elapsed)
  Total   time    4.07s  (  3.84s elapsed)

  Alloc rate    225,726,318 bytes per MUT second

  Productivity  91.6% of total user,97.1% of total elapsed

gc_alloc_block_sync: 9862
whitehole_spin: 0
gen[0].sync: 0
gen[1].sync: 2103

正如您所看到的,大多数火花在转换之前都是GC或fizzle.我已尝试尝试不同的严格性,findNearest返回自定义严格对数据类型而不是元组
,或者使用来自Control.Parallel.Strategies的rdeepseq,但我的火花仍然是GC’d.

我想知道

>为什么在转换之前我的火花是GC?
>如何更改程序以利用并行性?

解决方法

我不是并行策略方面的专家,所以我可能完全错了.但:

如果通过设置足够大的分配区域来禁用GC(例如使用-A20M运行时选项),您将看到大多数火花都是失败的,而不是GC.这意味着它们在相应的火花结束之前通过普通程序流程进行评估.

minimumBy立即强制parMap结果,开始评估它们.与此同时,火花被安排和执行,但为时已晚.当spark完成后,该值已由主线程评估.如果没有-A20M,火花就会被GC进行,因为即使在安排火花之前,也会评估该值并进行GC测量.

这是一个简化的测试用例:

import Control.Parallel.Strategies

f :: Integer -> Integer
f 0 = 1
f n = n * f (n - 1)

main :: IO ()
main = do
  let l = [n..n+10]
      n = 1
      res = parMap rdeepseq f l
  print res

在这种情况下,所有的火花都会失败:

SPARKS: 11 (0 converted,0 GC'd,11 fizzled)

(有时他们是GC’d)

但是如果我在打印结果之前产生主线程,

import Control.Parallel.Strategies
import Control.Concurrent

f :: Integer -> Integer
f 0 = 1
f n = n * f (n - 1)

main :: IO ()
main = do
  let l = [n..n+10]
      n = 1
      res = parMap rdeepseq f l
  res `seq` threadDelay 1
  print res

然后所有火花都被转换:

SPARKS: 11 (11 converted,0 fizzled)

所以,看起来你没有足够的火花(尝试在我的例子中设置l = [n..n 1000]),并且它们不够重(在我的例子中尝试设置n = 1000).

猜你在找的Java相关文章