![](/img/trans.png)
[英]purging the boost::pool_allocator for a std::map does not return the whole pool in VS2017
[英]How do you determine the size of the nodes created by a 'std::map' for use with 'boost::pool_allocator' (in a cross-platform way)?
更新
根据评论,答案和其他研究,我得出的结论是,就节点开销而言, set
和map
之间通常没有区别。 我的以下问题确实是:
如何确定节点开销以方便使用
boost::pool_allocator
作为自定义分配器?
并且,进一步更新 :节点开销可能永远不会超过4个指针的大小,因此只需清除Boost Pool中的sizeof(T)
, sizeof(T)+sizeof(int)
, sizeof(T) + 2*sizeof(int)
, sizeof(T) + 3*sizeof(int)
和sizeof(T) + 4*sizeof(int)
(或对于64位系统为int64_t
)应该可以。 那就是我实际上在做的,并且有效。
我想通过避免调用这些对象的析构函数,而不是在每条包含多个实例的单个条带中释放内存,来使用增强内存池来管理数千万个大小相同的微小对象。
我发布了有关此问题的另一个问题 ,这个问题的答案使我明白,我真正需要回答的问题就是我在这里问的那个问题。
考虑以下代码:
class Obj { // ... has an operator<() ... };
typedef std::set<Obj, std::less<Obj>, boost::fast_pool_allocator<Obj>> fast_set_obj;
// Deliberately do not use a managed pointer -
// I will *NOT* delete this object, but instead
// I will manage the memory using the memory pool!!!
fast_set_obj * mset = new fast_set_obj;
// ... add some Obj's to 'mset'
mset->insert(Obj());
mset->insert(Obj());
// Do something desireable with the set ...
...
// All done.
// It's time to release the memory, but do NOT call any Obj destructors.
// The following line of code works exactly as intended.
boost::singleton_pool<boost::fast_pool_allocator_tag, sizeof(Obj const)>::purge_memory();
如果您进入最后一行代码的purge_memory()
函数,您将看到fast_pool_allocator
可以按照fast_pool_allocator
很好地从系统中释放内存。 (没有调用Obj
析构函数,因为如上面链接的问题所述,自定义分配器的工作只是分配和释放内存, 而不是调用构造函数或析构函数。)
它完全可以按需工作。 大!
但是,这就是问题所在。 如果将set
替换为map
,然后尝试使用boost::pool_allocator
,则对purge_memory()
的调用不会发生任何 purge_memory()
!
typedef std::map<int, int, std::less<int>,
boost::fast_pool_allocator<std::pair<int const, int>>>
fast_map_obj;
// Ditto above: Deliberately do not use managed pointer
mast_map_obj * mmap = new fast_map_obj;
mmap[5] = Obj();
mmap[6] = Obj();
...
// Uh-oh. The following line of code DOES NOTHING, because I was using a map, not a set!
boost::singleton_pool<boost::fast_pool_allocator_tag,
sizeof(std::pair<int const, int>)>::purge_memory();
如前所述,最后一行代码不执行任何操作 。 原因是boost::fast_pool_allocator
是一个单例,仅对在编译时固定的给定大小的对象进行响应并管理其内存。 这就是为什么在调用purge_memory()
的表达式中使用sizeof
参数的原因-它告诉Boost Pool代码清除各种不同的单例内存池中的哪一个(假定由于先前已实例化而存在请求的内存池)。
不幸的是,由于选择要清除的内存池取决于大小,因此至关重要的是,要了解(即在通过调用自定义分配器分配的内存中创建和销毁的)内部对象的大小。 可悲的是,对于std::map
,由map
管理的内部对象的大小既不是sizeof(Obj)
也不是sizeof(std::pair<int const, Obj>)
。
我的问题是:如何严格按照以C ++ 11标准运行的跨平台方式,确定由std::map
内部管理以供boost::fast_pool_allocator
使用的对象的大小?
这有可能吗?
问题是您不知道set
用于节点的内部类型。
虽然我还没有弄清楚如何在编译时确定这一点,但是您可以编写一个跟踪分配器,该分配器在调用allocate
时打印出节点类型的sizeof
,如下所示:
template<typename T>
struct SimpleAllocator : private std::allocator<T>
{
using value_type = T;
using pointer = typename std::allocator<T>::pointer;
using size_type = typename std::allocator<T>::size_type;
pointer allocate(size_type n)
{
std::cout << "Allocator sizeof(T)==" << sizeof(T) << '\n';
return std::allocator<T>::allocate(n);
}
void deallocate(pointer p, size_type n)
{ return std::allocator<T>::deallocate(p, n); }
};
和一些测试程序(我正在测试一组整数):
std::set<int, std::less<int>, SimpleAllocator<int>> s;
s.insert(2);
在我的系统上,我得到以下输出:
分配器大小of(T)== 32
没有一种真正的跨平台方法可以推断出您要执行的操作,因为每个地图实现都不满意,但是通常将在池中分配地图节点。
对于标准库的不同实现,这看起来有所不同,因此对于不同的版本,您的代码将必须#ifdef
-ed。 带有易碎性警告之后,以下是g ++ / clang ++ / msc编译器及其std库的主要警告:
// libstdc++
boost::singleton_pool<boost::fast_pool_allocator_tag,
sizeof(std::_Rb_tree_node<fast_map_obj::value_type>)>::purge_memory()
// libc++
boost::singleton_pool<boost::fast_pool_allocator_tag,
sizeof(std::__tree_node<fast_map_obj::value_type, void*>)>::purge_memory()
// msvc 2013 (incl. nov ctp)
boost::singleton_pool<boost::fast_pool_allocator_tag,
sizeof(fast_map_obj::_Node)>::purge_memory()
以下是一些有用的链接,可用于查找必要的定义:
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.