Custom heap pre-allocation for entity-component system - c++

I have an OOP entity-component system that currently works like this:
// In the component system
struct Component { virtual void update() = 0; }
struct Entity
{
bool alive{true};
vector<unique_ptr<Component>> components;
void update() { for(const auto& c : components) c->update(); }
}
// In the user application
struct MyComp : Component
{
void update() override { ... }
}
To create new entities and components, I use C++'s usual new and delete:
// In the component system
struct Manager
{
vector<unique_ptr<Entity>> entities;
Entity& createEntity()
{
auto result(new Entity);
entities.emplace_back(result);
return *result;
}
template<typename TComp, typename... TArgs>
TComp& createComponent(Entity& mEntity, TArgs... mArgs)
{
auto result(new TComp(forward<TArgs>(mArgs)...));
mEntity.components.emplace_back(result);
return result;
}
void removeDead() { /* remove all entities with 'alive == false' - 'delete' is called here by the 'unique_ptr' */ }
}
// In the user application
{
Manager m;
auto& myEntity(m.createEntity());
auto& myComp(m.createComponent<MyComp>(myEntity));
// Do stuff with myEntity and myComp
m.removeDead();
}
The system works fine, and I like the syntax and flexibility. However, when continuously adding and removing entities and components to the manager, memory allocation/deallocation slows down the application. (I've profiled and determined that the slow down is caused by new and delete).
I've recently read that it's possible to pre-allocate heap memory in C++ - how can that be applied to my situation?
Desired result:
// In the user application
{
Manager m{1000};
// This manager can hold about 1000 entities with components
// (may not be 1000 because of dynamic component size,
// since the user can define it's on components, but it's ok for me)
auto& myEntity(m.createEntity());
auto& myComp(m.createComponent<MyComp>(myEntity));
// Do stuff with myEntity and myComp
m.removeDead();
// No 'delete' is called here! Memory of the 'dead' entities can
// be reused for new entity creation
}
// Manager goes out of scope: 'delete' is called here

There are a few things you can do to make the implementation of your design scale better.
In your current implementation there are two memory allocations per Entity and Component. The first one allocates an object and the second one when the object is put into the vector. The second one happens when the vector runs out of space and allocates a bigger array and moves the old elements into the new array.
In this case the best you can do is to use intrusive lists. That is, each of Entity and Component become also list nodes. Then, after these have been allocated no extra memory allocations are necessary to put the object into a list. Use a single or double-linked list from Boost.Intrusive, or write your own. This is how Linux kernel keeps track of many different objects.
The next step is to preallocate Entity and Component elements. Preallocating could be something as simple as a global array of these, or something more sophisticated, such as Boost.Pool. There are quite a few ways to build a memory pool of objects.
Once Entity and Component are preallocated and intrusive lists are used you are done.
An example which uses boost components:
#include <boost/intrusive/list.hpp>
#include <boost/pool/pool_alloc.hpp>
#include <new>
namespace bi = boost::intrusive;
// api.h
//
// Object pooling support begin.
//
template<class T>
struct Pool
{
static boost::pool_allocator<T> pool;
};
// Singleton. Although it is defined in the header, the linkers
// make sure there is only one instance of it in the application.
// It is instantiated on demand when Pool<T> is used.
template<class T>
boost::pool_allocator<T> Pool<T>::pool;
template<class Derived>
struct Pooled // use it on the most derived class only, not on intermediate base classes
{
// Automatically use the object pool for plain new/delete.
static void* operator new(size_t) { return Pool<Derived>::pool.allocate(1); }
static void operator delete(void* p) { return Pool<Derived>::pool.deallocate(static_cast<Derived*>(p), 1); }
};
//
// Object pooling support end.
//
// Using bi::list_base_hook<bi::link_mode<bi::auto_unlink> > because it automatically
// unlinks from the list when the object is destroyed. No need to manually
// remove the object from the list when an object is about to be destroyed.
struct Component
: bi::list_base_hook<bi::link_mode<bi::auto_unlink> > // make it an intrusive list node
{
virtual void update() = 0;
virtual ~Component() {}
};
struct Entity
: bi::list_base_hook<bi::link_mode<bi::auto_unlink> > // make it an intrusive list node
, Pooled<Entity> // optional, make it allocated from the pool
{
bool active = false;
bi::list<Component, bi::constant_time_size<false> > components;
~Entity() {
for(auto i = components.begin(), j = components.end(); i != j;)
delete &*i++; // i++ to make sure i stays valid after the object is destroyed
}
void update() {
for(auto& c : components)
c.update();
}
};
struct Manager
{
bi::list<Entity, bi::constant_time_size<false> > entities;
~Manager() {
for(auto i = entities.begin(), j = entities.end(); i != j;)
delete &*i++; // i++ to make sure i stays valid after the object is destroyed
}
Entity& createEntity() {
auto result = new Entity;
entities.push_back(*result);
return *result;
}
template<typename TComp, typename... TArgs>
TComp& createComponent(Entity& mEntity, TArgs... mArgs)
{
auto result = new TComp(std::forward<TArgs>(mArgs)...);
mEntity.components.push_back(*result);
return *result;
}
void removeDead() {
for(auto i = entities.begin(), j = entities.end(); i != j;) {
auto& entity = *i++;
if(!entity.active)
delete &entity;
}
}
};
// user.cc
struct MyComp
: Component
, Pooled<MyComp> // optional, make it allocated from the pool
{
void update() override {}
};
int main() {
Manager m;
auto& myEntity(m.createEntity());
auto& myComp(m.createComponent<MyComp>(myEntity));
m.removeDead();
}
In the above example boost::pool_allocator<T> actually uses new to allocate objects and then it keeps reusing destroyed objects rather than invoking delete on them. You can do better by preallocating all objects, but there are many ways to do so depending on your requirements, so that I use boost::pool_allocator<T> for simplicity to avoid hair splitting here. You can change the implementation of Pooled<T> to something like Pooled<T, N> where N stands for the maximum number of objects, the rest of the code stays the same because it uses plain new/delete which happen to be overridden for objects allocated from a pool.

C++ supports class-specific memory pools for this kind of thing. The general-purpose new/delete pair inevitably trades off among
Time spent searching for a free block of the right size to meet each request
Time spent coalescing free blocks
Time spent maintaining and perhaps reorganizing internal data structures to make the above two operations faster.
The primary way to gain speed is to avoid these tradeoffs entirely with custom allocators that - as you say - pre-allocate a big chunk of memory viewed as a simple array of free objects all of the same size. Initially these are all linked on a free list, where the link pointers occupy the first bytes of each block "overlaid" where the data will eventually go. Allocation is just unchaining a block from the head of the free list - a "pop" operation needing about 2 instructions. Deallocation is a "push:" two more instructions. In many cases, memory hardware can be set to generate a trap when the the pool is empty so there is no per-allocation overhead for detecting
this error condition. (In GC systems the same trick is used to initiate collection with no overhead.)
In your case you'd need two pools: one for Entities and one for Components.
Defining your own pool allocator is not so hard, especially if your application is single threaded. See this document for a tutorial treatment.

Using most of answers and Google as references, I implemented some pre-allocation utilities in my SSVUtils library.
Prealloc.h
Example:
using MemUnit = char;
using MemUnitPtr = MemUnit*;
using MemSize = decltype(sizeof(MemUnit)); // Should always be 1 byte
class MemBuffer
{
Uptr<MemUnit[]> buffer;
MemRange range;
MemBuffer(MemSize mSize) : ...
{
// initialize buffer from mSize
}
};
class PreAllocatorChunk
{
protected:
MemSize chunkSize;
MemBuffer buffer;
std::stack<MemRange> available;
public:
PreAllocatorChunk(MemSize mChunkSize, unsigned int mChunks) : ...
{
// Add "chunks" to to available...
}
template<typename T, typename... TArgs> T* create(TArgs&&... mArgs)
{
// create on first "chunk" using placement new
auto toUse(available.top().begin); available.pop();
return new (toUse) T{std::forward<TArgs>(mArgs)...};
}
};
More pre-allocation utilities are available:
PreAllocatorDynamic: pre-allocates a big buffer, then, when creating an object, splits the buffer in two parts:
[buffer start, buffer start + obj size)
[buffer start + obj size, buffer end)
When an object is destroyed, its occupied memory range is set as "available". If during creation of a new object no big enough "chunk" is found, the pre-allocator tries to unify contiguous memory chunks before throwing a runtime exception. This pre-allocator is sometimes faster than new/delete, but it greatly depends on the size of pre-allocated buffer.
PreAllocatorStatic<T>: inherited from PreAllocatorChunk. Size of a chunk is equal to sizeof(T). Fastest pre-allocator, less flexible. Almost always faster than new/delete.

One of your issues can be solved by allocating enough space in the vectors on their creation
For
vector<unique_ptr<Entity>> entities;
provide enough space in the constructor
Manager::Manager() : entities(10000)
{
//...
}
Thus, you avoid reallocations and copying during later stages.
The second issue is the creation of your unique_ptr<Entity> pointers. Here, as you will always use default constructed objects, you could also use a pre-allocated pool of objects from which you create the pointers. Instead of calling new you would call an own class
class EntityPool
{
public:
EntityPool(unsigned int size = 10000) : pool(size), nextEntity(0)
{
}
Entity* getNext(void)
{
if (nextEntity != pool.size()) // if pool is exhausted create new
{
pool.emplace_back(Entity());
}
return pool[nextEntity++];
}
private:
vector<Entity> pool;
unsigned int nextEntity; // index into the vector to the next Entity
};
struct Manager
{
vector<unique_ptr<Entity>> entities;
Entity& createEntity()
{
entities.emplace_back(entityPoolInstance.getNext());
return *result;
}
//...

Or you could wire in the standard 'placement new'. This allows you to allocate a big block of memory to construct (place) objects into as you choose. This will keep the block on the heap for as long as you need it, and allow you to allocate multiple short lived objects into this block instead of doing the costly allocations and de-allocations that just end up fragmenting the heap. There's a couple of gotcha's involved, but all in all its a REALLY simple solution without having to go down the custom memory manager route.
Here's an excellent treatment of removing some of the pitfalls and describing placement new in detail.
I've used data structures as simple as a stack to keep track of the next free block to allocate into: push the address of a block that's about to be deleted onto the stack. When allocating just pop the next free block off of the stack and use that as the arg to placement new. Super easy and super fast!

Related

C++ Event System - Heap based Event fire

I've started a journey on my Game Engine and I plan to make it multithreaded. Considering both that and heap allocation costs, I've written an EventPool class that is responsible for caching Event allocations, thus reducing heap allocations, but it adds an additional cost to search for a previously allocated 'free' event pointer of the same type. Ideally, you would want to do only stack allocations, but I'm not sure if that is even possible considering downcasting is required for each EventType.
Is it better to do heap allocation per event fire and delete per frame or keep the EventPool and search from it?
Event fire:
template<typename EventType>
static void Publish(const EventType& e)
{
const auto& handlerIt = m_Subscribers.find(typeid(EventType));
if (handlerIt == m_Subscribers.end())
{
return;
}
auto ev = EventPool::Allocate<EventType>(e);
for (auto& handler : handlerIt->second)
{
if (!ev->IsHandled())
{
ev->m_Handled = handler(ev);
}
}
ev->m_Handled = true;
}
If the EventPool approach is better, how can I optimize it a bit more?
EventPool allocator:
template<class EventType>
static EventType* Allocate(const EventType& e)
{
const auto& poolIt = m_EventPool.find(typeid(EventType));
EventType* newEvent;
if (poolIt == m_EventPool.end())
{
newEvent = new EventType;
memcpy(newEvent, &e, sizeof(EventType));
m_EventPool[typeid(EventType)].push_back(newEvent);
return newEvent;
}
else
{
for (Event* ev : poolIt->second)
{
if (ev->IsHandled())
{
newEvent = static_cast<EventType*>(ev);
memcpy(newEvent, &e, sizeof(EventType));
return newEvent;
}
}
newEvent = new EventType;
memcpy(newEvent, &e, sizeof(EventType));
poolIt->second.push_back(newEvent);
return newEvent;
}
}
According to
template<typename EventType> static void Publish(const EventType& e)
, the compiler knows all the types of your events in advance (because it has to instantiate the Publish template function for each of them). You can use this fact to avoid runtime type dispatch completely. For example:
template<typename E> using Subscriber = bool (*)(E const&); // or whatever it is
template<typename... Es> // all of the events which may be worked with go here
class EventPool {
private:
std::tuple<std::vector<Es>...> storages; // a storage per event type
std::tuple<std::vector<Subscriber<Es>>...> subscribers; // a list of subscribers per event type
// or whatever you prefer over vectors
public:
template<typename E> void publish(E const& event) {
get<E>(storages).push_back(event); // or whatever your (re)allocation policy is
for (auto& handler: get<Subscriber<E>>(subscribers))
if (event.handled = handler(event))
break;
}
};
Now all that RTTI find(typeid(E)) is gone - instead, you choose the appropriate storage for your event type at compile-time via std::get over a tuple of all possible variants. Besides, you need no unsafe downcasts with that
newEvent = static_cast<EventType*>(ev)
because you don't need to restore the type as long as you don't erase it, like in this approach.
Speaking about heap allocations - your events won't need to be newed one by one, which saves you a ton of allocations. Instead, a storage (dynamic array (std::vector) in my case) is provided for every event type, so the events can be stored in-place.
Data structure
It sounds like you would have need for a "pool allocator", which is a special kind of allocator type. We also have "stack allocator", "linear allocator", "double-ended stack allocator", "heap allocator", etc.
This article gives a good overview of the memory operations involved:
http://dmitrysoshnikov.com/compilers/writing-a-pool-allocator/
However, such an allocator only works when the type of event, ie. EventType, has a predictable and unmodified size in bytes. The implementation of a pool allocator depends on an array, where each element's memory is reinterpret_cast'ed to a EvenType* to the next free item. It thus uses an implicit free-list but without a separate list structure since the list is contained inside the data. This is very simple code to showcase how that would work:
// This static assert is very important!
static_assert(sizeof(EventType) >= sizeof(void*));
EvenType pool[5];
EventType* free_ptr = &pool[0];
for (int i = 0; i < 4; i++)
{
auto ptr = reinterpret_cast<EventType*>(&pool[i]);
*ptr = &pool[i+1];
}
auto ptr = reinterpret_cast<EventType*>(&pool[4]);
*ptr = nullptr;
// now allocate
EventType* new_ptr = nullptr;
if (free_ptr == nullptr)
{
// failed to allocate!
}
new_ptr = free_ptr;
free_ptr = reinterpret_cast<EventType*>(*free_ptr);
// Now, free_ptr should point to &pool[1]
This is kind of a blueprint for what would happen. This kind of code is very easy to make mistakes with (I likely made a few even though I have done this a lot), so I strongly encourage thorough unit-testing.
Benefits
It sounds like heap allocations was a concern for you, which should always be limited at runtime, especially in time-critical systems such as game engine message processing. With a heap allocator, you allocate one big chunk of memory up front and use that memory throughout program lifetime. So you need to know in advance the maximum number of messages that will ever be in-fly at once, at any given moment in time. If that number is surpassed messaged will be thrown away and an error message should be output to the logger.

How to take ownership of an object while looping over std::vector of std::unique_ptr using a range based for loop?

I have a std::vector<std::unique_ptr<Kind>> which I want to clean up while it is being iterated upon, without explicitly calling the destructor of its members (.reset()).
The Kind is a heavy struct and its size increases during the iteration. The next object doesn't need to know about previous objects so I'd like to clean up an iterand when its not needed.
I know vector will clean up in the end, but by then, lots of Kind and their dynamically allocated memory adds up. I'm trying to reduce peak memory to just one element.
I want to avoid reset since other developers may not know about the dynamic allocation, forget calling reset in the end of the loop and cost memory penalty.
I cannot create a copy,
for(std::unique_ptr<Kind> t : store)
I cannot move it like
for(std::unique_ptr<Kind> &&t : store)
Then how do I do it ?
#include <iostream>
#include <vector>
struct Kind{
char a;
char *array;
Kind(const char c): a(c)
{
}
~Kind(){
free(array); // internal custom deallocator.
}
};
int main() {
std::vector<std::unique_ptr<Kind>> store;
store.push_back(std::make_unique<Kind>('y'));
store.push_back(std::make_unique<Kind>('z'));
for(std::unique_ptr<Kind> &t : store){
// increase size of Kind.array.
std::cout << t->a;
// Use the Kind.array
// clean up t automatically.
}
return 0;
}
Example of moving the element out of the vector.
int main() {
std::vector<std::unique_ptr<Kind>> store;
store.push_back(std::make_unique<Kind>('y'));
for(std::unique_ptr<Kind> &t : store){
auto tmp = std::move(t); // leaving a valid but empty entry in store
std::cout << tmp->a;
// clean up t automatically.
// tmp runs out of scope and cleans up
}
return 0;
}
In effect not much different from the reset, but might be relevant for what you actually do in your real program.
How to take ownership of an object while looping over std::vector of std::unique_ptr using a range based for loop?
Loop with a reference to the element, and std::move the unique pointer into another. Example:
for(std::unique_ptr<Kind> &t : store){
std::unique_ptr<Kind> owner = std::move(t);
// do something with newly owned pointer
I want to clean up
there's no need to keep older structs around
You could deallocate the object by resetting the pointer:
for(std::unique_ptr<Kind> &t : store) {
// do something
t.reset();
That said, this is typically unnecessary. They will be automatically be destroyed when the vector goes out of scope.
I'm trying to save some memory here
If you allocate dynamic objects while iterating this may be useful. Otherwise it won't affect peak memory use.
If you want to make sure the instances are deleted immediately after each iteration and you cannot wait until the entire loop is done, you can write a wrapper that takes care of that and expresses your intent at the same time:
template <typename T>
struct Stealing {
std::unique_ptr<T> ptr;
Stealing(std::unique_ptr<T>& ptr) : ptr(std::move(ptr)) {
}
auto operator*() {
return ptr.operator*();
}
auto operator->() {
return ptr.operator->();
}
}
You can use that in the loop as a drop-in replacement for a unique_ptr as such:
for (Stealing<Kind> t: store) {
// do what you like with t as if it was a std::unique_ptr
// when t goes out of scope, so does its member -> Kind gets destroyed
}

What data type to use for the buffer of a custom stack-based allocator?

I want to create my own game engine so I bought a few books one being Game Engine Architecture Second Edition by Jason Gregory and in it he suggests implementing a few custom allocators. One type of allocator the book talked about was a stack-based allocator, but I got confused when reading it. How do you store data in it? What data type do you use? For example, do you use a void*, void**, an array of char[]? The book says you're meant to allocate one big block of memory using malloc in the begining and free it in the end, and "allocate" memory by incrementing a pointer. If you could help explain this more that would be great because I can't seem to find a tutorial that doesn't use std::allocator. I also thought this might help others interested in a custom allocator so I posted the question here.
This is the header file example they give in the book:
class StackAllocator
{
public:
// Represents the current top of the stack.
// You can only roll back to the marker not to arbitrary locations within the stack
typedef U32 Marker;
explicit StackAllocator(U32 stackSize_bytes);
void* alloc(U32 size_bytes); // Allocates a new block of the given size from stack top
Marker getMarker(); // Returns a Marker to the current stack top
void freeToMarker(Marker marker); // Rolls the stack back to a previous marker
void clear(); // Clears the entire stack(rolls the stack back to zero)
private:
// ...
}
EDIT:
After a while I got this working but I don't know if I'm doing it right
Header File
typedef std::uint32_t U32;
struct Marker {
size_t currentSize;
};
class StackAllocator
{
private:
void* m_buffer; // Buffer of memory
size_t m_currSize = 0;
size_t m_maxSize;
public:
void init(size_t stackSize_bytes); // allocates size of memory
void shutDown();
void* allocUnaligned(U32 size_bytes);
Marker getMarker();
void freeToMarker(Marker marker);
void clear();
};
.cpp File
void StackAllocator::init(size_t stackSize_bytes) {
this->m_buffer = malloc(stackSize_bytes);
this->m_maxSize = stackSize_bytes;
}
void StackAllocator::shutDown() {
this->clear();
free(m_buffer);
m_buffer = nullptr;
}
void* StackAllocator::allocUnaligned(U32 size_bytes) {
assert(m_maxSize - m_currSize >= size_bytes);
m_buffer = static_cast<char*>(m_buffer) + size_bytes;
m_currSize += size_bytes;
return m_buffer;
}
Marker StackAllocator::getMarker() {
Marker marker;
marker.currentSize = m_currSize;
return marker;
}
void StackAllocator::freeToMarker(Marker marker) {
U32 difference = m_currSize - marker.currentSize;
m_currSize -= difference;
m_buffer = static_cast<char*>(m_buffer) - difference;
}
void StackAllocator::clear() {
m_buffer = static_cast<char*>(m_buffer) - m_currSize;
}
Okay for simplicity let's say you're tracking a collection of MyFunClass for your engine. It could be anything, and your linear allocator doesn't necessarily have to track objects of a homogenous type, but often that's how it's done. In general, when using custom allocators you're trying to "shape" your memory allocations to separate static data from dynamic, infrequently accessed vs. frequently accessed, with a view towards optimizing your working set and achieving locality of reference.
Given the code you provided, first, you'd allocate your memory pool. For simplicity, assume you want enough space to pool 1000 objects of type MyFunClass.
StackAllocator sa;
sa.Init( 1000 * sizeof(MyFunClass) );
Then each time you need to "allocate" a new block of memory for a FunClass, you might do it like this:
void* mem = sa.allocUnaligned( sizeof(MyFunClass) );
Of course, this doesn't actually allocate anything. All the allocation already happened in Step 1. It just marks some of your already-allocated memory as in-use.
It also doesn't construct a MyFunClass. Your allocator isn't strongly typed, so the memory it returns can be interpreted however you want: as a stream of bytes; as a backing representation of a C++ class object; etc.
Now, how would you use a buffer allocated in this fashion? One common way is with placement new:
auto myObj = new (mem) MyFunClass();
So now you're constructing your C++ object in the memory space you reserved with the call to allocUnaligned.
(Note that the allocUnaligned bit gives you some insight into why we don't usually write our own custom allocators: because they're hard as heck to get right! We haven't even mentioned alignment issues yet.)
For extra credit, take a look at scope stacks which take the linear allocator approach to the next level.

C++11 memory pool design pattern?

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 Boost 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.
Your idea is great and millions of applications are already using it. This pattern is most famously known as «autorelease pool». It forms a base for ”smart” memory management in Cocoa and Cocoa Touch Objective-C frameworks. Despite the fact that C++ provides hell of a lot of other alternatives, I still think this idea got a lot of upside. But there are few things where I think your implementation as it stands may fall short.
The first problem that I can think of is thread safety. For example, what happens when objects of the same base are created from different threads? A solution might be to protect the pool access with mutually exclusive locks. Though I think a better way to do this is to make that pool a thread-specific object.
The second problem is invoking an undefined behavior in case where derived class's constructor throws an exception. You see, if that happens, the derived object won't be constructed, but your B's constructor would have already pushed a pointer to this to the vector. Later on, when the vector is cleared, it would try to call a destructor through a virtual table of the object that either doesn't exist or is in fact a different object (because new could reuse that address).
The third thing I don't like is that you have only one global pool, even if it is thread-specific, that just doesn't allow for a more fine grained control over the scope of allocated objects.
Taking the above into account, I would do a couple of improvements:
Have a stack of pools for more fine-grained scope control.
Make that pool stack a thread-specific object.
In case of failures (like exception in derived class constructor), make sure the pool doesn't hold a dangling pointer.
Here is my literally 5 minutes solution, don't judge for quick and dirty:
#include <new>
#include <set>
#include <stack>
#include <cassert>
#include <memory>
#include <stdexcept>
#include <iostream>
#define thread_local __thread // Sorry, my compiler doesn't C++11 thread locals
struct AutoReleaseObject {
AutoReleaseObject();
virtual ~AutoReleaseObject();
};
class AutoReleasePool final {
public:
AutoReleasePool() {
stack_.emplace(this);
}
~AutoReleasePool() noexcept {
std::set<AutoReleaseObject *> obj;
obj.swap(objects_);
for (auto *p : obj) {
delete p;
}
stack_.pop();
}
static AutoReleasePool &instance() {
assert(!stack_.empty());
return *stack_.top();
}
void add(AutoReleaseObject *obj) {
objects_.insert(obj);
}
void del(AutoReleaseObject *obj) {
objects_.erase(obj);
}
AutoReleasePool(const AutoReleasePool &) = delete;
AutoReleasePool &operator = (const AutoReleasePool &) = delete;
private:
// Hopefully, making this private won't allow users to create pool
// not on stack that easily... But it won't make it impossible of course.
void *operator new(size_t size) {
return ::operator new(size);
}
std::set<AutoReleaseObject *> objects_;
struct PrivateTraits {};
AutoReleasePool(const PrivateTraits &) {
}
struct Stack final : std::stack<AutoReleasePool *> {
Stack() {
std::unique_ptr<AutoReleasePool> pool
(new AutoReleasePool(PrivateTraits()));
push(pool.get());
pool.release();
}
~Stack() {
assert(!stack_.empty());
delete stack_.top();
}
};
static thread_local Stack stack_;
};
thread_local AutoReleasePool::Stack AutoReleasePool::stack_;
AutoReleaseObject::AutoReleaseObject()
{
AutoReleasePool::instance().add(this);
}
AutoReleaseObject::~AutoReleaseObject()
{
AutoReleasePool::instance().del(this);
}
// Some usage example...
struct MyObj : AutoReleaseObject {
MyObj() {
std::cout << "MyObj::MyObj(" << this << ")" << std::endl;
}
~MyObj() override {
std::cout << "MyObj::~MyObj(" << this << ")" << std::endl;
}
void bar() {
std::cout << "MyObj::bar(" << this << ")" << std::endl;
}
};
struct MyObjBad final : AutoReleaseObject {
MyObjBad() {
throw std::runtime_error("oops!");
}
~MyObjBad() override {
}
};
void bar()
{
AutoReleasePool local_scope;
for (int i = 0; i < 3; ++i) {
auto o = new MyObj();
o->bar();
}
}
void foo()
{
for (int i = 0; i < 2; ++i) {
auto o = new MyObj();
bar();
o->bar();
}
}
int main()
{
std::cout << "main start..." << std::endl;
foo();
std::cout << "main end..." << std::endl;
}
Hmm, I needed almost exactly the same thing recently (memory pool for one phase of a program that gets cleared all at once), except that I had the additional design constraint that all my objects would be fairly small.
I came up with the following "small-object memory pool" -- perhaps it will be of use to you:
#pragma once
#include "defs.h"
#include <cstdint> // uintptr_t
#include <cstdlib> // std::malloc, std::size_t
#include <type_traits> // std::alignment_of
#include <utility> // std::forward
#include <algorithm> // std::max
#include <cassert> // assert
// Small-object allocator that uses a memory pool.
// Objects constructed in this arena *must not* have delete called on them.
// Allows all memory in the arena to be freed at once (destructors will
// be called).
// Usage:
// SmallObjectArena arena;
// Foo* foo = arena::create<Foo>();
// arena.free(); // Calls ~Foo
class SmallObjectArena
{
private:
typedef void (*Dtor)(void*);
struct Record
{
Dtor dtor;
short endOfPrevRecordOffset; // Bytes between end of previous record and beginning of this one
short objectOffset; // From the end of the previous record
};
struct Block
{
size_t size;
char* rawBlock;
Block* prevBlock;
char* startOfNextRecord;
};
template<typename T> static void DtorWrapper(void* obj) { static_cast<T*>(obj)->~T(); }
public:
explicit SmallObjectArena(std::size_t initialPoolSize = 8192)
: currentBlock(nullptr)
{
assert(initialPoolSize >= sizeof(Block) + std::alignment_of<Block>::value);
assert(initialPoolSize >= 128);
createNewBlock(initialPoolSize);
}
~SmallObjectArena()
{
this->free();
std::free(currentBlock->rawBlock);
}
template<typename T>
inline T* create()
{
return new (alloc<T>()) T();
}
template<typename T, typename A1>
inline T* create(A1&& a1)
{
return new (alloc<T>()) T(std::forward<A1>(a1));
}
template<typename T, typename A1, typename A2>
inline T* create(A1&& a1, A2&& a2)
{
return new (alloc<T>()) T(std::forward<A1>(a1), std::forward<A2>(a2));
}
template<typename T, typename A1, typename A2, typename A3>
inline T* create(A1&& a1, A2&& a2, A3&& a3)
{
return new (alloc<T>()) T(std::forward<A1>(a1), std::forward<A2>(a2), std::forward<A3>(a3));
}
// Calls the destructors of all currently allocated objects
// then frees all allocated memory. Destructors are called in
// the reverse order that the objects were constructed in.
void free()
{
// Destroy all objects in arena, and free all blocks except
// for the initial block.
do {
char* endOfRecord = currentBlock->startOfNextRecord;
while (endOfRecord != reinterpret_cast<char*>(currentBlock) + sizeof(Block)) {
auto startOfRecord = endOfRecord - sizeof(Record);
auto record = reinterpret_cast<Record*>(startOfRecord);
endOfRecord = startOfRecord - record->endOfPrevRecordOffset;
record->dtor(endOfRecord + record->objectOffset);
}
if (currentBlock->prevBlock != nullptr) {
auto memToFree = currentBlock->rawBlock;
currentBlock = currentBlock->prevBlock;
std::free(memToFree);
}
} while (currentBlock->prevBlock != nullptr);
currentBlock->startOfNextRecord = reinterpret_cast<char*>(currentBlock) + sizeof(Block);
}
private:
template<typename T>
static inline char* alignFor(char* ptr)
{
const size_t alignment = std::alignment_of<T>::value;
return ptr + (alignment - (reinterpret_cast<uintptr_t>(ptr) % alignment)) % alignment;
}
template<typename T>
T* alloc()
{
char* objectLocation = alignFor<T>(currentBlock->startOfNextRecord);
char* nextRecordStart = alignFor<Record>(objectLocation + sizeof(T));
if (nextRecordStart + sizeof(Record) > currentBlock->rawBlock + currentBlock->size) {
createNewBlock(2 * std::max(currentBlock->size, sizeof(T) + sizeof(Record) + sizeof(Block) + 128));
objectLocation = alignFor<T>(currentBlock->startOfNextRecord);
nextRecordStart = alignFor<Record>(objectLocation + sizeof(T));
}
auto record = reinterpret_cast<Record*>(nextRecordStart);
record->dtor = &DtorWrapper<T>;
assert(objectLocation - currentBlock->startOfNextRecord < 32768);
record->objectOffset = static_cast<short>(objectLocation - currentBlock->startOfNextRecord);
assert(nextRecordStart - currentBlock->startOfNextRecord < 32768);
record->endOfPrevRecordOffset = static_cast<short>(nextRecordStart - currentBlock->startOfNextRecord);
currentBlock->startOfNextRecord = nextRecordStart + sizeof(Record);
return reinterpret_cast<T*>(objectLocation);
}
void createNewBlock(size_t newBlockSize)
{
auto raw = static_cast<char*>(std::malloc(newBlockSize));
auto blockStart = alignFor<Block>(raw);
auto newBlock = reinterpret_cast<Block*>(blockStart);
newBlock->rawBlock = raw;
newBlock->prevBlock = currentBlock;
newBlock->startOfNextRecord = blockStart + sizeof(Block);
newBlock->size = newBlockSize;
currentBlock = newBlock;
}
private:
Block* currentBlock;
};
To answer your question, you're not invoking undefined behaviour since nobody is using the pointer until the object is fully constructed (the pointer value itself is safe to copy around until then). However, it's a rather intrusive method, as the object(s) themselves need to know about the memory pool. Additionally, if you're constructing a large number of small objects, it would likely be faster to use an actual pool of memory (like my pool does) instead of calling out to new for every object.
Whatever pool-like approach you use, be careful that the objects are never manually deleteed, because that would lead to a double free!
I still think this is an interesting question without a definitive reply, but please let me break it down into the different questions you are actually asking:
1.) Does inserting a pointer to a base class into a vector before initialisation of a subclass prevent or cause issues with retrieving inherited classes from that pointer. [slicing for example.]
Answer: No, so long as you are 100% sure of the relevant type that is being pointed to, this mechanism does not cause these issues however note the following points:
If the derived constructor fails, you are left with an issue later when you are likely to have a dangling pointer at least sitting in the vector, as that address space it [the derived class] thought it was getting would be freed to the operating environment on failure, but the vector still has the address as being of the base class type.
Note that a vector, although kind of useful, is not the best structure for this, and even if it was, there should be some inversion of control involved here to allow the vector object to control initialisation of your objects, so that you have awareness of success/failure.
These points lead to the implied 2nd question:
2.) Is this a good pattern for pooling?
Answer: Not really, for the reasons mentioned above, plus others (Pushing a vector past it's end point basically ends up with a malloc which is unnecessary and will impact performance.) Ideally you want to use a pooling library, or a template class, and even better, separate the allocation/de-allocation policy implementation away from the pool implementation, with a low level solution already being hinted at, which is to allocate adequate pool memory from pool initialisation, and then use this using pointers to void from within the pool address space (See Alex Zywicki's solution above.) Using this pattern, the pool destruction is safe as the pool which will be contiguous memory can be destroyed en masse without any dangling issues, or memory leaks through losing all references to an object (losing all reference to an object whose address is allocated through the pool by the storage manager leaves you with dirty chunk/s, but will not cause a memory leak as it is managed by the pool implementation.
In the early days of C/C++ (before mass proliferation of the STL), this was a well discussed pattern and many implementations and designs can be found out there in good literature: As an example:
Knuth (1973 The art of computer programming: Multiple volumes), and for a more complete list, with more on pooling, see:
http://www.ibm.com/developerworks/library/l-memory/
The 3rd implied question seems to be:
3) Is this a valid scenario to use pooling?
Answer: This is a localised design decision based on what you are comfortable with, but to be honest, your implementation (no controlling structure/aggregate, possibly cyclic sharing of sub sets of objects) suggests to me that you would be better off with a basic linked list of wrapper objects, each of which contains a pointer to your superclass, used only for addressing purposes. Your cyclical structures are built on top of this, and you simply amend/grow shrink the list as required to accommodate all of your first class objects as required, and when finished, you can then easily destroy them in effectively an O(1) operation from within the linked list.
Having said that, I would personally recommend that at this time (when you have a scenario where pooling does have a use and so you are in the right mind-set) to carry out the building of a storage management/pooling set of classes that are paramaterised/typeless now as it will hold you in good stead for the future.
This sounds what I have heard called a Linear Allocator.
I will explain the basics of how I understand how it works.
Allocate a block of memory using ::operator new(size);
Have a void* that is your Pointer to the next free space in memory.
You will have an alloc(size_t size) function that will give you a pointer to the location in the block from step one for you to construct on to using Placement New
Placement new looks like... int* i = new(location)int(); where location is a void* to a block of memory you alloced from the allocator.
when you are done with all of your memory you will call a Flush() function that will dealloc the memory from the pool or at least wipe the data clean.
I programmed one of these recently and i will post my code here for you as well as do my best to explain.
#include <iostream>
class LinearAllocator:public ObjectBase
{
public:
LinearAllocator();
LinearAllocator(Pool* pool,size_t size);
~LinearAllocator();
void* Alloc(Size_t size);
void Flush();
private:
void** m_pBlock;
void* m_pHeadFree;
void* m_pEnd;
};
don't worry about what i'm inheriting from. i have been using this allocator in conjunction with a memory pool. but basically instead of getting the memory from operator new i am getting memory from a memory pool. the internal workings are the same essentially.
Here is the implementation:
LinearAllocator::LinearAllocator():ObjectBase::ObjectBase()
{
m_pBlock = nullptr;
m_pHeadFree = nullptr;
m_pEnd=nullptr;
}
LinearAllocator::LinearAllocator(Pool* pool,size_t size):ObjectBase::ObjectBase(pool)
{
if (pool!=nullptr) {
m_pBlock = ObjectBase::AllocFromPool(size);
m_pHeadFree = * m_pBlock;
m_pEnd = (void*)((unsigned char*)*m_pBlock+size);
}
else{
m_pBlock = nullptr;
m_pHeadFree = nullptr;
m_pEnd=nullptr;
}
}
LinearAllocator::~LinearAllocator()
{
if (m_pBlock!=nullptr) {
ObjectBase::FreeFromPool(m_pBlock);
}
m_pBlock = nullptr;
m_pHeadFree = nullptr;
m_pEnd=nullptr;
}
MemoryBlock* LinearAllocator::Alloc(size_t size)
{
if (m_pBlock!=nullptr) {
void* test = (void*)((unsigned char*)m_pEnd-size);
if (m_pHeadFree<=test) {
void* temp = m_pHeadFree;
m_pHeadFree=(void*)((unsigned char*)m_pHeadFree+size);
return temp;
}else{
return nullptr;
}
}else return nullptr;
}
void LinearAllocator::Flush()
{
if (m_pBlock!=nullptr) {
m_pHeadFree=m_pBlock;
size_t size = (unsigned char*)m_pEnd-(unsigned char*)*m_pBlock;
memset(*m_pBlock,0,size);
}
}
This code is fully functional except for a few lines which will need to be changed because of my inheritance and use of the memory pool. but I bet you can figure out what needs to change and just let me know if you need a hand changing the code. This code has not been tested in any sort of professional manor and is not guaranteed to be thread safe or anything fancy like that. i just whipped it up and thought i could share it with you since you seemed to need help.
I also have a working implementation of a fully generic memory pool if you think it may help you. I can explain how it works if you need.
Once again if you need any help let me know. Good luck.

C++ buffer pool?

I have a strong use case for pre-allocating all the memory I need upfront and releasing it upon completion.
I have came out with this real simple buffer pool C++ implementation which I have to test but I am not sure that the pointer arithmetic I am trying to use will allow me to do that. Basically the bit where I do next and release. I would prefer some trick around this idea and not relying on any sort of memory handler which just makes the client code more convoluted.
#include <stdio.h>
#include <queue>
#include "utils_mem.h"
using namespace std;
template <class T>
class tbufferpool {
private:
const int m_initial;
const int m_size;
const int m_total;
T* m_buffer;
vector<T*> m_queue;
public:
// constructor
tbufferpool(int initial, int size) : m_initial(initial), m_size(size), m_total(initial*size*sizeof(T)) {
m_buffer = (T*) malloc(m_total);
T* next_buffer = m_buffer;
for (int i=0; i < initial; ++i, next_buffer += i*size) {
m_queue.push_back(next_buffer);
}
}
// get next buffer element from the pool
T* next() {
// check for pool overflow
if (m_queue.empty()) {
printf("Illegal bufferpool state, our bufferpool has %d buffers only.", m_initial);
exit(EXIT_FAILURE);
}
T* next_buffer = m_queue.back();
m_queue.pop_back();
return next_buffer;
}
// release element, make it available back in the pool
void release(T* buffer) {
assert(m_buffer <= buffer && buffer < (buffer + m_total/sizeof(T)));
m_queue.push_back(buffer);
}
void ensure_size(int size) {
if (size >= m_size) {
printf("Illegal bufferpool state, maximum buffer size is %d.", m_size);
exit(EXIT_FAILURE);
}
}
// destructor
virtual ~tbufferpool() {
free(m_buffer);
}
};
First, when you increase a pointer to T, it will point the next element of T in the memory.
m_queue.push(m_buffer + (i*size*sizeof(T)));
This should be like
m_buffer = (T*) malloc(m_total);
T* next = m_buffer;
for (int i=0; i < initial; ++i) {
m_queue.push(next++);
}
Second,
assert(m_buffer <= buffer && buffer < m_total);
It should be like
assert(m_buffer <= buffer && buffer <= m_buffer + m_total/sizeof(T));
Hope it helps!
I don't understand why you're "wrapping" the STL queue<> container. Just put your "buffers" in the queue, and pull the addresses as you need them. When you're done with a "segment" in the buffer, just pop it off of the queue and it's released automatically. So instead of pointers to buffers, you just have the actual buffer classes.
It just strikes me as re-inventing the wheel. Now since you need the whole thing allocated at once, I'd use vector not queue, because the vector<> type can be allocated all at once on construction, and the push_back() method doesn't re-allocate unless it needs to, the same with pop_back(). See here for the methods used.
Basically, though, here's my back-of-the-envelope idea:
#include <myType.h> // Defines BufferType
const int NUMBUFFERS = 30;
int main()
{
vector<BufferType> myBuffers(NUMBUFFERS);
BufferType* segment = &(myBuffers[0]); // Gets first segment
myBuffers.pop_back(); // Reduces size by one
return 0;
}
I hope that gives you the general idea. You can just use the buffers in the vector that way, and there's only one allocation or de-allocation, and you can use stack-like logic if you wish. The dequeue type may also be worth looking at, or other standard containers, but if it's just "I only want one alloc or de-alloc" I'd just use vector, or even a smart pointer to an array possibly.
Some stuff I've found out using object pools:
I'm not sure about allocating all the objects at once. I like to descend all my pooled objects from a 'pooledObject' class that contains a private reference to its own pool, so allowing a simple, parameterless 'release' method and I'm always absolutely sure that an object is always released back to its own pool. I'm not sure how to load up every instance with the pool reference with a static array ctor - I've always constructed the objects one-by-one in a loop.
Another useful private member is an 'allocated' boolean, set when an object is depooled and cleared when released. This allows the pool class to detect and except immediately if an object is released twice. 'Released twice' errors can be insanely nasty if not immediately detected - weird behaviour or a crash happens minutes later and, often, in another thread in another module. Best to detect double-releases ASAP!
I find it useful and reassuring to dump the level of my pools to a status bar on a 1s timer. If a leak occurs, I can see it happening and, often, get an idea of where the leak is by the activity I'm on when a number drops alarmingly. Who needs Valgrind:)
On the subject of threads, if you have to make your pools thread-safe, it helps to use a blocking queue. If the pool runs out, threads trying to get objects can wait until they are released and the app just slows down instead of crashing/deadlocking. Also, be careful re. false sharing. You may have to use a 'filler' array data member to ensure that no two objects share a cache line.