haskell - 频繁的 GC 会阻止并行运行 Spark

标签 haskell garbage-collection

我尝试在这里运行第一个示例:http://chimera.labs.oreilly.com/books/1230000000929/ch03.html

代码:https://github.com/simonmar/parconc-examples/blob/master/strat.hs

import Control.Parallel
import Control.Parallel.Strategies (rpar, Strategy, using)
import Text.Printf
import System.Environment

-- <<fib
fib :: Integer -> Integer
fib 0 = 1
fib 1 = 1
fib n = fib (n-1) + fib (n-2)
-- >>

main = print pair
 where
  pair =
-- <<pair
   (fib 35, fib 36) `using` parPair
-- >>

-- <<parPair
parPair :: Strategy (a,b)
parPair (a,b) = do
  a' <- rpar a
  b' <- rpar b
  return (a',b')
-- >>

我使用 ghc 7.10.2(在 OSX 上,使用多核机器)使用以下命令进行构建:

ghc -O2 strat.hs -threaded -rtsopts -eventlog

并使用以下命令运行:

./strat +RTS -N2 -l -s

我预计 2 个 fibs 计算会并行运行(前一章的示例按预期工作,因此没有设置问题),但我根本没有获得任何加速,如下所示:

  % ./strat +RTS -N2 -l -s
(14930352,24157817)
   3,127,178,800 bytes allocated in the heap
       6,323,360 bytes copied during GC
          70,000 bytes maximum residency (2 sample(s))
          31,576 bytes maximum slop
               2 MB total memory in use (0 MB lost due to fragmentation)

                                     Tot time (elapsed)  Avg pause  Max pause
  Gen  0      5963 colls,  5963 par    0.179s   0.074s     0.0000s    0.0001s
  Gen  1         2 colls,     1 par    0.000s   0.000s     0.0001s    0.0001s

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

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

  SPARKS: 2 (0 converted, 0 overflowed, 0 dud, 1 GC'd, 1 fizzled)

  INIT    time    0.000s  (  0.001s elapsed)
  MUT     time    1.809s  (  1.870s elapsed)
  GC      time    0.180s  (  0.074s elapsed)
  EXIT    time    0.000s  (  0.000s elapsed)
  Total   time    1.991s  (  1.945s elapsed)

  Alloc rate    1,728,514,772 bytes per MUT second

  Productivity  91.0% of total user, 93.1% of total elapsed

gc_alloc_block_sync: 238
whitehole_spin: 0
gen[0].sync: 0
gen[1].sync: 0

-N1 得到类似的结果(省略)。

正如 #haskell-beginners 中的其他人指出的那样,GC 集合的数量似乎很可疑,因此我尝试在运行时添加 -A16M 。结果看起来更符合预期:

  % ./strat +RTS -N2 -l -s -A16M
(14930352,24157817)
   3,127,179,920 bytes allocated in the heap
         260,960 bytes copied during GC
          69,984 bytes maximum residency (2 sample(s))
          28,320 bytes maximum slop
              33 MB total memory in use (0 MB lost due to fragmentation)

                                     Tot time (elapsed)  Avg pause  Max pause
  Gen  0       115 colls,   115 par    0.105s   0.002s     0.0000s    0.0003s
  Gen  1         2 colls,     1 par    0.000s   0.000s     0.0002s    0.0002s

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

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

  SPARKS: 2 (1 converted, 0 overflowed, 0 dud, 0 GC'd, 1 fizzled)

  INIT    time    0.001s  (  0.001s elapsed)
  MUT     time    1.579s  (  1.087s elapsed)
  GC      time    0.106s  (  0.002s elapsed)
  EXIT    time    0.000s  (  0.000s elapsed)
  Total   time    1.686s  (  1.091s elapsed)

  Alloc rate    1,980,993,138 bytes per MUT second

  Productivity  93.7% of total user, 144.8% of total elapsed

gc_alloc_block_sync: 27
whitehole_spin: 0
gen[0].sync: 0
gen[1].sync: 0

问题是:为什么会出现这种行为?即使频繁进行 GC,我仍然直观地期望 2 个 Spark 在其余 90% 的运行时间内并行运行。

最佳答案

是的,这实际上是 GHC 8.0.1 及更早版本中的一个错误(我正在努力修复 8.0.2)。问题在于 fib 35fib 36 表达式是常量,因此 GHC 将它们作为 CAF 提升到顶层,而 RTS 错误地认为 CAF 无法访问等等垃圾收集 Spark 。

您可以通过在命令行上传递参数来使表达式变得非常量来解决这个问题:

main = do
     [a,b] <- map read <$> getArgs
     let pair = (fib a, fib b) `using` parPair
     print pair

然后使用./strat 35 36运行程序。

关于haskell - 频繁的 GC 会阻止并行运行 Spark ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33895982/

相关文章:

haskell - 为什么这个 Haskell 代码使用fundeps 进行类型检查,但对类型族产生不可触碰的错误?

haskell - 如何限制 Haskell 中的开放世界假设

haskell - ghc中的字节串链接

haskell - 常量的类型声明

c# - 我如何才能发现第 0 代堆中有哪些可终结对象?

haskell - 为 GADT 定义您自己的 Typeable 实例

java - 弱引用持有的类加载器?

Java 垃圾回收和大对象

c# - .Net 2.0 Windows 服务在垃圾回收期间挂起

c# - 如何保持对对象的弱引用?