1
0
Fork 0
allocatortest
Duncan Ogilvie 2019-08-19 15:38:42 +02:00
parent f57d69f91e
commit 208cee69eb
22 changed files with 2379 additions and 0 deletions

179
src/dbg/Allocator.h 100644
View File

@ -0,0 +1,179 @@
#ifndef AllocatorH
#define AllocatorH
#include <memory>
namespace Moya
{
template <class T, std::size_t growSize = 1024>
class MemoryPool
{
struct Block
{
Block* next;
};
class Buffer
{
static const std::size_t blockSize = sizeof(T) > sizeof(Block) ? sizeof(T) : sizeof(Block);
uint8_t data[blockSize * growSize];
public:
Buffer* const next;
Buffer(Buffer* next) :
next(next)
{
}
T* getBlock(std::size_t index)
{
return reinterpret_cast<T*>(&data[blockSize * index]);
}
};
Block* firstFreeBlock = nullptr;
Buffer* firstBuffer = nullptr;
std::size_t bufferedBlocks = growSize;
public:
MemoryPool() = default;
MemoryPool(MemoryPool && memoryPool) = delete;
MemoryPool(const MemoryPool & memoryPool) = delete;
MemoryPool operator =(MemoryPool && memoryPool) = delete;
MemoryPool operator =(const MemoryPool & memoryPool) = delete;
~MemoryPool()
{
while(firstBuffer)
{
Buffer* buffer = firstBuffer;
firstBuffer = buffer->next;
delete buffer;
}
}
T* allocate()
{
if(firstFreeBlock)
{
Block* block = firstFreeBlock;
firstFreeBlock = block->next;
return reinterpret_cast<T*>(block);
}
if(bufferedBlocks >= growSize)
{
firstBuffer = new Buffer(firstBuffer);
bufferedBlocks = 0;
}
return firstBuffer->getBlock(bufferedBlocks++);
}
void deallocate(T* pointer)
{
Block* block = reinterpret_cast<Block*>(pointer);
block->next = firstFreeBlock;
firstFreeBlock = block;
}
};
template <class T, std::size_t growSize = 1024>
class Allocator : private MemoryPool<T, growSize>
{
#ifdef _WIN32
Allocator* copyAllocator;
std::allocator<T>* rebindAllocator = nullptr;
#endif
public:
typedef std::size_t size_type;
typedef std::ptrdiff_t difference_type;
typedef T* pointer;
typedef const T* const_pointer;
typedef T & reference;
typedef const T & const_reference;
typedef T value_type;
template <class U>
struct rebind
{
typedef Allocator<U, growSize> other;
};
#ifdef _WIN32
Allocator() = default;
Allocator(Allocator & allocator) :
copyAllocator(&allocator)
{
}
template <class U>
Allocator(const Allocator<U, growSize> & other)
{
if(!std::is_same<T, U>::value)
rebindAllocator = new std::allocator<T>();
}
~Allocator()
{
delete rebindAllocator;
}
#endif
pointer allocate(size_type n, const void* hint = 0)
{
#ifdef _WIN32
if(copyAllocator)
return copyAllocator->allocate(n, hint);
if(rebindAllocator)
return rebindAllocator->allocate(n, hint);
#endif
if(n != 1 || hint)
throw std::bad_alloc();
return MemoryPool<T, growSize>::allocate();
}
void deallocate(pointer p, size_type n)
{
#ifdef _WIN32
if(copyAllocator)
{
copyAllocator->deallocate(p, n);
return;
}
if(rebindAllocator)
{
rebindAllocator->deallocate(p, n);
return;
}
#endif
MemoryPool<T, growSize>::deallocate(p);
}
void construct(pointer p, const_reference val)
{
new(p) T(val);
}
void destroy(pointer p)
{
p->~T();
}
};
}
#endif

View File

@ -0,0 +1,886 @@
/*===========================================================================
This library is released under the MIT license. See FSBAllocator.html
for further information and documentation.
Copyright (c) 2008-2011 Juha Nieminen
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
=============================================================================*/
#ifndef INCLUDE_FSBALLOCATOR_HH
#define INCLUDE_FSBALLOCATOR_HH
#include <new>
#include <cassert>
#include <vector>
#ifdef FSBALLOCATOR_USE_THREAD_SAFE_LOCKING_BOOST
#undef FSBALLOCATOR_USE_THREAD_SAFE_LOCKING_OPENMP
#undef FSBALLOCATOR_USE_THREAD_SAFE_LOCKING_PTHREAD
#undef FSBALLOCATOR_USE_THREAD_SAFE_LOCKING_GCC
#define FSBALLOCATOR_USE_THREAD_SAFE_LOCKING_OBJECT
#include <boost/thread.hpp>
typedef boost::mutex FSBAllocator_Mutex;
#endif
#ifdef FSBALLOCATOR_USE_THREAD_SAFE_LOCKING_OPENMP
#undef FSBALLOCATOR_USE_THREAD_SAFE_LOCKING_BOOST
#undef FSBALLOCATOR_USE_THREAD_SAFE_LOCKING_PTHREAD
#undef FSBALLOCATOR_USE_THREAD_SAFE_LOCKING_GCC
#define FSBALLOCATOR_USE_THREAD_SAFE_LOCKING_OBJECT
#include <omp.h>
class FSBAllocator_Mutex
{
omp_lock_t mutex;
public:
FSBAllocator_Mutex() { omp_init_lock(&mutex); }
~FSBAllocator_Mutex() { omp_destroy_lock(&mutex); }
void lock() { omp_set_lock(&mutex); }
void unlock() { omp_unset_lock(&mutex); }
};
#endif
#ifdef FSBALLOCATOR_USE_THREAD_SAFE_LOCKING_PTHREAD
#undef FSBALLOCATOR_USE_THREAD_SAFE_LOCKING_BOOST
#undef FSBALLOCATOR_USE_THREAD_SAFE_LOCKING_OPENMP
#undef FSBALLOCATOR_USE_THREAD_SAFE_LOCKING_GCC
#define FSBALLOCATOR_USE_THREAD_SAFE_LOCKING_OBJECT
#include <pthread.h>
class FSBAllocator_Mutex
{
pthread_mutex_t mutex;
public:
FSBAllocator_Mutex() { pthread_mutex_init(&mutex, NULL); }
~FSBAllocator_Mutex() { pthread_mutex_destroy(&mutex); }
void lock() { pthread_mutex_lock(&mutex); }
void unlock() { pthread_mutex_unlock(&mutex); }
};
#endif
#if defined(FSBALLOCATOR_USE_THREAD_SAFE_LOCKING_GCC) || defined(FSBALLOCATOR_USE_THREAD_SAFE_LOCKING_GCC_WITH_SCHED)
#undef FSBALLOCATOR_USE_THREAD_SAFE_LOCKING_BOOST
#undef FSBALLOCATOR_USE_THREAD_SAFE_LOCKING_OPENMP
#undef FSBALLOCATOR_USE_THREAD_SAFE_LOCKING_PTHREAD
#define FSBALLOCATOR_USE_THREAD_SAFE_LOCKING_OBJECT
#ifdef FSBALLOCATOR_USE_THREAD_SAFE_LOCKING_GCC_WITH_SCHED
#include <sched.h>
#endif
class FSBAllocator_Mutex
{
volatile int lockFlag;
public:
FSBAllocator_Mutex(): lockFlag(0) {}
void lock()
{
while(!__sync_bool_compare_and_swap(&lockFlag, 0, 1))
{
#ifdef FSBALLOCATOR_USE_THREAD_SAFE_LOCKING_GCC_WITH_SCHED
sched_yield();
#endif
}
}
void unlock() { lockFlag = 0; }
};
#endif
template<unsigned ElemSize>
class FSBAllocator_ElemAllocator
{
typedef std::size_t Data_t;
static const Data_t BlockElements = 512;
static const Data_t DSize = sizeof(Data_t);
static const Data_t ElemSizeInDSize = (ElemSize + (DSize-1)) / DSize;
static const Data_t UnitSizeInDSize = ElemSizeInDSize + 1;
static const Data_t BlockSize = BlockElements*UnitSizeInDSize;
class MemBlock
{
Data_t* block;
Data_t firstFreeUnitIndex, allocatedElementsAmount, endIndex;
public:
MemBlock():
block(0),
firstFreeUnitIndex(Data_t(-1)),
allocatedElementsAmount(0)
{}
bool isFull() const
{
return allocatedElementsAmount == BlockElements;
}
void clear()
{
delete[] block;
block = 0;
firstFreeUnitIndex = Data_t(-1);
}
void* allocate(Data_t vectorIndex)
{
if(firstFreeUnitIndex == Data_t(-1))
{
if(!block)
{
block = new Data_t[BlockSize];
if(!block) return 0;
endIndex = 0;
}
Data_t* retval = block + endIndex;
endIndex += UnitSizeInDSize;
retval[ElemSizeInDSize] = vectorIndex;
++allocatedElementsAmount;
return retval;
}
else
{
Data_t* retval = block + firstFreeUnitIndex;
firstFreeUnitIndex = *retval;
++allocatedElementsAmount;
return retval;
}
}
void deallocate(Data_t* ptr)
{
*ptr = firstFreeUnitIndex;
firstFreeUnitIndex = ptr - block;
if(--allocatedElementsAmount == 0)
clear();
}
};
struct BlocksVector
{
std::vector<MemBlock> data;
BlocksVector() { data.reserve(1024); }
~BlocksVector()
{
for(std::size_t i = 0; i < data.size(); ++i)
data[i].clear();
}
};
static BlocksVector blocksVector;
static std::vector<Data_t> blocksWithFree;
#ifdef FSBALLOCATOR_USE_THREAD_SAFE_LOCKING_OBJECT
static FSBAllocator_Mutex mutex;
#ifdef FSBALLOCATOR_USE_THREAD_SAFE_LOCKING_BOOST
struct Lock: boost::mutex::scoped_lock
{
Lock(): boost::mutex::scoped_lock(mutex) {}
};
#else
struct Lock
{
Lock() { mutex.lock(); }
~Lock() { mutex.unlock(); }
};
#endif
#endif
public:
static void* allocate()
{
#ifdef FSBALLOCATOR_USE_THREAD_SAFE_LOCKING_OBJECT
Lock lock;
#endif
if(blocksWithFree.empty())
{
blocksWithFree.push_back(blocksVector.data.size());
blocksVector.data.push_back(MemBlock());
}
const Data_t index = blocksWithFree.back();
MemBlock& block = blocksVector.data[index];
void* retval = block.allocate(index);
if(block.isFull())
blocksWithFree.pop_back();
return retval;
}
static void deallocate(void* ptr)
{
if(!ptr) return;
#ifdef FSBALLOCATOR_USE_THREAD_SAFE_LOCKING_OBJECT
Lock lock;
#endif
Data_t* unitPtr = (Data_t*)ptr;
const Data_t blockIndex = unitPtr[ElemSizeInDSize];
MemBlock& block = blocksVector.data[blockIndex];
if(block.isFull())
blocksWithFree.push_back(blockIndex);
block.deallocate(unitPtr);
}
};
template<unsigned ElemSize>
typename FSBAllocator_ElemAllocator<ElemSize>::BlocksVector
FSBAllocator_ElemAllocator<ElemSize>::blocksVector;
template<unsigned ElemSize>
std::vector<typename FSBAllocator_ElemAllocator<ElemSize>::Data_t>
FSBAllocator_ElemAllocator<ElemSize>::blocksWithFree;
#ifdef FSBALLOCATOR_USE_THREAD_SAFE_LOCKING_OBJECT
template<unsigned ElemSize>
FSBAllocator_Mutex FSBAllocator_ElemAllocator<ElemSize>::mutex;
#endif
template<unsigned ElemSize>
class FSBAllocator2_ElemAllocator
{
static const std::size_t BlockElements = 1024;
static const std::size_t DSize = sizeof(std::size_t);
static const std::size_t ElemSizeInDSize = (ElemSize + (DSize-1)) / DSize;
static const std::size_t BlockSize = BlockElements*ElemSizeInDSize;
struct Blocks
{
std::vector<std::size_t*> ptrs;
Blocks()
{
ptrs.reserve(256);
ptrs.push_back(new std::size_t[BlockSize]);
}
~Blocks()
{
for(std::size_t i = 0; i < ptrs.size(); ++i)
delete[] ptrs[i];
}
};
static Blocks blocks;
static std::size_t headIndex;
static std::size_t* freeList;
static std::size_t allocatedElementsAmount;
#ifdef FSBALLOCATOR_USE_THREAD_SAFE_LOCKING_OBJECT
static FSBAllocator_Mutex mutex;
#ifdef FSBALLOCATOR_USE_THREAD_SAFE_LOCKING_BOOST
struct Lock: boost::mutex::scoped_lock
{
Lock(): boost::mutex::scoped_lock(mutex) {}
};
#else
struct Lock
{
Lock() { mutex.lock(); }
~Lock() { mutex.unlock(); }
};
#endif
#endif
static void freeAll()
{
for(std::size_t i = 1; i < blocks.ptrs.size(); ++i)
delete[] blocks.ptrs[i];
blocks.ptrs.resize(1);
headIndex = 0;
freeList = 0;
}
public:
static void* allocate()
{
#ifdef FSBALLOCATOR_USE_THREAD_SAFE_LOCKING_OBJECT
Lock lock;
#endif
++allocatedElementsAmount;
if(freeList)
{
std::size_t* retval = freeList;
freeList = reinterpret_cast<std::size_t*>(*freeList);
return retval;
}
if(headIndex == BlockSize)
{
blocks.ptrs.push_back(new std::size_t[BlockSize]);
headIndex = 0;
}
std::size_t* retval = &(blocks.ptrs.back()[headIndex]);
headIndex += ElemSizeInDSize;
return retval;
}
static void deallocate(void* ptr)
{
if(ptr)
{
#ifdef FSBALLOCATOR_USE_THREAD_SAFE_LOCKING_OBJECT
Lock lock;
#endif
std::size_t* sPtr = (std::size_t*)ptr;
*sPtr = reinterpret_cast<std::size_t>(freeList);
freeList = sPtr;
if(--allocatedElementsAmount == 0)
freeAll();
}
}
static void cleanSweep(std::size_t unusedValue = std::size_t(-1))
{
#ifdef FSBALLOCATOR_USE_THREAD_SAFE_LOCKING_OBJECT
Lock lock;
#endif
while(freeList)
{
std::size_t* current = freeList;
freeList = reinterpret_cast<std::size_t*>(*freeList);
*current = unusedValue;
}
for(std::size_t i = headIndex; i < BlockSize; i += ElemSizeInDSize)
blocks.ptrs.back()[i] = unusedValue;
for(std::size_t blockInd = 1; blockInd < blocks.ptrs.size();)
{
std::size_t* block = blocks.ptrs[blockInd];
std::size_t freeAmount = 0;
for(std::size_t i = 0; i < BlockSize; i += ElemSizeInDSize)
if(block[i] == unusedValue)
++freeAmount;
if(freeAmount == BlockElements)
{
delete[] block;
blocks.ptrs[blockInd] = blocks.ptrs.back();
blocks.ptrs.pop_back();
}
else ++blockInd;
}
const std::size_t* lastBlock = blocks.ptrs.back();
for(headIndex = BlockSize; headIndex > 0; headIndex -= ElemSizeInDSize)
if(lastBlock[headIndex-ElemSizeInDSize] != unusedValue)
break;
const std::size_t lastBlockIndex = blocks.ptrs.size() - 1;
for(std::size_t blockInd = 0; blockInd <= lastBlockIndex; ++blockInd)
{
std::size_t* block = blocks.ptrs[blockInd];
for(std::size_t i = 0; i < BlockSize; i += ElemSizeInDSize)
{
if(blockInd == lastBlockIndex && i == headIndex)
break;
if(block[i] == unusedValue)
deallocate(block + i);
}
}
}
};
template<unsigned ElemSize>
typename FSBAllocator2_ElemAllocator<ElemSize>::Blocks
FSBAllocator2_ElemAllocator<ElemSize>::blocks;
template<unsigned ElemSize>
std::size_t FSBAllocator2_ElemAllocator<ElemSize>::headIndex = 0;
template<unsigned ElemSize>
std::size_t* FSBAllocator2_ElemAllocator<ElemSize>::freeList = 0;
template<unsigned ElemSize>
std::size_t FSBAllocator2_ElemAllocator<ElemSize>::allocatedElementsAmount = 0;
#ifdef FSBALLOCATOR_USE_THREAD_SAFE_LOCKING_OBJECT
template<unsigned ElemSize>
FSBAllocator_Mutex FSBAllocator2_ElemAllocator<ElemSize>::mutex;
#endif
template<typename Ty>
class FSBAllocator
{
public:
typedef std::size_t size_type;
typedef std::ptrdiff_t difference_type;
typedef Ty *pointer;
typedef const Ty *const_pointer;
typedef Ty& reference;
typedef const Ty& const_reference;
typedef Ty value_type;
pointer address(reference val) const { return &val; }
const_pointer address(const_reference val) const { return &val; }
template<class Other>
struct rebind
{
typedef FSBAllocator<Other> other;
};
FSBAllocator() throw() {}
template<class Other>
FSBAllocator(const FSBAllocator<Other>&) throw() {}
template<class Other>
FSBAllocator& operator=(const FSBAllocator<Other>&) { return *this; }
pointer allocate(size_type count, const void* = 0)
{
assert(count == 1);
return static_cast<pointer>
(FSBAllocator_ElemAllocator<sizeof(Ty)>::allocate());
}
void deallocate(pointer ptr, size_type)
{
FSBAllocator_ElemAllocator<sizeof(Ty)>::deallocate(ptr);
}
void construct(pointer ptr, const Ty& val)
{
new ((void *)ptr) Ty(val);
}
void destroy(pointer ptr)
{
ptr->Ty::~Ty();
}
size_type max_size() const throw() { return 1; }
};
template<typename Ty>
class FSBAllocator2
{
public:
typedef std::size_t size_type;
typedef std::ptrdiff_t difference_type;
typedef Ty *pointer;
typedef const Ty *const_pointer;
typedef Ty& reference;
typedef const Ty& const_reference;
typedef Ty value_type;
pointer address(reference val) const { return &val; }
const_pointer address(const_reference val) const { return &val; }
template<class Other>
struct rebind
{
typedef FSBAllocator2<Other> other;
};
FSBAllocator2() throw() {}
template<class Other>
FSBAllocator2(const FSBAllocator2<Other>&) throw() {}
template<class Other>
FSBAllocator2& operator=(const FSBAllocator2<Other>&) { return *this; }
pointer allocate(size_type count, const void* = 0)
{
assert(count == 1);
return static_cast<pointer>
(FSBAllocator2_ElemAllocator<sizeof(Ty)>::allocate());
}
void deallocate(pointer ptr, size_type)
{
FSBAllocator2_ElemAllocator<sizeof(Ty)>::deallocate(ptr);
}
void construct(pointer ptr, const Ty& val)
{
new ((void *)ptr) Ty(val);
}
void destroy(pointer ptr)
{
ptr->Ty::~Ty();
}
size_type max_size() const throw() { return 1; }
void cleanSweep(std::size_t unusedValue = std::size_t(-1))
{
FSBAllocator2_ElemAllocator<sizeof(Ty)>::cleanSweep(unusedValue);
}
};
typedef FSBAllocator2<std::size_t> FSBRefCountAllocator;
// <MyCustomAlloc>
extern long g_nCnt; // total # blocks allocated
extern long g_nTot; // total allocated
template <class T>
class MyCustomAlloc
/*
A custom allocator: given a pool of memory to start, just dole out consecutive memory blocks.
this could be faster than a general purpose allocator.
E.G. it could take advantage of constant sized requests (as in a RedBlack tree)
*/
{
public:
typedef T value_type;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef T* pointer;
typedef const T* const_pointer;
typedef T& reference;
typedef const T& const_reference;
MyCustomAlloc(byte *pool, int nPoolSize)
{
Init();
m_pool = pool;
m_nPoolSize = nPoolSize;
}
MyCustomAlloc(int n)
{
Init();
}
MyCustomAlloc()
{
Init();
}
void Init()
{
m_pool = 0;
m_nPoolSize = 0;
g_nCnt = 0;
g_nTot = 0;
}
MyCustomAlloc(const MyCustomAlloc &obj) // copy constructor
{
Init();
m_pool = obj.m_pool;
m_nPoolSize = obj.m_nPoolSize;
}
private:
void operator =(const MyCustomAlloc &);
public:
byte *m_pool;
unsigned m_nPoolSize;
template <class _Other>
MyCustomAlloc(const MyCustomAlloc<_Other> &other)
{
Init();
m_pool = other.m_pool;
m_nPoolSize = other.m_nPoolSize;
}
~MyCustomAlloc()
{
}
template <class U>
struct rebind
{
typedef MyCustomAlloc<U> other;
};
pointer
address(reference r) const
{
return &r;
}
const_pointer
address(const_reference r) const
{
return &r;
}
pointer
allocate(size_type n, const void* /*hint*/ = 0)
{
pointer p;
unsigned nSize = n * sizeof(T);
if (m_pool) // if we have a mem pool from which to allocated
{
p = (pointer)m_pool;// just return the next available mem in the pool
if (g_nTot + nSize > m_nPoolSize)
{
_ASSERT(0);//,"out of mem pool");
return 0;
}
m_pool += nSize; // and bump the pointer
}
else
{
p = (pointer)malloc(nSize);// no pool: just use malloc
}
g_nCnt += 1;
g_nTot += nSize;
_ASSERTE(p);
return p;
}
void
deallocate(pointer p, size_type /*n*/)
{
if (!m_pool)// if there's a pool, nothing to do
{
free(p);
}
}
void
construct(pointer p, const T& val)
{
new (p) T(val);
}
void
destroy(pointer p)
{
p->~T();
}
size_type
max_size() const
{
return ULONG_MAX / sizeof(T);
}
};
template <class T>
bool
operator==(const MyCustomAlloc<T>& left, const MyCustomAlloc<T>& right)
{
if (left.m_pool == right.m_pool)
{
return true;
}
return false;
}
template <class T>
bool
operator!=(const MyCustomAlloc<T>& left, const MyCustomAlloc<T>& right)
{
if (left.m_pool != right.m_pool)
{
return true;
}
return false;
}
// </MyCustomAlloc>
#endif

View File

@ -0,0 +1,141 @@
#include "Allocator.h"
#include "DataTypes.h"
#include <new>
#include <assert.h>
//------------------------------------------------------------------------------
// Constructor
//------------------------------------------------------------------------------
Allocator::Allocator(size_t size, UINT objects, CHAR* memory, const CHAR* name) :
m_blockSize(size < sizeof(long*) ? sizeof(long*) : size),
m_objectSize(size),
m_maxObjects(objects),
m_pHead(NULL),
m_poolIndex(0),
m_blockCnt(0),
m_blocksInUse(0),
m_allocations(0),
m_deallocations(0),
m_name(name)
{
// If using a fixed memory pool
if(m_maxObjects)
{
// If caller provided an external memory pool
if(memory)
{
m_pPool = memory;
m_allocatorMode = STATIC_POOL;
}
else
{
m_pPool = (CHAR*)new CHAR[m_blockSize * m_maxObjects];
m_allocatorMode = HEAP_POOL;
}
}
else
m_allocatorMode = HEAP_BLOCKS;
}
//------------------------------------------------------------------------------
// Destructor
//------------------------------------------------------------------------------
Allocator::~Allocator()
{
// If using pool then destroy it, otherwise traverse free-list and
// destroy each individual block
if(m_allocatorMode == HEAP_POOL)
delete [] m_pPool;
else if(m_allocatorMode == HEAP_BLOCKS)
{
while(m_pHead)
delete [](CHAR*)Pop();
}
}
//------------------------------------------------------------------------------
// Allocate
//------------------------------------------------------------------------------
void* Allocator::Allocate(size_t size)
{
assert(size <= m_objectSize);
// If can't obtain existing block then get a new one
void* pBlock = Pop();
if(!pBlock)
{
// If using a pool method then get block from pool,
// otherwise using dynamic so get block from heap
if(m_maxObjects)
{
// If we have not exceeded the pool maximum
if(m_poolIndex < m_maxObjects)
{
pBlock = (void*)(m_pPool + (m_poolIndex++ * m_blockSize));
}
else
{
// Get the pointer to the new handler
std::new_handler handler = std::set_new_handler(0);
std::set_new_handler(handler);
// If a new handler is defined, call it
if(handler)
(*handler)();
else
assert(0);
}
}
else
{
m_blockCnt++;
pBlock = (void*)new CHAR[m_blockSize];
}
}
m_blocksInUse++;
m_allocations++;
return pBlock;
}
//------------------------------------------------------------------------------
// Deallocate
//------------------------------------------------------------------------------
void Allocator::Deallocate(void* pBlock)
{
Push(pBlock);
m_blocksInUse--;
m_deallocations++;
}
//------------------------------------------------------------------------------
// Push
//------------------------------------------------------------------------------
void Allocator::Push(void* pMemory)
{
Block* pBlock = (Block*)pMemory;
pBlock->pNext = m_pHead;
m_pHead = pBlock;
}
//------------------------------------------------------------------------------
// Pop
//------------------------------------------------------------------------------
void* Allocator::Pop()
{
Block* pBlock = NULL;
if(m_pHead)
{
pBlock = m_pHead;
m_pHead = m_pHead->pNext;
}
return (void*)pBlock;
}

View File

@ -0,0 +1,118 @@
#ifndef __ALLOCATOR_H
#define __ALLOCATOR_H
#include "DataTypes.h"
#include <stddef.h>
/// See http://www.codeproject.com/Articles/1083210/An-efficient-Cplusplus-fixed-block-memory-allocato
class Allocator
{
public:
/// Constructor
/// @param[in] size - size of the fixed blocks
/// @param[in] objects - maximum number of object. If 0, new blocks are
/// created off the heap as necessary.
/// @param[in] memory - pointer to a block of static memory for allocator or NULL
/// to obtain memory from global heap. If not NULL, the objects argument
/// defines the size of the memory block (size x objects = memory size in bytes).
/// @param[in] name - optional allocator name string.
Allocator(size_t size, UINT objects = 0, CHAR* memory = NULL, const CHAR* name = NULL);
/// Destructor
~Allocator();
/// Get a pointer to a memory block.
/// @param[in] size - size of the block to allocate
/// @return Returns pointer to the block. Otherwise NULL if unsuccessful.
void* Allocate(size_t size);
/// Return a pointer to the memory pool.
/// @param[in] pBlock - block of memory deallocate (i.e push onto free-list)
void Deallocate(void* pBlock);
/// Get the allocator name string.
/// @return A pointer to the allocator name or NULL if none was assigned.
const CHAR* GetName() { return m_name; }
/// Gets the fixed block memory size, in bytes, handled by the allocator.
/// @return The fixed block size in bytes.
size_t GetBlockSize() { return m_blockSize; }
/// Gets the maximum number of blocks created by the allocator.
/// @return The number of fixed memory blocks created.
UINT GetBlockCount() { return m_blockCnt; }
/// Gets the number of blocks in use.
/// @return The number of blocks in use by the application.
UINT GetBlocksInUse() { return m_blocksInUse; }
/// Gets the total number of allocations for this allocator instance.
/// @return The total number of allocations.
UINT GetAllocations() { return m_allocations; }
/// Gets the total number of deallocations for this allocator instance.
/// @return The total number of deallocations.
UINT GetDeallocations() { return m_deallocations; }
private:
/// Push a memory block onto head of free-list.
/// @param[in] pMemory - block of memory to push onto free-list
void Push(void* pMemory);
/// Pop a memory block from head of free-list.
/// @return Returns pointer to the block. Otherwise NULL if unsuccessful.
void* Pop();
struct Block
{
Block* pNext;
};
enum AllocatorMode { HEAP_BLOCKS, HEAP_POOL, STATIC_POOL };
const size_t m_blockSize;
const size_t m_objectSize;
const UINT m_maxObjects;
AllocatorMode m_allocatorMode;
Block* m_pHead;
CHAR* m_pPool;
UINT m_poolIndex;
UINT m_blockCnt;
UINT m_blocksInUse;
UINT m_allocations;
UINT m_deallocations;
const CHAR* m_name;
};
// Template class to create external memory pool
template <class T, UINT Objects>
class AllocatorPool : public Allocator
{
public:
AllocatorPool() : Allocator(sizeof(T), Objects, m_memory)
{
}
private:
CHAR m_memory[sizeof(T) * Objects];
};
// macro to provide header file interface
#define DECLARE_ALLOCATOR \
public: \
void* operator new(size_t size) { \
return _allocator.Allocate(size); \
} \
void operator delete(void* pObject) { \
_allocator.Deallocate(pObject); \
} \
private: \
static Allocator _allocator;
// macro to provide source file interface
#define IMPLEMENT_ALLOCATOR(class, objects, memory) \
Allocator class::_allocator(sizeof(class), objects, memory, #class);
#endif

View File

@ -0,0 +1,42 @@
#ifndef _DATA_TYPES_H
#define _DATA_TYPES_H
#if WIN32
#include "windows.h"
#else
typedef signed char INT8;
typedef unsigned char UINT8;
typedef signed short INT16;
typedef unsigned short UINT16;
typedef unsigned int UINT32;
typedef int INT32;
typedef char CHAR;
typedef short SHORT;
typedef long LONG;
typedef int INT;
typedef unsigned int UINT;
typedef unsigned long DWORD;
typedef unsigned char BYTE;
typedef unsigned short WORD;
typedef float FLOAT;
typedef double DOUBLE;
typedef int BOOL;
#ifndef NULL
#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void *)0)
#endif
#endif
#ifndef FALSE
#define FALSE 0
#endif
#ifndef TRUE
#define TRUE 1
#endif
#endif
#endif

View File

@ -0,0 +1,16 @@
#include "Fault.h"
#include "DataTypes.h"
#include <assert.h>
//----------------------------------------------------------------------------
// FaultHandler
//----------------------------------------------------------------------------
void FaultHandler(const char* file, unsigned short line)
{
#if WIN32
// If you hit this line, it means one of the ASSERT macros failed.
DebugBreak();
#endif
assert(0);
}

View File

@ -0,0 +1,23 @@
#ifndef _FAULT_H
#define _FAULT_H
#ifdef __cplusplus
extern "C" {
#endif
#define ASSERT() \
FaultHandler(__FILE__, (unsigned short) __LINE__)
#define ASSERT_TRUE(condition) \
do {if (!(condition)) FaultHandler(__FILE__, (unsigned short) __LINE__);} while (0)
/// Handles all software assertions in the system.
/// @param[in] file - the file name that the software assertion occurred on
/// @param[in] line - the line number that the software assertion occurred on
void FaultHandler(const char* file, unsigned short line);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,194 @@
#include "stdafx.h"
#include <new>
#include <iostream>
#include "xlist.h"
#include "xmap.h"
#include "xqueue.h"
#include "xset.h"
#include "xsstream.h"
#include "xstring.h"
// On VisualStudio, to disable the debug heap for faster performance when using
// the debugger use this option:
// Debugging > Environment _NO_DEBUG_HEAP=1
using namespace std;
static int MAX_BENCHMARK = 10000;
typedef void (*TestFunc)();
void ListGlobalHeapTest();
void MapGlobalHeapTest();
void StringGlobalHeapTest();
void ListFixedBlockTest();
void MapFixedBlockTest();
void StringFixedBlockTest();
void Benchmark(const char* name, TestFunc testFunc);
static void out_of_memory()
{
// new-handler function called by Allocator when pool is out of memory
ASSERT();
}
//------------------------------------------------------------------------------
// main
//------------------------------------------------------------------------------
int main(void)
{
std::set_new_handler(out_of_memory);
xlist<int> myList;
myList.push_back(123);
xmap<char, int> myMap;
myMap['a'] = 10;
xqueue<int> myQueue;
myQueue.push(123);
xset<xstring> mySet;
mySet.insert("hello");
mySet.insert("world");
xstringstream myStringStream;
myStringStream << "hello world " << 2016 << ends;
xwstringstream myWStringStream;
myWStringStream << L"hello world " << 2016 << ends;
xstring myString("hello world");
Benchmark("std::list Global Heap (Run 1)", ListGlobalHeapTest);
Benchmark("std::list Global Heap (Run 2)", ListGlobalHeapTest);
Benchmark("std::list Global Heap (Run 3)", ListGlobalHeapTest);
Benchmark("xlist Fixed Block (Run 1)", ListFixedBlockTest);
Benchmark("xlist Fixed Block (Run 2)", ListFixedBlockTest);
Benchmark("xlist Fixed Block (Run 3)", ListFixedBlockTest);
Benchmark("std::map Global Heap (Run 1)", MapGlobalHeapTest);
Benchmark("std::map Global Heap (Run 2)", MapGlobalHeapTest);
Benchmark("std::map Global Heap (Run 3)", MapGlobalHeapTest);
Benchmark("xmap Fixed Block (Run 1)", MapFixedBlockTest);
Benchmark("xmap Fixed Block (Run 2)", MapFixedBlockTest);
Benchmark("xmap Fixed Block (Run 3)", MapFixedBlockTest);
Benchmark("std::string Global Heap (Run 1)", StringGlobalHeapTest);
Benchmark("std::string Global Heap (Run 2)", StringGlobalHeapTest);
Benchmark("std::string Global Heap (Run 3)", StringGlobalHeapTest);
Benchmark("xstring Fixed Block (Run 1)", StringFixedBlockTest);
Benchmark("xstring Fixed Block (Run 2)", StringFixedBlockTest);
Benchmark("xstring Fixed Block (Run 3)", StringFixedBlockTest);
xalloc_stats();
return 0;
}
//------------------------------------------------------------------------------
// MapGlobalHeapTest
//------------------------------------------------------------------------------
void MapGlobalHeapTest()
{
map<int, char> myMap;
for(int i = 0; i < MAX_BENCHMARK; i++)
myMap[i] = 'a';
myMap.clear();
}
//------------------------------------------------------------------------------
// MapFixedBlockTest
//------------------------------------------------------------------------------
void MapFixedBlockTest()
{
xmap<int, char> myMap;
for(int i = 0; i < MAX_BENCHMARK; i++)
myMap[i] = 'a';
myMap.clear();
}
//------------------------------------------------------------------------------
// ListGlobalHeapTest
//------------------------------------------------------------------------------
void ListGlobalHeapTest()
{
list<int> myList;
for(int i = 0; i < MAX_BENCHMARK; i++)
myList.push_back(123);
myList.clear();
}
//------------------------------------------------------------------------------
// ListFixedBlockTest
//------------------------------------------------------------------------------
void ListFixedBlockTest()
{
xlist<int> myList;
for(int i = 0; i < MAX_BENCHMARK; i++)
myList.push_back(123);
myList.clear();
}
//------------------------------------------------------------------------------
// StringGlobalHeapTest
//------------------------------------------------------------------------------
void StringGlobalHeapTest()
{
list<string> myList;
for(int i = 0; i < MAX_BENCHMARK; i++)
{
string myString("benchmark");
myString += "benchmark test benchmark test benchmark test benchmark test benchmark test benchmark test benchmark test "
"benchmark test benchmark test benchmark test benchmark test benchmark test benchmark test benchmark test";
myList.push_back(myString);
}
myList.clear();
}
//------------------------------------------------------------------------------
// StringFixedBlockTest
//------------------------------------------------------------------------------
void StringFixedBlockTest()
{
xlist<xstring> myList;
for(int i = 0; i < MAX_BENCHMARK; i++)
{
xstring myString("benchmark");
myString += "benchmark test benchmark test benchmark test benchmark test benchmark test benchmark test benchmark test "
"benchmark test benchmark test benchmark test benchmark test benchmark test benchmark test benchmark test";
myList.push_back(myString);
}
myList.clear();
}
//------------------------------------------------------------------------------
// Benchmark
//------------------------------------------------------------------------------
void Benchmark(const char* name, TestFunc testFunc)
{
#if WIN32
LARGE_INTEGER StartingTime, EndingTime, ElapsedMicroseconds = {0};
LARGE_INTEGER Frequency;
SetProcessPriorityBoost(GetCurrentProcess(), true);
QueryPerformanceFrequency(&Frequency);
QueryPerformanceCounter(&StartingTime);
// Call test function
testFunc();
QueryPerformanceCounter(&EndingTime);
ElapsedMicroseconds.QuadPart = EndingTime.QuadPart - StartingTime.QuadPart;
ElapsedMicroseconds.QuadPart *= 1000000;
ElapsedMicroseconds.QuadPart /= Frequency.QuadPart;
std::cout << name << " Elapsed time: " << ElapsedMicroseconds.QuadPart << std::endl;
SetProcessPriorityBoost(GetCurrentProcess(), false);
#endif
}

View File

@ -0,0 +1,8 @@
// stdafx.cpp : source file that includes just the standard includes
// Allocator.pch will be the pre-compiled header
// stdafx.obj will contain the pre-compiled type information
#include "stdafx.h"
// TODO: reference any additional headers you need in STDAFX.H
// and not in this file

View File

@ -0,0 +1,14 @@
// stdafx.h : include file for standard system include files,
// or project specific include files that are used frequently, but
// are changed infrequently
//
#pragma once