Diligent Engine API Reference: D:/GitHub/DiligentEngine/DiligentCore/Common/interface/FixedBlockMemoryAllocator.h Source File

Diligent Engine API Reference

FixedBlockMemoryAllocator.h
1 /* Copyright 2015-2018 Egor Yusov
2 *
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
10 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
11 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF ANY PROPRIETARY RIGHTS.
12 *
13 * In no event and under no legal theory, whether in tort (including negligence),
14 * contract, or otherwise, unless required by applicable law (such as deliberate
15 * and grossly negligent acts) or agreed to in writing, shall any Contributor be
16 * liable for any damages, including any direct, indirect, special, incidental,
17 * or consequential damages of any character arising as a result of this License or
18 * out of the use or inability to use the software (including but not limited to damages
19 * for loss of goodwill, work stoppage, computer failure or malfunction, or any and
20 * all other commercial damages or losses), even if such Contributor has been advised
21 * of the possibility of such damages.
22 */
23
24 #pragma once
25
28
29 #include <unordered_map>
30 #include <mutex>
31 #include <unordered_set>
32 #include <vector>
33 #include "MemoryAllocator.h"
34 #include "STDAllocator.h"
35 #include "Errors.h"
36
37 namespace Diligent
38 {
39
40 #ifdef _DEBUG
41 inline void FillWithDebugPattern(void *ptr, Uint8 Pattern, size_t NumBytes)
42 {
43 memset(ptr, Pattern, NumBytes);
44 }
45 #else
46 #define FillWithDebugPattern(...)
47 #endif
48
50 class FixedBlockMemoryAllocator : public IMemoryAllocator
51 {
52 public:
53 FixedBlockMemoryAllocator(IMemoryAllocator &RawMemoryAllocator, size_t BlockSize, Uint32 NumBlocksInPage);
54 ~FixedBlockMemoryAllocator();
55
57 virtual void* Allocate( size_t Size, const Char* dbgDescription, const char* dbgFileName, const Int32 dbgLineNumber)override final;
58
60 virtual void Free(void *Ptr)override final;
61
62 private:
63 FixedBlockMemoryAllocator(const FixedBlockMemoryAllocator&) = delete;
64 FixedBlockMemoryAllocator(FixedBlockMemoryAllocator&&) = delete;
65 FixedBlockMemoryAllocator& operator = (const FixedBlockMemoryAllocator&) = delete;
66 FixedBlockMemoryAllocator& operator = (FixedBlockMemoryAllocator&&) = delete;
67
68 void CreateNewPage();
69
70 // Memory page class is based on the fixed-size memory pool described in "Fast Efficient Fixed-Size Memory Pool"
71 // by Ben Kenwright
72 class MemoryPage
73 {
74 public:
75 static constexpr Uint8 NewPageMemPattern = 0xAA;
76 static constexpr Uint8 AllocatedBlockMemPattern = 0xAB;
77 static constexpr Uint8 DeallocatedBlockMemPattern = 0xDE;
78 static constexpr Uint8 InitializedBlockMemPattern = 0xCF;
79
80 MemoryPage(FixedBlockMemoryAllocator &OwnerAllocator):
81 m_pOwnerAllocator(&OwnerAllocator),
82 m_NumFreeBlocks(OwnerAllocator.m_NumBlocksInPage),
83 m_NumInitializedBlocks(0)
84 {
85 auto PageSize = OwnerAllocator.m_BlockSize * OwnerAllocator.m_NumBlocksInPage;
86 m_pPageStart = reinterpret_cast<Uint8*>(
87 OwnerAllocator.m_RawMemoryAllocator.Allocate(PageSize, "FixedBlockMemoryAllocator page", __FILE__, __LINE__)
88 );
89 m_pNextFreeBlock = m_pPageStart;
90 FillWithDebugPattern(m_pPageStart, NewPageMemPattern, PageSize);
91 }
92
93 MemoryPage(MemoryPage&& Page) :
94 m_NumFreeBlocks(Page.m_NumFreeBlocks),
95 m_NumInitializedBlocks(Page.m_NumInitializedBlocks),
96 m_pPageStart(Page.m_pPageStart),
97 m_pNextFreeBlock(Page.m_pNextFreeBlock),
98 m_pOwnerAllocator(Page.m_pOwnerAllocator)
99 {
100 Page.m_NumFreeBlocks = 0;
101 Page.m_NumInitializedBlocks = 0;
102 Page.m_pPageStart = nullptr;
103 Page.m_pNextFreeBlock = nullptr;
104 Page.m_pOwnerAllocator = nullptr;
105 }
106
107 ~MemoryPage()
108 {
109 if(m_pOwnerAllocator)
110 m_pOwnerAllocator->m_RawMemoryAllocator.Free(m_pPageStart);
111 }
112
113 void* GetBlockStartAddress(Uint32 BlockIndex) const
114 {
115 VERIFY_EXPR(m_pOwnerAllocator != nullptr);
116 VERIFY(BlockIndex >= 0 && BlockIndex < m_pOwnerAllocator->m_NumBlocksInPage, "Invalid block index" );
117 return reinterpret_cast<Uint8*>(m_pPageStart) + BlockIndex * m_pOwnerAllocator->m_BlockSize;
118 }
119
120 #ifdef _DEBUG
121 void dbgVerifyAddress(const void* pBlockAddr)const
122 {
123 size_t Delta = reinterpret_cast<const Uint8*>(pBlockAddr) - reinterpret_cast<Uint8*>(m_pPageStart);
124 VERIFY(Delta % m_pOwnerAllocator->m_BlockSize == 0, "Invalid address");
125 Uint32 BlockIndex = static_cast<Uint32>(Delta / m_pOwnerAllocator->m_BlockSize);
126 VERIFY(BlockIndex >= 0 && BlockIndex < m_pOwnerAllocator->m_NumBlocksInPage, "Invalid block index" );
127 }
128 #else
129 #define dbgVerifyAddress(...)
130 #endif
131
132 void* Allocate()
133 {
134 VERIFY_EXPR(m_pOwnerAllocator != nullptr);
135
136 if (m_NumFreeBlocks == 0)
137 {
138 VERIFY_EXPR(m_NumInitializedBlocks == m_pOwnerAllocator->m_NumBlocksInPage);
139 return nullptr;
140 }
141
142 // Initialize the next block
143 if (m_NumInitializedBlocks < m_pOwnerAllocator->m_NumBlocksInPage)
144 {
145 // Link next uninitialized block to the end of the list:
146
147 //
148 // ___________ ___________
149 // | | | |
150 // | 0xcdcdcd | -->| 0xcdcdcd | m_NumInitializedBlocks
151 // |-----------| | |-----------|
152 // | | | | |
153 // m_NumInitializedBlocks | 0xcdcdcd | ==> ---| |
154 // |-----------| |-----------|
155 //
156 // ~ ~ ~ ~
157 // | | | |
158 // 0 | | | |
159 // ----------- -----------
160 //
161 auto *pUninitializedBlock = GetBlockStartAddress(m_NumInitializedBlocks);
162 FillWithDebugPattern(pUninitializedBlock, InitializedBlockMemPattern, m_pOwnerAllocator->m_BlockSize);
163 void** ppNextBlock = reinterpret_cast<void**>( pUninitializedBlock );
164 ++m_NumInitializedBlocks;
165 if( m_NumInitializedBlocks < m_pOwnerAllocator->m_NumBlocksInPage )
166 *ppNextBlock = GetBlockStartAddress(m_NumInitializedBlocks);
167 else
168 *ppNextBlock = nullptr;
169 }
170
171 void* res = m_pNextFreeBlock;
172 dbgVerifyAddress(res);
173 // Move pointer to the next free block
174 m_pNextFreeBlock = *reinterpret_cast<void**>(m_pNextFreeBlock);
175 --m_NumFreeBlocks;
176 if(m_NumFreeBlocks != 0)
177 dbgVerifyAddress(m_pNextFreeBlock);
178 else
179 VERIFY_EXPR(m_pNextFreeBlock == nullptr);
180
181 FillWithDebugPattern(res, AllocatedBlockMemPattern, m_pOwnerAllocator->m_BlockSize);
182 return res;
183 }
184
185 void DeAllocate(void* p)
186 {
187 VERIFY_EXPR(m_pOwnerAllocator != nullptr);
188
189 dbgVerifyAddress(p);
190 FillWithDebugPattern(p, DeallocatedBlockMemPattern, m_pOwnerAllocator->m_BlockSize);
191 // Add block to the beginning of the linked list
192 *reinterpret_cast<void**>(p) = m_pNextFreeBlock;
193 m_pNextFreeBlock = p;
194 ++m_NumFreeBlocks;
195 }
196
197 bool HasSpace()const{return m_NumFreeBlocks>0;}
198 bool HasAllocations()const{return m_NumFreeBlocks<m_NumInitializedBlocks;}
199 private:
200
201 MemoryPage(const MemoryPage&)=delete;
202 MemoryPage& operator = (const MemoryPage)=delete;
203 MemoryPage& operator = (MemoryPage&&)=delete;
204
205 Uint32 m_NumFreeBlocks = 0; // Num of remaining blocks
206 Uint32 m_NumInitializedBlocks = 0; // Num of initialized blocks
207 void* m_pPageStart = nullptr; // Beginning of memory pool
208 void* m_pNextFreeBlock = nullptr; // Num of next free block
209 FixedBlockMemoryAllocator *m_pOwnerAllocator = nullptr;
210 };
211
212 std::vector<MemoryPage, STDAllocatorRawMem<MemoryPage> > m_PagePool;
213 std::unordered_set<size_t, std::hash<size_t>, std::equal_to<size_t>, STDAllocatorRawMem<size_t> > m_AvailablePages;
214 typedef std::pair<void* const, size_t> AddrToPageIdMapElem;
215 std::unordered_map<void*, size_t, std::hash<void*>, std::equal_to<void*>, STDAllocatorRawMem<AddrToPageIdMapElem> > m_AddrToPageId;
216
217 std::mutex m_Mutex;
218
219 IMemoryAllocator &m_RawMemoryAllocator;
220 size_t m_BlockSize;
221 Uint32 m_NumBlocksInPage;
222 };
223
224 IMemoryAllocator& GetRawAllocator();
225
226 template<typename ObjectType>
227 class ObjectPool
228 {
229 public:
230 static void SetRawAllocator(IMemoryAllocator &Allocator)
231 {
232 #ifdef _DEBUG
233 if(m_bPoolInitialized && m_pRawAllocator != &Allocator)
234 {
235 LOG_WARNING_MESSAGE("Setting pool raw allocator after the pool has been initialized has no effect");
236 }
237 #endif
238 m_pRawAllocator = &Allocator;
239 }
240 static void SetPageSize(Uint32 NumAllocationsInPage)
241 {
242 #ifdef _DEBUG
243 if(m_bPoolInitialized && m_NumAllocationsInPage != NumAllocationsInPage)
244 {
245 LOG_WARNING_MESSAGE("Setting pool page size after the pool has been initialized has no effect");
246 }
247 #endif
248 m_NumAllocationsInPage = NumAllocationsInPage;
249 }
250 static ObjectPool& GetPool()
251 {
252 static ObjectPool ThePool;
253 #ifdef _DEBUG
254 m_bPoolInitialized = true;
255 #endif
256 return ThePool;
257 }
258
259 template<typename ... CtorArgTypes>
260 ObjectType* NewObject(const Char* dbgDescription, const char* dbgFileName, const Int32 dbgLineNumber, CtorArgTypes&& ... CtorArgs)
261 {
262 void *pRawMem = m_FixedBlockAlloctor.Allocate(sizeof(ObjectType), dbgDescription, dbgFileName, dbgLineNumber);
263 try
264 {
265 return new(pRawMem) ObjectType(std::forward<CtorArgTypes>(CtorArgs)...);
266 }
267 catch (...)
268 {
269 m_FixedBlockAlloctor.Free(pRawMem);
270 return nullptr;
271 }
272 }
273
274 void Destroy(ObjectType* pObj)
275 {
276 if(pObj != nullptr)
277 {
278 pObj->~ObjectType();
279 m_FixedBlockAlloctor.Free(pObj);
280 }
281 }
282
283 private:
284 static Uint32 m_NumAllocationsInPage;
285 static IMemoryAllocator *m_pRawAllocator;
286
287 ObjectPool() :
288 m_FixedBlockAlloctor(m_pRawAllocator ? *m_pRawAllocator : GetRawAllocator(), sizeof(ObjectType), m_NumAllocationsInPage)
289 {}
290 #ifdef _DEBUG
291 static bool m_bPoolInitialized;
292 #endif
293 FixedBlockMemoryAllocator m_FixedBlockAlloctor;
294 };
295 template<typename ObjectType>
296 Uint32 ObjectPool<ObjectType>::m_NumAllocationsInPage = 64;
297
298 template<typename ObjectType>
299 IMemoryAllocator* ObjectPool<ObjectType>::m_pRawAllocator = nullptr;
300
301 #ifdef _DEBUG
302 template<typename ObjectType>
303 bool ObjectPool<ObjectType>::m_bPoolInitialized = false;
304 #endif
305
306 #define SET_POOL_RAW_ALLOCATOR(ObjectType, Allocator)ObjectPool<ObjectType>::SetRawAllocator(Allocator)
307 #define SET_POOL_PAGE_SIZE(ObjectType, NumAllocationsInPage)ObjectPool<ObjectType>::SetPageSize(NumAllocationsInPage)
308 #define NEW_POOL_OBJECT(ObjectType, Desc, ...)ObjectPool<ObjectType>::GetPool().NewObject(Desc, __FILE__, __LINE__, ##__VA_ARGS__)
309 #define DESTROY_POOL_OBJECT(pObject)ObjectPool< std::remove_reference<decltype(*pObject)>::type >::GetPool().Destroy(pObject)
310
311 }
Diligent
Namespace for the OpenGL implementation of the graphics engine.
Definition: BufferD3D11Impl.h:34
Diligent::FixedBlockMemoryAllocator::Allocate
virtual void * Allocate(size_t Size, const Char *dbgDescription, const char *dbgFileName, const Int32 dbgLineNumber) override final
Allocates block of memory.
Definition: FixedBlockMemoryAllocator.cpp:64
Diligent::GetRawAllocator
IMemoryAllocator & GetRawAllocator()
Returns raw memory allocator.
Definition: EngineMemory.cpp:46
Diligent::SetRawAllocator
void SetRawAllocator(IMemoryAllocator *pRawAllocator)
Sets raw memory allocator. This function must be called before any memory allocation/deallocation fun...
Definition: EngineMemory.cpp:36
Diligent::FixedBlockMemoryAllocator
Memory allocator that allocates memory in a fixed-size chunks.
Definition: FixedBlockMemoryAllocator.h:50
Diligent::FixedBlockMemoryAllocator::Free
virtual void Free(void *Ptr) override final
Releases memory.
Definition: FixedBlockMemoryAllocator.cpp:91