在预先知道大小时初始化std :: map

时间:2012-10-24 12:35:13

标签: c++ dictionary std

我想初始化一个std::map。现在我正在使用::insert,但我觉得我浪费了一些计算时间,因为我已经知道了我想要分配的大小。有没有办法分配固定大小的地图,然后填写地图?

5 个答案:

答案 0 :(得分:38)

不,地图成员内部存储在树形结构中。在知道要存储的键和值之前,无法构建树。

答案 1 :(得分:21)

简短的回答是:是的,这是可能的,但这不是微不足道的。您需要为地图定义自定义分配器。基本思想是你的自定义分配器将为地图留出一块内存。由于映射需要新节点,因此分配器将在预分配块中简单地分配它们。像这样:

std::map<KeyType, ValueType, std::less<KeyType>, MyAllocator> myMap;

myMap.get_allocator().reserve( nodeSize * numberOfNodes );

但是,您必须处理许多问题。

首先,您并不真正了解每个地图节点的大小或地图将执行的分配数量。这些是内部实现细节。您可以尝试找出答案,但您不能假设结果将适用于不同的编译器(甚至是同一编译器的未来版本)。因此,您不必担心分配“固定”大小的地图。相反,您的目标应该是减少少量所需的分配数量。

其次,如果你想支持删除,这个策略会变得相当复杂。

第三,不要忘记内存对齐问题。分配器返回的指针必须与内存将存储的各种类型的对象正确对齐。

所有这些,在你尝试之前,确保它是必要的。内存分配可能非常昂贵,但您仍然不应该认为这对您的程序来说是一个问题。找出答案。您还应该考虑更自然地允许预分配的替代策略。例如,排序列表或std :: unordered_map。

答案 2 :(得分:2)

您在谈论block allocators。但它很难实施。在考虑这些艰难的事情之前要先衡量无论如何Boost有一些关于实现块分配器的文章。或者使用已经实施的预分配地图Stree

答案 3 :(得分:1)

不确定这是否回答了您的问题,但 Boost.Container 有一个flat_map,您可以在其中预留空间。基本上,您可以将其视为(键,值)对的排序向量。但是如果你的输入已经不变,那么你首先不会使用std::map。额外提示:如果您也知道输入已排序,则可以使用带有提示的插入来获得最佳性能。

答案 4 :(得分:1)

这个问题已经有了好几个答案,但是它们遗漏了一些要点。

直接初始化地图

如果直接使用迭代器初始化地图,则地图会预先知道其大小:

auto mymap = std::map(it_begin, it_end);

这是躲避问题的最佳方法。如果您对实现不了解,则地图可以从迭代器中预先知道大小,然后将问题移到std::实现中就可以担心。

或者insert与迭代器一起使用,即:

mymap.insert(it_begin, it_end);

请参阅:https://en.cppreference.com/w/cpp/container/map/insert

提防过早优化

  

但是我感觉我在浪费一些计算时间。

这听起来很像是您过早地进行了优化(这意味着您不知道瓶颈在哪里-您正在猜测或看到的问题并不是一个真正的问题)。相反,请先进行测量,然后进行优化-如有必要,请重复。

可以在很大程度上优化内存分配

为地图滚动自己的块分配器可能几乎没有结果。在现代系统(她包括OS /硬件 c ++语言级别)上,对于一般情况,内存分配已经非常优化,如果滚动自己的块分配器,您可能会发现很少或没有改善。即使您非常注意并将地图放入一个contiguoes数组中(尽管本身有所改进),您仍然可能会面临这样的问题:最后,元素可以随机放置在数组中(例如,插入顺序)并且无论如何都对缓存不友好(尽管这在很大程度上取决于您的实际使用情况-我假设数据集非常大)。

使用其他容器或第三方地图

如果您仍然遇到此问题-最好的方法可能是使用另一个容器(例如,已排序的std::vector-使用std::lower_bound进行查找)或使用针对您的方式进行了优化的第三方地图正在使用地图。一个很好的例子是中的flat_map-请参见answer

结论

  1. 让std :: map担心该问题。
  2. 当性能是主要问题时:使用最适合您数据使用方式的数据结构(也许是第三方)(随机插入或批量插入/主要是迭代或主要是查找/等等)。 )。然后,您需要配置文件并收集性能指标进行比较。