我想初始化一个std::map
。现在我正在使用::insert
,但我觉得我浪费了一些计算时间,因为我已经知道了我想要分配的大小。有没有办法分配固定大小的地图,然后填写地图?
答案 0 :(得分:38)
不,地图成员内部存储在树形结构中。在知道要存储的键和值之前,无法构建树。
答案 1 :(得分:21)
简短的回答是:是的,这是可能的,但这不是微不足道的。您需要为地图定义自定义分配器。基本思想是你的自定义分配器将为地图留出一块内存。由于映射需要新节点,因此分配器将在预分配块中简单地分配它们。像这样:
std::map<KeyType, ValueType, std::less<KeyType>, MyAllocator> myMap;
myMap.get_allocator().reserve( nodeSize * numberOfNodes );
但是,您必须处理许多问题。
首先,您并不真正了解每个地图节点的大小或地图将执行的分配数量。这些是内部实现细节。您可以尝试找出答案,但您不能假设结果将适用于不同的编译器(甚至是同一编译器的未来版本)。因此,您不必担心分配“固定”大小的地图。相反,您的目标应该是减少少量所需的分配数量。
其次,如果你想支持删除,这个策略会变得相当复杂。
第三,不要忘记内存对齐问题。分配器返回的指针必须与内存将存储的各种类型的对象正确对齐。
所有这些,在你尝试之前,确保它是必要的。内存分配可能非常昂贵,但您仍然不应该认为这对您的程序来说是一个问题。找出答案。您还应该考虑更自然地允许预分配的替代策略。例如,排序列表或std :: unordered_map。
答案 2 :(得分:2)
答案 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
进行查找)或使用针对您的方式进行了优化的第三方地图正在使用地图。一个很好的例子是boost中的flat_map
-请参见answer。