如何找出GHC的数据类型的内存表示?

时间:2011-07-04 17:28:29

标签: haskell ghc

最近,Computing the Size of a Hashmap等博客文章解释了如何推断常用容器类型的空间复杂性。现在我面临的问题是如何实际“看到”我的GHC版本为奇怪的数据类型(构造函数)选择的内存布局(取决于编译标记和目标体系结构),例如

data BitVec257 = BitVec257 {-# UNPACK #-} !Word64
                           {-# UNPACK #-} !Word64
                           {-# UNPACK #-} !Bool
                           {-# UNPACK #-} !Word64
                           {-# UNPACK #-} !Word64

data BitVec514 = BitVec514 {-# UNPACK #-} !BitVec257
                           {-# UNPACK #-} !BitVec257

在C中有sizeofoffsetof运算符,它允许我“看到”为C struct字段选择的大小和对齐方式。

我试图看看GHC Core希望在那里找到一些提示,但我不知道该寻找什么。有人能指出我正确的方向吗?

2 个答案:

答案 0 :(得分:11)

我的第一个想法是使用这个neat litte function,由于Simon Marlow:

{-# LANGUAGE MagicHash,UnboxedTuples #-}
module Size where

import GHC.Exts
import Foreign

unsafeSizeof :: a -> Int
unsafeSizeof a =
  case unpackClosure# a of
    (# x, ptrs, nptrs #) ->
      sizeOf (undefined::Int) + -- one word for the header
        I# (sizeofByteArray# (unsafeCoerce# ptrs)
             +# sizeofByteArray# nptrs)

使用它:

Prelude> :!ghc -c Size.hs

Size.hs:15:18:
    Warning: Ignoring unusable UNPACK pragma on the
             third argument of `BitVec257'
    In the definition of data constructor `BitVec257'
    In the data type declaration for `BitVec257'
Prelude Size> unsafeSizeof $! BitVec514 (BitVec257 1 2 True 3 4) (BitVec257 1 2 True 3 4)
74

(请注意,GHC告诉您它不能取消装箱Bool,因为它是和类型。)

上述函数声称您的数据类型在64位计算机上使用74个字节。我觉得很难相信。我希望数据类型使用11个字= 88个字节,每个字段一个字。即使Bool也只取一个字,因为它们是指向(静态分配)构造函数的指针。我不太确定这里发生了什么。

至于对齐,我相信每个字段都应该是字对齐的。

答案 1 :(得分:4)

Memory footprints of Haskell Data Types

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

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

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

所以,例如。

data Uno = Uno a
data Due = Due a b

Uno需要2个单词,而Due需要3个。

此外,我相信可以编写一个haskell函数,它执行与sizeofoffsetof

相同的任务