Allocators Testing Added. Pull and Chunk allocators updates.

This commit is contained in:
IlushaShurupov 2023-07-07 22:11:22 +03:00
parent 4a2ab6f5d0
commit 9e5ac1e975
30 changed files with 704 additions and 701 deletions

View file

@ -8,15 +8,15 @@
#include "PoolAllocator.hpp"
namespace tp {
extern ModuleManifest gModuleAllocator;
};
extern ModuleManifest gModuleAllocators;
}
inline void* operator new(std::size_t aSize, void* aWhere) noexcept { return aWhere; }
void* operator new(std::size_t aSize);
void* operator new[](std::size_t aSize);
void operator delete(void* aPtr);
void operator delete[](void* aPtr);
void operator delete(void* aPtr) noexcept;
void operator delete[](void* aPtr) noexcept;
void* operator new(std::size_t aSize, tp::HeapAlloc& aAlloc);
void* operator new[](std::size_t aSize, tp::HeapAlloc& aAlloc);

View file

@ -1,32 +1,135 @@
#pragma once
/*
* Implementation uses embedded one-directional linked list to track free blocks.
* The embedded part ensures that there is no memory overhead on block specifically.
* Linked list is initialized iteratively on each allocation if it has not been already.
* Allocating:
* 1) updating list entry to stored in the entry itself next free pointer
* 2) returning entry before (1).
*
* Deallocating:
* 1) assigning list entry value to the deleted block
* 2) updating list entry to that block.
*/
#include "Environment.hpp"
#include "PrivateConfig.hpp"
namespace tp {
// Chunk Allocator
// Constant time allocations and de-allocations in any order.
// Memory blocks are fixed in size and number of blocks can not exceed given parameter.
struct ChunkAlloc {
template<typename tType, ualni tNumBlocks>
class ChunkAlloc {
ChunkAlloc(ualni aBlockSize, void* aMemory, ualni aMemSize);
ChunkAlloc(ualni aBlockSize, ualni aNBlocks);
enum : ualni {
ALIGNED_SIZE = ENV_ALNI_SIZE_B,
void* allocate();
void deallocate(void* aPtr);
[[nodiscard]] bool isFull() const;
[[nodiscard]] bool isEmpty() const;
WRAP_SIZE_ALN = MEM_WRAP_SIZE / 2,
WRAP_SIZE = WRAP_SIZE_ALN * ALIGNED_SIZE,
~ChunkAlloc();
WRAP_VAL = MEM_WRAP_FILL_VAL,
CLEAR_ALLOC_VAL = MEM_CLEAR_ON_ALLOC_VAL,
CLEAR_DEALLOC_VAL = MEM_CLEAR_ON_DEALLOC_VAL,
};
static constexpr ualni dataSize() {
auto BLOCK_SIZE_BYTES = sizeof(tType);
auto BLOCK_SIZE_ALIGNED = BLOCK_SIZE_BYTES / ALIGNED_SIZE;
return BLOCK_SIZE_ALIGNED;
}
static constexpr ualni blockSize() {
auto BLOCK_SIZE_BYTES = sizeof(tType);
auto BLOCK_SIZE = dataSize() + bool(BLOCK_SIZE_BYTES % ALIGNED_SIZE) + WRAP_SIZE_ALN * 2;
return BLOCK_SIZE;
}
private:
ualni mBSize; // Size of data in aligned units
ualni mNBlocks;
ualni* mNextBlock;
ualni mNumFreeBlocks;
ualni mNumInitBlocks;
ualni mBuff[tNumBlocks * blockSize() * ALIGNED_SIZE];
ualni* mBuff = nullptr;
ualni* mNextBlock = nullptr;
ualni mNFreeBlocks;
ualni mNInitBlocks = 0;
bool mOwnBuff = false;
public:
ChunkAlloc() {
mNumFreeBlocks = tNumBlocks;
mNumInitBlocks = 0;
mNextBlock = mBuff;
}
~ChunkAlloc() = default; // TODO : check for leaks
public:
void* allocate(ualni) {
DEBUG_ASSERT(mNumFreeBlocks && "Out Of Memory")
// 1) PreInitialize blocks
if (mNumInitBlocks < tNumBlocks) {
mBuff[mNumInitBlocks * blockSize()] = (ualni) (mBuff + (mNumInitBlocks + 1) * blockSize());
mNumInitBlocks++;
}
// 2) Find free block and update next free block
auto data = mNextBlock;
mNextBlock = (ualni*)(*data);
mNumFreeBlocks--;
#ifdef MEM_DEBUG
// 3) Fill Wrap and offset data
auto wrap_top = data;
auto wrap_bottom = data + WRAP_SIZE_ALN + dataSize();
memSetVal(wrap_top, WRAP_SIZE, WRAP_VAL);
memSetVal(wrap_bottom, WRAP_SIZE, WRAP_VAL);
// 4) Clear data
#ifdef MEM_CLEAR_ON_ALLOC
memSetVal(data + WRAP_SIZE_ALN, dataSize() * ALIGNED_SIZE, CLEAR_ALLOC_VAL);
#endif
data += WRAP_SIZE_ALN;
#endif
return data;
}
void deallocate(void* aPtr) {
DEBUG_ASSERT(aPtr >= mBuff && aPtr < mBuff + tNumBlocks * blockSize())
auto block = (ualni*)aPtr;
#ifdef MEM_DEBUG
// 3) Check Wrap and offset data
auto wrap_bottom = block + dataSize();
auto wrap_top = block - WRAP_SIZE_ALN;
block = wrap_top;
// 3) Check the wrap
ASSERT(!memCompareVal(wrap_top, WRAP_SIZE, WRAP_VAL) && "Allocated Block Wrap Corrupted!")
ASSERT(!memCompareVal(wrap_bottom, WRAP_SIZE, WRAP_VAL) && "Allocated Block Wrap Corrupted!")
// 4) Clear data
#ifdef MEM_CLEAR_ON_ALLOC
memSetVal(block, blockSize() * ALIGNED_SIZE, CLEAR_DEALLOC_VAL);
#endif
#endif
(*block) = (ualni)mNextBlock;
mNextBlock = block;
mNumFreeBlocks++;
}
[[nodiscard]] bool checkWrap() const { return false; }
void checkValid() {}
public:
[[nodiscard]] bool isFull() const { return !mNumFreeBlocks; }
[[nodiscard]] bool isEmpty() const { return mNumFreeBlocks == tNumBlocks; }
[[nodiscard]] const ualni* getBuff() const { return mBuff; }
};
};
}

View file

@ -4,15 +4,23 @@
namespace tp {
struct HeapAlloc {
class HeapAlloc {
#ifdef MEM_DEBUG
ualni mNumAllocations = 0;
struct MemHeadLocal* mEntry = nullptr;
#endif
public:
HeapAlloc() = default;
~HeapAlloc();
public:
void* allocate(ualni aBlockSize);
void deallocate(void* aPtr);
~HeapAlloc();
public:
[[nodiscard]] bool checkWrap() const { return false; }
void checkValid() {}
};
};
}

View file

@ -4,15 +4,24 @@
namespace tp {
struct HeapAllocGlobal {
class HeapAllocGlobal {
#ifdef MEM_DEBUG
static ualni mNumAllocations;
static struct MemHead* mEntry;
#endif
public:
HeapAllocGlobal() = default;
~HeapAllocGlobal();
public:
static void* allocate(ualni aBlockSize);
static void deallocate(void* aPtr);
~HeapAllocGlobal();
static bool checkLeaks();
public:
[[nodiscard]] bool checkWrap() const { return false; }
void checkValid() {}
};
};
}

View file

@ -1,13 +0,0 @@
#pragma once
#include "HeapAllocatorGlobal.hpp"
namespace tp {
struct PickAlloc {
PickAlloc();
void* allocate(ualni aBlockSize);
void deallocate(void* aPtr);
~PickAlloc();
};
};

View file

@ -1,42 +1,162 @@
#pragma once
/*
Implementation:
* Adding chunk pointer to each chunk to form one-directional list that keeps track of free chunk
* Storing ordered pointers to chunks in order to find desired chunk from delete pointer on de-allocation in log time
*
* Allocations:
* 1) allocate with chunk stored in list entry
* 2) ...
*
* De-allocations:
* 1) binary-search with delete pointer to find desired chunk
* 2) ...
*
*/
#include "ChunkAllocator.hpp"
namespace tp {
// Pool Allocator
// Overcomes chunk allocator fixed number of max allocations
struct PoolAlloc {
template<typename tType, ualni tNumBlocks>
class PoolAlloc {
PoolAlloc(ualni aBlockSize, ualni aChunkSize);
void* allocate();
void deallocate(void* aPtr);
~PoolAlloc();
private:
struct Chunk : public ChunkAlloc {
Chunk(ualni aBlockSize, ualni aChunlSize) : ChunkAlloc(aBlockSize, aChunlSize) {}
Chunk* mNext = NULL;
Chunk* mPrev = NULL;
};
typedef ChunkAlloc<tType, tNumBlocks> Chunk;
struct Chunks {
void add(Chunk*);
void remove(Chunk*);
Chunk* find(void* aPtr);
Chunk* findNotFull();
Chunk** mBuff = NULL;
void add(Chunk* aChunk){
if (!mBuff) {
mLen = 16;
mBuff = (Chunk**) HeapAllocGlobal::allocate(sizeof(Chunk*) * mLen);
mUsedLen = 1;
mBuff[0] = aChunk;
return;
}
// ensure order
auto smaller_address = findUtil(mBuff, mBuff + mUsedLen, aChunk);
for (auto iter = mBuff + mUsedLen; iter != smaller_address; iter--) {
*iter = *(iter - 1);
}
*(smaller_address) = aChunk;
mUsedLen++;
// check for buff overflow
if (mUsedLen == mLen) {
auto prevBuff = mBuff;
mBuff = (Chunk**) HeapAllocGlobal::allocate(sizeof(Chunk*) * mLen * 2);
memCopy(mBuff, prevBuff, sizeof(Chunk*) * mUsedLen);
mLen *= 2;
HeapAllocGlobal::deallocate(prevBuff);
}
}
void remove(Chunk** del_address){
if (mUsedLen == 1) {
mLen = 0;
mUsedLen = 0;
HeapAllocGlobal::deallocate(mBuff);
mBuff = nullptr;
return;
}
// ensure order
for (auto iter = del_address; iter != mBuff + mUsedLen - 1; iter++) {
*iter = *(iter + 1);
}
mUsedLen--;
// check for buff low usage
if ((halnf)mUsedLen / (halnf)mLen < 0.25f) {
auto prevBuff = mBuff;
mBuff = (Chunk**)HeapAllocGlobal::allocate(sizeof(Chunk*) * mLen / 2);
memCopy(mBuff, prevBuff, sizeof(Chunk*) * mUsedLen);
mLen /= 2;
HeapAllocGlobal::deallocate(prevBuff);
}
}
[[nodiscard]] Chunk** find(void* aPtr) {
return findUtil(mBuff, mBuff + mUsedLen, aPtr) - 1;
}
[[nodiscard]] Chunk* findNotFull() const {
for (ualni idx = 0; idx < mUsedLen; idx++) {
if (!mBuff[idx]->isFull()) {
return mBuff[idx];
}
}
return nullptr;
}
Chunk** mBuff = nullptr;
ualni mUsedLen = 0;
ualni mLen = 0;
private:
Chunk** findUtil(Chunk** aLeft, Chunk** aRight, void* aPtr);
Chunk** findUtil(Chunk** aLeft, Chunk** aRight, void* aPtr) {
auto range = ualni(aRight - aLeft);
if (range == 1) {
return (aPtr < *aLeft) ? aLeft : aRight;
}
auto middle = aLeft + range / 2;
return (aPtr >= (*middle)) ? findUtil(middle, aRight, aPtr) : findUtil(aLeft, middle, aPtr);
}
};
private:
Chunks mChunks;
Chunk* mFreeChunk = NULL;
ualni mBlockSize;
ualni mChunkSize;
Chunk* mFreeChunk = nullptr;
public:
PoolAlloc() = default;
~PoolAlloc() = default;
public:
void* allocate(ualni) {
if (!mFreeChunk || mFreeChunk->isFull()) {
auto new_free_chunk = mChunks.findNotFull();
if (!new_free_chunk) {
new_free_chunk = new (HeapAllocGlobal::allocate(sizeof(Chunk))) Chunk();
DEBUG_ASSERT(new_free_chunk)
mChunks.add(new_free_chunk);
}
mFreeChunk = new_free_chunk;
}
return mFreeChunk->allocate(0);
}
void deallocate(void* aPtr) {
auto chunk = mChunks.find(aPtr);
(*chunk)->deallocate(aPtr);
if ((*chunk)->isEmpty()) {
if (mFreeChunk == *chunk) mFreeChunk = nullptr;
HeapAllocGlobal::deallocate(*chunk);
mChunks.remove(chunk);
}
}
public:
[[nodiscard]] bool checkWrap() const { return false; }
void checkValid() {
return;
for (auto i = 0; i < mChunks.mUsedLen; i++) {
for (auto j = 0; j < mChunks.mUsedLen; j++) {
if (i > j) {
ASSERT(mChunks.mBuff[i] > mChunks.mBuff[j])
} else if (i < j) {
ASSERT(mChunks.mBuff[i] < mChunks.mBuff[j])
}
}
}
}
};
};
}

View file

@ -0,0 +1,10 @@
#pragma once
#define MEM_WRAP_SIZE 8 // Wrapping Size in aligned units
#define MEM_WRAP_FILL_VAL 0xBB // Wrapping Fill Value
#define MEM_CLEAR_ON_ALLOC // Clear data on allocation
#define MEM_CLEAR_ON_DEALLOC // Clear data on free
#define MEM_CLEAR_ON_DEALLOC_VAL 0xAA // Clear data on free
#define MEM_CLEAR_ON_ALLOC_VAL 0xCC // Clear data on free
#define MEM_STACK_TRACE // Save stack on allocation call
#define MEM_STACK_TRACE_MAX_DEPTH 32 // Call stack max depth