当我只能覆盖新的和删除的时候,为什么我要为 STL 容器编写自定义分配器?
Why would I write custom allocators for STL containers when I could just overwrite new and delete?
我想写一个自定义记忆manager/allocator来学习。我很想拥有一个主分配器,它从堆中请求 n 个字节的 ram(通过 new)。接下来是几个分配器……适配器?每个都将与 master 接口,请求一个内存块来管理,这些将是堆栈、线性、池、slab 分配器等,每个分配器管理来自它们的 master 池分配器的分配。
我的问题是我是否应该编写自定义 allocator_traits 来与各种 STL 容器进行交互;或者如果我应该忽略适配器的想法并简单地重载 new 和 delete 以使用自定义池 allocator/manager,主池。
我有兴趣了解的是,如果为 STL 容器使用单独的分配器,我会获得哪些切实的好处?似乎默认的 std::allocator 会根据需要调用 new 和 delete,所以如果我重载它们而不是从我的大自定义内存池请求,我将获得所有好处而无需自定义 std::allocator 代码的废话。
或者这是某些类型的分配器模型的问题,例如对 std::deque 使用堆栈分配器会比默认分配器更好地工作吗?如果是这样,正常的 stl 实现是否已经为各种容器类型专门化了默认分配器,或者在对默认分配器的调用中进行了优化?
如果这很重要,我正在通过 GCC 10+ 使用 C++20
如果你想替换全局分配器,包括你正在使用的每个库,你不必使用 std::allocator
。
std 分配器可让您执行诸如创建临时分配池之类的操作。假设你有一些你可以保证不会超过某个范围的数据结构,并且你知道(无论分配什么)90%+ 将保持分配到范围的末尾。
一个相对简单的 std 分配器可以分发内存,从不回收它,并在范围末尾清理它,这比任何全局 new
或 delete
运算符都快得多。
只要您对容器的内容和生命周期模式有特殊的了解,就可以 hand-tune 该特定容器的分配器。标准分配器不能。有时,当您愿意做出 std 容器不愿意做出的妥协时,您可以使用自定义分配器修补它们的行为。
std::deque
无法有效地使用堆栈分配器,因为它无法假定您主要将其用作堆栈。您可能主要将其用作队列。当你主要将它用作队列时,堆栈分配器将是一场灾难;但是如果你将它 90%+ 用作堆栈,堆栈分配器可能会更快,但代价是适度的内存开销(如果 99%+,堆栈分配器可以处理异常情况并清理 non-stack基于操作)。
最后,分配器可以让您区分不同类型的容器。您可能希望将文档(持久)状态的内存分配到一个内存区域,并将“临时”non-persistent 数据分配到其他地方。
是的,您不应该考虑使用标准分配器。优化是可替代的,在使系统的其余部分更高效和更实用后,您可以调整低级内存分配。只有当你有一些有用的东西,但速度不够快,并且你已经确定 new/delete 是你无法设计的基本瓶颈时,你应该说“好的,是时候更换分配了!”
回答两个问题in-order:
我应该编写自定义 allocator_traits 来为各种 STL 容器连接我的分配器吗?
是的,为了便于操作。很快在实现中,就会出现控制内存重叠等情况。例如,虽然 stress-testing 各个分配器的全部容量实现并为 re-allocation 找出算法。在这方面,您需要为分配器专门化 allocator_traits
class,而不是使用 new
和 delete
运算符从头开始实现其 member types
。
使用allocator_traits的原因是因为它便于处理某些需要遵守的规则。这样的规则出现在整个内存管理中。 [参考 here 分配器构造期间的三个此类规则。]
如果为 STL 容器使用单独的分配器,我会得到什么切实的好处?
绝对控制主分配器如何分配、re-assigns、复制、移动和销毁内存(增加对 quantifying/enhancing 性能的控制)。很酷,不是吗!如果使用默认的 std 分配器,您将失去此控制并依赖内存管理的默认实现(尽管非常好)。
我想写一个自定义记忆manager/allocator来学习。我很想拥有一个主分配器,它从堆中请求 n 个字节的 ram(通过 new)。接下来是几个分配器……适配器?每个都将与 master 接口,请求一个内存块来管理,这些将是堆栈、线性、池、slab 分配器等,每个分配器管理来自它们的 master 池分配器的分配。
我的问题是我是否应该编写自定义 allocator_traits 来与各种 STL 容器进行交互;或者如果我应该忽略适配器的想法并简单地重载 new 和 delete 以使用自定义池 allocator/manager,主池。
我有兴趣了解的是,如果为 STL 容器使用单独的分配器,我会获得哪些切实的好处?似乎默认的 std::allocator 会根据需要调用 new 和 delete,所以如果我重载它们而不是从我的大自定义内存池请求,我将获得所有好处而无需自定义 std::allocator 代码的废话。
或者这是某些类型的分配器模型的问题,例如对 std::deque 使用堆栈分配器会比默认分配器更好地工作吗?如果是这样,正常的 stl 实现是否已经为各种容器类型专门化了默认分配器,或者在对默认分配器的调用中进行了优化?
如果这很重要,我正在通过 GCC 10+ 使用 C++20
如果你想替换全局分配器,包括你正在使用的每个库,你不必使用 std::allocator
。
std 分配器可让您执行诸如创建临时分配池之类的操作。假设你有一些你可以保证不会超过某个范围的数据结构,并且你知道(无论分配什么)90%+ 将保持分配到范围的末尾。
一个相对简单的 std 分配器可以分发内存,从不回收它,并在范围末尾清理它,这比任何全局 new
或 delete
运算符都快得多。
只要您对容器的内容和生命周期模式有特殊的了解,就可以 hand-tune 该特定容器的分配器。标准分配器不能。有时,当您愿意做出 std 容器不愿意做出的妥协时,您可以使用自定义分配器修补它们的行为。
std::deque
无法有效地使用堆栈分配器,因为它无法假定您主要将其用作堆栈。您可能主要将其用作队列。当你主要将它用作队列时,堆栈分配器将是一场灾难;但是如果你将它 90%+ 用作堆栈,堆栈分配器可能会更快,但代价是适度的内存开销(如果 99%+,堆栈分配器可以处理异常情况并清理 non-stack基于操作)。
最后,分配器可以让您区分不同类型的容器。您可能希望将文档(持久)状态的内存分配到一个内存区域,并将“临时”non-persistent 数据分配到其他地方。
是的,您不应该考虑使用标准分配器。优化是可替代的,在使系统的其余部分更高效和更实用后,您可以调整低级内存分配。只有当你有一些有用的东西,但速度不够快,并且你已经确定 new/delete 是你无法设计的基本瓶颈时,你应该说“好的,是时候更换分配了!”
回答两个问题in-order:
我应该编写自定义 allocator_traits 来为各种 STL 容器连接我的分配器吗?
是的,为了便于操作。很快在实现中,就会出现控制内存重叠等情况。例如,虽然 stress-testing 各个分配器的全部容量实现并为 re-allocation 找出算法。在这方面,您需要为分配器专门化 allocator_traits
class,而不是使用 new
和 delete
运算符从头开始实现其 member types
。
使用allocator_traits的原因是因为它便于处理某些需要遵守的规则。这样的规则出现在整个内存管理中。 [参考 here 分配器构造期间的三个此类规则。]
如果为 STL 容器使用单独的分配器,我会得到什么切实的好处?
绝对控制主分配器如何分配、re-assigns、复制、移动和销毁内存(增加对 quantifying/enhancing 性能的控制)。很酷,不是吗!如果使用默认的 std 分配器,您将失去此控制并依赖内存管理的默认实现(尽管非常好)。