1. ホーム
  2. haskell

[解決済み] Haskellデータ型のメモリフットプリント

2022-07-27 09:19:17

質問

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 +s オプションがありますが、単一の値のメモリフットプリントを推定する方法は明らかではありません。

どのように解決するのですか?

(以下はGHCに適用されるもので、他のコンパイラは異なる保存規約を使用しているかもしれません)

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

1ワードは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語で済みますが、例外として Int64# , Word64# そして Double# (32ビットマシンの場合)は2を取ります。 GHC は実際には、小さな値のキャッシュを持っています。 IntChar であるため、多くの場合これらはヒープスペースを全く必要としません。 A String を使用しない限り、リストセルのためのスペースしか必要としません。 Char s > 255を使用しない限り、リストのセルだけがスペースを必要とします。

アン Int8 と同じ表現になります。 Int . Integer はこのように定義されます。

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

ということで、小さな Integer ( S# ) は2ワードで済みますが、大きな整数はその値に応じて可変のスペースを必要とします。 A ByteArray# は2語(ヘッダ+サイズ)と配列自体のためのスペースを取ります。

以下のことに注意してください。 で定義されたコンストラクタは newtype は自由です。 . newtype は純粋にコンパイル時のアイデアであり、実行時には何のスペースも取らず、何のインストラクションも要しません。

より詳細な情報は GHCの解説におけるヒープオブジェクトのレイアウト .