DS_MemoryPool.h 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356
  1. /*
  2. * Copyright (c) 2014, Oculus VR, Inc.
  3. * All rights reserved.
  4. *
  5. * This source code is licensed under the BSD-style license found in the
  6. * LICENSE file in the root directory of this source tree. An additional grant
  7. * of patent rights can be found in the PATENTS file in the same directory.
  8. *
  9. */
  10. /// \file DS_MemoryPool.h
  11. ///
  12. #ifndef __MEMORY_POOL_H
  13. #define __MEMORY_POOL_H
  14. #ifndef __APPLE__
  15. // Use stdlib and not malloc for compatibility
  16. #include <stdlib.h>
  17. #endif
  18. #include "RakAssert.h"
  19. #include "Export.h"
  20. #include "RakMemoryOverride.h"
  21. // DS_MEMORY_POOL_MAX_FREE_PAGES must be > 1
  22. #define DS_MEMORY_POOL_MAX_FREE_PAGES 4
  23. //#define _DISABLE_MEMORY_POOL
  24. namespace DataStructures
  25. {
  26. /// Very fast memory pool for allocating and deallocating structures that don't have constructors or destructors.
  27. /// Contains a list of pages, each of which has an array of the user structures
  28. template <class MemoryBlockType>
  29. class RAK_DLL_EXPORT MemoryPool
  30. {
  31. public:
  32. struct Page;
  33. struct MemoryWithPage
  34. {
  35. MemoryBlockType userMemory;
  36. Page *parentPage;
  37. };
  38. struct Page
  39. {
  40. MemoryWithPage** availableStack;
  41. int availableStackSize;
  42. MemoryWithPage* block;
  43. Page *next, *prev;
  44. };
  45. MemoryPool();
  46. ~MemoryPool();
  47. void SetPageSize(int size); // Defaults to 16384 bytes
  48. MemoryBlockType *Allocate(const char *file, unsigned int line);
  49. void Release(MemoryBlockType *m, const char *file, unsigned int line);
  50. void Clear(const char *file, unsigned int line);
  51. int GetAvailablePagesSize(void) const {return availablePagesSize;}
  52. int GetUnavailablePagesSize(void) const {return unavailablePagesSize;}
  53. int GetMemoryPoolPageSize(void) const {return memoryPoolPageSize;}
  54. protected:
  55. int BlocksPerPage(void) const;
  56. void AllocateFirst(void);
  57. bool InitPage(Page *page, Page *prev, const char *file, unsigned int line);
  58. // availablePages contains pages which have room to give the user new blocks. We return these blocks from the head of the list
  59. // unavailablePages are pages which are totally full, and from which we do not return new blocks.
  60. // Pages move from the head of unavailablePages to the tail of availablePages, and from the head of availablePages to the tail of unavailablePages
  61. Page *availablePages, *unavailablePages;
  62. int availablePagesSize, unavailablePagesSize;
  63. int memoryPoolPageSize;
  64. };
  65. template<class MemoryBlockType>
  66. MemoryPool<MemoryBlockType>::MemoryPool()
  67. {
  68. #ifndef _DISABLE_MEMORY_POOL
  69. //AllocateFirst();
  70. availablePagesSize=0;
  71. unavailablePagesSize=0;
  72. memoryPoolPageSize=16384;
  73. #endif
  74. }
  75. template<class MemoryBlockType>
  76. MemoryPool<MemoryBlockType>::~MemoryPool()
  77. {
  78. #ifndef _DISABLE_MEMORY_POOL
  79. Clear(_FILE_AND_LINE_);
  80. #endif
  81. }
  82. template<class MemoryBlockType>
  83. void MemoryPool<MemoryBlockType>::SetPageSize(int size)
  84. {
  85. memoryPoolPageSize=size;
  86. }
  87. template<class MemoryBlockType>
  88. MemoryBlockType* MemoryPool<MemoryBlockType>::Allocate(const char *file, unsigned int line)
  89. {
  90. #ifdef _DISABLE_MEMORY_POOL
  91. return (MemoryBlockType*) rakMalloc_Ex(sizeof(MemoryBlockType), file, line);
  92. #else
  93. if (availablePagesSize>0)
  94. {
  95. MemoryBlockType *retVal;
  96. Page *curPage;
  97. curPage=availablePages;
  98. retVal = (MemoryBlockType*) curPage->availableStack[--(curPage->availableStackSize)];
  99. if (curPage->availableStackSize==0)
  100. {
  101. --availablePagesSize;
  102. availablePages=curPage->next;
  103. RakAssert(availablePagesSize==0 || availablePages->availableStackSize>0);
  104. curPage->next->prev=curPage->prev;
  105. curPage->prev->next=curPage->next;
  106. if (unavailablePagesSize++==0)
  107. {
  108. unavailablePages=curPage;
  109. curPage->next=curPage;
  110. curPage->prev=curPage;
  111. }
  112. else
  113. {
  114. curPage->next=unavailablePages;
  115. curPage->prev=unavailablePages->prev;
  116. unavailablePages->prev->next=curPage;
  117. unavailablePages->prev=curPage;
  118. }
  119. }
  120. RakAssert(availablePagesSize==0 || availablePages->availableStackSize>0);
  121. return retVal;
  122. }
  123. availablePages = (Page *) rakMalloc_Ex(sizeof(Page), file, line);
  124. if (availablePages==0)
  125. return 0;
  126. availablePagesSize=1;
  127. if (InitPage(availablePages, availablePages, file, line)==false)
  128. return 0;
  129. // If this assert hits, we couldn't allocate even 1 block per page. Increase the page size
  130. RakAssert(availablePages->availableStackSize>1);
  131. return (MemoryBlockType *) availablePages->availableStack[--availablePages->availableStackSize];
  132. #endif
  133. }
  134. template<class MemoryBlockType>
  135. void MemoryPool<MemoryBlockType>::Release(MemoryBlockType *m, const char *file, unsigned int line)
  136. {
  137. #ifdef _DISABLE_MEMORY_POOL
  138. rakFree_Ex(m, file, line);
  139. return;
  140. #else
  141. // Find the page this block is in and return it.
  142. Page *curPage;
  143. MemoryWithPage *memoryWithPage = (MemoryWithPage*)m;
  144. curPage=memoryWithPage->parentPage;
  145. if (curPage->availableStackSize==0)
  146. {
  147. // The page is in the unavailable list so move it to the available list
  148. curPage->availableStack[curPage->availableStackSize++]=memoryWithPage;
  149. unavailablePagesSize--;
  150. // As this page is no longer totally empty, move it to the end of available pages
  151. curPage->next->prev=curPage->prev;
  152. curPage->prev->next=curPage->next;
  153. if (unavailablePagesSize>0 && curPage==unavailablePages)
  154. unavailablePages=unavailablePages->next;
  155. if (availablePagesSize++==0)
  156. {
  157. availablePages=curPage;
  158. curPage->next=curPage;
  159. curPage->prev=curPage;
  160. }
  161. else
  162. {
  163. curPage->next=availablePages;
  164. curPage->prev=availablePages->prev;
  165. availablePages->prev->next=curPage;
  166. availablePages->prev=curPage;
  167. }
  168. }
  169. else
  170. {
  171. curPage->availableStack[curPage->availableStackSize++]=memoryWithPage;
  172. if (curPage->availableStackSize==BlocksPerPage() &&
  173. availablePagesSize>=DS_MEMORY_POOL_MAX_FREE_PAGES)
  174. {
  175. // After a certain point, just deallocate empty pages rather than keep them around
  176. if (curPage==availablePages)
  177. {
  178. availablePages=curPage->next;
  179. RakAssert(availablePages->availableStackSize>0);
  180. }
  181. curPage->prev->next=curPage->next;
  182. curPage->next->prev=curPage->prev;
  183. availablePagesSize--;
  184. rakFree_Ex(curPage->availableStack, file, line );
  185. rakFree_Ex(curPage->block, file, line );
  186. rakFree_Ex(curPage, file, line );
  187. }
  188. }
  189. #endif
  190. }
  191. template<class MemoryBlockType>
  192. void MemoryPool<MemoryBlockType>::Clear(const char *file, unsigned int line)
  193. {
  194. #ifdef _DISABLE_MEMORY_POOL
  195. return;
  196. #else
  197. Page *cur, *freed;
  198. if (availablePagesSize>0)
  199. {
  200. cur = availablePages;
  201. #ifdef _MSC_VER
  202. #pragma warning(disable:4127) // conditional expression is constant
  203. #endif
  204. while (true)
  205. // do
  206. {
  207. rakFree_Ex(cur->availableStack, file, line );
  208. rakFree_Ex(cur->block, file, line );
  209. freed=cur;
  210. cur=cur->next;
  211. if (cur==availablePages)
  212. {
  213. rakFree_Ex(freed, file, line );
  214. break;
  215. }
  216. rakFree_Ex(freed, file, line );
  217. }// while(cur!=availablePages);
  218. }
  219. if (unavailablePagesSize>0)
  220. {
  221. cur = unavailablePages;
  222. while (1)
  223. //do
  224. {
  225. rakFree_Ex(cur->availableStack, file, line );
  226. rakFree_Ex(cur->block, file, line );
  227. freed=cur;
  228. cur=cur->next;
  229. if (cur==unavailablePages)
  230. {
  231. rakFree_Ex(freed, file, line );
  232. break;
  233. }
  234. rakFree_Ex(freed, file, line );
  235. } // while(cur!=unavailablePages);
  236. }
  237. availablePagesSize=0;
  238. unavailablePagesSize=0;
  239. #endif
  240. }
  241. template<class MemoryBlockType>
  242. int MemoryPool<MemoryBlockType>::BlocksPerPage(void) const
  243. {
  244. return memoryPoolPageSize / sizeof(MemoryWithPage);
  245. }
  246. template<class MemoryBlockType>
  247. bool MemoryPool<MemoryBlockType>::InitPage(Page *page, Page *prev, const char *file, unsigned int line)
  248. {
  249. int i=0;
  250. const int bpp = BlocksPerPage();
  251. page->block=(MemoryWithPage*) rakMalloc_Ex(memoryPoolPageSize, file, line);
  252. if (page->block==0)
  253. return false;
  254. page->availableStack=(MemoryWithPage**)rakMalloc_Ex(sizeof(MemoryWithPage*)*bpp, file, line);
  255. if (page->availableStack==0)
  256. {
  257. rakFree_Ex(page->block, file, line );
  258. return false;
  259. }
  260. MemoryWithPage *curBlock = page->block;
  261. MemoryWithPage **curStack = page->availableStack;
  262. while (i < bpp)
  263. {
  264. curBlock->parentPage=page;
  265. curStack[i]=curBlock++;
  266. i++;
  267. }
  268. page->availableStackSize=bpp;
  269. page->next=availablePages;
  270. page->prev=prev;
  271. return true;
  272. }
  273. }
  274. #endif
  275. /*
  276. #include "DS_MemoryPool.h"
  277. #include "DS_List.h"
  278. struct TestMemoryPool
  279. {
  280. int allocationId;
  281. };
  282. int main(void)
  283. {
  284. DataStructures::MemoryPool<TestMemoryPool> memoryPool;
  285. DataStructures::List<TestMemoryPool*> returnList;
  286. for (int i=0; i < 100000; i++)
  287. returnList.Push(memoryPool.Allocate(_FILE_AND_LINE_), _FILE_AND_LINE_);
  288. for (int i=0; i < returnList.Size(); i+=2)
  289. {
  290. memoryPool.Release(returnList[i], _FILE_AND_LINE_);
  291. returnList.RemoveAtIndexFast(i);
  292. }
  293. for (int i=0; i < 100000; i++)
  294. returnList.Push(memoryPool.Allocate(_FILE_AND_LINE_), _FILE_AND_LINE_);
  295. while (returnList.Size())
  296. {
  297. memoryPool.Release(returnList[returnList.Size()-1], _FILE_AND_LINE_);
  298. returnList.RemoveAtIndex(returnList.Size()-1);
  299. }
  300. for (int i=0; i < 100000; i++)
  301. returnList.Push(memoryPool.Allocate(_FILE_AND_LINE_), _FILE_AND_LINE_);
  302. while (returnList.Size())
  303. {
  304. memoryPool.Release(returnList[returnList.Size()-1], _FILE_AND_LINE_);
  305. returnList.RemoveAtIndex(returnList.Size()-1);
  306. }
  307. for (int i=0; i < 100000; i++)
  308. returnList.Push(memoryPool.Allocate(_FILE_AND_LINE_), _FILE_AND_LINE_);
  309. for (int i=100000-1; i <= 0; i-=2)
  310. {
  311. memoryPool.Release(returnList[i], _FILE_AND_LINE_);
  312. returnList.RemoveAtIndexFast(i);
  313. }
  314. for (int i=0; i < 100000; i++)
  315. returnList.Push(memoryPool.Allocate(_FILE_AND_LINE_), _FILE_AND_LINE_);
  316. while (returnList.Size())
  317. {
  318. memoryPool.Release(returnList[returnList.Size()-1], _FILE_AND_LINE_);
  319. returnList.RemoveAtIndex(returnList.Size()-1);
  320. }
  321. return 0;
  322. }
  323. */
粤ICP备19079148号