BsMemoryAllocator.h 11 KB

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