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

16

最近,一些博客文章(例如计算哈希映射大小)讲解了如何推断常用容器类型的空间复杂度。现在我的问题是如何“看到”我的 GHC 版本选择哪种内存布局(取决于编译标志和目标架构)适用于奇怪的数据类型(构造函数),例如:

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

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

C语言中有sizeofoffsetof操作符,它们可以让我“看到”C结构体字段的大小和对齐方式。

我尝试查看GHC Core以寻找一些提示,但不知道该查找什么内容。有人能指点我正确的方向吗?


你的动机是什么?纯粹出于好奇,还是你想要与另一种语言进行接口,或者其他原因? - dave4420
1
是的,主要是出于好奇心。我想验证 GHC 是否真正做到了我所期望/假设的...或者我需要修正我的假设... :-) - hvr
1个回答

11

我的第一个想法是使用这个非常聪明的小函数,由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 字节,每个字段一个字。即使是布尔值也占用一个字,因为它们是指向(静态分配的)构造函数的指针。我不确定这里发生了什么。
至于对齐,我认为每个字段都应该按字对齐。

6
哦,我认为这个函数存在一个错误,因为我们改变了ByteArray#的表示方式(现在它的长度是以字节而不是字为单位),所以 sizeOfByteArray# 不会乘以字大小。您需要将第一个 sizeOfByteArray# 的结果乘以字大小。 - Simon Marlow
1
顺便问一下,ptrsnptrs实际上代表什么? - hvr

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接