Haskell数据类型的内存占用量

sas*_*nin 120 haskell memory-management ghc algebraic-data-types

如何找到在Haskell中存储某些数据类型值所需的实际内存量(主要是使用GHC)?是否可以在运行时(例如在GHCi中)对其进行评估,还是可以从其组件中估算复合数据类型的内存要求?

在一般情况下,如果类型的存储需求ab已知的,什么是代数数据类型,如内存开销:

data Uno = Uno a
data Due = Due a b
Run Code Online (Sandbox Code Playgroud)

例如,这些值占用的内存中有多少字节?

1 :: Int8
1 :: Integer
2^100 :: Integer
\x -> x + 1
(1 :: Int8, 2 :: Int8)
[1] :: [Int8]
Just (1 :: Int8)
Nothing
Run Code Online (Sandbox Code Playgroud)

据我所知,由于垃圾收集延迟,实际的内存分配更高.由于惰性评估,它可能会有很大的不同(并且thunk大小与值的大小无关).问题是,给定数据类型,在完全评估时它的值会占用多少内存?

我发现:set +sGHCi中有一个选项可以查看内存统计信息,但目前尚不清楚如何估算单个值的内存占用量.

Sim*_*low 153

(以下适用于GHC,其他编译器可能使用不同的存储约定)

经验法则:构造函数为标题花费一个单词,为每个字段花费一个单词.例外:没有字段(如NothingTrue)的构造函数不占用空间,因为GHC创建这些构造函数的单个实例并在所有用途中共享它.

一个字在32位机器上是4个字节,在64位机器上是8个字节.

所以,例如

data Uno = Uno a
data Due = Due a b
Run Code Online (Sandbox Code Playgroud)

一个Uno需要2个字,一个Due需要3个.

Int类型定义为

data Int = I# Int#
Run Code Online (Sandbox Code Playgroud)

现在,只Int#需要一个字,所以Int总共需要2 个字.大多数拆箱类型取一个字,例外是Int64#,Word64#Double#(32位计算机上),这需要2 GHC实际上有型的小值的高速缓存IntChar,因此在很多情况下,这些不采取任何堆空间可言.一个String只需要空间列表细胞,除非你使用CharS> 255.

一个Int8具有相同的表示来Int. Integer定义如下:

data Integer
  = S# Int#                            -- small integers
  | J# Int# ByteArray#                 -- large integers
Run Code Online (Sandbox Code Playgroud)

所以small Integer(S#)需要2个单词,但是一个大整数根据其值取一个可变的空间.A ByteArray#需要2个字(标题+大小)加上数组本身的空间.

请注意,定义的构造函数newtype是免费的. newtype纯粹是一个编译时的想法,它不占用空间,并且在运行时不需要任何指令.

有关GHC评论中堆对象布局的更多细节.

  • 是的,但请注意这只适用于*thunks*.它不适用于构造函数.无论如何估计thunk的大小有点困难 - 你必须计算自由变量. (6认同)
  • @Edward:Thunks被间接覆盖(后来由GC删除),但这些只有2个字,并且每个堆对象的大小至少保证为2个字.没有任何分析或调试功能打开标题真的只有一个字.在GHC中,也就是说,其他实现可能以不同的方式做事. (5认同)
  • nominolo:是的,但是来自Closure.h:/*thunk有一个填充字来获取更新的值.这样更新不会覆盖有效负载,因此我们可以避免在进入和更新期间锁定thunk.注意:这不适用于没有有效负载的THUNK_STATICs.注意:我们以各种方式保留此填充词,而不仅仅是SMP,因此我们不必为SMP重新编译所有库.*/在间接期间不会覆盖有效负载.间接写入标题中的单独位置. (3认同)
  • 标题不是两个字?一个用于标记,一个用于在GC或评估期间使用的转发指针?那么这不会给你的总数增加一个字吗? (2认同)

mhw*_*bat 5

ghc-datasize 包提供了recursiveSize函数来计算 GHC 对象的大小。然而...

在计算大小之前执行垃圾收集,因为垃圾收集器会使堆遍历变得困难。

......所以经常打电话是不切实际的!

另请参阅如何找出 GHC 的数据类型的内存表示?以及如何确定 Haskell 中类型的大小?.