【问题标题】:Memory Footprint of Large Algebraic Data Types大型代数数据类型的内存占用
【发布时间】:2016-08-17 06:03:34
【问题描述】:

假设我有一个带有 很多 个构造函数的数据类型。

data ManyValues
  = Value0
  | Value1
  | Value2
  ...
  | Value255
  | Value256
  deriving (Show,Eq)

此数据类型的任何一个值的内存占用量是多少?我最初的理解是每个构造函数都是内存中的一个 8 位字,但是如果数据类型中的构造函数多于 8 位中可能的值怎么办。构造函数是否会增加到 16 位等等,直到它可以处理数据类型中存在的所有构造函数?还是我把这一切都搞混了?

【问题讨论】:

  • 这可能对你有帮助:stackoverflow.com/questions/3254758/…
  • 谢谢,我在发帖前看到了。当涉及到零字段构造函数时,它提出了关于对象共享的有趣观点,但它没有解决当构造函数(甚至是零字段构造函数)超过 8 位可寻址时会发生什么。这是假设这就是 8 位标头的用途。
  • 啊,但是在那个答案中,标题“单词”肯定至少是 32 位。当然,原则上问题仍然存在(例如,一种方法可能是使用前 32 位来缩小选择范围),但如果您的数据类型有 2^32 个构造函数,您可能会面临其他工程困难。
  • 各种ManyValues之间的区别在编译过程中被抹去;只要需要对代码进行类型检查,您就只需要这些信息。在运行时,每个值只是一个单词,基本上表明该值存在。
  • 看起来我的问题是由于对“单词”是什么感到困惑而提出的。在这里,我在考虑 8 位字并想知道“它将如何处理所有这些内存位置?”。但是,是的,在 32 位和 64 位机器上,“单词”将是 32 位和 64 位,上面链接的 SO 问题中也提到了这一点。因此,只有当我也像 pigworker 所说的那样触及虚拟内存的基本限制时,我的担忧才会成为一个合法的担忧。在发布这个问题之前,我应该睡个好觉。 :)

标签: haskell


【解决方案1】:

据我了解,空值构造函数占用 1 个机器字的存储空间(即,它是指向静态分配数据的指针)。所以无论你的数据结构有 1 个这样的构造函数还是 1,000,000 个,它仍然是 1 个机器字。

具有字段的构造函数占用更多空间,但 GHC 特殊情况下的空值构造函数在该值的所有实例之间共享单个静态单例。 (例如,整个程序中只有一个True。)

当然,当 thunk 评估为已经存在的值(any 值)时,GHC 会使用“重定向”节点覆盖 thunk,这会占用一些空间。垃圾收集器会定期删除重定向。

【讨论】:

  • 这很有意义,所以我将这个问题标记为已回答。事实证明,我的核心困惑是关于“1 个机器词”的实际含义。但我现在已经对这件事进行了自我教育。尽管这确实引发了一个关于内存碎片的进一步问题。
  • 如果 nullary 构造函数只是指向共享对象的指针,那是否意味着我可以引用一个在内存中距离实际调用站点非常远的对象(直到 GC 将其移近)?在这种情况下,将低级数据仅表示为未装箱线性数据结构中的智能构造 Word32Word64 值会更有效,从而放弃空间效率以实现更低的分配和更好的内存局部性?
  • 为什么“在记忆中很远”会成为一个问题?这不是内存局部性的工作方式。程序中的所有 nullary 构造函数都位于一个从不移动的连续内存块中。这应该对缓存非常友好。
  • 我也不知道他们是这样保持连续的,很高兴知道。
  • 值得注意的是,前几个(3 或 7 个分别用于 32 位和 64 位)构造函数得到特殊处理。对于那些构造函数编号存储在指向节点的指针中,节省了内存负载。
猜你喜欢
  • 2016-10-28
  • 2020-09-09
  • 2010-09-12
  • 1970-01-01
  • 2013-01-25
  • 1970-01-01
  • 1970-01-01
  • 2014-09-12
相关资源
最近更新 更多