aboutsummaryrefslogtreecommitdiff
path: root/src/zencore/memory
diff options
context:
space:
mode:
Diffstat (limited to 'src/zencore/memory')
-rw-r--r--src/zencore/memory/memoryarena.cpp128
1 files changed, 103 insertions, 25 deletions
diff --git a/src/zencore/memory/memoryarena.cpp b/src/zencore/memory/memoryarena.cpp
index 8807f3264..9eabaf71e 100644
--- a/src/zencore/memory/memoryarena.cpp
+++ b/src/zencore/memory/memoryarena.cpp
@@ -2,18 +2,52 @@
#include <zencore/memory/memoryarena.h>
+#include <algorithm>
#include <cstring>
namespace zen {
+MemoryArena::MemoryArena(size_t ChunkSize) : m_ChunkSize(ChunkSize == 0 ? kDefaultChunkSize : ChunkSize)
+{
+}
+
MemoryArena::~MemoryArena()
{
- for (auto Chunk : m_Chunks)
- delete[] Chunk;
+ for (const Chunk& C : m_Chunks)
+ {
+ delete[] C.Base;
+ }
+}
+
+// An allocation whose worst-case footprint won't fit inside a freshly
+// allocated standard-sized chunk is "oversize" — we route it to its own
+// dedicated chunk so it doesn't waste the rest of the regular chunk's
+// space, and so a subsequent small allocation still has a near-empty
+// regular chunk to bump-pointer into.
+//
+// Note: WorstCaseBytes is the upper bound on bytes the allocation could
+// consume from a freshly aligned chunk start. The actual returned
+// pointer + size fits within this footprint by construction.
+bool
+MemoryArena::IsOversizeRequest(size_t WorstCaseBytes) const
+{
+ return WorstCaseBytes > m_ChunkSize;
+}
+
+uint8_t*
+MemoryArena::AllocateDedicatedChunk(size_t Capacity)
+{
+ uint8_t* NewChunk = new (std::nothrow) uint8_t[Capacity];
+ if (!NewChunk)
+ {
+ return nullptr;
+ }
+ m_Chunks.push_back({NewChunk, Capacity});
+ return NewChunk;
}
void*
-MemoryArena::AllocateAligned(size_t ByteCount, size_t align)
+MemoryArena::AllocateAligned(size_t ByteCount, size_t Align)
{
if (ByteCount == 0)
{
@@ -23,19 +57,31 @@ MemoryArena::AllocateAligned(size_t ByteCount, size_t align)
void* Ptr = nullptr;
m_Lock.WithExclusiveLock([&] {
- size_t AlignedOffset = (m_CurrentOffset + (align - 1)) & ~(align - 1);
+ // Oversize fast path — give the request its own chunk, leave the
+ // regular bump pointer untouched so subsequent small allocs still
+ // reuse it.
+ const size_t WorstCase = ByteCount + Align - 1;
+ if (IsOversizeRequest(WorstCase))
+ {
+ if (uint8_t* Dedicated = AllocateDedicatedChunk(WorstCase))
+ {
+ const size_t Aligned = (reinterpret_cast<uintptr_t>(Dedicated) + (Align - 1)) & ~(uintptr_t(Align - 1));
+ Ptr = reinterpret_cast<void*>(Aligned);
+ }
+ return;
+ }
- if (m_CurrentChunk == nullptr || AlignedOffset + ByteCount > ChunkSize)
+ size_t AlignedOffset = (m_CurrentOffset + (Align - 1)) & ~(Align - 1);
+ if (m_CurrentChunk == nullptr || AlignedOffset + ByteCount > m_CurrentChunkCapacity)
{
- uint8_t* NewChunk = new uint8_t[ChunkSize];
+ uint8_t* NewChunk = AllocateDedicatedChunk(m_ChunkSize);
if (!NewChunk)
{
return;
}
-
- m_Chunks.push_back(NewChunk);
- m_CurrentChunk = NewChunk;
- AlignedOffset = 0;
+ m_CurrentChunk = NewChunk;
+ m_CurrentChunkCapacity = m_ChunkSize;
+ AlignedOffset = 0;
}
Ptr = m_CurrentChunk + AlignedOffset;
@@ -46,7 +92,7 @@ MemoryArena::AllocateAligned(size_t ByteCount, size_t align)
}
void*
-MemoryArena::AllocateAlignedWithOffset(size_t ByteCount, size_t align, size_t offset)
+MemoryArena::AllocateAlignedWithOffset(size_t ByteCount, size_t Align, size_t Offset)
{
if (ByteCount == 0)
{
@@ -56,19 +102,29 @@ MemoryArena::AllocateAlignedWithOffset(size_t ByteCount, size_t align, size_t of
void* Ptr = nullptr;
m_Lock.WithExclusiveLock([&] {
- size_t AlignedOffset = (m_CurrentOffset + (align - 1) + offset) & ~(align - 1);
+ const size_t WorstCase = ByteCount + Align - 1 + Offset;
+ if (IsOversizeRequest(WorstCase))
+ {
+ if (uint8_t* Dedicated = AllocateDedicatedChunk(WorstCase))
+ {
+ const uintptr_t Base = reinterpret_cast<uintptr_t>(Dedicated);
+ const uintptr_t Aligned = (Base + (Align - 1) + Offset) & ~(uintptr_t(Align - 1));
+ Ptr = reinterpret_cast<void*>(Aligned);
+ }
+ return;
+ }
- if (m_CurrentChunk == nullptr || AlignedOffset + ByteCount > ChunkSize)
+ size_t AlignedOffset = (m_CurrentOffset + (Align - 1) + Offset) & ~(Align - 1);
+ if (m_CurrentChunk == nullptr || AlignedOffset + ByteCount > m_CurrentChunkCapacity)
{
- uint8_t* NewChunk = new uint8_t[ChunkSize];
+ uint8_t* NewChunk = AllocateDedicatedChunk(m_ChunkSize);
if (!NewChunk)
{
return;
}
-
- m_Chunks.push_back(NewChunk);
- m_CurrentChunk = NewChunk;
- AlignedOffset = offset;
+ m_CurrentChunk = NewChunk;
+ m_CurrentChunkCapacity = m_ChunkSize;
+ AlignedOffset = Offset;
}
Ptr = m_CurrentChunk + AlignedOffset;
@@ -90,19 +146,28 @@ MemoryArena::Allocate(size_t Size)
constexpr size_t Alignment = alignof(std::max_align_t);
m_Lock.WithExclusiveLock([&] {
- size_t AlignedOffset = (m_CurrentOffset + Alignment - 1) & ~(Alignment - 1);
+ const size_t WorstCase = Size + Alignment - 1;
+ if (IsOversizeRequest(WorstCase))
+ {
+ if (uint8_t* Dedicated = AllocateDedicatedChunk(WorstCase))
+ {
+ const uintptr_t Aligned = (reinterpret_cast<uintptr_t>(Dedicated) + Alignment - 1) & ~(uintptr_t(Alignment - 1));
+ Ptr = reinterpret_cast<void*>(Aligned);
+ }
+ return;
+ }
- if (m_CurrentChunk == nullptr || AlignedOffset + Size > ChunkSize)
+ size_t AlignedOffset = (m_CurrentOffset + Alignment - 1) & ~(Alignment - 1);
+ if (m_CurrentChunk == nullptr || AlignedOffset + Size > m_CurrentChunkCapacity)
{
- uint8_t* NewChunk = new uint8_t[ChunkSize];
+ uint8_t* NewChunk = AllocateDedicatedChunk(m_ChunkSize);
if (!NewChunk)
{
return;
}
-
- m_Chunks.push_back(NewChunk);
- m_CurrentChunk = NewChunk;
- AlignedOffset = 0;
+ m_CurrentChunk = NewChunk;
+ m_CurrentChunkCapacity = m_ChunkSize;
+ AlignedOffset = 0;
}
Ptr = m_CurrentChunk + AlignedOffset;
@@ -112,6 +177,19 @@ MemoryArena::Allocate(size_t Size)
return Ptr;
}
+size_t
+MemoryArena::GetReservedBytes() const
+{
+ size_t Total = 0;
+ m_Lock.WithSharedLock([&] {
+ for (const Chunk& Item : m_Chunks)
+ {
+ Total += Item.Capacity;
+ }
+ });
+ return Total;
+}
+
const char*
MemoryArena::DuplicateString(std::string_view Str)
{