user239558 の回答に加えて、そこにあるあなたのコメントに応えて、値のヒープ表現を検査し、このような質問に対する回答を自分で見つけ、最適化の効果を確認し、コンパイルのさまざまな方法。
クロージャのサイズを示します。ここでは、(64 ビット マシン上で) 評価された形式でガベージ コレクション後Foo 1 2
に、それ自体で 24 バイト、依存関係を含めて合計 40 バイトが必要であることがわかります。
Prelude GHC.DataSize Test> let x = Foo 1 2
前奏曲 GHC.DataSize Test> x
フー{a = 1、b = 2}
プレリュード GHC.DataSize テスト> System.Mem.performGC
プレリュード GHC.DataSize Test> ClosureSize x
24
プレリュード GHC.DataSize Test> recursiveSize x
40
これを再現するには、 を使用してコンパイル済みの形式でデータ定義をロードする必要があります。-O
そうしないと、{-# UNPACK #-}
プラグマは効果がありません。
次に、サンクを作成して、サイズが大幅に増加することを確認します。
プレリュード GHC.DataSize Test> let thunk = 2 + 3::Int
Prelude GHC.DataSize Test> let x = Foo 1 サンク
前奏曲 GHC.DataSize Test> x `seq` return ()
プレリュード GHC.DataSize テスト> System.Mem.performGC
プレリュード GHC.DataSize Test> ClosureSize x
24
プレリュード GHC.DataSize Test> recursiveSize x
400
これはかなり過剰です。その理由は、この計算には静的クロージャやNum
型クラス辞書などへの参照が含まれており、一般に GHCi バイトコードは最適化されていないためです。それでは、それを適切な Haskell プログラムに入れましょう。ランニング
main = do
l <- getArgs
let n = length l
n `seq` return ()
let thunk = trace "I am evaluated" $ n + n
let x = Foo 1 thunk
a x `seq` return ()
performGC
s1 <- closureSize x
s2 <- closureSize thunk
r <- recursiveSize x
print (s1, s2, r)
を与える(24, 24, 48)
ので、Foo
値はFoo
それ自体とサンクで構成されます。n
なぜサンクだけなのか、さらに 16 バイトを追加することへの参照もあるはずがないのですか? これに答えるには、より優れたツールが必要です。
このライブラリ (私による) は、ヒープを調査し、データがそこでどのように表現されているかを正確に伝えることができます。したがって、上記のファイルに次の行を追加します。
buildHeapTree 1000 (asBox x) >>= putStrLn . ppHeapTree
(プログラムに 5 つのパラメーターを渡すと) 結果が得られFoo (_thunk 5) 1
ます。ポインタは常にデータの前に来るため、引数の順序がヒープ上で入れ替わることに注意してください。プレーン5
は、サンクのクロージャがその引数をアンボックス化して格納することを示します。
最後の演習として、サンクを遅延させてこれを検証しn
ます。
main = do
l <- getArgs
let n = length l
n `seq` return ()
let thunk = trace "I am evaluated" $ n
let x = Foo 1 thunk
a x `seq` return ()
performGC
s1 <- closureSize x
s2 <- closureSize thunk
s3 <- closureSize n
r <- recursiveSize x
buildHeapTree 1000 (asBox x) >>= putStrLn . ppHeapTree
print (s1, s2, s3, r)
(コンストラクターの存在によって示されるように) Foo (_thunk (I# 4)) 1
for の別のクロージャーを使用してのヒープ表現を提供し、値とその合計の予想されるサイズを示します。n
I#
(24,24,16,64)
ああ、これでもレベルが高すぎる場合は、getClosureRawで raw バイトが返されます。