Can std :: stack be used as an object pool container?

I need to create an object pool to eliminate dynamic allocations. Is it efficient to use std :: stack to place pointers to allocated objects?

I suspect that every time I pop the released object back onto the stack, a new stack item will be dynamically allocated. I'm right? Should I use std :: vector to make sure nothing new is allocated?

+2


source to share


3 answers


Whether the stack is suitable for your specific purpose or not is a problem that I will not deal with. Now, if you are worried about the number of allocations, the default inner container for std :: stack is std :: deque <>. There is no need to allocate new memory for the stack on every push (if any), and there is no need to move all existing elements as std :: vector <> when allocating it.

You can tell the stack to use std :: vector <> as a base container with a second template argument:



std::stack< int, std::vector<int> > vector_stack;

      

+4


source


STL containers of pointers do nothing with the objects they point to, it's up to you, so you're responsible for memory leaks, etc. Take a look at the Boost Pointer Container Library or try saving the actual objects, you will save yourself in debt.

If you want to reduce the number of dynamic allocations created by the container, and you know how many objects to store, you can use the vector (reserve) () method, which will preallocate the requested memory in one shot.

You can also specify the number of records you want in the constructor, but this way will create x objects for you and then store them, which you may not need.



If for some technical reason dynamic allocation is completely disabled, you can try using boost :: pool as your allocator, (as you know, you can specify a different library std allocator if you don't want to use the default).

However, when I tested it the default was always faster, at least with g ++, so it might not be worth the effort. Make sure you profile it, and don't assume you can give code to standards!

+2


source


Fulfilling ANY allocates during free is WRONG due to nothrow-guarantees. If you need to make allocations to make free, as well as allocations, where do you put the pointer? You are either silently catching the exception or leaking or propagating the exception. Exception propagation means that objects that use your YourObject

cant are placed in STL containers. And the leak is, well, the leak. Either way, you broke the rules.

But which data structure depends on your object lifecycle management idiom.

Is idiom an object pool for use with factory (s) and freeInstance

 YourObject* pO = YourObject::getInstance(... reinitialize parameters ...);
 ......object public lifetime....
 pO->freeInstance();

      

or a memory pool to be used with a specific new / operator delete operator class (or allocator)?

YourObject::operator new(size_t);
......object public lifetime....
delete pO;

      

If it is an object pool and you have an idea of ​​the amount of YourObject * vector used in the released code and deque, or preferably in a circular buffer (since deque has no reserve, so you need to add this, where as a dynamic sizing, the circular buffer is exactly what what you want) in your debug code and reserve the approximate number. Highlight LIFO in release and FIFO in debug so you have a history in debug.

In a path where there are no free objects, be sure to do reserve(nMade+1)

in the collection YourObject*

before you dynamically create the object.

(The reason for this reserve is twofold. First, it must be done in time createInstance

. Second, it simplifies the code. Otherwise, you have the option of throwing std :: badalloc in freeInstance

, which can OUCH ! Eg - class Y has in it YourObject*

and does it freeInstance

for this YourObject*

in its destructor - if you don't reserve space for YourObject

* when you do this, where do you store that pointer in freeInstance

time? If you reserve space after that in getInstance

, then you need to catch std :: badalloc for the reserve. let go of what you just did YourObject

and rethrow

.)

If it is a memory pool then the memory blocks use the intrusive sibling list in release and the double linked list in debug (I guess sizeof(YourObject)>=2*sizeof(void*)

) BTW there are many MemoryPool implementations out there. Highlight LIFO in release and FIFO in debug again so you have a history in debug.

By the way, if you are using an idiom factory, don't go to the overloaded getIntances () and add re-creation methods. It just opens up the opportunity to refuse a second call. The getInstance methods are your "constructors" in the sense that they get the object into the state you want. Note that in the case of an object pool, you need freeInstance, which may require a "destructor" object.

In this case, it makes sense to talk about "open class invariants" and "private class invariants" - the object is in the limbo state, where the open class invariants can NOT be executed when in a free pool. Its value is YourObject

as fas as YourObject

, but all public class invariants may fail. The job of YourObject :: getInstance is to get an instance and make sure that its public invariants are met. In an additional way, freeInstance releases resources that getInstance may have obtained to ensure that "public class invariants" are satisfied, can be freed when an object in the free list is "idle".

The LIFO in the release also has the advantage of SIGNIFICANT caching the last used objects / blocks, where the FIFO is guaranteed not to be cached if there is a large enough number of objects / blocks - or even a page if the number is greater! But you probably already figured this out when you decided to use the stack.

strong text

0


source







All Articles