Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is my little STRef Int require allocating gigabytes?

3,200,056,496 bytes allocated in the heap

Wut? This is a small test for STRef:

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

modifySTRef' is strict. STRef is supposed to operate directly on memory so I don't see the need for lots of allocations.

Here's the full code:

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

Build for profiling like:

ghc -O2 -rtsopts -prof -auto-all -caf-all -fforce-recomp tryST.hs

Run like:

./tryST +RTS -pa -sstderr

Highlight from tryST.prof

 bigNumber    Main  95 1   95.7  100.0    95.7  100.0   1357 1600000032

The RTS report:

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

This program isn't as fast as I'd like but the productivity is 98%. Great. Maximum residency 46k. Cool. But what is with all that allocation?

like image 775
Michael Fox Avatar asked Dec 03 '14 00:12

Michael Fox


1 Answers

The Int type is a box integer representation. When (+1) acts on the contents of the STRef, a new heap object is created. Internally, the STRef holds a pointer to the heap object, and writes to that STRef modify the pointer, rather than updating an integer field. As you can see, doing this 1,000,000,000 times can result in a large number of Int objects being created, churning through a lot of memory.

Fortunately, these objects are not long lived, which is why relatively few bytes are copied by the garbage collector. Indeed, this program is spending only a modest amount of time performing GC. Short lived objects like this are quite common in Haskell (and many other functional programming languages), and the garbage collector is designed to handle this efficiently.

like image 124
sabauma Avatar answered Nov 16 '22 08:11

sabauma