BsMemoryAllocator.h 12 KB

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