为什么我的小STRef Int需要分配千兆字节?

Mic*_*Fox 13 optimization haskell

3,200,056,496 bytes allocated in the heap
Run Code Online (Sandbox Code Playgroud)

武汉理工大学?这是STRef的一个小测试:

bigNumber =
    runST $ do
        ref <- newSTRef (0 :: Int)
        replicateM_ 100000000 $ modifySTRef' ref (+1)
        readSTRef ref
Run Code Online (Sandbox Code Playgroud)

modifySTRef'是严格的.STRef应该直接在内存上运行,所以我不认为需要大量的分配.

这是完整的代码:

import Control.Monad.ST
import Control.Monad
import Data.STRef

bigNumber :: Int
bigNumber =
    runST $ do
        ref <- newSTRef (0 :: Int)
        replicateM_ 100000000 $ modifySTRef' ref (+1)
        readSTRef ref

main :: IO ()
main = print bigNumber
Run Code Online (Sandbox Code Playgroud)

构建用于分析如下:

ghc -O2 -rtsopts -prof -auto-all -caf-all -fforce-recomp tryST.hs
Run Code Online (Sandbox Code Playgroud)

运行如下:

./tryST +RTS -pa -sstderr
Run Code Online (Sandbox Code Playgroud)

从中突出显示 tryST.prof

 bigNumber    Main  95 1   95.7  100.0    95.7  100.0   1357 1600000032
Run Code Online (Sandbox Code Playgroud)

RTS报告:

3,200,056,496 bytes allocated in the heap
      360,624 bytes copied during GC
        46,040 bytes maximum residency (2 sample(s))
        23,592 bytes maximum slop
            1 MB total memory in use (0 MB lost due to fragmentation)

                                  Tot time (elapsed)  Avg pause  Max pause
Gen  0      6102 colls,     0 par    0.03s    0.03s     0.0000s    0.0002s
Gen  1         2 colls,     0 par    0.00s    0.00s     0.0007s    0.0013s

INIT    time    0.00s  (  0.00s elapsed)
MUT     time    1.33s  (  1.38s elapsed)
GC      time    0.03s  (  0.04s elapsed)
RP      time    0.00s  (  0.00s elapsed)
PROF    time    0.00s  (  0.00s elapsed)
EXIT    time    0.00s  (  0.00s elapsed)
Total   time    1.35s  (  1.42s elapsed)

%GC     time       1.9%  (2.5% elapsed)

Alloc rate    2,413,129,982 bytes per MUT second

Productivity  98.1% of total user, 93.6% of total elapsed
Run Code Online (Sandbox Code Playgroud)

这个程序没有我想要的那么快,但生产率是98%.大.最大居住46k.凉.但是所有这些分配是什么呢?

sab*_*uma 9

Int类型是一个盒子整数表示.当(+1)对内容进行操作时STRef,会创建一个新的堆对象.在内部,它STRef保存一个指向堆对象的指针,并写入STRef修改指针,而不是更新整数字段.如您所见,执行此1,000,000,000次可能会导致创建大量Int对象,从而产生大量内存.

幸运的是,这些对象并不长,这就是垃圾收集器复制相对较少的字节的原因.实际上,这个程序只花费适量的时间来执行GC.像Haskell(以及许多其他函数式编程语言)这样的短命对象非常常见,垃圾收集器旨在有效地处理这个问题.

  • @sabauma我无法在现代GHC上编译ArrayRef,但这个想法很合理.我使用一个未装箱的`Vector`重新实现了上面的程序,它在堆上分配了51kb.见:http://lpaste.net/115549 (4认同)