Haskell数据类型的内存占用量

时间:2010-07-15 10:39:03

标签: haskell memory-management ghc algebraic-data-types

如何在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

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

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

2 个答案:

答案 0 :(得分:153)

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

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

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

所以,例如

data Uno = Uno a
data Due = Due a b

Uno需要2个字,Due需要3个。

Int类型定义为

data Int = I# Int#

现在,Int#只占一个字,因此Int总共需要2个字。大多数未装箱的类型只占一个字,例外是Int64#Word64#Double#(在32位机器上),取2。GHC实际上有一个小值类型的缓存IntChar,因此在很多情况下,它们根本不占用堆空间。 String只需要列表单元格的空间,除非您使用Char s> 255。

Int8Int具有相同的表示形式。 Integer的定义如下:

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

所以一个小的IntegerS#)需要2个单词,但是一个大整数会根据其值获取可变的空间量。 ByteArray#需要2个单词(标题+大小)加上数组本身的空格。

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

The Layout of Heap Objects in the GHC Commentary中的更多详情。

答案 1 :(得分:4)

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

  

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

...所以经常这样做是不切实际的!

另见How to find out GHC's memory representations of data types?How can I determine size of a type in Haskell?