2014-12-03 69 views
13
3,200,056,496 bytes allocated in the heap 

Wut?這是STRef的一個小測試:爲什麼我的小STRef Int需要分配千兆字節?

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。涼。但是,所有這些分配是什麼?

回答

9

Int類型是一個盒子整數表示。當(+1)作用於STRef的內容 時,會創建一個新的堆對象。在內部,STRef包含一個指向堆對象的指針,並寫入該修改指針的STRef,而不是更新整數字段。正如你所看到的,做這個1,000,000,000次可能會導致大量的Int對象被創建,在大量的內存中攪動。

幸運的是,這些對象並不是很長壽,這就是爲什麼垃圾收集器複製的字節數相對較少的原因 。事實上,這個計劃僅花費適量的時間來執行GC。像Haskell這樣的短期對象在Haskell(以及其他許多函數式編程語言)中很常見,並且垃圾收集器旨在有效地處理這種情況。

+0

有趣。我剛剛在無盒裝矢量的背景下了解拆箱的好處。有沒有在STRef中有一個unboxed Int的方法? – 2014-12-03 01:42:41

+1

@MichaelFox [This](https://hackage.haskell.org/package/ArrayRef-0.1.3.1/docs/Data-Ref-Unboxed.html)是一種可能的解決方案。但是,我不知道是否有標準化的解決方案。如果你絕望,我想你可以使用長度爲1的無盒矢量。 – sabauma 2014-12-03 01:59:04

+0

它在實踐中並不重要。您不要將常用值放在Ref類型中,而是將它們傳遞給它。如果您的代碼和值的類型允許,則該值將被拆箱。 – Carl 2014-12-03 03:37:19