Memory Allocators In-depth
This project is a single header library of Aligned Memory Allocators. It's built to fill my need for some of these tools when working on projects such as Tank Engine.
It contains the following allocators:
- Aligned Fixed Type Allocator (Thread Safe, Can reallocate)
- Aligned General Purpose Allocator (Thread Safe, Can reallocate)
It also contains a wrapper around a memory pool with the ability to reallocate.
Aligned Fixed Type Allocator
template <
typename Type,
uint64_t Size = 1024U,
bool Reallocates = true,
bool ThreadSafe = false,
std::enable_if_t<constexpr_mod<Size, 8>::value == 0, int> = 0>
class FixedTypeAllocator
: public Allocator
{ ... }
The Fixed Type Allocator makes use of reallocatable memory pools to store objects of a fixed Type. You can specify the starting Size, if the pool Reallocates, and if it is ThreadSafe.
To make sure reallocation is possible, these containers use OffsetPtrs. These keep track of their base container as well as their internal position. The operator-> operator has been overridden to provide transparent access to the internal memory.
Thread safety, when enabled, uses mutexes to make sure that only one thread can make changes at any given time.
Alc::FixedTypeAllocator<Particle, 128, true, false> allocator{};
Alc::OffsetPtr<Particle> newParticle = allocator.Get();
newParticle->SetLifetime(100.f);
allocator.Pop(newParticle);
Aligned General Purpose Allocator
template<uint64_t PoolSize = 128U,
bool Reallocates = true,
bool ThreadSafe = false,
uint64_t ...SubPoolSize>
class GeneralPurposeAllocator
{ ... }
The General Purpose Allocator(GPA), in similar fashion to the Fixed Type Allocator, makes use of reallocatable memory pools to store objects. What separates these two is the amount of pool. The GPA keeps a memory pool for each allocation SubPoolSize that the user wants to support.
Reallocation is supported through the use of the aforementioned OffsetPtrs.
Thread safety, when enabled, uses mutexes to make sure that only one thread can make changes at any given time.
struct V4 { float x, y, z, w; }; // sizeof(V4) == 16
struct V3 { float x, y, z; }; // sizeof(V3) == 12
struct V2 { float x, y; }; // sizeof(V2) == 8
struct Mat4x4 { V4 mat[4]; }; // sizeof(Mat4x4) == 64
Alc::GeneralPurposeAllocator<128U, true, false, 8U, 16U, 32U, 64U, 128U, 256U> generalAllocator{};
OffsetPtr<V4> vector4 = generalAllocator.New<V4>(); // Would be allocated in the pool of 16 byte elements
OffsetPtr<V3> vector3 = generalAllocator.New<V3>(); // Would be allocated in the pool of 16 byte elements
OffsetPtr<V3> vector2 = generalAllocator.New<V2>(); // Would be allocated in the pool of 8 byte elements
OffsetPtr<Mat4x4> matrix4x4 = generalAllocator.New<Mat4x4>(); // Would be allocated in the pool of 64 byte elements
generalAllocator.Delete(vector4);
generalAllocator.Delete(vector3);
generalAllocator.Delete(vector2);
generalAllocator.Delete(matrix4x4);