具有单个严格字段的​​存在数据类型

时间:2017-09-19 12:05:18

标签: haskell existential-type strictness

所以我有一个存在数据类型,只有一个严格的字段:

data Uncurry (a :: i -> j -> *) (z :: (i,j)) =
  forall x y. z ~ '(x,y) => Uncurry !(a x y) 

使用unsafeSizeof(从this answer中窃取)的实验让我相信它可以是零内存开销:

λ p = (0, '\0') :: (Int, Char)
λ q = Uncurry p
λ unsafeSizeof p
10
λ unsafeSizeof q
10

所以看起来Uncurry有点像newtype,只在编译时使用。

这对我来说很有意义,因为平等断言不需要提供字典。

这是一个有效的解释吗?我是否对GHC(或Haskell报告)有任何保证,或者我只是运气好吗?

2 个答案:

答案 0 :(得分:6)

data永远不会转换为newtypeUncurry确实添加了一个新的闭包,实际上也有一个~字典的指针,从GHC 8.0.2开始。因此,Uncurry有三个单词的闭包。

unsafeSizeof不正确,因为Array#以字为单位存储其大小,而ByteArray#以字节为单位存储其大小,因此sizeofByteArray# (unsafeCoerce# ptrs)会返回字数而非预期字数字节数。在64位系统上,正确的版本看起来像这样:

unsafeSizeof :: a -> Int
unsafeSizeof !a =
  case unpackClosure# a of
    (# x, ptrs, nptrs #) ->
      I# (8# +# sizeofArray# prts *# 8# +# sizeofByteArray# nptrs)

但请注意,unsafeSizeof仅给出了最顶层闭包的大小。因此,任何盒装元组的闭包大小都是24,这与Uncurry t的闭包大小一致,因为Uncurry有一个信息指针,~的无用指针,以及元组字段的指针。这种巧合也适用于之前的错误unsafeSizeof实施。但Uncurry t的总大小大于t的大小。

答案 1 :(得分:5)

编辑以修复一些细节:quads是8个字节并解释静态链接字段。

我认为unsafeSizeOf不正确并且你误解了它的输出。请注意,它仅用于显示顶级闭包的内存使用情况,而不是对象的总空间使用情况。我认为,你所看到的是q添加中需要10个字节到元组p(而p需要10个字节在< em>添加到已装箱的Char并装箱Int)。此外,我的测试表明,顶层构造函数实际上每个需要24个字节(在64位架构上),即使unsafeSizeOf也为我报告了10个字节。

特别是,如果我用stack ghc -- -fforce-recomp -ddump-asm -dsuppress-all -O2 ZeroMemory.hs使用GHC 8.0.2编译以下测试程序:

{-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeFamilies #-}

module ZeroMemory where

data Uncurry (a :: i -> j -> *) (z :: (i, j)) =
  forall x y . z ~ '(x,y) => Uncurry !(a x y)

q :: Uncurry (,) '(Int, Char)
q = Uncurry (0, '\0')

r :: Uncurry (,) '(Int, Char)
r = Uncurry (1, '\1')

然后顶级q闭包的内存占用量如下:

q_closure:
    .quad   Uncurry_static_info
    .quad   $s$WUncurry_$d~~_closure+1
    .quad   q1_closure+1
    .quad   3

注意,这里的每个.quad实际上是8个字节;它是&#34; quad&#34;旧式的16位&#34;单词&#34;。我相信这里的最终quad,值为3,是&#34;静态链接字段&#34;在the GHC implementation commentary中描述,因此并不适用于&#34;典型的&#34;堆分配对象。

因此,忽略此最终字段,顶级q闭包的总大小为24个字节,它引用代表包含元组的q1_closure

q1_closure:
    .quad   (,)_static_info
    .quad   q3_closure+1
    .quad   q2_closure+1
    .quad   3

另外24个字节。

q2q3闭包是装箱IntChar,因此每个闭包两个四边形(16个字节)。因此,q总共占用10个四边形或80个字节。 (我将r作为完整性检查,以确保我不会错误地识别任何共享信息。)

p元组本身的内存占用量等于q1_closure,所以7个四边形或56个字节。