From 3940805f0eaa13ed039e3d9d3aad478f5028b840 Mon Sep 17 00:00:00 2001 From: ouczbs Date: Wed, 13 Nov 2024 16:37:15 +0800 Subject: [PATCH] add my memory pool --- .../engine/asset/include/asset/res/guid.h | 1 + .../engine/zlib/include/pmr/frame_allocator.h | 12 +- .../zlib/include/pmr/frame_allocator.inl | 6 - .../modules/engine/zlib/include/pmr/memory.h | 99 +++++++ .../engine/zlib/include/pmr/memory.inl | 96 ------- .../zlib/include/pmr/memory/memory_pool.h | 267 ++++++++++++++++++ .../engine/zlib/include/pmr/memory/type.h | 17 ++ engine/src/engine/zlib.cpp | 4 +- game/zworld/editor/zworld_editor.cpp | 2 +- 9 files changed, 393 insertions(+), 111 deletions(-) create mode 100644 engine/modules/engine/zlib/include/pmr/memory.h delete mode 100644 engine/modules/engine/zlib/include/pmr/memory.inl create mode 100644 engine/modules/engine/zlib/include/pmr/memory/memory_pool.h create mode 100644 engine/modules/engine/zlib/include/pmr/memory/type.h diff --git a/engine/modules/engine/asset/include/asset/res/guid.h b/engine/modules/engine/asset/include/asset/res/guid.h index 5f095e9..c4973a6 100644 --- a/engine/modules/engine/asset/include/asset/res/guid.h +++ b/engine/modules/engine/asset/include/asset/res/guid.h @@ -24,6 +24,7 @@ namespace api Guid(string_view str) noexcept : Guid{str.data()} {} USING_OVERLOAD_CTOR(Guid, const char*) UFUNCTION({}, ref = USING_CTOR_NAME) + Guid(const std::string& str) noexcept : Guid(str.data()){} Guid(const char* str) noexcept : Guid{} { diff --git a/engine/modules/engine/zlib/include/pmr/frame_allocator.h b/engine/modules/engine/zlib/include/pmr/frame_allocator.h index db79a5c..439e7f5 100644 --- a/engine/modules/engine/zlib/include/pmr/frame_allocator.h +++ b/engine/modules/engine/zlib/include/pmr/frame_allocator.h @@ -1,9 +1,7 @@ #pragma once #include #include -constexpr inline size_t meta_align_size(size_t size, size_t alignment = 8) { - return (size + alignment - 1) / alignment * alignment; -}; +#include "memory/type.h" namespace pmr { using std::pmr::unsynchronized_pool_resource; using std::pmr::memory_resource; @@ -50,5 +48,9 @@ inline void* operator new(size_t size, pmr::FrameAllocatorPool* pool, size_t ali size = (size + alignment - 1) & ~(alignment - 1); return pool->allocate(size, alignment); } -#include "frame_allocator.inl" -#include "memory.inl" \ No newline at end of file +//全局生命周期,不回收内存 +//局部生命周期,每帧回收内存 +ZLIB_API extern pmr::FrameAllocatorPool* MetaGlobalPool(); +ZLIB_API extern pmr::FrameAllocatorPool* GlobalPool(); +ZLIB_API extern pmr::FrameAllocatorPool* FramePool(); +#include "frame_allocator.inl" \ No newline at end of file diff --git a/engine/modules/engine/zlib/include/pmr/frame_allocator.inl b/engine/modules/engine/zlib/include/pmr/frame_allocator.inl index bccb554..e16ec52 100644 --- a/engine/modules/engine/zlib/include/pmr/frame_allocator.inl +++ b/engine/modules/engine/zlib/include/pmr/frame_allocator.inl @@ -12,10 +12,6 @@ namespace pmr { size_t space = capacity - offset; void* ptr = buffer + offset; if (bytes > capacity) { - if (std::align(alignment, bytes, ptr, space)) { - offset = capacity - space + bytes; - return ptr; - } throw std::bad_alloc(); } if (std::align(alignment, bytes, ptr, space)) { @@ -47,7 +43,6 @@ namespace pmr { count = count > 0 ? count : 1; allocators.erase(allocators.begin() + count, allocators.end()); } -#ifdef ZLIB_API_VAL //内存分配和释放必须位于同一模块(DLL) inline FrameAllocator& FrameAllocator::operator=(FrameAllocator&& o)noexcept { std::construct_at(this, std::forward(o)); @@ -68,5 +63,4 @@ namespace pmr { if (buffer) delete[] buffer; } -#endif }; \ No newline at end of file diff --git a/engine/modules/engine/zlib/include/pmr/memory.h b/engine/modules/engine/zlib/include/pmr/memory.h new file mode 100644 index 0000000..13ccfa4 --- /dev/null +++ b/engine/modules/engine/zlib/include/pmr/memory.h @@ -0,0 +1,99 @@ +#pragma once +#include "frame_allocator.h" +#include "memory/memory_pool.h" +ZLIB_API inline pmr::FrameAllocatorPool* MetaGlobalPool() { + static pmr::FrameAllocatorPool* globalPool = new pmr::FrameAllocatorPool(); + return globalPool; +} +ZLIB_API inline pmr::FrameAllocatorPool* GlobalPool() { + static pmr::FrameAllocatorPool* globalPool = new pmr::FrameAllocatorPool(); + return globalPool; +} +ZLIB_API inline pmr::FrameAllocatorPool* FramePool() { + static pmr::FrameAllocatorPool* framePool = new pmr::FrameAllocatorPool(); + return framePool; +} +namespace pmr { + inline MemoryPoolManager* pMainPool; + inline BlockAllocator* pMetaAlloc; + void CleanMainMemPool() { + if (!pMainPool) return; + pMainPool->~MemoryPoolManager(); + pMetaAlloc->~BlockAllocator(); + } + void* meta_malloc(size_t bytes, size_t alignment) + { + if (bytes > META_BLOCK_EMEM_SIZE * MAX_BLOCK_ELEM_GROUP_N || !pMetaAlloc->try_allocate(bytes)) { + return pMainPool->do_allocate(bytes, alignment); + } + using MemBlock = BlockAllocator::MemBlock; + bytes += sizeof(MemBlock); + MemBlock* pBlock = (MemBlock*)pMetaAlloc->do_allocate(bytes, alignment); + new(pBlock)MemBlock(bytes); + return (char*)pBlock + sizeof(MemBlock); + } + void meta_free(void* ptr) + { + if (!ptr) { + return; + } + using MemBlock = BlockAllocator::MemBlock; + MemBlock* pBlock = (MemBlock*)((char*)ptr - sizeof(MemBlock)); + if (!pBlock) { + pMainPool->do_deallocate(ptr); + return; + } + pMetaAlloc->do_deallocate(ptr, pBlock->size, 0); + } +} +namespace pmr { + struct MemPoolWrap { + inline static char AllocData[sizeof(BlockAllocator)]; + char pMemory[sizeof(MemoryPoolManager)]; + bool isMainTread; + MemPoolWrap() { + isMainTread = !pMainPool; + if (isMainTread) { + pMainPool = (MemoryPoolManager*)&pMemory; + pMetaAlloc = (BlockAllocator*)&AllocData; + std::construct_at(pMetaAlloc, MEMORY_BLOCK_SIZE, META_BLOCK_EMEM_SIZE); + std::atexit(pmr::CleanMainMemPool); + } + std::construct_at((MemoryPoolManager*)&pMemory); + } + ~MemPoolWrap() { + auto pool = (MemoryPoolManager*)&pMemory; + if (!isMainTread) {//主线程最后析构 + pool->~MemoryPoolManager(); + } + } + MemoryPoolManager* operator->() { + return (MemoryPoolManager*)&pMemory; + } + }; +} +thread_local pmr::MemPoolWrap MemPool; + +//#define mempool_allocate(...) MemPool->allocate(__VA_ARGS__); +//#define mempool_deallocate(ptr) MemPool->deallocate(ptr, 0); +void* mempool_allocate(size_t size, size_t align = 8) { + return MemPool->do_allocate(size, align); +} +void mempool_deallocate(void* ptr) { + return MemPool->do_deallocate(ptr); +} +void* operator new(size_t size) { + return mempool_allocate(size); +} +// 默认对齐方式 delete +void operator delete(void* ptr) noexcept { + mempool_deallocate(ptr); +} +// Array new and delete +void* operator new[](size_t size) { + return mempool_allocate(size); +} + +void operator delete[](void* ptr) noexcept { + mempool_deallocate(ptr); +} \ No newline at end of file diff --git a/engine/modules/engine/zlib/include/pmr/memory.inl b/engine/modules/engine/zlib/include/pmr/memory.inl deleted file mode 100644 index a0ccf93..0000000 --- a/engine/modules/engine/zlib/include/pmr/memory.inl +++ /dev/null @@ -1,96 +0,0 @@ -#pragma once -#include "singleton.h" -//全局生命周期,不回收内存 -//局部生命周期,每帧回收内存 -ZLIB_API extern pmr::FrameAllocatorPool* MetaGlobalPool(); -ZLIB_API extern pmr::FrameAllocatorPool* GlobalPool(); -ZLIB_API extern pmr::FrameAllocatorPool* FramePool(); -extern void* operator new(std::size_t size); -extern void operator delete(void* ptr) noexcept; -extern void* operator new[](std::size_t size); -extern void operator delete[](void* ptr) noexcept; -#ifdef ZLIB_API_VAL -ZLIB_API inline pmr::FrameAllocatorPool* MetaGlobalPool() { - static pmr::FrameAllocatorPool* globalPool = new pmr::FrameAllocatorPool(); - return globalPool; -} -ZLIB_API inline pmr::FrameAllocatorPool* GlobalPool() { - static pmr::FrameAllocatorPool* globalPool = new pmr::FrameAllocatorPool(); - return globalPool; -} -ZLIB_API inline pmr::FrameAllocatorPool* FramePool() { - static pmr::FrameAllocatorPool* framePool = new pmr::FrameAllocatorPool(); - return framePool; -} -struct MemPoolWrap { - char pMemory[sizeof(pmr::unsynchronized_pool_resource)]; - uint32_t id; - MemPoolWrap() { - static uint32_t sThreadId = 0; - id = sThreadId++; - std::construct_at((pmr::unsynchronized_pool_resource*)&pMemory); - } - ~MemPoolWrap() { - auto pool = (pmr::unsynchronized_pool_resource*)&pMemory; - if (id) {//主线程不能析构,会有顺序问题 - pool->~unsynchronized_pool_resource(); - } - } - pmr::unsynchronized_pool_resource* operator->() { - return (pmr::unsynchronized_pool_resource*)&pMemory; - } -}; -thread_local MemPoolWrap MemPool; - -//#define mempool_allocate(...) MemPool->allocate(__VA_ARGS__); -//#define mempool_deallocate(ptr) MemPool->deallocate(ptr, 0); - -#define MEM_BLOCK_BEGIN_MASK 0x5555 -#define MEM_BLOCK_END_MASK 0xAAAA -struct MemBlock { - uint16_t begin_mask; - uint16_t size; - uint16_t size_mask; - uint16_t end_mask; -}; -void* mempool_allocate(size_t size, size_t align = alignof(std::max_align_t)) { - if (size < 16) { - return MemPool->allocate(size, align); - } - size_t all_size = sizeof(MemBlock) + meta_align_size(size, align); - MemBlock* pBlock = (MemBlock*)MemPool->allocate(all_size, align); - pBlock->begin_mask = MEM_BLOCK_BEGIN_MASK; - pBlock->size = size; - pBlock->size_mask = size; - pBlock->end_mask = MEM_BLOCK_END_MASK; - return (char*)pBlock + sizeof(MemBlock); -} -void mempool_deallocate(void* ptr) { - if (!ptr) { - return; - } - MemBlock* pBlock = (MemBlock*)((char*)ptr - sizeof(MemBlock)); - if (pBlock->begin_mask == MEM_BLOCK_BEGIN_MASK && pBlock->end_mask == MEM_BLOCK_END_MASK - && pBlock->size == pBlock->size_mask) { - MemPool->deallocate(pBlock, sizeof(MemBlock) + pBlock->size); - } - else { - MemPool->deallocate(ptr, 0); - } -} -void* operator new(size_t size) { - return mempool_allocate(size); -} -// 默认对齐方式 delete -void operator delete(void* ptr) noexcept { - mempool_deallocate(ptr); -} -// Array new and delete -void* operator new[](size_t size) { - return mempool_allocate(size); -} - -void operator delete[](void* ptr) noexcept { - mempool_deallocate(ptr); -} -#endif // \ No newline at end of file diff --git a/engine/modules/engine/zlib/include/pmr/memory/memory_pool.h b/engine/modules/engine/zlib/include/pmr/memory/memory_pool.h new file mode 100644 index 0000000..a2886f9 --- /dev/null +++ b/engine/modules/engine/zlib/include/pmr/memory/memory_pool.h @@ -0,0 +1,267 @@ +#pragma once +#include "type.h" +#include +#include +#include +#include +#include +namespace pmr { + using std::pmr::memory_resource; + class MemoryPagePool { + public: + static void* do_allocate(size_t bytes, size_t alignment) { + void* ptr = (void*)VirtualAlloc(NULL, bytes, MEM_COMMIT, PAGE_READWRITE); + return ptr; + } + static void do_deallocate(void* ptr) { + if (!ptr) { + return; + } + VirtualFree(ptr, 0, MEM_RELEASE); + } + }; + class BlockAllocator : public memory_resource { + private: + friend class MemoryPoolManager; + uint16_t* m_stack; + uint8_t* m_bitmap; // 位图起始地址 + char* m_data; + uint16_t m_block_size; + uint16_t m_block_count; + uint16_t m_used_count; + uint16_t m_head; + public: + struct MemBlock { + uint16_t size; + uint16_t begin_mask; + uint16_t end_mask; + uint16_t padding3; + MemBlock(size_t bytes) : size(bytes) + , begin_mask(0x5555) + , end_mask(0xAAAA) {} + operator bool() { + return begin_mask == 0x5555 && end_mask == 0xAAAA; + } + }; + BlockAllocator(size_t MemorySize, uint16_t block_size):m_block_size(block_size){ + uint16_t count = 8 * MemorySize / (8 * block_size + 8 * sizeof(uint16_t) + 1); + uint16_t bitmap_size = (count + 7) / 8; + size_t offset = meta_align_size(count * 2 + bitmap_size, 8); + if (offset + count * block_size > MemorySize) { + count--; + offset = meta_align_size(count * 2 + bitmap_size, 8); + } + m_stack = (uint16_t*)MemoryPagePool::do_allocate(MemorySize, 8); + m_bitmap = (uint8_t*)m_stack + count * 2; + m_data = (char*)m_stack + offset; + std::memset(m_bitmap, 0, bitmap_size); + std::iota(m_stack, m_stack + count, 0); + m_head = 0; + m_used_count = 0; + m_block_count = count; + } + ~BlockAllocator() { + if (m_stack) { + MemoryPagePool::do_deallocate(m_stack); + } + m_stack = nullptr; + } + bool try_allocate(size_t bytes) { + return bytes <= m_block_size || m_block_count - m_block_size >= MAX_BLOCK_ELEM_GROUP_N; + } + bool invert_bitmap(uint16_t bit_pos, uint8_t flag) { + size_t index = bit_pos / 8; + uint8_t invert_mask = 1 << (7 - bit_pos % 8); + m_bitmap[index] ^= invert_mask; + return flag ? (m_bitmap[index] & invert_mask) == invert_mask : + (m_bitmap[index] & invert_mask) == 0; + } + bool invert_bitmap_group(uint16_t bit_pos, uint8_t count, uint8_t flag) { + size_t index = bit_pos / 8; + uint8_t offset = bit_pos % 8; + uint8_t left_mask = 0xff >> offset;//00011111 + offset = (16 - offset - count) % 8; + uint8_t right_mask = 0xff << offset;//11111110 + if (offset + count <= 8) { + uint8_t mask = left_mask & right_mask;//00011110 + uint8_t mask_result = flag ? mask : 0; + m_bitmap[index] ^= mask; + return (m_bitmap[index] & mask) == mask_result; + } + m_bitmap[index] ^= left_mask; + m_bitmap[index + 1] ^= right_mask; + uint8_t left_mask_result = flag ? left_mask : 0; + uint8_t right_mask_result = flag ? right_mask : 0; + return (m_bitmap[index] & left_mask) == left_mask_result && (m_bitmap[index + 1] & right_mask) == right_mask_result; + } + void* do_allocate(size_t bytes, size_t alignment)override { + uint16_t index = m_stack[m_head]; + uint16_t count = (bytes + m_block_size - 1) / m_block_size; + if (count == 1 && !invert_bitmap(index, 1)) { + invert_bitmap(index, 0); + throw std::runtime_error("Error: Attempting to allocate an already allocated block."); + } + if (count > 1 && !invert_bitmap_group(index, count, 1)) { + invert_bitmap_group(index, count, 1); + throw std::runtime_error("Error: Attempting to allocate an already allocated block group."); + } + m_used_count += count; + m_head = (m_head + count) % m_block_count; + return m_data + m_block_size * index; + } + void do_deallocate(void* ptr, size_t bytes, size_t alignment) override { + uint16_t index = size_t((char*)ptr - m_data) / m_block_size; + uint16_t count = (bytes + m_block_size - 1) / m_block_size; + if (count == 1) { + if (!invert_bitmap(index, 0)) { + invert_bitmap(index, 0); + throw std::runtime_error("Error: Double deallocation attempt!"); + } + m_stack[(m_head + m_block_count - m_used_count) % m_block_count] = index; + m_used_count--; + return; + } + if (count > 1 && !invert_bitmap_group(index, count, 0)) { + invert_bitmap_group(index, count, 0); + throw std::runtime_error("Error: Double deallocation group attempt!"); + } + alignment = (m_head + m_block_count - m_used_count) % m_block_count; + if (alignment + count < m_block_count) [[likely]] { + std::iota(m_stack + alignment, m_stack + alignment + count, index); + } + else [[unlikely]] { + for (uint16_t i = 0; i < count; i++) { + m_stack[(alignment + i) % m_block_count] = index + i; + } + } + m_used_count -= count; + } + bool do_is_equal(const memory_resource& other) const noexcept override { return this == &other; }; + }; + class MemoryPoolManager{ + public: + struct MemBlock { + uint32_t begin_mask; + uint16_t size; + uint8_t index; + uint8_t alloc_index; + uint16_t size_mask; + uint8_t index_mask; + uint8_t alloc_index_mask; + uint32_t end_mask; + MemBlock(size_t bytes, uint8_t index, uint8_t alloc_index) + : size(bytes) + , size_mask(bytes) + , index(index) + , alloc_index(alloc_index) + , begin_mask(0x55555555) + , end_mask(0xAAAAAAAA){ + } + operator bool() { + return size && (uint32_t)size == (uint32_t)size_mask + && begin_mask == 0x55555555 + && end_mask == 0xAAAAAAAA; + } + }; + struct AllocatorPtr { + BlockAllocator* alloc; + AllocatorPtr* next; + AllocatorPtr() :alloc(nullptr), next(nullptr){} + operator bool() { + return alloc; + } + BlockAllocator* operator->() { + return alloc; + } + ~AllocatorPtr() { + if (alloc) { + alloc->~BlockAllocator(); + } + if (next) { + next->~AllocatorPtr(); + meta_free(this); + } + } + }; + private: + AllocatorPtr mAllocators[MAX_BLOCK_ELEM_SIZE_N] = {}; + public: + MemoryPoolManager(){} + ~MemoryPoolManager() { + for (uint32_t i = 0; i < MAX_BLOCK_ELEM_SIZE_N; i++) { + mAllocators[i].~AllocatorPtr(); + meta_free(mAllocators[i].alloc); + } + } + int find_pool_index(size_t bytes) { + size_t block_size = std::bit_ceil(bytes); + int index = __builtin_ctz(block_size); + if (index < MAX_BLOCK_ELEM_SIZE_N) { + int count = ((bytes << 3) + block_size - 1) / block_size; + if (count >= 7) { + return index; + } + if (count >= 6) { + return index - 2; + } + return index > 7 ? index - 3 : 4; + } + return index; + } + void* do_allocate(size_t bytes, size_t alignment) { + size_t bytes_block = bytes + sizeof(MemBlock); + int index = find_pool_index(bytes_block); + if (index < MAX_BLOCK_ELEM_SIZE_N) { + uint16_t alloc_index = 0; + AllocatorPtr& pAlloc = mAllocators[index]; + if (!pAlloc) { + pAlloc.alloc = (BlockAllocator*)meta_malloc(sizeof(BlockAllocator)); + new (pAlloc.alloc) BlockAllocator(MEMORY_BLOCK_SIZE, 1 << index); + return do_allocate_block(pAlloc.alloc, bytes_block, index, alloc_index); + } + while(pAlloc) { + if (pAlloc->try_allocate(bytes_block)) { + return do_allocate_block(pAlloc.alloc, bytes_block, index, alloc_index); + } + if (!pAlloc.next) { + void* pMemory = meta_malloc(sizeof(AllocatorPtr) + sizeof(BlockAllocator)); + AllocatorPtr* next = new(pMemory) AllocatorPtr(); + next->alloc = new((char*)pMemory + sizeof(AllocatorPtr)) BlockAllocator(MEMORY_BLOCK_SIZE, 1 << index); + pAlloc.next = next; + return do_allocate_block(next->alloc, bytes_block, index, alloc_index); + } + pAlloc = *pAlloc.next; + alloc_index++; + } + throw std::bad_alloc(); + } + return malloc(bytes); + } + void* do_allocate_block(BlockAllocator* alloc, size_t bytes, uint8_t index, uint8_t alloc_index) { + MemBlock* pBlock = (MemBlock*)alloc->do_allocate(bytes, 0); + new(pBlock)MemBlock(bytes, index, alloc_index); + return (char*)pBlock + sizeof(MemBlock); + } + void do_deallocate(void* ptr) { + if (!ptr) { + return; + } + MemBlock* pBlock = (MemBlock*)((char*)ptr - sizeof(MemBlock)); + if (!pBlock) { + free(ptr); + return; + } + AllocatorPtr& pAlloc = mAllocators[pBlock->index]; + uint16_t alloc_index = pBlock->alloc_index + 1; + while (alloc_index && pAlloc) { + alloc_index--; + if (!alloc_index) { + pAlloc->do_deallocate(pBlock, pBlock->size, 0); + return; + } + pAlloc = *pAlloc.next; + } + throw std::runtime_error("Warning: deallocate erorr!!!"); + } + }; +} \ No newline at end of file diff --git a/engine/modules/engine/zlib/include/pmr/memory/type.h b/engine/modules/engine/zlib/include/pmr/memory/type.h new file mode 100644 index 0000000..fb1bc4c --- /dev/null +++ b/engine/modules/engine/zlib/include/pmr/memory/type.h @@ -0,0 +1,17 @@ +#pragma once +#include "singleton.h" +#define MEMORY_BLOCK_SIZE 1048576 +#define META_BLOCK_EMEM_SIZE 16 +#define MAX_BLOCK_ELEM_SIZE_N 16 +#define MAX_BLOCK_ELEM_GROUP_N 8 +constexpr inline size_t meta_align_size(size_t size, size_t alignment = 8) { + return (size + alignment - 1) / alignment * alignment; +}; +namespace pmr { + void* meta_malloc(size_t bytes, size_t alignment = 16); + void meta_free(void* ptr); +} +extern void* operator new(std::size_t size); +extern void operator delete(void* ptr) noexcept; +extern void* operator new[](std::size_t size); +extern void operator delete[](void* ptr) noexcept; \ No newline at end of file diff --git a/engine/src/engine/zlib.cpp b/engine/src/engine/zlib.cpp index 81f7085..562b73a 100644 --- a/engine/src/engine/zlib.cpp +++ b/engine/src/engine/zlib.cpp @@ -1,9 +1,7 @@ #ifndef ZLIB_API_VAL #define ZLIB_API_VAL 1 - -#include "pmr/frame_allocator.h" #include "pmr/name.h" - +#include "pmr/memory.h" #include "refl/detail/uclass.inl" #endif // !ZLIB_API_VAL \ No newline at end of file diff --git a/game/zworld/editor/zworld_editor.cpp b/game/zworld/editor/zworld_editor.cpp index ad96a63..8abd152 100644 --- a/game/zworld/editor/zworld_editor.cpp +++ b/game/zworld/editor/zworld_editor.cpp @@ -1,8 +1,8 @@ #define ZLIB_API -#define ZLIB_API_VAL #define CORE_API #define CORE_API_VAL #include "archive/pch.h" +#include "pmr/memory.h" #include "test_refl.h" using namespace api; using namespace std;