C ++ 11内存池设计模式? [英] C++11 memory pool design pattern?

查看:166
本文介绍了C ++ 11内存池设计模式?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个程序,包含一个处理阶段,需要使用一堆不同的对象实例(所有分配在堆上)从一个多态类型的树,所有最终派生自一个共同的基类。



由于实例可能循环引用,没有明确的所有者,我想用 new 分配它们,原始指针,并将它们留在内存中的阶段(即使它们变得未引用),然后在使用这些实例的程序阶段后,我想一次删除所有这些。



我如何认为它的结构如下:

  struct B; // common base class 

vector< unique_ptr< B>> memory_pool;

struct B
{
B(){memory_pool.emplace_back(this); }

virtual〜B(){}
};

struct D:B {...}

int main()
{
...

/ / phase starts
D * p = new D(...);

...

//阶段结束
memory_pool.clear();
//所有B实例被删除,指针无效

...
}

除了小心所有的B实例被分配新的,并且没有人在内存池被清除后使用任何指针,这个实现有问题吗?



具体来说,我关心的是 this 指针用于构造一个 std :: unique_ptr 在基类构造函数中,在派生类构造函数完成之前。这是否会导致未定义的行为?如果是,是否有解决方法?

解决方案

如果您还没有熟悉 Boost.Pool 。从文档


什么是池?



池分配是一种非常快的内存分配方案,但
的使用受到限制。有关池分配的更多信息(还有
称为简单隔离存储,请参阅概念概念和简单
分隔存储)。



我使用池?



使用池可以更好地控制
程序中内存的使用方式。例如,你可能有一种情况,你想
在一个点分配一堆小对象,然后到达一个点
在你的程序,其中不再需要任何一个。使用pool
接口,你可以选择运行它们的析构函数,或者直接把它们放到遗忘中。池接口将保证没有
系统内存泄漏。



我什么时候应该使用池? b
$ b

池通常用于有大量分配和
释放小对象。另一个常见的用法是上面的情况
,其中许多对象可能被丢弃的内存。



一般来说,当你需要一个更有效的方法做不寻常的
内存控制。



我应该使用哪一个池分配器?



pool_allocator 是一个更通用的解决方案,面向
高效地为任意数量的连续块提供服务。



fast_pool_allocator 也是一个通用解决方案,但是面向
,以便一次有效地服务于一个块的请求;它
将用于连续的块,但不如 pool_allocator



关注性能,在处理容器时使用
fast_pool_allocator ,例如 std :: list
,并在处理诸如
std :: vector 等容器时使用 pool_allocator


内存管理是棘手的业务(线程,缓存,对齐,分段等)对于严重的生产代码,精心设计和仔细优化的库除非你的分析器显示瓶颈。


I have a program that contains a processing phase that needs to use a bunch of different object instances (all allocated on the heap) from a tree of polymorphic types, all eventually derived from a common base class.

As the instances may cyclically reference each other, and do not have a clear owner, I want allocated them with new, handle them with raw pointers, and leave them in memory for the phase (even if they become unreferenced), and then after the phase of the program that uses these instances, I want to delete them all at once.

How I thought to structure it is as follows:

struct B; // common base class

vector<unique_ptr<B>> memory_pool;

struct B
{
    B() { memory_pool.emplace_back(this); }

    virtual ~B() {}
};

struct D : B { ... }

int main()
{
    ...

    // phase begins
    D* p = new D(...);

    ...

    // phase ends
    memory_pool.clear();
    // all B instances are deleted, and pointers invalidated

    ...
}

Apart from being careful that all B instances are allocated with new, and that noone uses any pointers to them after the memory pool is cleared, are there problems with this implementation?

Specifically I am concerned about the fact that the this pointer is used to construct a std::unique_ptr in the base class constructor, before the derived class constructor has completed. Does this result in undefined behaviour? If so is there a workaround?

解决方案

In case you haven't already, familiarize yourself with Boost.Pool. From the documentation

What is Pool?

Pool allocation is a memory allocation scheme that is very fast, but limited in its usage. For more information on pool allocation (also called simple segregated storage, see concepts concepts and Simple Segregated Storage).

Why should I use Pool?

Using Pools gives you more control over how memory is used in your program. For example, you could have a situation where you want to allocate a bunch of small objects at one point, and then reach a point in your program where none of them are needed any more. Using pool interfaces, you can choose to run their destructors or just drop them off into oblivion; the pool interface will guarantee that there are no system memory leaks.

When should I use Pool?

Pools are generally used when there is a lot of allocation and deallocation of small objects. Another common usage is the situation above, where many objects may be dropped out of memory.

In general, use Pools when you need a more efficient way to do unusual memory control.

Which pool allocator should I use?

pool_allocator is a more general-purpose solution, geared towards efficiently servicing requests for any number of contiguous chunks.

fast_pool_allocator is also a general-purpose solution but is geared towards efficiently servicing requests for one chunk at a time; it will work for contiguous chunks, but not as well as pool_allocator.

If you are seriously concerned about performance, use fast_pool_allocator when dealing with containers such as std::list, and use pool_allocator when dealing with containers such as std::vector.

Memory management is tricky business (threading, caching, alignment, fragmentation, etc. etc.) For serious production code, well-designed and carefully optimized libraries are the way to go, unless your profiler demonstrates a bottleneck.

这篇关于C ++ 11内存池设计模式?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆