2

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); } }; 
6
  • 1
    you forgot about pop_back()... Commented Apr 5, 2012 at 19:33
  • please, please please never call exit from a class. Throw an exception! Commented Apr 5, 2012 at 19:47
  • I know ... I am porting a huge C project into C++ and is a step by step process :) Commented Apr 5, 2012 at 19:53
  • @GiovanniAzua: Why? What's wrong with C? Commented Apr 5, 2012 at 20:41
  • no function overloading, cant have transparent memory pooling for matrices and vectors without making a hell of a code spaghetti, can't do Expression Templates for efficient vectorized code of math expressions along with operator overloading ... just to name a few. I think the C vs C++ depends very on the project but in this one is a must to switch. Commented Apr 5, 2012 at 20:50

3 Answers 3

1

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!

Sign up to request clarification or add additional context in comments.

Comments

1

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.

1 Comment

Same reason as usual, I want to abstract my client code away from all these low technical details on how to store, retrieve and keep consistent my pool and not scatter this all over the place. I need two pool instances one for matrices and one for vectors to start with ... I would not put a bunch of queue code in there but rather the bufferpool. Thanks for the hint with vector.
0

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.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.