I needed to speed up a piece of software that I maintain, so I wrote a memory pool library because I didn't like any of the ones that I found on the internet. All my tests check out, but I'll bet someone here can make it crash. Anyone see any bugs?
main.cpp
Code:
#include <iostream>
#include <map>
#include "mempool.h"
size_t failed = 0;
size_t passed = 0;
size_t ctor = 0;
size_t dtor = 0;
size_t mallocs = 0;
size_t frees = 0;
//test structure for checking that nothing overlaps
//tells me if everything constructed gets destructed
std::map<void *, bool> object_mapping;
//test to be sure of a condition
#define assertEquals(one, two) if (!((one) == (two))){ std::cout << #one << " != " << #two << ", " << #one << " == " << one << ", " << #two << " == " << two << std::endl; ++failed; } else { ++passed; }
#define assertEqualsO(one, two) if (!((one) == (two))){ std::cout << #one << " != " << #two << std::endl; ++failed; } else { ++passed; }
#define assertNotEqualsO(one, two) if (!((one) != (two))){ std::cout << #one << " == " << #two << std::endl; ++failed; } else { ++passed; }
#define assertZero(d) assertEquals(d, 0)
#ifdef __GNUC__
#pragma pack(push, 1)
#elif _MSC_VER
#pragma pack(push, testStruct_pack, 1)
#endif
struct testStruct {
public:
testStruct(void){
//make sure it's not already constructed
assertEqualsO(object_mapping.find(this), object_mapping.end());
object_mapping[this] = true;
++ctor;
}
testStruct(char _c, short _s, int _i, long _l) : c(_c), s(_s), i(_i), l(_l){
//make sure it's not already constructed
assertEqualsO(object_mapping.find(this), object_mapping.end());
object_mapping[this] = true;
++ctor;
}
~testStruct(void){
//make sure that it's been constructed
assertNotEqualsO(object_mapping.find(this), object_mapping.end());
object_mapping.erase(this);
++dtor;
c = 0; s = 0; i = 0; l = 0;
}
char c;
short s;
int i;
long l;
private:
testStruct & operator = (const testStruct & other);
testStruct(const testStruct & other);
};
#ifdef __GNUC__
#pragma pack(pop)
#elif _MSC_VER
#pragma pack(pop, testStruct_pack)
#endif
//tests the memory_pool object
static void test_memory_pool(bool announce = true){
if (announce){
std::cout << "Testing Memory Pool" << std::endl;
}
memory_pool<sizeof(testStruct), 10> pool;
testStruct * test = (testStruct*) pool.allocate();
test -> c = 1;
test -> s = 2;
test -> i = 3;
test -> l = 4;
testStruct * test2 = (testStruct*) pool.allocate();
test2 -> c = 21;
test2 -> s = 22;
test2 -> i = 23;
test2 -> l = 24;
assertEquals(test -> c, 1);
assertEquals(test -> s, 2);
assertEquals(test -> i, 3);
assertEquals(test -> l, 4);
assertEquals(test2 -> c, 21);
assertEquals(test2 -> s, 22);
assertEquals(test2 -> i, 23);
assertEquals(test2 -> l, 24);
for(int i = 0; i < 5; ++i){
testStruct * mems[15]; // more than what's in the pool
for(int j = 0; j < 15; ++j){
mems[j] = (testStruct*)pool.allocate();
mems[j] -> c = 31;
mems[j] -> s = 32;
mems[j] -> i = 33;
mems[j] -> l = 34;
assertEquals(test -> c, 1);
assertEquals(test -> s, 2);
assertEquals(test -> i, 3);
assertEquals(test -> l, 4);
assertEquals(test2 -> c, 21);
assertEquals(test2 -> s, 22);
assertEquals(test2 -> i, 23);
assertEquals(test2 -> l, 24);
}
assertEquals(pool.load(), 10);
for(int j = 0; j < 15; ++j){
pool.deallocate(mems[j]);
}
assertEquals(pool.load(), 2);
}
}
//tests the object_memory_pool
static void test_object_memory_pool(void){
std::cout << "Testing Object Memory Pool" << std::endl;
object_memory_pool<testStruct, 10> pool;
assertEquals(ctor, 0);
assertEquals(dtor, 0);
testStruct * test = new_object(pool, testStruct(1, 2, 3, 4));
assertEquals(test -> c, 1);
assertEquals(test -> s, 2);
assertEquals(test -> i, 3);
assertEquals(test -> l, 4);
assertEquals(ctor, 1);
assertEquals(dtor, 0);
testStruct * test2 = pool.allocate();
test2 -> c = 21;
test2 -> s = 22;
test2 -> i = 23;
test2 -> l = 24;
assertEquals(ctor, 2);
assertEquals(dtor, 0);
assertEquals(test -> c, 1);
assertEquals(test -> s, 2);
assertEquals(test -> i, 3);
assertEquals(test -> l, 4);
assertEquals(test2 -> c, 21);
assertEquals(test2 -> s, 22);
assertEquals(test2 -> i, 23);
assertEquals(test2 -> l, 24);
for(int i = 0; i < 5; ++i){
testStruct * mems[15]; // more than what's in the pool
for(int j = 0; j < 15; ++j){
mems[j] = pool.allocate();
mems[j] -> c = 31;
mems[j] -> s = 32;
mems[j] -> i = 33;
mems[j] -> l = 34;
assertEquals(test -> c, 1);
assertEquals(test -> s, 2);
assertEquals(test -> i, 3);
assertEquals(test -> l, 4);
assertEquals(test2 -> c, 21);
assertEquals(test2 -> s, 22);
assertEquals(test2 -> i, 23);
assertEquals(test2 -> l, 24);
}
assertEquals(pool.load(), 10);
for(int j = 0; j < 15; ++j){
pool.deallocate(mems[j]);
}
assertEquals(pool.load(), 2);
}
assertEquals(ctor, 77);
assertEquals(dtor, 75);
}
//tests the bucket_pools
static void test_bucket_pool(void){
std::cout << "Testing Bucket Memory Pool" << std::endl;
{
bucket_pool_3<4, 10, 8, 10, 64, 30> pool;
#ifdef MEMPOOL_DETERMINE_DISTRIBUTION
pool.profile_on_delete(50, "pool"); //50 is the longest string
#endif
char * buffer = (char*)pool.allocate(20);
memcpy(buffer, "I am nineteen chars", 20); //plus a null terminator
assertZero(strcmp(buffer, "I am nineteen chars"));
for(int i = 0; i < 5; ++i){
char * mems[50];
for(int j = 0; j < 50; ++j){
mems[j] = (char*)pool.allocate(j);
//just set it to something random
memcpy(mems[j], "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", j);
assertZero(strcmp(buffer, "I am nineteen chars"));
}
assertEquals(pool.load()[0], 5);
assertEquals(pool.load()[1], 4);
assertEquals(pool.load()[2], 30);
for(int j = 0; j < 50; ++j){
pool.deallocate(mems[j]);
}
assertEquals(pool.load()[0], 0);
assertEquals(pool.load()[1], 0);
assertEquals(pool.load()[2], 1);
}
}
{
bucket_pool_4<4, 10, 8, 10, 20, 30, 64, 10> pool;
#ifdef MEMPOOL_DETERMINE_DISTRIBUTION
pool.profile_on_delete(50, "normal distribution"); //50 is the max length
#endif
char * buffer = (char*)pool.allocate(20);
memcpy(buffer, "I am nineteen chars", 20); //plus a null terminator
assertZero(strcmp(buffer, "I am nineteen chars"));
for(int i = 0; i < 5; ++i){
char * mems[50];
for(int j = 0; j < 50; ++j){
//make a normal distribution between 0 and 50
size_t bytes = rand() % 50;
bytes += rand() % 50;
bytes += rand() % 50;
bytes /= 3;
mems[j] = (char*)pool.allocate(bytes);
//just set it to something random
memcpy(mems[j], "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", bytes);
assertZero(strcmp(buffer, "I am nineteen chars"));
}
for(int j = 0; j < 50; ++j){
pool.deallocate(mems[j]);
}
assertEquals(pool.load()[0], 0);
assertEquals(pool.load()[1], 0);
assertEquals(pool.load()[2], 1);
assertEquals(pool.load()[3], 0);
}
pool.deallocate(buffer);
assertEquals(pool.load()[0], 0);
assertEquals(pool.load()[1], 0);
assertEquals(pool.load()[2], 0);
assertEquals(pool.load()[3], 0);
}
}
static void * _malloc(size_t bytes){
++mallocs;
return malloc(bytes);
}
static void _free(void * ptr){
++frees;
free(ptr);
}
static void test_memory_callbacks(void){
std::cout << "Testing Callbacks" << std::endl;
mempool_callbacks::set(_malloc, _free);
test_memory_pool(false);
assertEquals(mallocs, 35);
assertEquals(frees, 35);
mempool_callbacks::set(malloc, free);
}
int main (int argc, const char * argv[]){
test_memory_pool();
test_object_memory_pool();
test_bucket_pool();
test_memory_callbacks();
std::cout << "Passed: " << passed << std::endl;
std::cout << "Failed: " << failed << std::endl;
//Put code to try and crash the library here
return 0;
}
mempool.h
Code:
#ifndef mempool___mempool_h
#define mempool___mempool_h
/*
* This is where you may alter options to give mempool++
*/
//#define MEMPOOL_DEBUGGING //Causes mempool++ to spit out what it's doing to the console
#define MEMPOOL_ASSERTS //Causes mempool++ to check what it's doing and look for impossible cases
#define MEMPOOL_OVERFLOW 3.0f / 4.0f //Changes how full a pool is before going to fallbacks
//#define MEMPOOL_DETERMINE_DISTRIBUTION //Allows mempool++ to automatically give you the best distribution
#define MEMPOOL_DETERMINE_SCALAR 5.0f / 3.0f //Gives you this times the max number at any given time from distribution
/*
* This is where special function / macro for special options are
*/
#ifdef MEMPOOL_DEBUGGING
#include <iostream>
#define MEMPOOL_DEBUG(x) std::cout << x << std::endl;
#else
#define MEMPOOL_DEBUG(x)
#endif
#ifdef MEMPOOL_ASSERTS
#include <iostream>
#define MEMPOOL_ASSERT(condition) if (pool_unlikely(!(condition))){ std::cout << #condition << " isn't true" << std::endl; }
#define MEMPOOL_ASSERT2(condition, out) if (pool_unlikely(!(condition))){ std::cout << out << std::endl; }
#else
#define MEMPOOL_ASSERT(condition)
#define MEMPOOL_ASSERT2(condition, out)
#endif
/*
* This is where compiler-specific code goes
*/
#ifdef __GNUC__
#if (__GNUC__ >= 3)
#define POOL_GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__)
#else
#define POOL_GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100)
#endif
#if (POOL_GCC_VERSION >= 40300)
#define pool_hot pool_nothrow __attribute__ ((hot))
#else
#define pool_hot pool_nothrow
#endif
#if (POOL_GCC_VERSION >= 29600)
#define pool_likely(x) __builtin_expect((long)((bool)(x)),1)
#define pool_unlikely(x) __builtin_expect((long)((bool)(x)),0)
#else
#define pool_likely(x) x
#define pool_unlikely(x) x
#endif
#define pool_nothrow throw()
#else
#define pool_hot pool_nothrow
#define pool_likely(x) x
#define pool_unlikely(x) x
#define pool_nothrow
#endif
#include <cstring>
/*
* This is where the classes are
*/
//Callbacks for fallback if the pool is out of space
class mempool_callbacks {
public:
typedef void * (*mallocer_t)(size_t);
typedef void(*freer_t)(void *);
//Allows the user to alter where the fallbacks point to
static inline void set(mallocer_t mallocer, freer_t freer) pool_nothrow {
get_instance()._malloc = mallocer;
get_instance()._free = freer;
}
//allocates memory
static inline void * allocate(size_t size) pool_nothrow {
MEMPOOL_DEBUG("Returing malloced memory:" << size << " bytes");
return get_instance()._malloc(size);
}
//frees memory
static inline void deallocate(void * ptr) pool_nothrow {
MEMPOOL_DEBUG("Freeing malloced memory: " << ptr);
get_instance()._free(ptr);
}
private:
//Retrieves a Meyers singleton
static inline mempool_callbacks & get_instance(void) pool_nothrow {
static mempool_callbacks _single(malloc, free);
return _single;
}
//The constructor
inline mempool_callbacks(mallocer_t mallocer, freer_t freer) :
_malloc(mallocer),
_free(freer){
}
//not copyable
mempool_callbacks & operator = (const mempool_callbacks & other);
mempool_callbacks(const mempool_callbacks & other);
//member callbacks
mallocer_t _malloc;
freer_t _free;
};
//The workhorse of the library, a class that holds a pool that it allocates memory from
template <typename T, size_t size>
class object_memory_pool; //forward declaration
template <size_t bytes, size_t size>
class memory_pool {
public:
memory_pool<bytes, size>():
current(0),
threshold((size_t)((float)size * (MEMPOOL_OVERFLOW))),
memoryPool_end(memoryPool_start + (size * bytes)),
used_end(used_start + size),
runningPointer(used_start)
{
memset(used_start, 0, size * sizeof(bool));
}
inline size_t load(void) const pool_nothrow {
return current;
}
inline void * allocate(void) pool_hot {
if (void * res = allocate_nofallback()){
return res;
}
return mempool_callbacks::allocate(bytes);
}
inline void deallocate(void * ptr) pool_hot {
//if it's not in the pool, free it from the fallbacks
if ((ptr < memoryPool_start) || (ptr >= memoryPool_end)){
mempool_callbacks::deallocate(ptr);
} else {
deallocate_nofallback(ptr);
}
}
inline bool contains(void * ptr) const pool_hot {
return ((ptr >= memoryPool_start) && (ptr < memoryPool_end));
}
void * allocate_nofallback() pool_hot {
if (current < threshold){
//pool is a queue that continually loops
for(const bool * position = runningPointer++; position != runningPointer; ++runningPointer){
if (runningPointer >= used_end) runningPointer = used_start;
if (!(*runningPointer)){
return _return_current();
}
}
MEMPOOL_ASSERT2(false, "Got to impossible code location");
}
//increament the pointer and loop it if necessary
if (++runningPointer >= used_end){
runningPointer = used_start;
}
//see if it's free
if (!(*runningPointer)){
MEMPOOL_DEBUG("Lucky catch Return");
return _return_current();
}
MEMPOOL_DEBUG("Returing null");
return NULL;
}
void deallocate_nofallback(void * ptr) pool_hot {
MEMPOOL_ASSERT2(current, "current not positive");
--current;
MEMPOOL_DEBUG("Freeing slot " << ((char*)ptr - memoryPool_start) / bytes);
MEMPOOL_DEBUG(" pointer=" << ptr);
MEMPOOL_ASSERT2((((char*)ptr - memoryPool_start) / bytes) < size, "Freeing slot " << (((char*)ptr - memoryPool_start) / bytes) << " in a pool with only " << size << " items");
MEMPOOL_ASSERT2(used_start[((char*)ptr - memoryPool_start) / bytes], "Freeing " << ptr << " and it's already been freed");
used_start[((char*)ptr - memoryPool_start) / bytes] = false;
}
private:
//copy ctors and assignment operator
memory_pool & operator = (const memory_pool & other);
memory_pool(const memory_pool & other);
inline void * _return_current(void) pool_hot {
*runningPointer = true;
++current;
MEMPOOL_DEBUG("Returning slot " << runningPointer - used_start);
MEMPOOL_DEBUG(" memoryPool_start=" << (void*)memoryPool_start);
MEMPOOL_DEBUG(" memoryPool_end =" << (void*)memoryPool_end);
MEMPOOL_DEBUG(" return value =" << (void*)(memoryPool_start + ((runningPointer - used_start) * bytes)));
MEMPOOL_ASSERT2(((memoryPool_start + ((runningPointer - used_start) * bytes))) < memoryPool_end, "Returning pointer outside the high end of the pool");
MEMPOOL_ASSERT2(((memoryPool_start + ((runningPointer - used_start) * bytes))) >= memoryPool_start, "Returning pointer outside the low end of the pool");
return memoryPool_start + ((runningPointer - used_start) * bytes);
}
size_t current; //The current number of items in the pool
size_t threshold; //The number of items in the pool before it starts using fallback
char memoryPool_start[size * bytes]; //The memory pool
char * memoryPool_end; //The end of the memory pool
bool used_start[size]; //A pool to know whether or not an item is currently used
bool * used_end; //The end of the boolean flags
bool * runningPointer; //A pointer that loops, keeping an eye on what is taken and what isnt
template <typename T, size_t s> friend class object_memory_pool;
};
//A memory pool for a specific type of object
#define new_object(pool, ctor) new (pool.allocate_noctor()) ctor //allows user to call a specific ctor on the object [ new_object(mypool, T(x, y)) ]
template <typename T, size_t size>
class object_memory_pool {
public:
inline size_t load(void) const pool_nothrow {
return _pool.load();
}
virtual ~object_memory_pool() pool_nothrow { } //so that it can be overloaded
inline T * allocate(void) pool_hot { return new (_pool.allocate()) T(); }
inline void * allocate_noctor(void) pool_nothrow { return _pool.allocate(); }
inline void deallocate(T * ptr) pool_hot {
ptr -> ~T();
_pool.deallocate(ptr);
}
inline bool contains(T * ptr) const pool_hot {
return _pool.contains((void*)ptr);
}
inline T * alloc_nofallback() pool_hot {
if (void * res = _pool.allocate_nofallback()){
return new (res) T();
}
return NULL;
}
inline void deallocate_nofallback(T * ptr) pool_hot {
ptr -> ~T();
_pool.deallocate_nofallback(ptr);
}
private:
memory_pool<sizeof(T), size> _pool;
};
#define MEMPOOL_TEMPLATE_PAIR(x) size_t bytes ## x , size_t count ## x
#define MEMPOOL_ALLOC_CHECK(x) if (bytes <= bytes ## x ){ if (void * res = (_pool ## x).allocate_nofallback()) return res; }
#define MEMPOOL_DEALLOC_CHECK(x) if ((_pool ## x).contains(ptr)){ (_pool ## x).deallocate_nofallback(ptr); return; }
#define MEMPOOL_MEMBER_POOL(x) memory_pool< bytes ## x , count ## x > _pool ## x;
#ifdef MEMPOOL_DETERMINE_DISTRIBUTION
#include <map>
#include <iostream>
#define MEMPOOL_ALLOC_METHOD(number, code)\
bucket_pool_ ## number (void) : _profile_on_delete(0) { }\
~bucket_pool_ ## number (void) { \
if (_profile_on_delete){ \
dump_atonce(_profile_on_delete, _profile_on_delete / 40); \
dump_template(_profile_on_delete); \
} \
}\
void * allocate(size_t bytes) pool_hot {\
if (mapping.find(bytes) != mapping.end()){\
++mapping[bytes];\
++current_mapping[bytes];\
if (current_mapping[bytes] > max_mapping[bytes]) max_mapping[bytes] = current_mapping[bytes];\
} else {\
mapping[bytes] = 1;\
max_mapping[bytes] = 1;\
current_mapping[bytes] = 1;\
}\
void * res = mempool_callbacks::allocate(bytes);\
mem_mapping[res] = bytes;\
return res;\
}
#define MEMPOOL_DEALLOC_METHOD(code)\
void deallocate(void * ptr) pool_hot {\
--current_mapping[mem_mapping[ptr]];\
mem_mapping.erase(ptr);\
mempool_callbacks::deallocate(ptr);\
}
#define MEMPOOL_ANALYZERS(macro_count)\
inline size_t _max(size_t one, size_t two){ return (one > two) ? one : two; }\
void dump_total(size_t max, size_t sep = 16, size_t tlen = 30){\
std::cout << "-------- Total --------" << std::endl;\
size_t max_amount = 0;\
for(size_t i = 0; i < max;){\
size_t amount = 0;\
for(size_t j = 0; j < sep; ++j, ++i){\
if (mapping.find(i) != mapping.end()){\
amount += mapping[i];\
}\
}\
if (amount > max_amount) max_amount = amount;\
}\
float scalar = ((float)max_amount) / ((float)tlen);\
\
for(size_t i = 0; i < max;){\
size_t amount = 0;\
for(size_t j = 0; j < sep; ++j, ++i){\
if (mapping.find(i) != mapping.end()){\
amount += mapping[i];\
}\
}\
\
if (i < 10) std::cout << ' ';\
if (i < 100) std::cout << ' ';\
if (i < 1000) std::cout << ' ';\
if (i < 10000) std::cout << ' ';\
std::cout << i << ':';\
\
for(size_t j = 0; j < (size_t)((float)amount / scalar); ++j){\
std::cout << '*';\
}\
std::cout << '(' << amount << ')' << std::endl;\
}\
}\
\
void dump_atonce(size_t max, size_t sep = 16, size_t tlen = 30){\
std::cout << "------ Distribution for \"" << _str << "\" ------" << std::endl;\
size_t max_amount = 0;\
for(size_t i = 0; i < max;){\
size_t amount = 0;\
for(size_t j = 0; j < sep; ++j, ++i){\
if (max_mapping.find(i) != max_mapping.end()){\
amount += max_mapping[i];\
}\
}\
if (amount > max_amount) max_amount = amount;\
}\
float scalar = ((float)max_amount) / ((float)tlen);\
\
for(size_t i = 0; i < max;){\
size_t amount = 0;\
for(size_t j = 0; j < sep; ++j, ++i){\
if (max_mapping.find(i) != max_mapping.end()){\
amount += max_mapping[i];\
}\
}\
\
if (i < 10) std::cout << ' ';\
if (i < 100) std::cout << ' ';\
if (i < 1000) std::cout << ' ';\
if (i < 10000) std::cout << ' ';\
std::cout << i << ':';\
\
for(size_t j = 0; j < (size_t)((float)amount / scalar); ++j){\
std::cout << '*';\
}\
std::cout << '(' << amount << ')' << std::endl;\
}\
}\
\
void dump_template(size_t max){\
std::cout << "Recommended Template for \"" << _str << "\" = ";\
size_t total_at_once = 0;\
size_t highest = 0;\
for(size_t i = 0; i < max; ++i){\
if (max_mapping.find(i) != max_mapping.end()){\
total_at_once += max_mapping[i];\
highest = i;\
}\
}\
\
size_t count = 0;\
size_t total_at_once_part = total_at_once / macro_count;\
size_t current = 0;\
size_t totalsofar = 0;\
std::cout << '<';\
for(size_t i = 0; ((i < max) && (count < (macro_count -1))); ++i){\
if (max_mapping.find(i) != max_mapping.end()){\
current += max_mapping[i];\
totalsofar += max_mapping[i];\
if (current > total_at_once_part){\
std::cout << (i - 1) << ", " << (size_t)(((float)current - max_mapping[i]) * (MEMPOOL_DETERMINE_SCALAR)) << ", ";\
current = max_mapping[i];\
++count;\
}\
}\
}\
std::cout << max << ", " << _max((size_t)((float)(total_at_once - totalsofar) * (MEMPOOL_DETERMINE_SCALAR)), total_at_once_part / 2) << '>' << std::endl;\
}\
\
inline void profile_on_delete(size_t var, const std::string & str){ _profile_on_delete = var; _str = str; }
#define MEMPOOL_MEMBERS(code)\
std::map<size_t, size_t> mapping;\
std::map<size_t, size_t> current_mapping;\
std::map<size_t, size_t> max_mapping;\
std::map<void *, size_t> mem_mapping;\
size_t _profile_on_delete;\
std::string _str;
#define MEMPOOL_LOAD(number, code) inline size_t * load(void) const pool_nothrow { static size_t _load[number]; return &_load[0]; }
#else
#define MEMPOOL_ALLOC_METHOD(number, code)\
void * allocate(size_t bytes) pool_hot {\
code\
return mempool_callbacks::allocate(bytes);\
}
#define MEMPOOL_DEALLOC_METHOD(code)\
void deallocate(void * ptr) pool_hot {\
code\
mempool_callbacks::deallocate(ptr);\
}
#define MEMPOOL_ANALYZERS(macro_count)
#define MEMPOOL_MEMBERS(code) code
#define MEMPOOL_LOAD(number, code) inline size_t * load(void) const pool_nothrow { static size_t _load[number]; code return &_load[0]; }
#endif
template<
MEMPOOL_TEMPLATE_PAIR(1),
MEMPOOL_TEMPLATE_PAIR(2)>
class bucket_pool_2 {
public:
MEMPOOL_ALLOC_METHOD(
2,
MEMPOOL_ALLOC_CHECK(1)
MEMPOOL_ALLOC_CHECK(2)
)
MEMPOOL_LOAD(
2,
_load[0] = _pool1.load();
_load[1] = _pool2.load();
)
MEMPOOL_DEALLOC_METHOD(
MEMPOOL_DEALLOC_CHECK(1)
MEMPOOL_DEALLOC_CHECK(2)
)
MEMPOOL_ANALYZERS(2)
private:
MEMPOOL_MEMBERS(
MEMPOOL_MEMBER_POOL(1)
MEMPOOL_MEMBER_POOL(2)
)
};
template<
MEMPOOL_TEMPLATE_PAIR(1),
MEMPOOL_TEMPLATE_PAIR(2),
MEMPOOL_TEMPLATE_PAIR(3)>
class bucket_pool_3 {
public:
MEMPOOL_ALLOC_METHOD(
3,
MEMPOOL_ALLOC_CHECK(1)
MEMPOOL_ALLOC_CHECK(2)
MEMPOOL_ALLOC_CHECK(3)
)
MEMPOOL_LOAD(
3,
_load[0] = _pool1.load();
_load[1] = _pool2.load();
_load[2] = _pool3.load();
)
MEMPOOL_DEALLOC_METHOD(
MEMPOOL_DEALLOC_CHECK(1)
MEMPOOL_DEALLOC_CHECK(2)
MEMPOOL_DEALLOC_CHECK(3)
)
MEMPOOL_ANALYZERS(3)
private:
MEMPOOL_MEMBERS(
MEMPOOL_MEMBER_POOL(1)
MEMPOOL_MEMBER_POOL(2)
MEMPOOL_MEMBER_POOL(3)
)
};
template<
MEMPOOL_TEMPLATE_PAIR(1),
MEMPOOL_TEMPLATE_PAIR(2),
MEMPOOL_TEMPLATE_PAIR(3),
MEMPOOL_TEMPLATE_PAIR(4)>
class bucket_pool_4 {
public:
MEMPOOL_ALLOC_METHOD(
4,
MEMPOOL_ALLOC_CHECK(1)
MEMPOOL_ALLOC_CHECK(2)
MEMPOOL_ALLOC_CHECK(3)
MEMPOOL_ALLOC_CHECK(4)
)
MEMPOOL_LOAD(
4,
_load[0] = _pool1.load();
_load[1] = _pool2.load();
_load[2] = _pool3.load();
_load[3] = _pool4.load();
)
MEMPOOL_DEALLOC_METHOD(
MEMPOOL_DEALLOC_CHECK(1)
MEMPOOL_DEALLOC_CHECK(2)
MEMPOOL_DEALLOC_CHECK(3)
MEMPOOL_DEALLOC_CHECK(4)
)
MEMPOOL_ANALYZERS(4)
private:
MEMPOOL_MEMBERS(
MEMPOOL_MEMBER_POOL(1)
MEMPOOL_MEMBER_POOL(2)
MEMPOOL_MEMBER_POOL(3)
MEMPOOL_MEMBER_POOL(4)
)
};
template<
MEMPOOL_TEMPLATE_PAIR(1),
MEMPOOL_TEMPLATE_PAIR(2),
MEMPOOL_TEMPLATE_PAIR(3),
MEMPOOL_TEMPLATE_PAIR(4),
MEMPOOL_TEMPLATE_PAIR(5)>
class bucket_pool_5 {
public:
MEMPOOL_ALLOC_METHOD(
5,
MEMPOOL_ALLOC_CHECK(1)
MEMPOOL_ALLOC_CHECK(2)
MEMPOOL_ALLOC_CHECK(3)
MEMPOOL_ALLOC_CHECK(4)
MEMPOOL_ALLOC_CHECK(5)
)
MEMPOOL_LOAD(
5,
_load[0] = _pool1.load();
_load[1] = _pool2.load();
_load[2] = _pool3.load();
_load[3] = _pool4.load();
_load[4] = _pool5.load();
)
MEMPOOL_DEALLOC_METHOD(
MEMPOOL_DEALLOC_CHECK(1)
MEMPOOL_DEALLOC_CHECK(2)
MEMPOOL_DEALLOC_CHECK(3)
MEMPOOL_DEALLOC_CHECK(4)
MEMPOOL_DEALLOC_CHECK(5)
)
MEMPOOL_ANALYZERS(5)
private:
MEMPOOL_MEMBERS(
MEMPOOL_MEMBER_POOL(1)
MEMPOOL_MEMBER_POOL(2)
MEMPOOL_MEMBER_POOL(3)
MEMPOOL_MEMBER_POOL(4)
MEMPOOL_MEMBER_POOL(5)
)
};
template<
MEMPOOL_TEMPLATE_PAIR(1),
MEMPOOL_TEMPLATE_PAIR(2),
MEMPOOL_TEMPLATE_PAIR(3),
MEMPOOL_TEMPLATE_PAIR(4),
MEMPOOL_TEMPLATE_PAIR(5),
MEMPOOL_TEMPLATE_PAIR(6)>
class bucket_pool_6 {
public:
MEMPOOL_ALLOC_METHOD(
6,
MEMPOOL_ALLOC_CHECK(1)
MEMPOOL_ALLOC_CHECK(2)
MEMPOOL_ALLOC_CHECK(3)
MEMPOOL_ALLOC_CHECK(4)
MEMPOOL_ALLOC_CHECK(5)
MEMPOOL_ALLOC_CHECK(6)
)
MEMPOOL_LOAD(
6,
_load[0] = _pool1.load();
_load[1] = _pool2.load();
_load[2] = _pool3.load();
_load[3] = _pool4.load();
_load[4] = _pool5.load();
_load[5] = _pool6.load();
)
MEMPOOL_DEALLOC_METHOD(
MEMPOOL_DEALLOC_CHECK(1)
MEMPOOL_DEALLOC_CHECK(2)
MEMPOOL_DEALLOC_CHECK(3)
MEMPOOL_DEALLOC_CHECK(4)
MEMPOOL_DEALLOC_CHECK(5)
MEMPOOL_DEALLOC_CHECK(6)
)
MEMPOOL_ANALYZERS(6)
private:
MEMPOOL_MEMBERS(
MEMPOOL_MEMBER_POOL(1)
MEMPOOL_MEMBER_POOL(2)
MEMPOOL_MEMBER_POOL(3)
MEMPOOL_MEMBER_POOL(4)
MEMPOOL_MEMBER_POOL(5)
MEMPOOL_MEMBER_POOL(6)
)
};
template<
MEMPOOL_TEMPLATE_PAIR(1),
MEMPOOL_TEMPLATE_PAIR(2),
MEMPOOL_TEMPLATE_PAIR(3),
MEMPOOL_TEMPLATE_PAIR(4),
MEMPOOL_TEMPLATE_PAIR(5),
MEMPOOL_TEMPLATE_PAIR(6),
MEMPOOL_TEMPLATE_PAIR(7)>
class bucket_pool_7 {
public:
MEMPOOL_ALLOC_METHOD(
7,
MEMPOOL_ALLOC_CHECK(1)
MEMPOOL_ALLOC_CHECK(2)
MEMPOOL_ALLOC_CHECK(3)
MEMPOOL_ALLOC_CHECK(4)
MEMPOOL_ALLOC_CHECK(5)
MEMPOOL_ALLOC_CHECK(6)
MEMPOOL_ALLOC_CHECK(7)
)
MEMPOOL_LOAD(
7,
_load[0] = _pool1.load();
_load[1] = _pool2.load();
_load[2] = _pool3.load();
_load[3] = _pool4.load();
_load[4] = _pool5.load();
_load[5] = _pool6.load();
_load[6] = _pool7.load();
)
MEMPOOL_DEALLOC_METHOD(
MEMPOOL_DEALLOC_CHECK(1)
MEMPOOL_DEALLOC_CHECK(2)
MEMPOOL_DEALLOC_CHECK(3)
MEMPOOL_DEALLOC_CHECK(4)
MEMPOOL_DEALLOC_CHECK(5)
MEMPOOL_DEALLOC_CHECK(6)
MEMPOOL_DEALLOC_CHECK(7)
)
MEMPOOL_ANALYZERS(7)
private:
MEMPOOL_MEMBERS(
MEMPOOL_MEMBER_POOL(1)
MEMPOOL_MEMBER_POOL(2)
MEMPOOL_MEMBER_POOL(3)
MEMPOOL_MEMBER_POOL(4)
MEMPOOL_MEMBER_POOL(5)
MEMPOOL_MEMBER_POOL(6)
MEMPOOL_MEMBER_POOL(7)
)
};
template<
MEMPOOL_TEMPLATE_PAIR(1),
MEMPOOL_TEMPLATE_PAIR(2),
MEMPOOL_TEMPLATE_PAIR(3),
MEMPOOL_TEMPLATE_PAIR(4),
MEMPOOL_TEMPLATE_PAIR(5),
MEMPOOL_TEMPLATE_PAIR(6),
MEMPOOL_TEMPLATE_PAIR(7),
MEMPOOL_TEMPLATE_PAIR(8)>
class bucket_pool_8 {
public:
MEMPOOL_ALLOC_METHOD(
8,
MEMPOOL_ALLOC_CHECK(1)
MEMPOOL_ALLOC_CHECK(2)
MEMPOOL_ALLOC_CHECK(3)
MEMPOOL_ALLOC_CHECK(4)
MEMPOOL_ALLOC_CHECK(5)
MEMPOOL_ALLOC_CHECK(6)
MEMPOOL_ALLOC_CHECK(7)
MEMPOOL_ALLOC_CHECK(8)
)
MEMPOOL_LOAD(
8,
_load[0] = _pool1.load();
_load[1] = _pool2.load();
_load[2] = _pool3.load();
_load[3] = _pool4.load();
_load[4] = _pool5.load();
_load[5] = _pool6.load();
_load[6] = _pool7.load();
_load[7] = _pool8.load();
)
MEMPOOL_DEALLOC_METHOD(
MEMPOOL_DEALLOC_CHECK(1)
MEMPOOL_DEALLOC_CHECK(2)
MEMPOOL_DEALLOC_CHECK(3)
MEMPOOL_DEALLOC_CHECK(4)
MEMPOOL_DEALLOC_CHECK(5)
MEMPOOL_DEALLOC_CHECK(6)
MEMPOOL_DEALLOC_CHECK(7)
MEMPOOL_DEALLOC_CHECK(8)
)
MEMPOOL_ANALYZERS(8)
private:
MEMPOOL_MEMBERS(
MEMPOOL_MEMBER_POOL(1)
MEMPOOL_MEMBER_POOL(2)
MEMPOOL_MEMBER_POOL(3)
MEMPOOL_MEMBER_POOL(4)
MEMPOOL_MEMBER_POOL(5)
MEMPOOL_MEMBER_POOL(6)
MEMPOOL_MEMBER_POOL(7)
MEMPOOL_MEMBER_POOL(8)
)
};
template<
MEMPOOL_TEMPLATE_PAIR(1),
MEMPOOL_TEMPLATE_PAIR(2),
MEMPOOL_TEMPLATE_PAIR(3),
MEMPOOL_TEMPLATE_PAIR(4),
MEMPOOL_TEMPLATE_PAIR(5),
MEMPOOL_TEMPLATE_PAIR(6),
MEMPOOL_TEMPLATE_PAIR(7),
MEMPOOL_TEMPLATE_PAIR(8),
MEMPOOL_TEMPLATE_PAIR(9)>
class bucket_pool_9 {
public:
MEMPOOL_ALLOC_METHOD(
9,
MEMPOOL_ALLOC_CHECK(1)
MEMPOOL_ALLOC_CHECK(2)
MEMPOOL_ALLOC_CHECK(3)
MEMPOOL_ALLOC_CHECK(4)
MEMPOOL_ALLOC_CHECK(5)
MEMPOOL_ALLOC_CHECK(6)
MEMPOOL_ALLOC_CHECK(7)
MEMPOOL_ALLOC_CHECK(8)
MEMPOOL_ALLOC_CHECK(9)
)
MEMPOOL_LOAD(
9,
_load[0] = _pool1.load();
_load[1] = _pool2.load();
_load[2] = _pool3.load();
_load[3] = _pool4.load();
_load[4] = _pool5.load();
_load[5] = _pool6.load();
_load[6] = _pool7.load();
_load[7] = _pool8.load();
_load[8] = _pool9.load();
)
MEMPOOL_DEALLOC_METHOD(
MEMPOOL_DEALLOC_CHECK(1)
MEMPOOL_DEALLOC_CHECK(2)
MEMPOOL_DEALLOC_CHECK(3)
MEMPOOL_DEALLOC_CHECK(4)
MEMPOOL_DEALLOC_CHECK(5)
MEMPOOL_DEALLOC_CHECK(6)
MEMPOOL_DEALLOC_CHECK(7)
MEMPOOL_DEALLOC_CHECK(8)
MEMPOOL_DEALLOC_CHECK(9)
)
MEMPOOL_ANALYZERS(9)
private:
MEMPOOL_MEMBERS(
MEMPOOL_MEMBER_POOL(1)
MEMPOOL_MEMBER_POOL(2)
MEMPOOL_MEMBER_POOL(3)
MEMPOOL_MEMBER_POOL(4)
MEMPOOL_MEMBER_POOL(5)
MEMPOOL_MEMBER_POOL(6)
MEMPOOL_MEMBER_POOL(7)
MEMPOOL_MEMBER_POOL(8)
MEMPOOL_MEMBER_POOL(9)
)
};
template<
MEMPOOL_TEMPLATE_PAIR(1),
MEMPOOL_TEMPLATE_PAIR(2),
MEMPOOL_TEMPLATE_PAIR(3),
MEMPOOL_TEMPLATE_PAIR(4),
MEMPOOL_TEMPLATE_PAIR(5),
MEMPOOL_TEMPLATE_PAIR(6),
MEMPOOL_TEMPLATE_PAIR(7),
MEMPOOL_TEMPLATE_PAIR(8),
MEMPOOL_TEMPLATE_PAIR(9),
MEMPOOL_TEMPLATE_PAIR(10)>
class bucket_pool_10 {
public:
MEMPOOL_ALLOC_METHOD(
10,
MEMPOOL_ALLOC_CHECK(1)
MEMPOOL_ALLOC_CHECK(2)
MEMPOOL_ALLOC_CHECK(3)
MEMPOOL_ALLOC_CHECK(4)
MEMPOOL_ALLOC_CHECK(5)
MEMPOOL_ALLOC_CHECK(6)
MEMPOOL_ALLOC_CHECK(7)
MEMPOOL_ALLOC_CHECK(8)
MEMPOOL_ALLOC_CHECK(9)
MEMPOOL_ALLOC_CHECK(10)
)
MEMPOOL_LOAD(
10,
_load[0] = _pool1.load();
_load[1] = _pool2.load();
_load[2] = _pool3.load();
_load[3] = _pool4.load();
_load[4] = _pool5.load();
_load[5] = _pool6.load();
_load[6] = _pool7.load();
_load[7] = _pool8.load();
_load[8] = _pool9.load();
_load[9] = _pool10.load();
)
MEMPOOL_DEALLOC_METHOD(
MEMPOOL_DEALLOC_CHECK(1)
MEMPOOL_DEALLOC_CHECK(2)
MEMPOOL_DEALLOC_CHECK(3)
MEMPOOL_DEALLOC_CHECK(4)
MEMPOOL_DEALLOC_CHECK(5)
MEMPOOL_DEALLOC_CHECK(6)
MEMPOOL_DEALLOC_CHECK(7)
MEMPOOL_DEALLOC_CHECK(8)
MEMPOOL_DEALLOC_CHECK(9)
MEMPOOL_DEALLOC_CHECK(10)
)
MEMPOOL_ANALYZERS(10)
private:
MEMPOOL_MEMBERS(
MEMPOOL_MEMBER_POOL(1)
MEMPOOL_MEMBER_POOL(2)
MEMPOOL_MEMBER_POOL(3)
MEMPOOL_MEMBER_POOL(4)
MEMPOOL_MEMBER_POOL(5)
MEMPOOL_MEMBER_POOL(6)
MEMPOOL_MEMBER_POOL(7)
MEMPOOL_MEMBER_POOL(8)
MEMPOOL_MEMBER_POOL(9)
MEMPOOL_MEMBER_POOL(10)
)
};
#endif
README.txt
Code:
=======================================================================
| Index
=======================================================================
About Memory Pools
Memory Pool Classes
memory_pool
object_memory_pool
bucket_memory_pool
determining distribution
mempool_callbacks
License
=======================================================================
| About Memory Pools
=======================================================================
mempool++ is a header-only library that contains several classes for optimizing a program using
memory pools. Memory pools are chunks of data that have been preallocated, and can be quickly
used to store data, since malloc/free and new/delete require operating system calls, which are
slow in comparison to just flagging preallocated memory.
=======================================================================
| Memory Pool Classes
=======================================================================
------------------------------------------
| memory_pool
------------------------------------------
memory_pool<bytes, size> - Is a memory pool for blocks of a specific size. If you constantly
allocate data that is always the same size, then this type is what you want. The bytes
template tells the library how big each block that gets returned should be. The size
parameter tells it haw large to make the memory pool. The pool will not grow larger
than this, if it gets full, it will start dynamically allocating memory.
memory_pool<16, 50> pool(); //creates a pool with 50 blocks, 16 bytes each
void * block = pool.allocate(); //returns a valid block of 16 free bytes
pool.deallocate(block); //returns the block to the pool
------------------------------------------
| Interface
------------------------------------------
void * allocate();
deallocate(void *);
------------------------------------------
| object_memory_pool
------------------------------------------
object_memory_pool<type, size> - Is a memory pool for a specific type of object.
This allows you to have constructors called, so that you don't have to worry about
doing that yourself.
object_memory_pool<string, 50> pool(); //creates a pool with room for 50 strings
string * test = pool.allocate(); //returns a valid string
pool.deallocate(test); //returns the block to the pool
test = new_object(pool, string("hi")); //returns a valid string, after calling const char * ctor
pool.deallocate(test); //returns the block to the pool
------------------------------------------
| Interface
------------------------------------------
Type * allocate();
deallocate(Type *);
Type * new_object(object_memory_pool &, ctor); //Actually a macro
------------------------------------------
| bucket_pool_*
------------------------------------------
bucket_pool_*<bytes, size, …> - These are flexible memory pools that hold a range of sizes. These are
especially useful for dynamically allocating strings, since you aren't really sure what
size the strings we be at compile time. This type of memory pool contains several other
memory_pool objects of various sizes and distributions. You can have mempool++
recommend some template parameters by running it with some common data with the
MEMPOOL_DETERMINE_DISTRIBUTION macro defined. There are also predefined versions of this
bucket_pool_3< //creates a bucket pool with 3 buckets
8, 10, //10 blocks of 8 bytes each
16, 20, //20 blocks of 16 bytes each
64, 20> pool(); //20 blocks of 64 bytes each
#ifdef MEMPOOL_DETERMINE_DISTRIBUTION
pool.profile_on_delete(64, "mypool"); //64 is the longest string, mypool is the identifier
#endif
char * str = (char*)pool.allocate(12); //returns a block of at least 12 chars (will be 16)
char * str2 = (char*)pool.allocate(90); //will return a block of at least 90 chars
char * str3 = (char*)pool.allocate(7); //will return a block of at least 7 chars (will be 8)
pool.deallocate(str);
pool.deallocate(str2);
pool.deallocate(str3);
------------------------------------------
| Interface
------------------------------------------
void * allocate(size_t);
deallocate(void *);
profile_on_delete(size_t, const std::string &); //Only if MEMPOOL_DETERMINE_DISTRIBUTION is defined
------------------------------------------
| Determining distribution
------------------------------------------
bucket_pools are hard to determine what the best setup for them is, so this library includes
a simple tool to figure it out for you. On like 13 of mempool.h there is a commented line:
//#define MEMPOOL_DETERMINE_DISTRIBUTION
Uncomment that. It will turn all pooling off for the time being, but the pools will still work, they are
simply always going to the fallback. Now find the declaration of the pool that you want to optimize and
place a profile_on_delete call after it. This will tell the pool to print out it's recommendation
when it goes out of scope. Run it on an average set of data, then replace the template with what gets
printed out and an optimized bucket pool is ready to go.
------------------------------------------
| mempool_callbacks
------------------------------------------
mempool_callbacks - This is a singleton class that holds pointers to fallback functions. The
pools do not grow if they run out of space, they simply start dynamically allocating
memory, this class holds pointers to user-defined functions that are to be used in that
case, if they are not set, then malloc and free are used.
mempool_callbacks::set(malloc, free);
=======================================================================
| License
=======================================================================
Copyright 2011 [email protected]. All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are
permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of
conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list
of conditions and the following disclaimer in the documentation and/or other materials
provided with the distribution.
THIS SOFTWARE IS PROVIDED BY [email protected] ``AS IS'' AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL [email protected]
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
The views and conclusions contained in the software and documentation are those of the authors
and should not be interpreted as representing official policies, either expressed or implied,
of [email protected].
|
|
Bookmarks