sas*_*nin 120 haskell memory-management ghc algebraic-data-types
如何找到在Haskell中存储某些数据类型值所需的实际内存量(主要是使用GHC)?是否可以在运行时(例如在GHCi中)对其进行评估,还是可以从其组件中估算复合数据类型的内存要求?
在一般情况下,如果类型的存储需求a
和b
已知的,什么是代数数据类型,如内存开销:
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 +s
GHCi中有一个选项可以查看内存统计信息,但目前尚不清楚如何估算单个值的内存占用量.
Sim*_*low 153
(以下适用于GHC,其他编译器可能使用不同的存储约定)
经验法则:构造函数为标题花费一个单词,为每个字段花费一个单词.例外:没有字段(如Nothing
或True
)的构造函数不占用空间,因为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实际上有型的小值的高速缓存Int
和Char
,因此在很多情况下,这些不采取任何堆空间可言.一个String
只需要空间列表细胞,除非你使用Char
S> 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评论中堆对象布局的更多细节.
ghc-datasize 包提供了recursiveSize函数来计算 GHC 对象的大小。然而...
在计算大小之前执行垃圾收集,因为垃圾收集器会使堆遍历变得困难。
......所以经常打电话是不切实际的!
另请参阅如何找出 GHC 的数据类型的内存表示?以及如何确定 Haskell 中类型的大小?.