std :: map分配节点打包?

时间:2010-11-18 07:48:10

标签: c++ stl memory-management

我注意到Visual Studio(2010)的std :: map实现为其红黑树中的每个节点分配了一个新的单个内存块。也就是说,对于映射中的每个元素,将通过operator new ... malloc使用Visual Studio STL实现的std :: map的默认分配方案分配单个新的原始内存块。

这对我来说有点浪费:在“(小)n”块中分配节点会不会更有意义,就像std :: vector实现在增长时过度分配一样?

所以我想澄清以下几点:

  • 我对默认分配方案的断言是否真的正确?
  • std :: map的“all”STL实现是否以这种方式工作?
  • std中是否有任何东西阻止std :: map实现将其节点放入内存块而不是为每个节点分配一个新的内存块(通过其分配器)? (复杂性保证等)?

注意:这是关于过早优化的 如果关于优化,那么关于如果一个应用程序有问题(std::)地图内存碎片,是否有使用自定义分配器使用内存池的替代方法?这个问题不是关于自定义分配器,而是关于地图实现如何使用其分配器。 (或者我希望是这样。)

5 个答案:

答案 0 :(得分:5)

对于大多数std :: map的实现,你的断言都是正确的。

据我所知,标准中没有任何内容阻止地图使用您描述的分配方案。但是,您可以使用自定义分配器获得您所描述的内容 - 但在所有地图上强制执行该方案可能会造成浪费。由于map没有先验知道如何使用它,因此某些使用模式可以防止大部分未使用的块的解除分配。例如,假设块一次分配给4个节点,但是一个特定的映射填充了40个节点,然后删除了30个节点,最坏的情况是每个块留下一个节点,因为映射不能使指针/引用/迭代器无效最后一个节点。

答案 1 :(得分:4)

当您将元素插入到地图中时,可以保证现有的迭代器不会失效。因此,如果在两个节点A和C之间插入一个元素“B”,这个节点恰好是连续的并位于同一个堆分配区域内,则不能将它们随机播放以腾出空间,而B必须放在其他位置。我没有看到任何特别的问题,除了管理这种复杂性会使实现膨胀。如果擦除元素,则迭代器也不会失效,这意味着任何内存分配必须在其中的所有节点都被擦除之前一直存在。你可能需要在每个“肿胀的节点”/矢量/你想要调用的任何内容中使用一个空闲列表 - 有效地复制新/删除当前为你做的至少一些耗时的操作。

答案 2 :(得分:2)

我很确定我从未见过试图将多个节点合并为一个分配块的std::map实现。至少在右边,我无法想到无法工作的原因,但我认为大多数实现者会认为这是不必要的,并且将内存分配优化留给分配器而不是担心它很多在map本身。

不可否认,大多数自定义分配器都是为了更好地处理大量小块的分配而编写的。您可以通过编写map(当然还有setmultisetmultimap)来渲染浩大大部分此类优化。只是使用更大的分配。 OTOH,鉴于优化小块分配的分配器很容易/普遍/广泛可用,可能没有很多动机以这种方式改变map实现。

答案 3 :(得分:1)

我认为唯一不能做的就是使迭代器无效,如果必须重新分配存储,则可能需要这样做。话虽如此,我已经看到使用std :: map接口中包含的单个排序对象数组的实现。当然,这是出于某种原因。

实际上,你可以做的就是用自定义分配器实例化你的std :: map,它会以一种特殊的,非浪费的方式为新节点找到内存。

答案 4 :(得分:1)

  

这对我来说有点浪费。在“(小)n”块中分配节点会不会更有意义,就像std :: vector实现在生长上过度分配一样

有趣的是,我以完全不同的方式看待它。我发现这是合适的,它不会浪费任何记忆。至少使用Windows上的默认STL分配器(MS VS 2008),HP-UX(带有STLport的gcc)和Linux(没有STLport的gcc)。重要的是这些分配器确实关心内存碎片,看起来他们可以很好地处理这个问题。例如,在Windows上查找Low-fragmentation Heap或在HP-UX上查找SBA(小块分配器)。我的意思是,一次只为一个节点分配和释放内存不必导致内存碎片。我在我的一个程序中测试了std::map,并且它确实没有导致这些分配器出现任何内存碎片。

  

我的断言是关于默认的   分配方案实际上是否正确?

我有MS VisualStudio 2008,它的std :: map的行为方式相同。在HP-UX上,我使用带有和不带STLport的gcc,看起来他们的STL映射具有为std::map中的节点分配内存的相同方法。

  

std中有什么东西吗?   阻止std :: map实现   从将节点放入块中   内存而不是分配新的   内存块(通过其分配器)   对于每个节点?

如果可能的话,首先在您的平台上调整默认分配器。这里引用Douglas Lea

的作者DL-Malloc是有用的
  

...首先我写了一些   C ++中的专用分配器,   通常通过重载运算符new   适合各种课程。 ...

     

然而,我很快意识到建筑   每个新类的特殊分配器   这往往是动态的   分配和大量使用不是一个   建设各种类型的好策略   通用编程支持   我当时写的课。   (从1986年到1991年,我就是那个   libg ++的主要作者,GNU C ++   图书馆。)更广泛的解决方案是   需要 - 写一个分配器   在正常的C ++和C下,它已经足够好了   加载,以便程序员不会   试图写出特殊目的   分配器除非非常特殊   条件。

或者作为一个更难的想法,您甚至可以尝试使用Hoard分配器测试您的应用程序。我的意思是只测试你的应用程序,看看性能或碎片是否有任何好处。