From f9cb4ddae24cf10e9fe7b6b71e4869395eb0a9ab Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sat, 24 May 2025 05:08:32 +0200 Subject: [PATCH] Add new Arena Allocator for optimized dynamic memory allocations with a shared lifetime --- Source/Engine/Core/Memory/Allocation.cpp | 44 +++++++++++++++++ Source/Engine/Core/Memory/ArenaAllocation.h | 55 +++++++++++++++++++++ 2 files changed, 99 insertions(+) create mode 100644 Source/Engine/Core/Memory/Allocation.cpp create mode 100644 Source/Engine/Core/Memory/ArenaAllocation.h diff --git a/Source/Engine/Core/Memory/Allocation.cpp b/Source/Engine/Core/Memory/Allocation.cpp new file mode 100644 index 000000000..87c9dbc63 --- /dev/null +++ b/Source/Engine/Core/Memory/Allocation.cpp @@ -0,0 +1,44 @@ +// Copyright (c) Wojciech Figat. All rights reserved. + +#include "ArenaAllocation.h" +#include "../Math/Math.h" + +void ArenaAllocator::Free() +{ + // Free all pages + Page* page = _first; + while (page) + { + Allocator::Free(page->Memory); + Page* next = page->Next; + Allocator::Free(page); + page = next; + } +} + +void* ArenaAllocator::Allocate(uint64 size, uint64 alignment) +{ + // Find the first page that has some space left + Page* page = _first; + while (page && page->Offset + size + alignment > page->Size) + page = page->Next; + + // Create a new page if need to + if (!page) + { + uint64 pageSize = Math::Max(_pageSize, size); + page = (Page*)Allocator::Allocate(sizeof(Page)); + page->Memory = Allocator::Allocate(pageSize); + page->Next = _first; + page->Offset = 0; + page->Size = pageSize; + _first = page; + } + + // Allocate within a page + page->Offset = Math::AlignUp(page->Offset, (uint32)alignment); + void* mem = (byte*)page->Memory + page->Offset; + page->Offset += size; + + return mem; +} \ No newline at end of file diff --git a/Source/Engine/Core/Memory/ArenaAllocation.h b/Source/Engine/Core/Memory/ArenaAllocation.h new file mode 100644 index 000000000..18915853d --- /dev/null +++ b/Source/Engine/Core/Memory/ArenaAllocation.h @@ -0,0 +1,55 @@ +// Copyright (c) Wojciech Figat. All rights reserved. + +#include "Allocation.h" + +/// +/// Allocator that uses pages for stack-based allocs without freeing memory during it's lifetime. +/// +class ArenaAllocator +{ +private: + struct Page + { + void* Memory; + Page* Next; + uint32 Offset, Size; + }; + + int32 _pageSize; + Page* _first = nullptr; + +public: + ArenaAllocator(int32 pageSizeBytes = 1024 * 1024) // 1 MB by default + : _pageSize(pageSizeBytes) + { + } + + ~ArenaAllocator() + { + Free(); + } + + // Allocates a chunk of unitialized memory. + void* Allocate(uint64 size, uint64 alignment = 1); + + // Frees all memory allocations within allocator. + void Free(); + + // Creates a new object within the arena allocator. + template + inline T* New(Args&&...args) + { + T* ptr = (T*)Allocate(sizeof(T)); + new(ptr) T(Forward(args)...); + return ptr; + } + + // Invokes destructor on values in a dictionary and clears it. + template + void ClearDelete(Dictionary& collection) + { + for (auto it = collection.Begin(); it.IsNotEnd(); ++it) + Memory::DestructItem(it->Value); + collection.Clear(); + } +};