浏览代码

Changes in the buddy allocator

Panagiotis Christopoulos Charitos 4 年之前
父节点
当前提交
816c0be7a7
共有 3 个文件被更改,包括 94 次插入51 次删除
  1. 38 19
      AnKi/Util/BuddyAllocator.h
  2. 47 23
      AnKi/Util/BuddyAllocator.inl.h
  3. 9 9
      Tests/Util/BuddyAllocator.cpp

+ 38 - 19
AnKi/Util/BuddyAllocator.h

@@ -14,32 +14,50 @@ namespace anki {
 
 /// This is a generic implementation of a buddy allocator.
 /// @tparam T_MAX_MEMORY_RANGE_LOG2 The max memory to allocate.
-template<U32 T_MAX_MEMORY_RANGE_LOG2>
+template<U32 T_MAX_MEMORY_RANGE_LOG2 = 32>
 class BuddyAllocator
 {
 public:
 	/// The type of the address.
 	using Address = std::conditional_t<(T_MAX_MEMORY_RANGE_LOG2 > 32), PtrSize, U32>;
 
+	BuddyAllocator()
+	{
+	}
+
+	/// @copydoc init
+	BuddyAllocator(GenericMemoryPoolAllocator<U8> alloc, U32 maxMemoryRangeLog2)
+	{
+		init(alloc, maxMemoryRangeLog2);
+	}
+
+	BuddyAllocator(const BuddyAllocator&) = delete; // Non-copyable
+
 	~BuddyAllocator()
 	{
-		ANKI_ASSERT(m_userAllocatedSize == 0 && "Forgot to deallocate");
+		destroy();
 	}
 
-	/// Allocate memory.
+	BuddyAllocator& operator=(const BuddyAllocator&) = delete; // Non-copyable
+
+	/// Init the allocator.
 	/// @param alloc The allocator used for internal structures of the BuddyAllocator.
+	/// @param maxMemoryRangeLog2 The max memory to allocate.
+	void init(GenericMemoryPoolAllocator<U8> alloc, U32 maxMemoryRangeLog2);
+
+	/// Destroy the allocator.
+	void destroy();
+
+	/// Allocate memory.
 	/// @param size The size of the allocation.
 	/// @param[out] address The returned address if the allocation didn't fail.
 	/// @return True if the allocation succeeded.
-	template<typename TAllocator>
-	ANKI_USE_RESULT Bool allocate(TAllocator alloc, PtrSize size, Address& address);
+	ANKI_USE_RESULT Bool allocate(PtrSize size, Address& address);
 
 	/// Free memory.
-	/// @param alloc The allocator used for internal structures of the BuddyAllocator.
 	/// @param address The address to free.
 	/// @param size The size of the allocation.
-	template<typename TAllocator>
-	void free(TAllocator alloc, Address address, PtrSize size);
+	void free(Address address, PtrSize size);
 
 	/// Print a debug representation of the internal structures.
 	void debugPrint() const;
@@ -52,7 +70,6 @@ public:
 	}
 
 private:
-	/// Because we need a constexpr version of pow.
 	template<typename T>
 	static constexpr T pow2(T exp)
 	{
@@ -65,26 +82,28 @@ private:
 		return U32(__builtin_ctzll(v));
 	}
 
-	static constexpr U32 ORDER_COUNT = T_MAX_MEMORY_RANGE_LOG2 + 1;
-	static constexpr PtrSize MAX_MEMORY_RANGE = pow2<PtrSize>(T_MAX_MEMORY_RANGE_LOG2);
-
 	using FreeList = DynamicArray<Address, PtrSize>;
-	Array<FreeList, ORDER_COUNT> m_freeLists;
+	GenericMemoryPoolAllocator<U8> m_alloc;
+	DynamicArray<FreeList> m_freeLists;
+	PtrSize m_maxMemoryRange = 0;
 	PtrSize m_userAllocatedSize = 0;
 	PtrSize m_realAllocatedSize = 0;
 
-	template<typename TAllocator>
-	PtrSize popFree(TAllocator& alloc, U32 order)
+	U32 orderCount() const
+	{
+		return m_freeLists.getSize();
+	}
+
+	PtrSize popFree(U32 order)
 	{
 		ANKI_ASSERT(m_freeLists[order].getSize() > 0);
 		const PtrSize address = m_freeLists[order].getBack();
-		m_freeLists[order].popBack(alloc);
-		ANKI_ASSERT(address < MAX_MEMORY_RANGE);
+		m_freeLists[order].popBack(m_alloc);
+		ANKI_ASSERT(address < m_maxMemoryRange);
 		return address;
 	}
 
-	template<typename TAllocator>
-	void freeInternal(TAllocator& alloc, PtrSize address, PtrSize size);
+	void freeInternal(PtrSize address, PtrSize size);
 };
 /// @}
 

+ 47 - 23
AnKi/Util/BuddyAllocator.inl.h

@@ -8,16 +8,38 @@
 namespace anki {
 
 template<U32 T_MAX_MEMORY_RANGE_LOG2>
-template<typename TAllocator>
-Bool BuddyAllocator<T_MAX_MEMORY_RANGE_LOG2>::allocate(TAllocator alloc, PtrSize size, Address& outAddress)
+void BuddyAllocator<T_MAX_MEMORY_RANGE_LOG2>::init(GenericMemoryPoolAllocator<U8> alloc, U32 maxMemoryRangeLog2)
 {
-	ANKI_ASSERT(size > 0 && size <= MAX_MEMORY_RANGE);
+	ANKI_ASSERT(maxMemoryRangeLog2 >= 1 && maxMemoryRangeLog2 <= T_MAX_MEMORY_RANGE_LOG2);
+	ANKI_ASSERT(m_freeLists.getSize() == 0 && m_userAllocatedSize == 0 && m_realAllocatedSize == 0);
+
+	const U32 orderCount = maxMemoryRangeLog2 + 1;
+	m_maxMemoryRange = pow2<PtrSize>(maxMemoryRangeLog2);
+
+	m_alloc = alloc;
+	m_freeLists.create(m_alloc, orderCount);
+}
+
+template<U32 T_MAX_MEMORY_RANGE_LOG2>
+void BuddyAllocator<T_MAX_MEMORY_RANGE_LOG2>::destroy()
+{
+	ANKI_ASSERT(m_userAllocatedSize == 0 && "Forgot to free all memory");
+	m_freeLists.destroy(m_alloc);
+	m_maxMemoryRange = 0;
+	m_userAllocatedSize = 0;
+	m_realAllocatedSize = 0;
+}
+
+template<U32 T_MAX_MEMORY_RANGE_LOG2>
+Bool BuddyAllocator<T_MAX_MEMORY_RANGE_LOG2>::allocate(PtrSize size, Address& outAddress)
+{
+	ANKI_ASSERT(size > 0 && size <= m_maxMemoryRange);
 
 	// Lazy initialize
 	if(m_userAllocatedSize == 0)
 	{
 		const Address startingAddress = 0;
-		m_freeLists[ORDER_COUNT - 1].create(alloc, 1, startingAddress);
+		m_freeLists.getBack().create(m_alloc, 1, startingAddress);
 	}
 
 	// Find the order to start the search
@@ -35,7 +57,7 @@ Bool BuddyAllocator<T_MAX_MEMORY_RANGE_LOG2>::allocate(TAllocator alloc, PtrSize
 	}
 
 	// Iterate
-	PtrSize address = popFree(alloc, order);
+	PtrSize address = popFree(order);
 	while(true)
 	{
 		const PtrSize orderSize = pow2<PtrSize>(order);
@@ -46,15 +68,15 @@ Bool BuddyAllocator<T_MAX_MEMORY_RANGE_LOG2>::allocate(TAllocator alloc, PtrSize
 		}
 
 		const PtrSize buddyAddress = address + orderSize / 2;
-		ANKI_ASSERT(buddyAddress < MAX_MEMORY_RANGE && buddyAddress <= getMaxNumericLimit<Address>());
+		ANKI_ASSERT(buddyAddress < m_maxMemoryRange && buddyAddress <= getMaxNumericLimit<Address>());
 
 		ANKI_ASSERT(order > 0);
-		m_freeLists[order - 1].emplaceBack(alloc, Address(buddyAddress));
+		m_freeLists[order - 1].emplaceBack(m_alloc, Address(buddyAddress));
 
 		--order;
 	}
 
-	ANKI_ASSERT(address + alignedSize <= MAX_MEMORY_RANGE);
+	ANKI_ASSERT(address + alignedSize <= m_maxMemoryRange);
 	m_userAllocatedSize += size;
 	m_realAllocatedSize += alignedSize;
 	ANKI_ASSERT(address <= getMaxNumericLimit<Address>());
@@ -63,11 +85,10 @@ Bool BuddyAllocator<T_MAX_MEMORY_RANGE_LOG2>::allocate(TAllocator alloc, PtrSize
 }
 
 template<U32 T_MAX_MEMORY_RANGE_LOG2>
-template<typename TAllocator>
-void BuddyAllocator<T_MAX_MEMORY_RANGE_LOG2>::free(TAllocator alloc, Address address, PtrSize size)
+void BuddyAllocator<T_MAX_MEMORY_RANGE_LOG2>::free(Address address, PtrSize size)
 {
 	const PtrSize alignedSize = nextPowerOfTwo(size);
-	freeInternal(alloc, address, alignedSize);
+	freeInternal(address, alignedSize);
 
 	ANKI_ASSERT(m_userAllocatedSize >= size);
 	m_userAllocatedSize -= size;
@@ -86,13 +107,12 @@ void BuddyAllocator<T_MAX_MEMORY_RANGE_LOG2>::free(TAllocator alloc, Address add
 }
 
 template<U32 T_MAX_MEMORY_RANGE_LOG2>
-template<typename TAllocator>
-void BuddyAllocator<T_MAX_MEMORY_RANGE_LOG2>::freeInternal(TAllocator& alloc, PtrSize address, PtrSize size)
+void BuddyAllocator<T_MAX_MEMORY_RANGE_LOG2>::freeInternal(PtrSize address, PtrSize size)
 {
 	ANKI_ASSERT(isPowerOfTwo(size));
-	ANKI_ASSERT(address + size <= MAX_MEMORY_RANGE);
+	ANKI_ASSERT(address + size <= m_maxMemoryRange);
 
-	if(size == MAX_MEMORY_RANGE)
+	if(size == m_maxMemoryRange)
 	{
 		return;
 	}
@@ -110,7 +130,7 @@ void BuddyAllocator<T_MAX_MEMORY_RANGE_LOG2>::freeInternal(TAllocator& alloc, Pt
 		buddyAddress = address + size;
 	}
 
-	ANKI_ASSERT(buddyAddress + size <= MAX_MEMORY_RANGE);
+	ANKI_ASSERT(buddyAddress + size <= m_maxMemoryRange);
 
 	// Adjust the free lists
 	const U32 order = log2(size);
@@ -119,9 +139,9 @@ void BuddyAllocator<T_MAX_MEMORY_RANGE_LOG2>::freeInternal(TAllocator& alloc, Pt
 	{
 		if(m_freeLists[order][i] == buddyAddress)
 		{
-			m_freeLists[order].erase(alloc, m_freeLists[order].getBegin() + i);
+			m_freeLists[order].erase(m_alloc, m_freeLists[order].getBegin() + i);
 
-			freeInternal(alloc, (buddyIsLeft) ? buddyAddress : address, size * 2);
+			freeInternal((buddyIsLeft) ? buddyAddress : address, size * 2);
 			buddyFound = true;
 			break;
 		}
@@ -130,15 +150,19 @@ void BuddyAllocator<T_MAX_MEMORY_RANGE_LOG2>::freeInternal(TAllocator& alloc, Pt
 	if(!buddyFound)
 	{
 		ANKI_ASSERT(address <= getMaxNumericLimit<Address>());
-		m_freeLists[order].emplaceBack(alloc, Address(address));
+		m_freeLists[order].emplaceBack(m_alloc, Address(address));
 	}
 }
 
 template<U32 T_MAX_MEMORY_RANGE_LOG2>
 void BuddyAllocator<T_MAX_MEMORY_RANGE_LOG2>::debugPrint() const
 {
-	BitSet<MAX_MEMORY_RANGE>* freeBytes = new BitSet<MAX_MEMORY_RANGE>(false);
-	for(I32 order = ORDER_COUNT - 1; order >= 0; --order)
+	constexpr PtrSize MAX_MEMORY_RANGE = pow2<PtrSize>(T_MAX_MEMORY_RANGE_LOG2);
+
+	// Allocate because we can't possibly have that in the stack
+	BitSet<MAX_MEMORY_RANGE>* freeBytes = m_alloc.newInstance<BitSet<MAX_MEMORY_RANGE>>(false);
+
+	for(I32 order = orderCount() - 1; order >= 0; --order)
 	{
 		const PtrSize orderSize = pow2<PtrSize>(order);
 		freeBytes->unsetAll();
@@ -152,14 +176,14 @@ void BuddyAllocator<T_MAX_MEMORY_RANGE_LOG2>::debugPrint() const
 			}
 		}
 
-		for(PtrSize i = 0; i < MAX_MEMORY_RANGE; ++i)
+		for(PtrSize i = 0; i < m_maxMemoryRange; ++i)
 		{
 			putc(freeBytes->get(i) ? 'F' : '?', stdout);
 		}
 
 		printf("\n");
 	}
-	delete freeBytes;
+	m_alloc.deleteInstance(freeBytes);
 }
 
 } // end namespace anki

+ 9 - 9
Tests/Util/BuddyAllocator.cpp

@@ -14,17 +14,17 @@ ANKI_TEST(Util, BuddyAllocator)
 
 	// Simple
 	{
-		BuddyAllocator<4> buddy;
+		BuddyAllocator<4> buddy(alloc, 4);
 
 		Array<U32, 2> addr;
-		Bool success = buddy.allocate(alloc, 1, addr[0]);
-		success = buddy.allocate(alloc, 3, addr[1]);
+		Bool success = buddy.allocate(1, addr[0]);
+		success = buddy.allocate(3, addr[1]);
 		(void)success;
 
 		// buddy.debugPrint();
 
-		buddy.free(alloc, addr[0], 1);
-		buddy.free(alloc, addr[1], 3);
+		buddy.free(addr[0], 1);
+		buddy.free(addr[1], 3);
 
 		// printf("\n");
 		// buddy.debugPrint();
@@ -32,7 +32,7 @@ ANKI_TEST(Util, BuddyAllocator)
 
 	// Fuzzy
 	{
-		BuddyAllocator<32> buddy;
+		BuddyAllocator<32> buddy(alloc, 32);
 		std::vector<std::pair<U32, U32>> allocations;
 		for(U32 it = 0; it < 1000; ++it)
 		{
@@ -41,7 +41,7 @@ ANKI_TEST(Util, BuddyAllocator)
 				// Do an allocation
 				U32 addr;
 				const U32 size = max<U32>(getRandom() % 512, 1);
-				const Bool success = buddy.allocate(alloc, size, addr);
+				const Bool success = buddy.allocate(size, addr);
 				if(success)
 				{
 					allocations.push_back({addr, size});
@@ -53,7 +53,7 @@ ANKI_TEST(Util, BuddyAllocator)
 				if(allocations.size())
 				{
 					const PtrSize randPos = getRandom() % allocations.size();
-					buddy.free(alloc, allocations[randPos].first, allocations[randPos].second);
+					buddy.free(allocations[randPos].first, allocations[randPos].second);
 
 					allocations.erase(allocations.begin() + randPos);
 				}
@@ -63,7 +63,7 @@ ANKI_TEST(Util, BuddyAllocator)
 		// Remove the remaining
 		for(const auto& pair : allocations)
 		{
-			buddy.free(alloc, pair.first, pair.second);
+			buddy.free(pair.first, pair.second);
 		}
 	}
 }