Memory.h 11 KB

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