我相信如果我制作一个像这样的构造函数:
data MyThing = MyThing
Word8#
Word8#
Word8#
Word8#
GHC(打开优化)现在会将未装箱的单词打包在一个单词中,而不是让它们每个占用 8 个字节。
如果我这样做怎么办:
data MyThing = MyThing
{-# UNPACK #-} !Word8
{-# UNPACK #-} !Word8
{-# UNPACK #-} !Word8
{-# UNPACK #-} !Word8
有同样的包装发生吗?
是的,两个版本的
MyThing
具有相同的内存布局。 您可以通过编译以下程序来观察这一点:
ghc -O -ddump-cmm -ddump-asm -fforce-recomp -dsuppress-all Thing8.hs
并研究生成的坐标测量机或装配体。 有了这个程序:
{-# LANGUAGE MagicHash #-}
module Thing8 where
import Data.Word
import GHC.Base
import GHC.Word
data MyThing1 = MyThing1
Word8#
Word8#
Word8#
Word8#
data MyThing2 = MyThing2
{-# UNPACK #-} !Word8
{-# UNPACK #-} !Word8
{-# UNPACK #-} !Word8
{-# UNPACK #-} !Word8
foo :: MyThing1 -> MyThing2
foo (MyThing1 a b c d) = MyThing2 (W8# a) (W8# b) (W8# c) (W8# d)
foo
的核心CMM代码是:
cJG:
_sID::I8 = I8[R1 + 7];
_sIE::I8 = I8[R1 + 8];
_sIF::I8 = I8[R1 + 9];
_sIG::I8 = I8[R1 + 10];
I64[Hp - 8] = MyThing2_con_info;
I8[Hp] = _sID::I8;
I8[Hp + 1] = _sIE::I8;
I8[Hp + 2] = _sIF::I8;
I8[Hp + 3] = _sIG::I8;
R1 = Hp - 7;
Sp = Sp + 8;
call (P64[Sp])(R1) args: 8, res: 0, upd: 8;
在这里您可以看到,连续 4 个字节从源对象复制到新创建的堆对象中。
在这种情况下,本机代码生成器不够聪明,无法将其重写为单个字大小的移动,并将其保留为四个字节大小的移动(请参阅汇编),即使使用
-O2
进行编译,但 LLVM 后端将如果您使用 -fllvm
进行编译,则可以正确优化它。