最近,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中有sizeof
和offsetof
运算符,它允许我“看到”为C struct
字段选择的大小和对齐方式。
我试图看看GHC Core希望在那里找到一些提示,但我不知道该寻找什么。有人能指出我正确的方向吗?
答案 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函数,它执行与sizeof
或offsetof