allocator fix

This commit is contained in:
cdemeyer-teachx
2025-08-27 10:40:32 +09:00
parent d27a6a41fb
commit 1dc9328f50

View File

@@ -63,13 +63,23 @@ namespace WFC {
*/
class WFCStackAllocator {
private:
struct MemoryPool {
void* ptr;
size_t size;
size_t used;
MemoryPool() : ptr(nullptr), size(0), used(0) {}
MemoryPool(void* p, size_t s) : ptr(p), size(s), used(0) {}
};
struct Block {
void* ptr;
size_t size;
size_t alignment;
size_t poolIndex; // Which pool this allocation came from
Block() : ptr(nullptr), size(0), alignment(0) {}
Block(void* p, size_t s, size_t a) : ptr(p), size(s), alignment(a) {}
Block() : ptr(nullptr), size(0), alignment(0), poolIndex(0) {}
Block(void* p, size_t s, size_t a, size_t pi) : ptr(p), size(s), alignment(a), poolIndex(pi) {}
};
public:
@@ -78,19 +88,15 @@ public:
* @param initialCapacity Initial memory pool size in bytes
*/
explicit WFCStackAllocator(size_t initialCapacity = 1024 * 1024) // 1MB default
: m_capacity(initialCapacity)
, m_used(0)
, m_pool(nullptr)
{
m_pool = allocate_aligned_memory(64, m_capacity); // 64-byte alignment for cache efficiency
if (!m_pool) {
throw std::bad_alloc();
}
addPool(initialCapacity);
}
~WFCStackAllocator() {
if (m_pool) {
free_aligned_memory(m_pool);
for (auto& pool : m_pools) {
if (pool.ptr) {
free_aligned_memory(pool.ptr);
}
}
}
@@ -107,20 +113,36 @@ public:
* @return Pointer to allocated memory
*/
void* allocate(size_t size, size_t alignment = 8) {
// Align the current position
size_t alignedUsed = alignUp(m_used, alignment);
// Try to allocate from existing pools
for (size_t i = 0; i < m_pools.size(); ++i) {
auto& pool = m_pools[i];
// Check if we have enough space
if (alignedUsed + size > m_capacity) {
// Grow the pool if needed
growPool(alignedUsed + size);
// Align the current position in this pool
size_t alignedUsed = alignUp(pool.used, alignment);
// Check if we have enough space in this pool
if (alignedUsed + size <= pool.size) {
void* ptr = static_cast<char*>(pool.ptr) + alignedUsed;
pool.used = alignedUsed + size;
// Track this allocation for proper cleanup
m_allocations.emplace_back(ptr, size, alignment, i);
return ptr;
}
}
void* ptr = static_cast<char*>(m_pool) + alignedUsed;
m_used = alignedUsed + size;
// No existing pool has enough space, add a new one
size_t newPoolSize = std::max(m_pools.back().size * 2, size * 2); // Grow exponentially
addPool(newPoolSize);
// Now allocate from the new pool (which is the last one)
auto& newPool = m_pools.back();
void* ptr = static_cast<char*>(newPool.ptr);
newPool.used = size;
// Track this allocation for proper cleanup
m_allocations.emplace_back(ptr, size, alignment);
m_allocations.emplace_back(ptr, size, alignment, m_pools.size() - 1);
return ptr;
}
@@ -141,19 +163,39 @@ public:
class StackFrame {
private:
WFCStackAllocator& m_allocator;
size_t m_savedUsed;
std::vector<size_t> m_savedUsed;
size_t m_savedAllocCount;
public:
StackFrame(WFCStackAllocator& allocator)
: m_allocator(allocator)
, m_savedUsed(allocator.m_used)
, m_savedAllocCount(allocator.m_allocations.size())
{}
{
// Save the current used state of all pools
m_savedUsed.reserve(allocator.m_pools.size());
for (const auto& pool : allocator.m_pools) {
m_savedUsed.push_back(pool.used);
}
}
~StackFrame() {
// Restore the stack to the previous state
m_allocator.m_used = m_savedUsed;
// Restore the used state of all pools
for (size_t i = 0; i < m_savedUsed.size() && i < m_allocator.m_pools.size(); ++i) {
m_allocator.m_pools[i].used = m_savedUsed[i];
}
// Remove any new pools that were added during this frame
if (m_allocator.m_pools.size() > m_savedUsed.size()) {
// Free the additional pools that were added
for (size_t i = m_savedUsed.size(); i < m_allocator.m_pools.size(); ++i) {
if (m_allocator.m_pools[i].ptr) {
free_aligned_memory(m_allocator.m_pools[i].ptr);
}
}
m_allocator.m_pools.resize(m_savedUsed.size());
}
// Remove allocations that were made during this frame
m_allocator.m_allocations.resize(m_savedAllocCount);
}
@@ -174,12 +216,24 @@ public:
/**
* @brief Get current memory usage
*/
size_t getUsed() const { return m_used; }
size_t getUsed() const {
size_t total = 0;
for (const auto& pool : m_pools) {
total += pool.used;
}
return total;
}
/**
* @brief Get total capacity
*/
size_t getCapacity() const { return m_capacity; }
size_t getCapacity() const {
size_t total = 0;
for (const auto& pool : m_pools) {
total += pool.size;
}
return total;
}
/**
* @brief Get allocation count
@@ -190,41 +244,27 @@ public:
* @brief Reset the allocator (useful for reusing between WFC runs)
*/
void reset() {
m_used = 0;
for (auto& pool : m_pools) {
pool.used = 0;
}
m_allocations.clear();
}
private:
void addPool(size_t size) {
void* ptr = allocate_aligned_memory(64, size); // 64-byte alignment for cache efficiency
if (!ptr) {
throw std::bad_alloc();
}
m_pools.emplace_back(ptr, size);
}
size_t alignUp(size_t value, size_t alignment) const {
return (value + alignment - 1) & ~(alignment - 1);
}
void growPool(size_t requiredSize) {
size_t newCapacity = std::max(m_capacity * 2, requiredSize);
void* newPool = allocate_aligned_memory(64, newCapacity);
if (!newPool) {
throw std::bad_alloc();
}
// Copy existing data
std::memcpy(newPool, m_pool, m_used);
// Update all existing pointers in allocations
ptrdiff_t offset = static_cast<char*>(newPool) - static_cast<char*>(m_pool);
for (auto& block : m_allocations) {
block.ptr = static_cast<char*>(block.ptr) + offset;
}
free_aligned_memory(m_pool);
m_pool = newPool;
m_capacity = newCapacity;
}
private:
size_t m_capacity;
size_t m_used;
void* m_pool;
std::vector<MemoryPool> m_pools;
std::vector<Block> m_allocations;
};