std :: set <myclass>?</myclass>的实际内存开销是多少?

时间:2014-07-26 15:55:18

标签: c++ set

假设MyClass的大小为N字节,我将M MyClassstd::set<MyClass>对象放入N*M。理论内存开销是std::set字节,但据我所知,set是使用树结构实现的,所以我认为必须有额外的内存开销。如何估计{{1}}的近似合理内存成本,因为当我运行程序时,内存成本高于我的预期?

2 个答案:

答案 0 :(得分:3)

如@SirDarius所述,这是依赖于实现的,因此您需要检查每个实现。

通常std::set实现为带有精益迭代器的红黑树。这意味着对于集合中的每个元素,都有一个节点,并且此节点大致包含:

  • 3个指针:1表示父节点,1表示左侧子节点,1个表示右侧子节点
  • 标签:红色或黑色
  • 实际用户类型

因此,{64}平台上此节点的最小足迹是footprint(node)

  • 3 * 8字节
  • sizeof(MyClass),舍入为8个字节(出于对齐原因:查找&#34; struct padding&#34;)

注意:很容易在某处使用未使用的位来隐藏红/黑标志而无需额外费用,例如考虑到给定节点对齐指针的最低有效位总是这样的事实0,或插入MyClass填充(如果有的话)。

然而,当你通过T分配一个对象new时({1}}在幕后做了什么,以及它本身使用std::allocator除非超载),否则不分配 malloc字节:

  • sizeof(T)的典型实现使用 size buckets ,这意味着如果在分配9字节对象时有8个字节桶和16个字节桶,则会进入16字节桶(浪费7个字节)
  • 此外,实现还有一些开销来跟踪那些分配的内存块(知道在释放之前不要重用该内存);尽管很小,但开销可以是不可忽视的
  • 最后,操作系统本身对程序请求的每页内存都有一些开销;虽然这可能是微不足道的

因此,确实使用malloc很可能会消耗更多而不仅仅是set,尽管开销取决于您的标准库实现。对于小型对象(例如set.size() * sizeof(MyClass)),这种开销可能很大:在64位平台上,您至少看到+ 500%的开销),因为将节点链接在一起的成本通常是固定的,而对于大型物体可能不会引人注意。


为了减少开销,您通常使用数组。这就是B-Tree所做的事情,尽管它是以稳定性为代价的(即每个元素都停留在固定的内存地址而不管其他内容发生了什么)。

答案 1 :(得分:1)

有序关联容器具有约束,这几乎要求它们被实现为平衡树。很可能每棵树中的节点都有两个指向子节点的指针和一个指向父节点的指针。根据增量是否在指针中存储比特技巧以存储附加信息,在分配的节点中可能存在额外的字。此外,存储器管理系统可以为每个分配的存储器块存储一个字,并将其对准例如16字节边界。

实现的实际操作当然没有在标准中指定,但我认为M大小为N的元素的最小内存消耗为

M * (N + 2 * sizeof(void*) + sizeof(int))

可能更多:我可能也会使用指向父级的指针。