如何找出GHC的数据类型的内存表示?
最近,诸如计算散列表大小的博客条目解释了如何推理常用容器类型的空间复杂性。 现在我面临的问题是如何实际“查看”我GHC版本选择哪种内存布局(取决于编译标志和目标体系结构)奇怪的数据类型(构造函数),例如
data BitVec257 = BitVec257 {-# UNPACK #-} !Word64
{-# UNPACK #-} !Word64
{-# UNPACK #-} !Bool
{-# UNPACK #-} !Word64
{-# UNPACK #-} !Word64
data BitVec514 = BitVec514 {-# UNPACK #-} !BitVec257
{-# UNPACK #-} !BitVec257
在C中,有sizeof
和offsetof
运算符,它允许我“查看”C struct
域的大小和对齐方式。
我试图看看GHC Core,希望在那里找到一些提示,但我不知道要寻找什么。 有人能指出我正确的方向吗?
我的第一个想法是使用这个整洁的litte函数,由于西蒙马洛:
{-# 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
需要一个词,因为它们是指向(静态分配的)构造函数的指针。 我不太确定这里发生了什么事。
至于对齐,我相信每个领域应该是字对齐的。
Haskell数据类型的内存占用情况
(以下适用于GHC,其他编译器可能使用不同的存储约定)
经验法则:一个构造函数为一个标题花费一个单词,每个字段花费一个单词。 例外:没有字段的构造函数(如Nothing或True)不会占用空间,因为GHC会创建这些构造函数的单个实例并在所有用途中共享它。
一个字在32位机器上是4个字节,而在64位机器上是8个字节。
所以例如
data Uno = Uno a
data Due = Due a b
Uno需要2个单词,Due需要3个。
另外我相信可以编写一个haskell函数来执行与sizeof
或offsetof
相同的任务
上一篇: How to find out GHC's memory representations of data types?