A very fast cross-platform memory pool mechanism for C++ built using a data-oriented approach. I hope this simple feature will help you increase your software's performance - and there are more projects and features to come under the AppShift library name, wait for it ;)
- MemoryPool For C++
- Table of Contents
- Usage
- Methodology
- Benchmark
- About
- Contributors - Thank You! :D
- More to come in later versions
To use the memory pool features you just need to copy the MemoryPool.cpp & MemoryPool.h files to your project. The memory pool structure is AppShift::Memory::MemoryPool
. The Memory Pool Is Not Thread Safe - In case of threads it is better to create a memory pool for each thread
- Create a memory pool:
AppShift::Memory::MemoryPool * mp = new AppShift::Memory::MemoryPool(size);
Create a new memory pool structure and a first memory block. If you don't specify a size then by default it will be theMEMORYPOOL_DEFAULT_BLOCK_SIZE
macro. - Allocate space:
Type* allocated = new (mp) Type[size];
orType* allocated = (Type*) mp->allocate(size * sizeof(Type));
orType* allocated = mp->allocate<Type>(size);
WhereType
is the object\primitive type to create,mp
is the memory pool object address, andsize
is a represention of the amount of types to allocate. - Deallocate space:
mp->free(allocated)
Remove an allocated space - Reallocate space:
Type* allocated = mp->reallocate<Type>(allocated, size);
orType* allocated = (Type*) mp->reallocate(allocated, size);
Rellocate a pre-allocated space, will copy the previous values to the new memory allocated. - Dump data of a memory pool:
mp->dumpPoolData()
This function prints outs the data about the blocks and units in the pool.
Scoping is a fast way to deallocate many allocations at once. If for example you need to allocate more than once in a given part of the code, and then you deallocate all the allocations that happaned, then you can "scope" all these allocations together. it works the same way as a stack in a function scope.
- Start A Scope:
mp->startScope()
where mp is the memory pool structure. This function creates a "checkpoint" of the offset and block in the memory pool. - End A Scope:
mp->endScope()
Will free all the allocations made after the scope started. - Scope Inside A Scope: You can nest scopes inside scopes by strating a new scope again, just the same way that the stack works with function scopes. Each scope is pointing to the previous one to create a chain that allows the memory pool manager to manage scope nesting.
There are some helpful macros available to indicate how you want the MemoryPool to manage your memory allocations.
#define MEMORYPOOL_DEFAULT_BLOCK_SIZE 1024 * 1024
: The MemoryPool allocates memory into blocks, each block can have a maximum size avalable to use - when it exceeds this size, the MemoryPool allocates a new block - use this macro to define the maximum size to give to each block. By default the value is1024 * 1024
which is 1MB.
The MemoryPool is a structure pointing to the start of a chain of blocks, which size of every block is by default MEMORYPOOL_BLOCK_MAX_SIZE
macro (See Macros) or the size passed into the AppShift::Memory::MemoryPool(size)
constructor. The MemoryPool is an object holding the necessary functions to work with the a memory pool. What's also good is that you can also access the MemoryPool structure data directly if needed (everything is public).
The memory pool structure holds meta-data about the memory space that is allocated and stored in the pool.
SMemoryBlockHeader* firstBlock;
- Holds the first block in the chain of memory blocks.SMemoryBlockHeader* currentBlock;
- Holds the last block in the chain that is used first for allocating (allocations are happening in a stack manner, where each memory unit allocated is on top of the previous one, when a block reaches it's maximum size then a new block is allocated and added to the block chain of the pool).size_t defaultBlockSize;
- Default size to use when creating a new block, the size is defined by theMEMORYPOOL_BLOCK_MAX_SIZE
macro or by passing thesize
as a parameter for theAppShift::Memory::MemoryPoolManager::create(size)
function.SMemoryScopeHeader* currentScope;
- A pointer to the current scope in the memory pool.
Each block contains a block header the size of 48 bytes containing the following information:
size_t blockSize;
- Size of the blocksize_t offset;
- Offset in the block from which the memory is free (The block is filled in sequencial order)SMemoryBlockHeader* next;
- Pointer to the next blockSMemoryBlockHeader* prev;
- Pointer to the previous blocksize_t numberOfAllocated
- Number of units currently allocated in this block. Helps smart garbage collection when block data has been freed.size_t numberOfDeleted
- Number of units that have been flaged as deleted. The system removes blocks by comparing the deleted with the allocated.
When a block is fully filled the MemoryPool creates a new block and relates it to the previous block, and the previous to the current, them uses the new pool as the current block.
When allocating a space, MemoryPool creates a SMemoryUnitHeader and moves the blocks offset forward by the header size plus the amount of space requested. The header is 16 bytes long and contains the following data:
size_t length;
- The length in bytes of the allocated spaceSMemoryBlockHeader* container
- Block which this unit belongs to
A scope has it's own structure - it has an offset and a pointer to the starting block of the scope, and also a pointer to the previous scope (parent).
size_t scopeOffset;
- Saves the offset of the block when start scope is declared.SMemoryBlockHeader* firstScopeBlock;
- Saves the current block when a start scope is declared, helps to know until which block to free everything when the scope ends.SMemoryScopeHeader* prevScope;
- Pointer to the previous scope/NULL if no parent scope is present.
About 21-24 times faster than standard new/delete in each test.
About 10-13 times faster than standard new/delete in each test.
About 8-10 times faster than standard new/delete in each test.
- Sapir Shemer is the proud business owner of DevShift and an Open-Source enthusiast. Have been programming since the age of 7. Mathematics Student :)
A list of people that were kind enough to help:
In the next versions I'm planning to add some interesting features:
- Ability to put thread safety on the memory pool to make a thread shared memory pool.
- Ability to create an inter-process memory pool which can be shared between different processes.
compressGarbage()
: Will compress deleted units that are next to eachother into one unit.