BsMemoryAllocator.h 11 KB

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