BsMemoryAllocator.h 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415
  1. //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
  2. //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
  3. #pragma once
  4. #undef min
  5. #undef max
  6. #include <atomic>
  7. namespace BansheeEngine
  8. {
  9. class MemoryAllocatorBase;
  10. /** @addtogroup Internal-Utility
  11. * @{
  12. */
  13. /** @addtogroup Memory-Internal
  14. * @{
  15. */
  16. #if BS_PLATFORM == BS_PLATFORM_WIN32
  17. inline void* platformAlignedAlloc16(size_t size)
  18. {
  19. return _aligned_malloc(size, 16);
  20. }
  21. inline void platformAlignedFree16(void* ptr)
  22. {
  23. _aligned_free(ptr);
  24. }
  25. inline void* platformAlignedAlloc(size_t size, size_t alignment)
  26. {
  27. return _aligned_malloc(size, alignment);
  28. }
  29. inline void platformAlignedFree(void* ptr)
  30. {
  31. _aligned_free(ptr);
  32. }
  33. #elif BS_PLATFORM == BS_PLATFORM_LINUX || BS_PLATFORM == BS_PLATFORM_ANDROID
  34. inline void* platformAlignedAlloc16(size_t size)
  35. {
  36. return ::memalign(16, size);
  37. }
  38. inline void platformAlignedFree16(void* ptr)
  39. {
  40. ::free(ptr);
  41. }
  42. inline void* platformAlignedAlloc(size_t size, size_t alignment)
  43. {
  44. return ::memalign(alignment, size);
  45. }
  46. inline void platformAlignedFree(void* ptr)
  47. {
  48. ::free(ptr);
  49. }
  50. #else // 16 byte aligment by default
  51. inline void* platformAlignedAlloc16(size_t size)
  52. {
  53. return ::malloc(size);
  54. }
  55. inline void platformAlignedFree16(void* ptr)
  56. {
  57. ::free(ptr);
  58. }
  59. inline void* platformAlignedAlloc(size_t size, size_t alignment)
  60. {
  61. void* data = ::malloc(size + (alignment - 1) + sizeof(void*));
  62. if (data == nullptr)
  63. return nullptr;
  64. UINT8* alignedData = ((UINT8*)data) + sizeof(void*);
  65. alignedData += alignment - ((uintptr_t)alignedData) & (alignment - 1);
  66. ((void**)alignedData)[-1] = data;
  67. return alignedData;
  68. }
  69. inline void platformAlignedFree(void* ptr)
  70. {
  71. ::free(((void**)ptr)[-1]);
  72. }
  73. #endif
  74. /**
  75. * Thread safe class used for storing total number of memory allocations and deallocations, primarily for statistic
  76. * purposes.
  77. */
  78. class MemoryCounter
  79. {
  80. public:
  81. static BS_UTILITY_EXPORT UINT64 getNumAllocs()
  82. {
  83. return Allocs;
  84. }
  85. static BS_UTILITY_EXPORT UINT64 getNumFrees()
  86. {
  87. return Frees;
  88. }
  89. private:
  90. friend class MemoryAllocatorBase;
  91. // Threadlocal data can't be exported, so some magic to make it accessible from MemoryAllocator
  92. static BS_UTILITY_EXPORT void incAllocCount() { Allocs++; }
  93. static BS_UTILITY_EXPORT void incFreeCount() { Frees++; }
  94. static BS_THREADLOCAL UINT64 Allocs;
  95. static BS_THREADLOCAL UINT64 Frees;
  96. };
  97. /** Base class all memory allocators need to inherit. Provides allocation and free counting. */
  98. class MemoryAllocatorBase
  99. {
  100. protected:
  101. static void incAllocCount() { MemoryCounter::incAllocCount(); }
  102. static void incFreeCount() { MemoryCounter::incFreeCount(); }
  103. };
  104. /**
  105. * Memory allocator providing a generic implementation. Specialize for specific categories as needed.
  106. *
  107. * @note For example you might implement a pool allocator for specific types in order
  108. * to reduce allocation overhead. By default standard malloc/free are used.
  109. */
  110. template<class T>
  111. class MemoryAllocator : public MemoryAllocatorBase
  112. {
  113. public:
  114. /** Allocates @p bytes bytes. */
  115. static void* allocate(size_t bytes)
  116. {
  117. #if BS_PROFILING_ENABLED
  118. incAllocCount();
  119. #endif
  120. return malloc(bytes);
  121. }
  122. /**
  123. * Allocates @p bytes and aligns them to the specified boundary (in bytes). If the aligment is less or equal to
  124. * 16 it is more efficient to use the allocateAligned16() alternative of this method. Alignment must be power of two.
  125. */
  126. static void* allocateAligned(size_t bytes, size_t alignment)
  127. {
  128. #if BS_PROFILING_ENABLED
  129. incAllocCount();
  130. #endif
  131. return platformAlignedAlloc(bytes, alignment);
  132. }
  133. /** Allocates @p bytes and aligns them to a 16 byte boundary. */
  134. static void* allocateAligned16(size_t bytes)
  135. {
  136. #if BS_PROFILING_ENABLED
  137. incAllocCount();
  138. #endif
  139. return platformAlignedAlloc16(bytes);
  140. }
  141. /** Frees the memory at the specified location. */
  142. static void free(void* ptr)
  143. {
  144. #if BS_PROFILING_ENABLED
  145. incFreeCount();
  146. #endif
  147. ::free(ptr);
  148. }
  149. /** Frees memory allocated with allocateAligned() */
  150. static void freeAligned(void* ptr)
  151. {
  152. #if BS_PROFILING_ENABLED
  153. incFreeCount();
  154. #endif
  155. platformAlignedFree(ptr);
  156. }
  157. /** Frees memory allocated with allocateAligned16() */
  158. static void freeAligned16(void* ptr)
  159. {
  160. #if BS_PROFILING_ENABLED
  161. incFreeCount();
  162. #endif
  163. platformAlignedFree16(ptr);
  164. }
  165. };
  166. /**
  167. * General allocator provided by the OS. Use for persistent long term allocations, and allocations that don't
  168. * happen often.
  169. */
  170. class GenAlloc
  171. { };
  172. /** @} */
  173. /** @} */
  174. /** @addtogroup Memory
  175. * @{
  176. */
  177. /** Allocates the specified number of bytes. */
  178. template<class Alloc>
  179. inline void* bs_alloc(UINT32 count)
  180. {
  181. return MemoryAllocator<Alloc>::allocate(count);
  182. }
  183. /** Allocates enough bytes to hold the specified type, but doesn't construct it. */
  184. template<class T, class Alloc>
  185. inline T* bs_alloc()
  186. {
  187. return (T*)MemoryAllocator<Alloc>::allocate(sizeof(T));
  188. }
  189. /** Creates and constructs an array of @p count elements. */
  190. template<class T, class Alloc>
  191. inline T* bs_newN(UINT32 count)
  192. {
  193. T* ptr = (T*)MemoryAllocator<Alloc>::allocate(sizeof(T) * count);
  194. for(unsigned int i = 0; i < count; i++)
  195. new ((void*)&ptr[i]) T;
  196. return ptr;
  197. }
  198. /** Create a new object with the specified allocator and the specified parameters. */
  199. template<class Type, class Alloc, class... Args>
  200. Type* bs_new(Args &&...args)
  201. {
  202. return new (bs_alloc<Alloc>(sizeof(Type))) Type(std::forward<Args>(args)...);
  203. }
  204. /** Frees all the bytes allocated at the specified location. */
  205. template<class Alloc>
  206. inline void bs_free(void* ptr)
  207. {
  208. MemoryAllocator<Alloc>::free(ptr);
  209. }
  210. /** Destructs and frees the specified object. */
  211. template<class T, class Alloc = GenAlloc>
  212. inline void bs_delete(T* ptr)
  213. {
  214. (ptr)->~T();
  215. MemoryAllocator<Alloc>::free(ptr);
  216. }
  217. /** Destructs and frees the specified array of objects. */
  218. template<class T, class Alloc = GenAlloc>
  219. inline void bs_deleteN(T* ptr, UINT32 count)
  220. {
  221. for(unsigned int i = 0; i < count; i++)
  222. ptr[i].~T();
  223. MemoryAllocator<Alloc>::free(ptr);
  224. }
  225. /*****************************************************************************/
  226. /* Default versions of all alloc/free/new/delete methods which call GenAlloc */
  227. /*****************************************************************************/
  228. /** Allocates the specified number of bytes. */
  229. inline void* bs_alloc(UINT32 count)
  230. {
  231. return MemoryAllocator<GenAlloc>::allocate(count);
  232. }
  233. /** Allocates enough bytes to hold the specified type, but doesn't construct it. */
  234. template<class T>
  235. inline T* bs_alloc()
  236. {
  237. return (T*)MemoryAllocator<GenAlloc>::allocate(sizeof(T));
  238. }
  239. /**
  240. * Allocates the specified number of bytes aligned to the provided boundary. Boundary is in bytes and must be a power
  241. * of two.
  242. */
  243. inline void* bs_alloc_aligned(UINT32 count, UINT32 align)
  244. {
  245. return MemoryAllocator<GenAlloc>::allocateAligned(count, align);
  246. }
  247. /** Allocates the specified number of bytes aligned to a 16 bytes boundary. */
  248. inline void* bs_alloc_aligned16(UINT32 count)
  249. {
  250. return MemoryAllocator<GenAlloc>::allocateAligned16(count);
  251. }
  252. /** Allocates enough bytes to hold an array of @p count elements the specified type, but doesn't construct them. */
  253. template<class T>
  254. inline T* bs_allocN(UINT32 count)
  255. {
  256. return (T*)MemoryAllocator<GenAlloc>::allocate(count * sizeof(T));
  257. }
  258. /** Creates and constructs an array of @p count elements. */
  259. template<class T>
  260. inline T* bs_newN(UINT32 count)
  261. {
  262. T* ptr = (T*)MemoryAllocator<GenAlloc>::allocate(count * sizeof(T));
  263. for(unsigned int i = 0; i < count; i++)
  264. new ((void*)&ptr[i]) T;
  265. return ptr;
  266. }
  267. /** Create a new object with the specified allocator and the specified parameters. */
  268. template<class Type, class... Args>
  269. Type* bs_new(Args &&...args)
  270. {
  271. return new (bs_alloc<GenAlloc>(sizeof(Type))) Type(std::forward<Args>(args)...);
  272. }
  273. /** Frees all the bytes allocated at the specified location. */
  274. inline void bs_free(void* ptr)
  275. {
  276. MemoryAllocator<GenAlloc>::free(ptr);
  277. }
  278. /** Frees memory previously allocated with bs_alloc_aligned(). */
  279. inline void bs_free_aligned(void* ptr)
  280. {
  281. MemoryAllocator<GenAlloc>::freeAligned(ptr);
  282. }
  283. /** Frees memory previously allocated with bs_alloc_aligned16(). */
  284. inline void bs_free_aligned16(void* ptr)
  285. {
  286. MemoryAllocator<GenAlloc>::freeAligned16(ptr);
  287. }
  288. /************************************************************************/
  289. /* MACRO VERSIONS */
  290. /* You will almost always want to use the template versions but in some */
  291. /* cases (private destructor) it is not possible. In which case you may */
  292. /* use these instead. */
  293. /************************************************************************/
  294. #define BS_PVT_DELETE(T, ptr) \
  295. (ptr)->~T(); \
  296. MemoryAllocator<GenAlloc>::free(ptr);
  297. #define BS_PVT_DELETE_A(T, ptr, Alloc) \
  298. (ptr)->~T(); \
  299. MemoryAllocator<Alloc>::free(ptr);
  300. /** @} */
  301. /** @addtogroup Internal-Utility
  302. * @{
  303. */
  304. /** @addtogroup Memory-Internal
  305. * @{
  306. */
  307. /** Allocator for the standard library that internally uses Banshee memory allocator. */
  308. template <class T, class Alloc = GenAlloc>
  309. class StdAlloc
  310. {
  311. public:
  312. typedef T value_type;
  313. StdAlloc() noexcept {}
  314. template<class T, class Alloc> StdAlloc(const StdAlloc<T, Alloc>&) noexcept {}
  315. template<class T, class Alloc> bool operator==(const StdAlloc<T, Alloc>&) const noexcept { return true; }
  316. template<class T, class Alloc> bool operator!=(const StdAlloc<T, Alloc>&) const noexcept { return false; }
  317. /** Allocate but don't initialize number elements of type T. */
  318. T* allocate(const size_t num) const
  319. {
  320. if (num == 0)
  321. return nullptr;
  322. if (num > static_cast<size_t>(-1) / sizeof(T))
  323. return nullptr; // Error
  324. void* const pv = bs_alloc<Alloc>((UINT32)(num * sizeof(T)));
  325. if (!pv)
  326. return nullptr; // Error
  327. return static_cast<T*>(pv);
  328. }
  329. /** Deallocate storage p of deleted elements. */
  330. void deallocate(T* p, size_t num) const noexcept
  331. {
  332. bs_free<Alloc>((void*)p);
  333. }
  334. };
  335. /** @} */
  336. /** @} */
  337. }
  338. #include "BsMemStack.h"
  339. #include "BsGlobalFrameAlloc.h"
  340. #include "BsMemAllocProfiler.h"