see ya later allocator
This commit is contained in:
parent
57d5591d4a
commit
fa2d0aaa92
44 changed files with 32 additions and 1693 deletions
|
|
@ -1,27 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "Utils.hpp"
|
||||
|
||||
#include "ChunkAllocator.hpp"
|
||||
#include "HeapAllocator.hpp"
|
||||
#include "HeapAllocatorGlobal.hpp"
|
||||
#include "PoolAllocator.hpp"
|
||||
|
||||
namespace tp {
|
||||
extern ModuleManifest gModuleAllocators;
|
||||
}
|
||||
|
||||
void* operator new(std::size_t aSize);
|
||||
void* operator new[](std::size_t aSize);
|
||||
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);
|
||||
void operator delete(void* aPtr, tp::HeapAlloc& aAlloc);
|
||||
void operator delete[](void* aPtr, tp::HeapAlloc& aAlloc);
|
||||
|
||||
void* operator new(std::size_t aSize, tp::HeapAllocGlobal& aAlloc);
|
||||
void* operator new[](std::size_t aSize, tp::HeapAllocGlobal& aAlloc);
|
||||
void operator delete(void* aPtr, tp::HeapAllocGlobal& aAlloc);
|
||||
void operator delete[](void* aPtr, tp::HeapAllocGlobal& aAlloc);
|
||||
|
|
@ -1,136 +0,0 @@
|
|||
#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 "HeapAllocatorGlobal.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.
|
||||
template <typename tType, ualni tNumBlocks>
|
||||
class ChunkAlloc {
|
||||
|
||||
enum : ualni {
|
||||
ALIGNED_SIZE = ENV_ALNI_SIZE_B,
|
||||
|
||||
WRAP_SIZE_ALN = MEM_WRAP_SIZE / 2,
|
||||
WRAP_SIZE = WRAP_SIZE_ALN * ALIGNED_SIZE,
|
||||
|
||||
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* mNextBlock;
|
||||
ualni mNumFreeBlocks;
|
||||
ualni mNumInitBlocks;
|
||||
ualni mBuff[tNumBlocks * blockSize() * ALIGNED_SIZE];
|
||||
|
||||
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; }
|
||||
};
|
||||
}
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "Environment.hpp"
|
||||
|
||||
namespace tp {
|
||||
|
||||
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);
|
||||
|
||||
public:
|
||||
[[nodiscard]] bool checkWrap() const { return false; }
|
||||
void checkValid() {}
|
||||
};
|
||||
}
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "Callstack.hpp"
|
||||
#include <mutex>
|
||||
|
||||
namespace tp {
|
||||
|
||||
class HeapAllocGlobal {
|
||||
|
||||
#ifdef MEM_DEBUG
|
||||
static ualni mNumAllocations;
|
||||
static struct MemHead* mEntry;
|
||||
static std::mutex mMutex;
|
||||
static bool mIgnore;
|
||||
static bool mEnableCallstack;
|
||||
#ifdef MEM_STACK_TRACE // Save stack on allocation call
|
||||
static CallStackCapture mCallstack;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
public:
|
||||
HeapAllocGlobal() = default;
|
||||
~HeapAllocGlobal();
|
||||
|
||||
public:
|
||||
static void* allocate(ualni aBlockSize);
|
||||
static void deallocate(void* aPtr);
|
||||
|
||||
static bool checkLeaks();
|
||||
static void startIgnore();
|
||||
static void stopIgnore();
|
||||
static ualni getNAllocations();
|
||||
|
||||
static void enableCallstack();
|
||||
static void disableCallstack();
|
||||
|
||||
public:
|
||||
[[nodiscard]] bool checkWrap() const { return false; }
|
||||
void checkValid() {}
|
||||
};
|
||||
}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "Common.hpp"
|
||||
|
||||
#ifndef ENV_OS_WINDOWS
|
||||
inline void* operator new(std::size_t aSize, void* aWhere) noexcept { return aWhere; }
|
||||
inline void* operator new[](std::size_t aSize, void* aWhere) noexcept { return aWhere; }
|
||||
#endif
|
||||
|
|
@ -1,161 +0,0 @@
|
|||
#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
|
||||
template <typename tType, ualni tNumBlocks>
|
||||
class PoolAlloc {
|
||||
|
||||
typedef ChunkAlloc<tType, tNumBlocks> Chunk;
|
||||
|
||||
struct Chunks {
|
||||
|
||||
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) {
|
||||
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 = 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) {
|
||||
if (!aPtr) return;
|
||||
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])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
#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_MAX_DEPTH 32 // Call stack max depth
|
||||
Loading…
Add table
Add a link
Reference in a new issue