每个线程的GHC垃圾收集策略

7
我有一个Scotty API服务器,它构建Elasticsearch查询,从ES获取结果并呈现JSON。
PhoenixGin等其他服务器相比,我使用BloodHound为服务ES响应提供更高的CPU利用率和吞吐量,但GinPhoenix在内存效率方面比Scotty好得多。
Scotty的统计数据
 wrk -t30 -c100 -d30s "http://localhost:3000/filters?apid=1&hfa=true"
Running 30s test @ http://localhost:3000/filters?apid=1&hfa=true
  30 threads and 100 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   192.04ms  305.45ms   1.95s    83.06%
    Req/Sec   133.42    118.21     1.37k    75.54%
  68669 requests in 30.10s, 19.97MB read
Requests/sec:   2281.51
Transfer/sec:    679.28KB

这些统计数据是在我的Mac上安装了GHC 7.10.1的情况下得出的。
处理器信息2.5GHx i5 内存信息8GB 1600 Mhz DDR3
我对GHC基于轻量级线程的并发性能印象深刻,但内存效率仍然是一个大问题。
通过分析内存使用情况,我得到了以下统计数据。
    39,222,354,072 bytes allocated in the heap
     277,239,312 bytes copied during GC
     522,218,848 bytes maximum residency (14 sample(s))
         761,408 bytes maximum slop
            1124 MB total memory in use (0 MB lost due to fragmentation)

                                     Tot time (elapsed)  Avg pause  Max pause
  Gen  0       373 colls,   373 par    2.802s   0.978s     0.0026s    0.0150s
  Gen  1        14 colls,    13 par    0.534s   0.166s     0.0119s    0.0253s

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

  TASKS: 18 (1 bound, 17 peak workers (17 total), using -N4)

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

  INIT    time    0.001s  (  0.008s elapsed)
  MUT     time   31.425s  ( 36.161s elapsed)
  GC      time    3.337s  (  1.144s elapsed)
  EXIT    time    0.000s  (  0.001s elapsed)
  Total   time   34.765s  ( 37.314s elapsed)

  Alloc rate    1,248,117,604 bytes per MUT second

  Productivity  90.4% of total user, 84.2% of total elapsed

gc_alloc_block_sync: 27215
whitehole_spin: 0
gen[0].sync: 8919
gen[1].sync: 30902

Phoenix 从未超过150 MB,而Gin 的内存占用要低得多。

我认为 GHC 使用标记-清除策略进行垃圾回收。我也相信使用类似于 Erlang VM 的每个线程增量 GC 策略会更好地提高内存效率。

通过解释 Don Stewart 对相关问题的回答,我认为在 GHC 中必须有一些方法可以更改 GC 策略。

我还注意到,当并发级别较低时,内存使用保持稳定且非常低,因此我认为只有在并发水平相当高时,内存使用才会急剧增加。

有什么想法/指针来解决这个问题吗?


你的实际问题是什么?如何减少内存使用? - MathematicalOrchid
大体上是可以的。更具体地说,如果我能找到一种在GHC中启用每个线程GC策略的方法。 - user2512324
我曾经认为最近版本的GHC已经实现了每个线程的垃圾回收,但是我可能对此有所错误... - MathematicalOrchid
我相信 GHC 使用标记和清除策略。它使用分代机制来管理内存。如果我错了,请纠正我。 - user2512324
这肯定是生成的,并且我认为它是标记/清除算法。但是,我曾经以为最年轻的一代是按核心计算的。不过我会看看能否找到实际参考资料。 - MathematicalOrchid
对于像Web服务器这样的用例,每个请求都会生成一个线程,因此每个线程内存清理策略似乎是一种合理的策略。 - user2512324
1个回答

1

http://community.haskell.org/~simonmar/papers/local-gc.pdf

这篇由Simon Marlow撰写的论文介绍了线程本地堆,并声称这已经在GHC中实现。它的日期是2011年。我不能确定当前版本的GHC是否实际上采用了这种方法(即,这是否已经进入GHC的发布版本,它是否仍然是当前的现状等),但似乎我的回忆并非完全虚构。
我还要指出GHC手册的一部分,其中解释了可以调整垃圾收集器设置的选项。

https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/runtime-control.html#rts-options-gc

特别地,默认情况下,GHC使用2空格收集器,但添加-c RTS选项会使其使用稍微慢一点的1空间收集器,这应该会占用更少的RAM。(我完全不清楚这些信息适用于哪些世代。)

我有印象Simon Marlow是处理大多数RTS事物(包括垃圾回收)的人,所以如果你想直接了解真相,可以在IRC上找到他。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接