【问题标题】:How can i estimate memory usage of std::map?我如何估计 std::map 的内存使用情况?
【发布时间】:2010-10-17 18:09:54
【问题描述】:

例如,我有一个已知 sizeof(A) 和 sizeof(B) 的 std::map,而 map 里面有 N 个条目。你如何估计它的内存使用量? 我会说是这样的

(sizeof(A) + sizeof(B)) * N * factor

但因素是什么?可能有不同的公式?

也许要求上限更容易?

【问题讨论】:

  • 明确一点,是std::map<A, B>,对吧?

标签: c++ memory stl stdmap


【解决方案1】:

我也在寻找一些东西来计算std::map 的大小。我已经尝试了Diomidis Spinellis 的答案中解释的内容,并在此处扩展了他的答案,这可能对其他人有所帮助。

我通过添加几行代码来扩展他的答案。

#include <bits/stl_tree.h>
int main(int argc, char *argv[])
{
    std::cout << sizeof(std::_Rb_tree_node_base) << std::endl;
    return 0;
}

输出(在我的 ARM Cortex A-9 iMX6Solo-X 处理器上运行 Linux [4.9.175] 和编译器:arm-fslc-linux-gnueabi-gcc (GCC) 7.3.0):

16

考虑到std::map&lt;A, B&gt;,我对ELEMENT_OVERHEAD 的大小感兴趣,因为它随着地图中存在的元素数量线性增长。 ELEMENT_OVERHEAD 被发现等同于 sizeof(std::_Rb_tree_node_base),因此我的系统的值为 16。

(sizeof(A) + sizeof(B) + ELEMENT_OVERHEAD) * N + CONTAINER_OVERHEAD

【讨论】:

    【解决方案2】:

    我最近需要自己回答这个问题,并使用我在 MSVC 2012 下以 64 位模式编译的 std::map 简单地编写了一个小型基准程序。

    1.5 亿个节点的 map 吸收了 ~ 15GB,这意味着 8 byte L、8 byte R、8 byte int key 和 8 byte datum,总共 32 字节,吸收了 大约 2/3rds map 的内存用于内部节点,剩下 1/3 留给叶子。

    就个人而言,我发现这是非常糟糕的内存效率,但事实就是如此。

    希望这可以形成一个方便的经验法则。

    PS:std::map 的开销是单个节点大小 AFAICT 的开销。

    【讨论】:

    • 如果你需要更好的内存效率,你可以选择code.google.com/p/cpp-btree
    • 查看我在 Diomidis Spinellis 答案中的评论。除了键值对之外,我发现每个映射条目的元素开销为 3 个指针 + 2 个字符 -> 32/64 位上的 14/26 个字节。开销的比例当然取决于您的键和值的大小。
    【解决方案3】:

    估计会更接近

    (sizeof(A) + sizeof(B) + ELEMENT_OVERHEAD) * N + CONTAINER_OVERHEAD
    

    您添加的每个元素都有一个开销,并且还有一个固定的开销用于维护用于存储地图的数据结构的数据结构。这通常是二叉树,例如Red-Black Tree。例如,在 GCC C++ STL 实现中,ELEMENT_OVERHEADsizeof(_Rb_tree_node_base)CONTAINER_OVERHEADsizeof(_Rb_tree)。在上图中,您还应该添加用于存储地图元素的内存管理结构的开销。

    通过测量代码对各种大型集合的内存消耗可能更容易得出估计值。

    【讨论】:

    • 虽然我完全同意你的看法,但我想在不知道内部 STL 结构大小的情况下有一些上限。
    • 不会有N个树节点吗?
    • 我不同意这个估计,一般叶子节点和非叶子节点大小一样,所以'+ TREE_NODE_SIZE * log2(N)'部分不准确。
    • 对于 MSVC (VS2010),我在文件 xtree::class _Tree_nod::struct _Node 中找到了相应的节点构造。一个节点包含 3 个 _NodePtrs(左、右、父 -> 32/64 位系统上的 12 / 24 字节)加上 2 个字符(_Color 和 _Isnil)以及一个 value_type(即 std::pair) .这给我留下了每个映射条目 14/26 字节的元素开销。
    • @Diomidis,是否有可能获得 sizeof(_Rb_tree)?我收到“缺少模板参数”错误...谢谢
    【解决方案4】:

    如果您真的想知道运行时内存占用情况,请使用自定义分配器并在创建映射时将其传入。请参阅 Josuttis 的书和他的 this 页面(用于自定义分配器)。

    也许要求上限更容易?

    上限将取决于确切的实现(例如,使用的平衡树的特定变体)。也许,您可以告诉我们您为什么需要这些信息以便我们提供更好的帮助?

    【讨论】:

    • 这是获得准确数字的唯一方法。
    • STL内部分配器能否提供信息STL总共使用了多少内存?
    • @Drakosha:不,那个分配器不打算这样做。这就是为什么您需要创建一个自定义报告,以报告您想要的任何内容。
    • Stephan T. Lavavej 也有一篇关于自定义分配器的好文章:blogs.msdn.com/vcblog/archive/2008/08/28/the-mallocator.aspx 阅读 cmets 以了解有关 使用的一些琐事。现在我可以在我的 C++ 代码中继续使用 C 头文件的 .h 变体了!
    • @Michael:为什么突然出现标题?一些上下文会有所帮助!
    【解决方案5】:

    您可以使用 Curtis Bartley 的 MemTrack。它是一种内存分配器,可以替换默认分配器,并且可以跟踪内存使用情况,直至分配类型。

    输出示例:

    -----------------------
    Memory Usage Statistics
    -----------------------
    
    allocated type                        blocks          bytes  
    --------------                        ------          -----  
    struct FHRDocPath::IndexedRec          11031  13.7% 2756600  45.8%
    class FHRDocPath                       10734  13.3%  772848  12.8%
    class FHRDocElemPropLst                13132  16.3%  420224   7.0%
    struct FHRDocVDict::IndexedRec          3595   4.5%  370336   6.2%
    struct FHRDocMDict::IndexedRec         13368  16.6%  208200   3.5%
    class FHRDocObject *                      36   0.0%  172836   2.9%
    struct FHRDocData::IndexedRec            890   1.1%  159880   2.7%
    struct FHRDocLineTable::IndexedRec       408   0.5%  152824   2.5%
    struct FHRDocMList::IndexedRec          2656   3.3%  119168   2.0%
    class FHRDocMList                       1964   2.4%   62848   1.0%
    class FHRDocVMpObj                      2096   2.6%   58688   1.0%
    class FHRDocProcessColor                1259   1.6%   50360   0.8%
    struct FHRDocTextBlok::IndexedRec        680   0.8%   48756   0.8%
    class FHRDocUString                     1800   2.2%   43200   0.7%
    class FHRDocGroup                        684   0.8%   41040   0.7%
    class FHRDocObject * (__cdecl*)(void)     36   0.0%   39928   0.7%
    class FHRDocXform                        516   0.6%   35088   0.6%
    class FHRDocTextColumn                   403   0.5%   33852   0.6%
    class FHRDocTString                      407   0.5%   29304   0.5%
    struct FHRDocUString::IndexedRec        1800   2.2%   27904   0.5%
    

    【讨论】:

      【解决方案6】:

      公式更像:

      (sizeof(A) + sizeof(B) + factor) * N
      

      其中 factor 是每个条目的开销。 C++ 映射通常实现为红黑树。这些是二叉树,因此左/右节点至少有两个指针。还会有一些实现的东西——可能是一个父指针和一个“颜色”指示器,所以因素可能类似于

      (sizeof( RBNode *) * 3 + 1) / 2
      

      但是,所有这些都高度依赖于实现 - 确定您确实需要检查您自己的库实现的代码。

      【讨论】:

        【解决方案7】:

        地图的大小实际上取决于地图的实现。您可能在不同的编译器/平台上有不同的大小,具体取决于它们提供的 STL 实现。

        为什么需要这个尺寸?

        【讨论】:

        • 好的,g++。在我开始交换之前,我需要这个大小来知道我可以在内存中保存多少条目。我需要上限。
        • 如果需要上限,建议看一下map的实现,看看map节点对象是如何实现的,考虑map节点成员的大小为超出键和元素的大小。请注意,这种方法是特定于平台和编译器的。
        • 禁用虚拟系统,写一个基准来慢慢增加测试图的大小。当您用完内存时,通过查看 Windows 任务管理器的峰值工作集指标,您很快就会知道。我猜有些人宁愿讨论到死,也不愿设计一个简单的测试并确定无疑。这些人对这个世界一无所获,却花费了大量的时间和脑力,因为他们无法停下来思考如何对所讨论的变量构建一个好的测试。啊啊啊
        猜你喜欢
        • 1970-01-01
        • 2013-03-12
        • 1970-01-01
        • 2012-08-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-05-27
        • 2011-02-07
        相关资源
        最近更新 更多