最近,诸如计算Hashmap的大小之类的博客条目解释了如何推断常用容器类型的空间复杂性.现在我面临的问题是如何实际"看到"我的GHC版本选择的内存布局(取决于编译标志和目标体系结构),用于奇怪的数据类型(构造函数),例如
data BitVec257 = BitVec257 {-# UNPACK #-} !Word64
{-# UNPACK #-} !Word64
{-# UNPACK #-} !Bool
{-# UNPACK #-} !Word64
{-# UNPACK #-} !Word64
data BitVec514 = BitVec514 {-# UNPACK #-} !BitVec257
{-# UNPACK #-} !BitVec257
Run Code Online (Sandbox Code Playgroud)
在C中有sizeof
和offsetof
运算符,它允许我"看到"为C字段选择的大小和对齐方式struct
.
我试着看看GHC Core希望在那里找到一些提示,但我不知道该找什么.有人能指出我正确的方向吗?
tib*_*bbe 11
我的第一个想法是使用这个整洁的litte功能,由于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)
Run Code Online (Sandbox Code Playgroud)
使用它:
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
Run Code Online (Sandbox Code Playgroud)
(请注意,GHC告诉您它不能取消装箱,Bool
因为它是一种总和类型.)
上述函数声称您的数据类型在64位计算机上使用74个字节.我觉得很难相信.我希望数据类型使用11个字= 88个字节,每个字段一个字.即使Bool
是一个词,因为它们是指向(静态分配)构造函数的指针.我不太确定这里发生了什么.
至于对齐,我相信每个字段都应该是字对齐的.
(以下适用于GHC,其他编译器可能使用不同的存储约定)
经验法则:构造函数为标题花费一个词,为每个字段花费一个词。例外:没有字段的构造函数(如 Nothing 或 True)不占用空间,因为 GHC 创建这些构造函数的单个实例并在所有用途中共享它。
一个字在 32 位机器上是 4 个字节,在 64 位机器上是 8 个字节。
所以例如
data Uno = Uno a
data Due = Due a b
Run Code Online (Sandbox Code Playgroud)
Uno 需要 2 个单词,Due 需要 3 个。
此外,我相信可以编写一个执行相同任务的 haskell 函数sizeof
或offsetof