Подтвердить что ты не робот

Почему мой маленький STRef Int требует выделения гигабайт?

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

Wut? Это небольшой тест для STRef:

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

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

Создайте для профилирования, например:

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

Выполнить как:

./tryST +RTS -pa -sstderr

Выделите tryST.prof

 bigNumber    Main  95 1   95.7  100.0    95.7  100.0   1357 1600000032

Отчет 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

Эта программа работает не так быстро, как хотелось бы, но производительность составляет 98%. Отлично. Максимальное место жительства 46k. Круто. Но что со всем этим распределением?

4b9b3361

Ответ 1

Тип Int представляет собой целочисленное представление в виде прямоугольника. Когда (+1) действует на содержимое из STRef создается новый объект кучи. Внутри STRef содержит указатель на объект кучи и записывает на него STRef изменение указателя вместо обновления целочисленного поля. Как вы можете видеть, выполнение этого 1 000 000 000 раз может привести к созданию большого количества создаваемых объектов Int, из-за большого количества памяти.

К счастью, эти объекты недолговечны, поэтому относительно мало байт копируются сборщиком мусора. Действительно, эта программа тратит только скромное количество времени на выполнение GC. Такие короткоживущие объекты довольно распространены в Haskell (и многих других языках функционального программирования), и сборщик мусора предназначен для эффективного управления этим продуктом.