Haskellデータ型のメモリフットプリント


124

Haskellにデータタイプの値を格納するために必要な実際のメモリ容量を見つけるにはどうすればよいですか(主にGHCを使用)。実行時に(GHCiなどで)評価することは可能ですか、それともそのコンポーネントから複合データ型のメモリ要件を見積もることは可能ですか?

一般的に、タイプのメモリ要件場合ab知られている、などの代数データ型のメモリオーバーヘッドは何です。

data Uno = Uno a
data Due = Due a b

たとえば、これらの値が占めるメモリ内のバイト数は?

1 :: Int8
1 :: Integer
2^100 :: Integer
\x -> x + 1
(1 :: Int8, 2 :: Int8)
[1] :: [Int8]
Just (1 :: Int8)
Nothing

ガベージコレクションの遅延により、実際のメモリ割り当てが高くなることを理解しています。遅延評価のために大きく異なる場合があります(サンクサイズは値のサイズとは関係ありません)。問題は、データ型が与えられた場合、完全に評価されたときにその値がどれだけのメモリを消費するかということです。

:set +sGHCiにはメモリ統計を表示するオプションがあることがわかりましたが、単一の値のメモリフットプリントを推定する方法は明確ではありません。

回答:


156

(以下はGHCに適用され、他のコンパイラは異なる格納規則を使用する場合があります)

経験則:コンストラクターは、ヘッダーに1ワード、各フィールドに1ワードかかります。例外:GHCはこれらのコンストラクターの単一のインスタンスを作成し、すべての用途で共有するため、フィールドのないコンストラクター(NothingまたはTrue)はスペースを取りません。

ワードは、32ビットマシンでは4バイト、64ビットマシンでは8バイトです。

だから例えば

data Uno = Uno a
data Due = Due a b

an Unoは2ワード、a Dueは3 ワードかかります。

Intタイプは以下のように定義されます

data Int = I# Int#

今でInt#は、1単語なのでInt、合計で2 単語かかります。ほとんどのボックス化されていない型は1ワードを取りますがInt64#、例外は、、Word64#およびDouble#(32ビットマシンでは)2です。GHCは実際には型Intとの小さな値のキャッシュを持っているCharため、多くの場合、これらはヒープスペースをまったく消費しません。あStringあなたが使用しない限りのみ、リストセルのためのスペースが必要ですChar > 255秒。

はとInt8同じ表現を持っていIntます。 Integerこのように定義されています:

data Integer
  = S# Int#                            -- small integers
  | J# Int# ByteArray#                 -- large integers

したがって、小さなIntegerS#)は2ワードを使用しますが、大きな整数はその値に応じて可変量のスペースを取ります。あByteArray#は、2ワード(ヘッダー+サイズ)と配列自体のスペースが必要です。

定義されたコンストラクタnewtypeは無料であることに注意しください。 newtype純粋にコンパイル時のアイデアであり、実行時にスペースをとらず、命令のコストもかかりません。

詳細については、GHC解説のヒープオブジェクトのレイアウトを参照してください。


1
サイモン、ありがとう。これはまさに私が知りたかったことです。
サスタニン

2
ヘッダーは2語ではありませんか?1つはタグ用で、もう1つはGCまたは評価中に使用するための転送ポインタ用ですか?だから、あなたの合計に1単語追加しませんか?
Edward KMETT 2010

5
@Edward:サンクは間接参照(後でGCによって削除されます)によって上書きされますが、それらは2ワードのみであり、すべてのヒープオブジェクトは少なくとも2ワードの2ワードであることが保証されています。プロファイリングまたはデバッグ機能をオンにしないと、ヘッダーは実際には1語にすぎません。つまり、GHCでは、他の実装では動作が異なる場合があります。
nominolo 2010

3
nominolo:はい、ただしClosure.hから:/ *サンクには、更新された値を取得するためのパディングワードがあります。これは、更新によってペイロードが上書きされないようにするためであり、エントリと更新中にサンクをロックする必要を回避できます。注:これは、ペイロードがないTHUNK_STATICには適用されません。注:このパディングワードはSMPだけでなくすべての方法で残しているため、SMP用にすべてのライブラリを再コンパイルする必要はありません。* /間接参照中にペイロードが上書きされることはありません。間接指定は、ヘッダーの別の場所に書き込まれます。
エドワードKMETT 2010

6
はい。ただし、これはサンクのみを対象としています。コンストラクターには適用されません。サンクのサイズを見積もるのは少し難しいです。自由変数を数える必要があります。
nominolo 2010

4

ghc-datasizeパッケージは、GHCオブジェクトのサイズを計算するためのrecursiveSize関数を提供します。しかしながら...

ガベージコレクターはヒープのウォークを困難にするため、サイズが計算される前にガベージコレクションが実行されます。

...そのため、これを頻繁に呼び出すのは現実的ではありません。

データ型のGHCのメモリ表現を見つける方法も参照してくださいそしてHaskellで型のサイズをどのように決定できますか?

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.