Haskell 为什么这段代码要消耗这么多堆?
。这是一个非常简单的测试,它使用postgresql简单数据库绑定将50000个随机内容插入数据库。它使用MonadRandom,可以懒洋洋地生成东西 以及使用Thing generator的特定代码片段:Haskell 为什么这段代码要消耗这么多堆?,haskell,profiling,heap,space-leak,Haskell,Profiling,Heap,Space Leak,。这是一个非常简单的测试,它使用postgresql简单数据库绑定将50000个随机内容插入数据库。它使用MonadRandom,可以懒洋洋地生成东西 以及使用Thing generator的特定代码片段: insertThings c = do ts <- genThings withTransaction c $ do executeMany c "insert into things (a, b, c) values (?, ?, ?)" $ map (\(Thin
insertThings c = do
ts <- genThings
withTransaction c $ do
executeMany c "insert into things (a, b, c) values (?, ?, ?)" $ map (\(Thing ta tb tc) -> (ta, tb, tc)) $ take 50000 ts
而在第二种情况下,时间很好:
cabal-dev/bin/dumptest +RTS -s > out
1,492,068,768 bytes allocated in the heap
7,941,456 bytes copied during GC
2,054,008 bytes maximum residency (3 sample(s))
70,656 bytes maximum slop
6 MB total memory in use (0 MB lost due to fragmentation)
Tot time (elapsed) Avg pause Max pause
Gen 0 2888 colls, 0 par 0.13s 0.16s 0.0001s 0.0089s
Gen 1 3 colls, 0 par 0.01s 0.01s 0.0020s 0.0043s
INIT time 0.00s ( 0.00s elapsed)
MUT time 2.00s ( 2.37s elapsed)
GC time 0.14s ( 0.16s elapsed)
RP time 0.00s ( 0.00s elapsed)
PROF time 0.00s ( 0.00s elapsed)
EXIT time 0.00s ( 0.00s elapsed)
Total time 2.14s ( 2.53s elapsed)
%GC time 6.5% (6.4% elapsed)
Alloc rate 744,750,084 bytes per MUT second
Productivity 93.5% of total user, 79.0% of total elapsed
我曾尝试应用堆分析,但什么都不懂。看起来所有的50000个字符串都是首先在内存中构造的,然后通过查询转换成bytestring,然后将这些字符串发送到数据库。但为什么会这样呢?我如何确定有罪的代码
GHC版本为7.4.2
编译标志是所有库和包本身的-O2(由cabal dev在sandbox中编译)我已经用formatMany和50k东西检查了配置文件。记忆逐渐积累,然后迅速下降。使用的最大内存略高于40mb。主要成本中心是buildQuery和escapeStringConn,其次是toRow。一半的数据是ARR_字(字节字符串)、操作和列表
formatMany
通过testring从嵌套的动作列表中组合的片段,几乎可以生成一个长的ByteString
。操作被转换为ByteString
生成器,这些生成器保留ByteString
,直到用于生成最终的long strictByteString
。
这些BYTESTRING的使用寿命很长,直到最终BS建成为止
字符串需要用libPQ转义,因此任何非普通操作BS
都会传递给libPQ,并在escapeStringConn和friends中替换为新操作,从而添加更多垃圾。
若用另一个Int替换Thing中的文本,GC时间将从75%下降到45%
我试图通过formatMany和buildQuery来减少临时列表的使用,用foldM over Builder替换mapM。这没有多大帮助,但会稍微增加代码的复杂性
TLDR-Builders
不能被懒散地使用,因为它们都需要生成最终严格的ByteString
(相当于字节数组)。
如果内存有问题,请在同一事务中将executeMany拆分为多个块。我不明白第一种情况和第二种情况的代码有什么不同。你能发布1)自我包含的代码或至少明确说明案例是什么2)GHC版本3)编译器标志吗?这对我来说几乎不使用堆-你是在没有优化的情况下编译的吗?这将是一个问题。我已经用-O2重新编译了所有库,但没有效果。我自己解决了这个问题,但不是很清楚。构建器也可以生成惰性bytestring,但遗憾的是libPQ不使用惰性字符串。这是haskell的ByteStrings中真正愚蠢的问题。
cabal-dev/bin/posttest +RTS -s
1,750,661,104 bytes allocated in the heap
619,896,664 bytes copied during GC
92,560,976 bytes maximum residency (10 sample(s))
990,512 bytes maximum slop
239 MB total memory in use (0 MB lost due to fragmentation)
Tot time (elapsed) Avg pause Max pause
Gen 0 3323 colls, 0 par 11.01s 11.46s 0.0034s 0.0076s
Gen 1 10 colls, 0 par 0.74s 0.77s 0.0769s 0.2920s
INIT time 0.00s ( 0.00s elapsed)
MUT time 2.97s ( 3.86s elapsed)
GC time 11.75s ( 12.23s elapsed)
RP time 0.00s ( 0.00s elapsed)
PROF time 0.00s ( 0.00s elapsed)
EXIT time 0.00s ( 0.00s elapsed)
Total time 14.72s ( 16.09s elapsed)
%GC time 79.8% (76.0% elapsed)
Alloc rate 588,550,530 bytes per MUT second
Productivity 20.2% of total user, 18.5% of total elapsed
cabal-dev/bin/dumptest +RTS -s > out
1,492,068,768 bytes allocated in the heap
7,941,456 bytes copied during GC
2,054,008 bytes maximum residency (3 sample(s))
70,656 bytes maximum slop
6 MB total memory in use (0 MB lost due to fragmentation)
Tot time (elapsed) Avg pause Max pause
Gen 0 2888 colls, 0 par 0.13s 0.16s 0.0001s 0.0089s
Gen 1 3 colls, 0 par 0.01s 0.01s 0.0020s 0.0043s
INIT time 0.00s ( 0.00s elapsed)
MUT time 2.00s ( 2.37s elapsed)
GC time 0.14s ( 0.16s elapsed)
RP time 0.00s ( 0.00s elapsed)
PROF time 0.00s ( 0.00s elapsed)
EXIT time 0.00s ( 0.00s elapsed)
Total time 2.14s ( 2.53s elapsed)
%GC time 6.5% (6.4% elapsed)
Alloc rate 744,750,084 bytes per MUT second
Productivity 93.5% of total user, 79.0% of total elapsed