web-dev-qa-db-ja.com

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

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

一般に、タイプaおよびbのメモリ要件がわかっている場合、次のような代数的データタイプのメモリオーバーヘッドはどのくらいですか。

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

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

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

120
sastanin

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

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

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

したがって、たとえば.

data Uno = Uno a
data Due = Due a b

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

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

data Int = I# Int#

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

Int8の表現はIntと同じです。 Integerは次のように定義されています:

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

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

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

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

153
Simon Marlow

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

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

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

データ型のGHCのメモリ表現を見つける方法は? および Haskellで型のサイズを確認するにはどうすればよいですか? も参照してください。

4
mhwombat