2
0

BsMemoryAllocator.h 10 KB

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