为什么我要为STL容器编写自定义分配器,而我只能覆盖新的和删除的



我想编写一个用于学习的自定义内存管理器/分配器。我很想拥有一个从堆中请求n字节ram的主分配器(通过new)。接下来是几个分配器。。。适配器?每个都将与主控器接口,请求一块内存进行管理,这些将是堆栈、线性、池、slab分配器等,每个分配器都从主池分配器的切片中管理分配。

我遇到的问题是,我是否应该为各种STL容器编写自定义分配器_;或者,我应该忽略适配器的想法,简单地重载new和delete以使用自定义池分配器/管理器,即主池分配器。

我有兴趣了解的是,为STL容器提供单独的分配器会给我带来什么实际好处?默认的std::分配器似乎根据需要调用new和delete,所以如果我重载它们,转而从我的大型自定义内存池中请求,那么如果没有自定义std::分配器代码的kruft,我将获得所有好处。

或者,某些类型的分配器模型(比如为std::deque使用堆栈分配器)比默认分配器工作得更好,这是问题吗?如果是这样的话,正常的stl实现是否已经专门为各种容器类型指定了默认分配器,或者在对默认分配器的调用中进行了优化?

如果这很重要的话,我正在通过GCC 10+使用C++20

如果您想替换全局分配器,包括在您正在使用的每个库中,您不必使用std::allocator

std分配器允许您执行诸如创建临时分配池之类的操作。假设您有一些数据结构,您可以保证不会超过某个范围,并且您知道(无论分配了什么)90%+将保留分配到范围的末尾。

一个相对简单的std分配器可以分发内存,从不回收内存,并且在作用域结束时清理内存的速度比任何全局newdelete操作符都快得多。

只要您对容器的内容和生存模式有专门的了解,就可以手动调整该特定容器的分配器。标准分配器不能。有时,当您愿意做出std容器没有的妥协时,您可以使用自定义分配器来修补它们的行为。

std::deque不能有效地使用堆栈分配器,因为它不能假定您主要将其用作堆栈。您可能主要将其用作队列。当您主要将堆栈分配器用作队列时,它将是一场灾难;但是,如果使用90%+作为堆栈,则堆栈分配器可能会更快,但代价是适度的内存开销(如果使用99%+,则是处理异常情况并清理非基于堆栈的操作的堆栈分配器)。

最后,分配器可以允许您区分容器的种类。您可能希望将文档(持久)状态的内存分配在一个内存区域中;划痕;要分配到其他地方的非持久性数据。

是的,使用std分配器是你应该考虑不做的事情。优化是可替代的,调整低级别内存分配是在使系统的其他部分更高效、更实用之后可以进行的工作。只有当你有一些有效的、不够快的东西,并且你已经确定新的/删除是一个基本的瓶颈,你不能设计它时,你应该说"好的,是时候更换分配了">

用例:安全软件在删除时需要shred内存,因为它无法让敏感数据保留在物理RAM中的某个位置,可以由稍后实例化的进程访问。标准运行时间的delete运算符不会执行这种昂贵的操作。重写堆运算符可能会导致库的链接器问题,这取决于这些库的运行时版本。

按顺序回答两个问题:

我应该为各种STL容器编写自定义分配器traits来接口我的分配器吗

是的,便于操作。在实现过程中,很快就会出现诸如控制内存重叠之类的情况。例如,在各个分配器满负荷的情况下对实现进行压力测试,并找出重新分配的算法。在这方面,您需要为分配器专门化allocator_traits类,而不是使用newdelete运算符从头开始实现它的member types

之所以使用allocator_traits,是因为它有助于轻松处理需要遵守的某些规则。这样的规则发生在整个内存管理中。[在分配器构造过程中,请参阅这里的三条规则。]

为STL容器提供单独的分配器会给我带来什么实际好处

绝对控制主分配器如何分配、重新分配、复制、移动和销毁内存(增加了对量化/增强性能的控制)。很酷,不是吗!如果使用默认的std分配器,您将失去这种控制,并依赖于内存管理的默认实现(尽管非常好)。

最新更新