如何在GHC中追踪/缩短GC时间?

时间:2015-12-31 18:47:46

标签: haskell garbage-collection ghc

我最近发布了一个关于improving memory usage/GC的问题,并且已经能够使用ShortByteString将内存消耗减少到我认为合适/比例级别(并且在此过程中从“永远”完成“只是”非常非常慢的“当前测试”,但似乎仍然会考虑过多的GC时间。

对测试进行概要分析得出以下结果:

49,463,229,848 bytes allocated in the heap
68,551,129,400 bytes copied during GC
   212,535,336 bytes maximum residency (500 sample(s))
     3,739,704 bytes maximum slop
           602 MB total memory in use (0 MB lost due to fragmentation)

                                   Tot time (elapsed)  Avg pause  Max pause
Gen  0     14503 colls,     0 par    1.529s   1.681s     0.0001s    0.0164s
Gen  1       500 colls,     0 par   79.202s  79.839s     0.1597s    0.3113s

TASKS: 3 (1 bound, 2 peak workers (2 total), using -N1)

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

INIT    time    0.000s  (  0.001s elapsed)
MUT     time   29.500s  ( 82.361s elapsed)
GC      time   47.253s  ( 47.983s elapsed)
RP      time    0.000s  (  0.000s elapsed)
PROF    time   33.478s  ( 33.537s elapsed)
EXIT    time    0.000s  (  0.025s elapsed)
Total   time  110.324s  (130.372s elapsed)

Alloc rate    1,676,731,643 bytes per MUT second

Productivity  26.8% of total user, 22.7% of total elapsed

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

以下堆可视化:

Heap Visualization

目前我正在使用似乎有帮助的-H,并且已经尝试了增加线程,代,因子和使用压缩(-N -G -F -c)的组合,所有这些都没有导致明显的变化或性能下降。很明显,一切都是长寿的(当我增加-G然后gen 1统计基本上转移到最老的一代,它与gen 0之间没有任何东西),但我不明白为什么GC不能只是“不要管它”。根据我的阅读,我认为GC仅在分配空间不足时运行,但增加分配/因子/堆似乎没有效果。

为了减少GC工作量,还有什么我可以尝试的吗?或者有什么方法可以让我知道我的代码存在根本问题,这使得无法减少这段时间?我相信我必须在内存中构建一个大型数据结构,目前是ST内可变向量的哈希表。我唯一的另一个想法是内部我的数据结构被[不必要地]复制并强制GC运行,但我正在使用ST期望避免这种行为。

1 个答案:

答案 0 :(得分:3)

我还没看过你的代码,所以你应该把它作为一个扩展的评论而不是一个答案。如果,正如您所说,必须构建一个非常大的结构,那么您可以采取两项措施来限制性能影响:

限制指针

第一步也是最容易(但不容易!)的步骤是尽可能减少指针。一个巨大的HashMap构建成本可能比一个巨大的哈希要贵得多,并且由一个未装箱的向量索引。 GC无需追踪未装箱的东西,这是一个巨大的优势,但它们仍然会被它复制。您可能会发现这是可以接受的,因为复制大型连续向量应该非常便宜。

固定一切

下一步是将垃圾收集器中的内容放入固定内存中。我相信标准(不小)ByteString这样做,以及通过外部函数接口创建的数组。