Memory.h 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409
  1. // Copyright (C) 2009-2021, Panagiotis Christopoulos Charitos and contributors.
  2. // All rights reserved.
  3. // Code licensed under the BSD License.
  4. // http://www.anki3d.org/LICENSE
  5. #pragma once
  6. #include <AnKi/Util/StdTypes.h>
  7. #include <AnKi/Util/Atomic.h>
  8. #include <AnKi/Util/Assert.h>
  9. #include <AnKi/Util/Array.h>
  10. #include <AnKi/Util/Thread.h>
  11. #include <utility> // For forward
  12. namespace anki {
  13. /// @addtogroup util_memory
  14. /// @{
  15. #define ANKI_MEM_EXTRA_CHECKS ANKI_EXTRA_CHECKS
  16. /// Allocate aligned memory
  17. void* mallocAligned(PtrSize size, PtrSize alignmentBytes);
  18. /// Free aligned memory
  19. void freeAligned(void* ptr);
  20. /// The function signature of a memory allocation/deallocation. See allocAligned function for the explanation of
  21. /// arguments
  22. using AllocAlignedCallback = void* (*)(void* userData, void* ptr, PtrSize size, PtrSize alignment);
  23. /// An internal type.
  24. using PoolSignature = U32;
  25. /// This is a function that allocates and deallocates heap memory. If the @a ptr is nullptr then it allocates using the
  26. /// @a size and @a alignment. If the @a ptr is not nullptr it deallocates the memory and the @a size and @a alignment is
  27. /// ignored.
  28. ///
  29. /// @param userData Used defined data
  30. /// @param ptr The pointer to deallocate or nullptr
  31. /// @param size The size to allocate or 0
  32. /// @param alignment The allocation alignment or 0
  33. /// @return On allocation mode it will return the newelly allocated block or nullptr on error. On deallocation mode
  34. /// returns nullptr
  35. void* allocAligned(void* userData, void* ptr, PtrSize size, PtrSize alignment);
  36. /// Generic memory pool. The base of HeapMemoryPool or StackMemoryPool or ChainMemoryPool.
  37. class BaseMemoryPool
  38. {
  39. public:
  40. BaseMemoryPool(const BaseMemoryPool&) = delete; // Non-copyable
  41. virtual ~BaseMemoryPool();
  42. BaseMemoryPool& operator=(const BaseMemoryPool&) = delete; // Non-copyable
  43. /// Allocate memory. This operation MAY be thread safe
  44. /// @param size The size to allocate
  45. /// @param alignmentBytes The alignment of the returned address
  46. /// @return The allocated memory or nullptr on failure
  47. void* allocate(PtrSize size, PtrSize alignmentBytes);
  48. /// Free memory.
  49. /// @param[in, out] ptr Memory block to deallocate
  50. void free(void* ptr);
  51. /// Get refcount.
  52. Atomic<U32>& getRefcount()
  53. {
  54. return m_refcount;
  55. }
  56. /// Get number of users.
  57. U32 getUsersCount() const
  58. {
  59. return m_refcount.load();
  60. }
  61. /// Get allocation callback.
  62. AllocAlignedCallback getAllocationCallback() const
  63. {
  64. return m_allocCb;
  65. }
  66. /// Get allocation callback user data.
  67. void* getAllocationCallbackUserData() const
  68. {
  69. return m_allocCbUserData;
  70. }
  71. /// Return number of allocations
  72. U32 getAllocationsCount() const
  73. {
  74. return m_allocationsCount.load();
  75. }
  76. protected:
  77. /// Pool type.
  78. enum class Type : U8
  79. {
  80. NONE,
  81. HEAP,
  82. STACK,
  83. CHAIN
  84. };
  85. /// User allocation function.
  86. AllocAlignedCallback m_allocCb = nullptr;
  87. /// User allocation function data.
  88. void* m_allocCbUserData = nullptr;
  89. /// Allocations count.
  90. Atomic<U32> m_allocationsCount = {0};
  91. BaseMemoryPool(Type type)
  92. : m_type(type)
  93. {
  94. }
  95. /// Check if already created.
  96. Bool isInitialized() const;
  97. private:
  98. /// Refcount.
  99. Atomic<U32> m_refcount = {0};
  100. /// Type.
  101. Type m_type = Type::NONE;
  102. };
  103. /// A dummy interface to match the StackMemoryPool and ChainMemoryPool interfaces in order to be used by the same
  104. /// allocator template.
  105. class HeapMemoryPool final : public BaseMemoryPool
  106. {
  107. public:
  108. /// Default constructor.
  109. HeapMemoryPool();
  110. /// Destroy
  111. ~HeapMemoryPool();
  112. /// The real constructor.
  113. /// @param allocCb The allocation function callback
  114. /// @param allocCbUserData The user data to pass to the allocation function
  115. void init(AllocAlignedCallback allocCb, void* allocCbUserData);
  116. /// Allocate memory
  117. void* allocate(PtrSize size, PtrSize alignment);
  118. /// Free memory.
  119. /// @param[in, out] ptr Memory block to deallocate.
  120. void free(void* ptr);
  121. private:
  122. #if ANKI_MEM_EXTRA_CHECKS
  123. PoolSignature m_signature = 0;
  124. #endif
  125. };
  126. /// Thread safe memory pool. It's a preallocated memory pool that is used for memory allocations on top of that
  127. /// preallocated memory. It is mainly used by fast stack allocators
  128. class StackMemoryPool final : public BaseMemoryPool
  129. {
  130. public:
  131. /// The type of the pool's snapshot
  132. using Snapshot = void*;
  133. /// Default constructor
  134. StackMemoryPool();
  135. /// Destroy
  136. ~StackMemoryPool();
  137. /// Init with parameters.
  138. /// @param allocCb The allocation function callback.
  139. /// @param allocCbUserData The user data to pass to the allocation function.
  140. /// @param initialChunkSize The size of the first chunk.
  141. /// @param nextChunkScale Value that controls the next chunk.
  142. /// @param nextChunkBias Value that controls the next chunk.
  143. /// @param ignoreDeallocationErrors Method free() may fail if the ptr is not in the top of the stack. Set that to
  144. /// true to suppress such errors.
  145. /// @param alignmentBytes The maximum supported alignment for returned memory.
  146. void init(AllocAlignedCallback allocCb, void* allocCbUserData, PtrSize initialChunkSize, F32 nextChunkScale = 2.0,
  147. PtrSize nextChunkBias = 0, Bool ignoreDeallocationErrors = true,
  148. PtrSize alignmentBytes = ANKI_SAFE_ALIGNMENT);
  149. /// Allocate aligned memory.
  150. /// @param size The size to allocate.
  151. /// @param alignmentBytes The alignment of the returned address.
  152. /// @return The allocated memory or nullptr on failure.
  153. ///
  154. /// @note The operation is thread safe with allocate() and free() methods.
  155. void* allocate(PtrSize size, PtrSize alignmentBytes);
  156. /// Free memory in StackMemoryPool. It will not actually do anything.
  157. /// @param[in, out] ptr Memory block to deallocate.
  158. void free(void* ptr);
  159. /// Reinit the pool. All existing allocated memory is effectively invalidated.
  160. /// @note It's not thread safe with other methods.
  161. void reset();
  162. /// Get the physical memory allocated by the pool.
  163. /// @note It's not thread safe with other methods.
  164. PtrSize getMemoryCapacity() const
  165. {
  166. return m_allocatedMemory;
  167. }
  168. private:
  169. /// The memory chunk.
  170. class Chunk
  171. {
  172. public:
  173. /// The base memory of the chunk.
  174. U8* m_baseMem = nullptr;
  175. /// The moving ptr for the next allocation.
  176. Atomic<U8*> m_mem = {nullptr};
  177. /// The chunk size.
  178. PtrSize m_size = 0;
  179. /// Check that it's initialized.
  180. void check() const
  181. {
  182. ANKI_ASSERT(m_baseMem != nullptr);
  183. ANKI_ASSERT(m_mem.load() >= m_baseMem);
  184. ANKI_ASSERT(m_size > 0);
  185. }
  186. // Check that it's in reset state.
  187. void checkReset() const
  188. {
  189. ANKI_ASSERT(m_baseMem != nullptr);
  190. ANKI_ASSERT(m_mem.load() == m_baseMem);
  191. ANKI_ASSERT(m_size > 0);
  192. }
  193. };
  194. /// Alignment of allocations
  195. PtrSize m_alignmentBytes = 0;
  196. /// The size of the first chunk.
  197. PtrSize m_initialChunkSize = 0;
  198. /// Chunk scale.
  199. F32 m_nextChunkScale = 0.0;
  200. /// Chunk bias.
  201. PtrSize m_nextChunkBias = 0;
  202. /// Allocated memory.
  203. PtrSize m_allocatedMemory = 0;
  204. /// Ignore deallocation errors.
  205. Bool m_ignoreDeallocationErrors = false;
  206. /// The current chunk. Chose the more strict memory order to avoid compiler re-ordering of instructions
  207. Atomic<I32, AtomicMemoryOrder::SEQ_CST> m_crntChunkIdx = {-1};
  208. /// The max number of chunks.
  209. static const U MAX_CHUNKS = 256;
  210. /// The chunks.
  211. Array<Chunk, MAX_CHUNKS> m_chunks;
  212. /// Protect the m_crntChunkIdx.
  213. Mutex m_lock;
  214. };
  215. /// Chain memory pool. Almost similar to StackMemoryPool but more flexible and at the same time a bit slower.
  216. class ChainMemoryPool final : public BaseMemoryPool
  217. {
  218. public:
  219. /// Default constructor
  220. ChainMemoryPool();
  221. /// Destroy
  222. ~ChainMemoryPool();
  223. /// Init the pool.
  224. /// @param allocCb The allocation function callback.
  225. /// @param allocCbUserData The user data to pass to the allocation function.
  226. /// @param initialChunkSize The size of the first chunk.
  227. /// @param nextChunkScale Value that controls the next chunk.
  228. /// @param nextChunkBias Value that controls the next chunk.
  229. /// @param alignmentBytes The maximum supported alignment for returned memory.
  230. void init(AllocAlignedCallback allocCb, void* allocCbUserData, PtrSize initialChunkSize, F32 nextChunkScale = 2.0,
  231. PtrSize nextChunkBias = 0, PtrSize alignmentBytes = ANKI_SAFE_ALIGNMENT);
  232. /// Allocate memory. This operation is thread safe
  233. /// @param size The size to allocate
  234. /// @param alignmentBytes The alignment of the returned address
  235. /// @return The allocated memory or nullptr on failure
  236. void* allocate(PtrSize size, PtrSize alignmentBytes);
  237. /// Free memory. If the ptr is not the last allocation of the chunk then nothing happens and the method returns
  238. /// false.
  239. /// @param[in, out] ptr Memory block to deallocate
  240. void free(void* ptr);
  241. /// @name Methods used for optimizing future chains.
  242. /// @{
  243. PtrSize getChunksCount() const;
  244. PtrSize getAllocatedSize() const;
  245. /// @}
  246. private:
  247. /// A chunk of memory
  248. class Chunk
  249. {
  250. public:
  251. /// Pre-allocated memory chunk.
  252. U8* m_memory = nullptr;
  253. /// Size of the pre-allocated memory chunk
  254. PtrSize m_memsize = 0;
  255. /// Points to the memory and more specifically to the top of the stack
  256. U8* m_top = nullptr;
  257. /// Used to identify if the chunk can be deleted
  258. PtrSize m_allocationsCount = 0;
  259. /// Previous chunk in the list
  260. Chunk* m_prev = nullptr;
  261. /// Next chunk in the list
  262. Chunk* m_next = nullptr;
  263. };
  264. /// Alignment of allocations.
  265. PtrSize m_alignmentBytes = 0;
  266. /// The first chunk.
  267. Chunk* m_headChunk = nullptr;
  268. /// Current chunk to allocate from.
  269. Chunk* m_tailChunk = nullptr;
  270. /// Size of the first chunk.
  271. PtrSize m_initSize = 0;
  272. /// Fast thread locking.
  273. SpinLock m_lock;
  274. /// Chunk scale.
  275. F32 m_scale = 2.0;
  276. /// Chunk bias.
  277. PtrSize m_bias = 0;
  278. /// Cache a value.
  279. PtrSize m_headerSize = 0;
  280. /// Compute the size for the next chunk.
  281. /// @param size The current allocation size.
  282. PtrSize computeNewChunkSize(PtrSize size) const;
  283. /// Create a new chunk.
  284. Chunk* createNewChunk(PtrSize size);
  285. /// Allocate from chunk.
  286. void* allocateFromChunk(Chunk* ch, PtrSize size, PtrSize alignment);
  287. /// Destroy a chunk.
  288. void destroyChunk(Chunk* ch);
  289. };
  290. inline void* BaseMemoryPool::allocate(PtrSize size, PtrSize alignmentBytes)
  291. {
  292. void* out = nullptr;
  293. switch(m_type)
  294. {
  295. case Type::HEAP:
  296. out = static_cast<HeapMemoryPool*>(this)->allocate(size, alignmentBytes);
  297. break;
  298. case Type::STACK:
  299. out = static_cast<StackMemoryPool*>(this)->allocate(size, alignmentBytes);
  300. break;
  301. default:
  302. ANKI_ASSERT(m_type == Type::CHAIN);
  303. out = static_cast<ChainMemoryPool*>(this)->allocate(size, alignmentBytes);
  304. }
  305. return out;
  306. }
  307. inline void BaseMemoryPool::free(void* ptr)
  308. {
  309. switch(m_type)
  310. {
  311. case Type::HEAP:
  312. static_cast<HeapMemoryPool*>(this)->free(ptr);
  313. break;
  314. case Type::STACK:
  315. static_cast<StackMemoryPool*>(this)->free(ptr);
  316. break;
  317. default:
  318. ANKI_ASSERT(m_type == Type::CHAIN);
  319. static_cast<ChainMemoryPool*>(this)->free(ptr);
  320. }
  321. }
  322. /// @}
  323. } // end namespace anki