【发布时间】:2013-05-24 23:29:32
【问题描述】:
我正在尝试升级我们的池化固定块内存分配器以利用 C++11 类型特征。
目前可以通过以传统方式覆盖the global new operator 来强制将任何对象的任何分配分配到正确的池中,例如
void* operator new (std::size_t size)
{ // if-cascade just for simplest possible example
if ( size <= 64 ) { return g_BlockPool64.Allocate(); }
else if ( size <= 256 ) { return g_BlockPool256.Allocate(); }
// etc .. else assume arguendo that we know the following will work properly
else return malloc(size);
}
在许多情况下,如果可以根据类型特征(例如 is_trivially_destructible)将对象分派到不同的池中,我们可以进一步提高性能。 是否可以创建一个模板化的全局 new 运算符,它知道分配的类型,而不仅仅是请求的大小? 相当于
template<class T>
void *operator new( size_t size)
{
if ( size < 64 )
{ return std::is_trivially_destructible<T>::value ?
g_BlockPool64_A.Allocate() :
g_BlockPool64_B.Allocate(); } // etc
}
在每个类中重写成员 new 运算符在这里不起作用;我们真的需要它来自动为任何地方的任何分配工作。放置 new 也不起作用:要求每个 alloc 看起来像
Foo *p = new (mempool<Foo>) Foo();
太麻烦了,人家会忘记用的。
【问题讨论】:
-
好主意。虽然可以简单地破坏如何帮助内存管理器?经理不在乎建造或破坏。
-
@GManNickG 如果所有可简单破坏的对象都进入同一个池,我们可以通过取消映射内存页面而不调用任何析构函数来整体释放它们。这是一个操作系统调用,而不是数百万次释放。当一个关卡块的所有分配都进入一个块并且您可以在离开该区域时将其扔到海里时很有用。已经有其他机制可以防止人们在其生命周期内持有指向该块的指针。
-
没有。另外,
return ::new(size);是无限递归。 -
当然,但这与生命周期策略有关,而不是内存分配。您不能只是决定随时释放内存块,您必须等待该内存中的对象的生命周期结束,无论这种破坏多么微不足道。要做到这一点,您必须在应用程序代码中的一个点做出决定,“好的,这整个块可以消失(并且驻留在那里的对象的生命周期将被隐式结束)”。我不知道内存管理器如何决定一些可简单破坏的对象是否属于一组或另一组,所以这段代码必须驻留在它之外。
-
不管怎样,通常的做法是编写一个
arena_allocator,在应用层明确使用它来将分配组合在一起。
标签: c++ c++11 typetraits