BuddyAllocatorBuilder.inl.h 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. // Copyright (C) 2009-present, Panagiotis Christopoulos Charitos and contributors.
  2. // All rights reserved.
  3. // Code licensed under the BSD License.
  4. // http://www.anki3d.org/LICENSE
  5. #include <AnKi/Util/BuddyAllocatorBuilder.h>
  6. namespace anki {
  7. template<U32 kMaxMemoryRangeLog2, typename TLock, typename TMemoryPool>
  8. void BuddyAllocatorBuilder<kMaxMemoryRangeLog2, TLock, TMemoryPool>::init(U32 maxMemoryRangeLog2)
  9. {
  10. ANKI_ASSERT(maxMemoryRangeLog2 >= 1 && maxMemoryRangeLog2 <= kMaxMemoryRangeLog2);
  11. ANKI_ASSERT(m_freeLists.getSize() == 0 && m_userAllocatedSize == 0 && m_realAllocatedSize == 0);
  12. const U32 orderCount = maxMemoryRangeLog2 + 1;
  13. m_maxMemoryRange = pow2<PtrSize>(maxMemoryRangeLog2);
  14. m_freeLists.resize(orderCount, getMemoryPool());
  15. }
  16. template<U32 kMaxMemoryRangeLog2, typename TLock, typename TMemoryPool>
  17. void BuddyAllocatorBuilder<kMaxMemoryRangeLog2, TLock, TMemoryPool>::destroy()
  18. {
  19. ANKI_ASSERT(m_userAllocatedSize == 0 && "Forgot to free all memory");
  20. m_freeLists.destroy();
  21. m_maxMemoryRange = 0;
  22. m_userAllocatedSize = 0;
  23. m_realAllocatedSize = 0;
  24. }
  25. template<U32 kMaxMemoryRangeLog2, typename TLock, typename TMemoryPool>
  26. Bool BuddyAllocatorBuilder<kMaxMemoryRangeLog2, TLock, TMemoryPool>::allocate(PtrSize size, PtrSize alignment, Address& outAddress)
  27. {
  28. ANKI_ASSERT(size > 0 && size <= m_maxMemoryRange);
  29. PtrSize alignedSize = nextPowerOfTwo(size);
  30. U32 order = log2(alignedSize);
  31. const PtrSize orderSize = pow2<PtrSize>(order);
  32. // The alignment for the requested "size" is the "orderSize". If the "orderSize" doesn't satisfy the "alignment"
  33. // parameter then we need to align the allocation address
  34. const Bool needsPadding = !isAligned(alignment, orderSize);
  35. if(needsPadding)
  36. {
  37. // We need more space to accommodate possible unaligned allocation address
  38. alignedSize = nextPowerOfTwo(size + alignment);
  39. // Re-calcuate the order as well
  40. order = log2(alignedSize);
  41. }
  42. LockGuard<TLock> lock(m_mutex);
  43. // Lazy initialize
  44. if(m_userAllocatedSize == 0)
  45. {
  46. const Address startingAddress = 0;
  47. m_freeLists.getBack().resize(1, startingAddress);
  48. }
  49. // Find the order to start the search
  50. while(m_freeLists[order].getSize() == 0)
  51. {
  52. ++order;
  53. if(order >= m_freeLists.getSize())
  54. {
  55. // Out of memory
  56. return false;
  57. }
  58. }
  59. // Iterate
  60. PtrSize address = popFree(order);
  61. while(true)
  62. {
  63. const PtrSize orderSize = pow2<PtrSize>(order);
  64. if(orderSize == alignedSize)
  65. {
  66. // Found the address
  67. break;
  68. }
  69. const PtrSize buddyAddress = address + orderSize / 2;
  70. ANKI_ASSERT(buddyAddress < m_maxMemoryRange && buddyAddress <= getMaxNumericLimit<Address>());
  71. ANKI_ASSERT(order > 0);
  72. m_freeLists[order - 1].emplaceBack(Address(buddyAddress));
  73. --order;
  74. }
  75. // Align the returned address if needed
  76. if(needsPadding)
  77. {
  78. alignRoundUp(alignment, address);
  79. }
  80. ANKI_ASSERT(address + size <= m_maxMemoryRange);
  81. ANKI_ASSERT(isAligned(alignment, address));
  82. m_userAllocatedSize += size;
  83. m_realAllocatedSize += alignedSize;
  84. ANKI_ASSERT(address <= getMaxNumericLimit<Address>());
  85. outAddress = Address(address);
  86. return true;
  87. }
  88. template<U32 kMaxMemoryRangeLog2, typename TLock, typename TMemoryPool>
  89. void BuddyAllocatorBuilder<kMaxMemoryRangeLog2, TLock, TMemoryPool>::free(Address address, PtrSize size, PtrSize alignment)
  90. {
  91. PtrSize alignedSize = nextPowerOfTwo(size);
  92. U32 order = log2(alignedSize);
  93. const PtrSize orderSize = pow2<PtrSize>(order);
  94. // See allocate()
  95. const Bool needsPadding = !isAligned(alignment, orderSize);
  96. if(needsPadding)
  97. {
  98. alignedSize = nextPowerOfTwo(size + alignment);
  99. // Address was rounded up on allocate(), do the opposite
  100. alignRoundDown(orderSize, address);
  101. }
  102. LockGuard<TLock> lock(m_mutex);
  103. freeInternal(address, alignedSize);
  104. ANKI_ASSERT(m_userAllocatedSize >= size);
  105. m_userAllocatedSize -= size;
  106. ANKI_ASSERT(m_realAllocatedSize >= alignedSize);
  107. m_realAllocatedSize -= alignedSize;
  108. // Some checks
  109. if(m_userAllocatedSize == 0)
  110. {
  111. ANKI_ASSERT(m_realAllocatedSize == 0);
  112. for([[maybe_unused]] const FreeList& freeList : m_freeLists)
  113. {
  114. ANKI_ASSERT(freeList.getSize() == 0);
  115. }
  116. }
  117. }
  118. template<U32 kMaxMemoryRangeLog2, typename TLock, typename TMemoryPool>
  119. void BuddyAllocatorBuilder<kMaxMemoryRangeLog2, TLock, TMemoryPool>::freeInternal(PtrSize address, PtrSize size)
  120. {
  121. ANKI_ASSERT(size);
  122. ANKI_ASSERT(isPowerOfTwo(size));
  123. ANKI_ASSERT(address + size <= m_maxMemoryRange);
  124. if(size == m_maxMemoryRange)
  125. {
  126. return;
  127. }
  128. // Find if the buddy is in the left side of the memory address space or the right one
  129. const Bool buddyIsLeft = ((address / size) % 2) != 0;
  130. PtrSize buddyAddress;
  131. if(buddyIsLeft)
  132. {
  133. ANKI_ASSERT(address >= size);
  134. buddyAddress = address - size;
  135. }
  136. else
  137. {
  138. buddyAddress = address + size;
  139. }
  140. ANKI_ASSERT(buddyAddress + size <= m_maxMemoryRange);
  141. // Adjust the free lists
  142. const U32 order = log2(size);
  143. Bool buddyFound = false;
  144. for(PtrSize i = 0; i < m_freeLists[order].getSize(); ++i)
  145. {
  146. if(m_freeLists[order][i] == buddyAddress)
  147. {
  148. m_freeLists[order].erase(m_freeLists[order].getBegin() + i);
  149. freeInternal((buddyIsLeft) ? buddyAddress : address, size * 2);
  150. buddyFound = true;
  151. break;
  152. }
  153. }
  154. if(!buddyFound)
  155. {
  156. ANKI_ASSERT(address <= getMaxNumericLimit<Address>());
  157. m_freeLists[order].emplaceBack(Address(address));
  158. }
  159. }
  160. template<U32 kMaxMemoryRangeLog2, typename TLock, typename TMemoryPool>
  161. void BuddyAllocatorBuilder<kMaxMemoryRangeLog2, TLock, TMemoryPool>::debugPrint() const
  162. {
  163. constexpr PtrSize kMaxMemoryRange = pow2<PtrSize>(kMaxMemoryRangeLog2);
  164. // Allocate because we can't possibly have that in the stack
  165. BitSet<kMaxMemoryRange>* freeBytes = newInstance<BitSet<kMaxMemoryRange>>(getMemoryPool(), false);
  166. LockGuard<TLock> lock(m_mutex);
  167. for(I32 order = orderCount() - 1; order >= 0; --order)
  168. {
  169. const PtrSize orderSize = pow2<PtrSize>(order);
  170. freeBytes->unsetAll();
  171. printf("%d: ", order);
  172. for(PtrSize address : m_freeLists[order])
  173. {
  174. for(PtrSize byte = address; byte < address + orderSize; ++byte)
  175. {
  176. freeBytes->set(byte);
  177. }
  178. }
  179. for(PtrSize i = 0; i < m_maxMemoryRange; ++i)
  180. {
  181. putc(freeBytes->get(i) ? 'F' : '?', stdout);
  182. }
  183. printf("\n");
  184. }
  185. deleteInstance(getMemoryPool(), freeBytes);
  186. }
  187. template<U32 kMaxMemoryRangeLog2, typename TLock, typename TMemoryPool>
  188. void BuddyAllocatorBuilder<kMaxMemoryRangeLog2, TLock, TMemoryPool>::getStats(BuddyAllocatorBuilderStats& stats) const
  189. {
  190. LockGuard<TLock> lock(m_mutex);
  191. stats.m_userAllocatedSize = m_userAllocatedSize;
  192. stats.m_realAllocatedSize = m_realAllocatedSize;
  193. // Compute external fragmetation (wikipedia has the definition)
  194. U32 order = 0;
  195. U32 orderWithTheBiggestBlock = kMaxU32;
  196. for(const FreeList& list : m_freeLists)
  197. {
  198. if(list.getSize())
  199. {
  200. orderWithTheBiggestBlock = order;
  201. }
  202. ++order;
  203. }
  204. const PtrSize biggestBlockSize = (orderWithTheBiggestBlock == kMaxU32) ? m_maxMemoryRange : pow2<PtrSize>(orderWithTheBiggestBlock);
  205. const PtrSize realFreeMemory = m_maxMemoryRange - m_realAllocatedSize;
  206. stats.m_externalFragmentation = F32(1.0 - F64(biggestBlockSize) / F64(realFreeMemory));
  207. // Internal fragmentation
  208. stats.m_internalFragmentation = F32(1.0 - F64(m_userAllocatedSize) / F64(m_realAllocatedSize));
  209. }
  210. } // end namespace anki