Browse Source

Allocator stuff

Panagiotis Christopoulos Charitos 13 years ago
parent
commit
5ee471d109
5 changed files with 120 additions and 217 deletions
  1. 15 89
      include/anki/util/Allocator.h
  2. 30 16
      include/anki/util/Memory.h
  3. 0 2
      src/core/ThreadPool.cpp
  4. 1 90
      src/util/Allocator.cpp
  5. 74 20
      src/util/Memory.cpp

+ 15 - 89
include/anki/util/Allocator.h

@@ -3,10 +3,10 @@
 
 #include "anki/util/Exception.h"
 #include "anki/util/Assert.h"
+#include "anki/util/Memory.h"
 #include <cstddef> // For ptrdiff_t
-#include <cstring> // For memset
-#include <atomic>
 #include <utility> // For forward
+#include <memory> // For shared_ptr
 
 #define ANKI_DEBUG_ALLOCATORS ANKI_DEBUG
 #define ANKI_PRINT_ALLOCATOR_MESSAGES 1
@@ -179,51 +179,6 @@ inline bool operator!=(const Allocator<T1>&, const AnotherAllocator&)
 	return true;
 }
 
-namespace detail {
-
-/// Thread safe memory pool
-struct MemoryPool
-{
-	/// Allocated memory
-	U8* memory = nullptr;
-	/// Size of the allocated memory
-	PtrSize size = 0;
-	/// Points to the memory and more specifically to the address of the next
-	/// allocation
-	std::atomic<U8*> ptr = {nullptr};
-	/// Reference counter
-	std::atomic<I32> refCounter = {1};
-
-#if ANKI_DEBUG_ALLOCATORS
-	std::atomic<PtrSize> wastedSize = {0};
-#endif
-};
-
-/// Internal members for the @ref StackAllocator. They are separate because we 
-/// don't want to pollute the @ref StackAllocator template with specialized 
-/// functions that take space
-class StackAllocatorInternal
-{
-public:
-	/// Used for debugging
-	Bool dump();
-
-protected:
-	/// The memory pool
-	detail::MemoryPool* mpool = nullptr;
-
-	/// Init the memory pool with the given size
-	void init(const PtrSize size);
-
-	/// Deinit the memory pool
-	void deinit();
-
-	/// Copy
-	void copy(const StackAllocatorInternal& b);
-};
-
-} // end namespace detail
-
 /// Stack based allocator
 ///
 /// @tparam T The type
@@ -239,7 +194,7 @@ protected:
 /// @note Don't ever EVER remove the double copy constructor and the double
 ///       operator=. The compiler will create defaults
 template<typename T, Bool deallocationFlag = false, U32 alignmentBits = 16>
-class StackAllocator: public detail::StackAllocatorInternal
+class StackAllocator
 {
 	template<typename U, Bool deallocationFlag_, U32 alignmentBits_>
 	friend class StackAllocator;
@@ -272,19 +227,17 @@ public:
 	/// Constuctor with size
 	StackAllocator(size_type size) throw()
 	{
-		init(size);
+		mpool.reset(new StackMemoryPool(size, alignmentBits));
 	}
 
 	/// Destructor
 	~StackAllocator()
-	{
-		deinit();
-	}
+	{}
 
 	/// Copy
 	StackAllocator& operator=(const StackAllocator& b)
 	{
-		copy(b);
+		mpool = b.mpool;
 		return *this;
 	}
 	/// Copy
@@ -292,7 +245,7 @@ public:
 	StackAllocator& operator=(
 		const StackAllocator<U, deallocationFlag, alignmentBits>& b)
 	{
-		copy(b);
+		mpool = b.mpool;
 		return *this;
 	}
 
@@ -313,18 +266,10 @@ public:
 		ANKI_ASSERT(mpool != nullptr);
 		(void)hint;
 		size_type size = n * sizeof(value_type);
-		size_type alignedSize = calcAlignSize(size);
-
-		U8* out = mpool->ptr.fetch_add(alignedSize);
+		
+		void* out = mpool->allocate(size);
 
-#if ANKI_PRINT_ALLOCATOR_MESSAGES
-			std::cout << "Allocating: size: " << size
-				<< ", size after alignment: " << alignedSize
-				<< ", returned address: " << (void*)out
-				<< ", hint: " << hint <<std::endl;
-#endif
-
-		if(out + alignedSize <= mpool->memory + mpool->size)
+		if(out != nullptr)
 		{
 			// Everything ok
 		}
@@ -345,28 +290,13 @@ public:
 
 		if(deallocationFlag)
 		{
-			size_type alignedSize = calcAlignSize(n * sizeof(value_type));
-#if ANKI_PRINT_ALLOCATOR_MESSAGES
-			std::cout << "Deallocating: size: " << (n * sizeof(value_type))
-				<< " alignedSize: " << alignedSize
-				<< " pointer: " << p << std::endl;
-#endif
-			U8* headPtr = mpool->ptr.fetch_sub(alignedSize);
+			Bool ok = mpool->free(p);
 
-			if(headPtr - alignedSize != p)
+			if(!ok)
 			{
 				throw ANKI_EXCEPTION("Freeing wrong pointer. "
 					"The deallocations on StackAllocator should be in order");
 			}
-
-			ANKI_ASSERT((headPtr - alignedSize) >= mpool->memory);
-		}
-		else
-		{
-#if ANKI_DEBUG_ALLOCATORS
-			size_type alignedSize = calcAlignSize(n * sizeof(value_type));
-			mpool->wastedSize += alignedSize;
-#endif
 		}
 	}
 
@@ -400,7 +330,7 @@ public:
 	size_type max_size() const
 	{
 		ANKI_ASSERT(mpool != nullptr);
-		return mpool->size;
+		return mpool->getSize();
 	}
 
 	/// A struct to rebind the allocator to another allocator of type U
@@ -414,15 +344,11 @@ public:
 	void reset()
 	{
 		ANKI_ASSERT(mpool != nullptr);
-		mpool->ptr = mpool->memory;
+		mpool->reset();
 	}
 
 private:
-	/// Calculate tha align size
-	size_type calcAlignSize(size_type size)
-	{
-		return size + (size % (alignmentBits / 8));
-	}
+	std::shared_ptr<StackMemoryPool> mpool;
 };
 
 /// Another allocator of the same type can deallocate from this one

+ 30 - 16
include/anki/util/Memory.h

@@ -4,6 +4,7 @@
 #include "anki/util/StdTypes.h"
 #include "anki/util/NonCopyable.h"
 #include <atomic>
+#include <algorithm>
 
 namespace anki {
 
@@ -13,42 +14,55 @@ namespace anki {
 /// @{
 
 /// Thread safe memory pool
-class StackedMemoryPool: public NonCopyable
+class StackMemoryPool: public NonCopyable
 {
 public:
 	/// Default constructor
-	StackedMemoryPool(PtrSize size, U32 alignmentBits = 16);
+	StackMemoryPool(PtrSize size, U32 alignmentBits = 16);
 
 	/// Move
-	StackedMemoryPool(StackedMemoryPool&& other);
+	StackMemoryPool(StackMemoryPool&& other)
+	{
+		*this = std::move(other);
+	}
+
+	/// Destroy
+	~StackMemoryPool();
+
+	/// Move
+	StackMemoryPool& operator=(StackMemoryPool&& other);
 
-	~StackedMemoryPool();
+	/// Access the max size
+	PtrSize getSize() const
+	{
+		return memsize;
+	}
 
-	/// Allocate memory in StackedMemoryPool
+	/// Allocate memory
 	void* allocate(PtrSize size) throw();
 
-	/// Free memory in StackedMemoryPool
+	/// Free memory in StackMemoryPool. If the ptr is not the last allocation
+	/// then nothing happens and the method returns false
 	Bool free(void* ptr) throw();
 
+	/// Reinit the pool. All existing allocated memory will be lost
+	void reset();
+
 private:
-	/// Allocated memory
+	/// Pre-allocated memory memory chunk
 	U8* memory = nullptr;
 
-	/// Size of the allocated memory
+	/// Size of the allocated memory chunk
 	PtrSize memsize = 0;
 
-	/// Points to the memory and more specifically to the address of the next
-	/// allocation
-	std::atomic<U8*> ptr = {nullptr};
+	/// Points to the memory and more specifically to the top of the stack
+	std::atomic<U8*> top = {nullptr};
 
 	/// Alignment
 	U32 alignmentBits;
 
-	/// Calculate tha aligned size
-	PtrSize calcAlignSize(PtrSize size) const
-	{
-		return size + (size % (alignmentBits / 8));
-	}
+	/// Calculate tha aligned size of an allocation
+	PtrSize calcAlignSize(PtrSize size) const;
 };
 
 /// @}

+ 0 - 2
src/core/ThreadPool.cpp

@@ -72,12 +72,10 @@ void ThreadPool::init(U threadsNum)
 	barrier.reset(new Barrier(threadsNum + 1));
 
 	jobs.resize(threadsNum);
-
 	for(U i = 0; i < threadsNum; i++)
 	{
 		jobs[i] = new ThreadWorker(i, barrier.get(), this);
 	}
 }
 
-
 } // end namespace anki

+ 1 - 90
src/util/Allocator.cpp

@@ -1,5 +1,6 @@
 #include "anki/util/Allocator.h"
 #include <iostream>
+#include <cstring>
 
 namespace anki {
 
@@ -70,94 +71,4 @@ void AllocatorInternal::gfree(void* p, PtrSize size)
 
 } // end namespace detail
 
-//==============================================================================
-// StackAllocatorInternal                                                      =
-//==============================================================================
-
-namespace detail {
-
-//==============================================================================
-void StackAllocatorInternal::init(const PtrSize size)
-{
-	ANKI_ASSERT(mpool == nullptr);
-	mpool = new detail::MemoryPool;
-
-	if(mpool != nullptr)
-	{
-		mpool->memory = (U8*)::malloc(size);
-
-		if(mpool->memory != nullptr)
-		{
-			mpool->size = size;
-			mpool->ptr = mpool->memory;
-			// Memory pool's refcounter is 1
-
-			ANKI_ALLOCATOR_PRINT("New MemoryPool created: Address: " << mpool
-				<< ", size: " << size
-				<< ", pool address: " << (void*)mpool->memory);
-
-			return;
-		}
-		else
-		{
-			delete mpool;
-		}
-	}
-
-	std::cerr << "Stack allocator constuctor failed. I will cannot "
-		"throw but I have to exit" << std::endl;
-	exit(0);
-}
-
-//==============================================================================
-void StackAllocatorInternal::deinit()
-{
-	if (mpool)
-	{
-		I32 refCounterPrev = mpool->refCounter.fetch_sub(1);
-
-		if(refCounterPrev == 1)
-		{
-			ANKI_ALLOCATOR_PRINT("Deleting MemoryPool " << mpool);
-
-			::free(mpool->memory);
-			delete mpool;
-			mpool = nullptr;
-		}
-	}
-}
-
-//==============================================================================
-void StackAllocatorInternal::copy(const StackAllocatorInternal& b)
-{
-	deinit();
-	mpool = b.mpool;
-	if(mpool)
-	{
-		// Retain the mpool
-		++mpool->refCounter;
-	}
-}
-
-//==============================================================================
-Bool StackAllocatorInternal::dump()
-{
-	Bool ret = true;
-
-	if(mpool)
-	{
-		auto diff = mpool->ptr.load() - mpool->memory;
-
-		if(diff > 0)
-		{
-			ANKI_ALLOCATOR_PRINT("Lost bytes: " << diff);
-			ret = false;
-		}
-	}
-
-	return ret;
-}
-
-} // end namespace detail
-
 } // end namespace anki

+ 74 - 20
src/util/Memory.cpp

@@ -7,13 +7,13 @@
 namespace anki {
 
 //==============================================================================
-struct StackedMemoryPoolBlock
+struct MemoryBlockHeader
 {
 	U32 size;
 };
 
 //==============================================================================
-StackedMemoryPool::StackedMemoryPool(PtrSize size_, U32 alignmentBits_)
+StackMemoryPool::StackMemoryPool(PtrSize size_, U32 alignmentBits_)
 	: memsize(size_), alignmentBits(alignmentBits_)
 {
 	ANKI_ASSERT(memsize > 0);
@@ -21,7 +21,7 @@ StackedMemoryPool::StackedMemoryPool(PtrSize size_, U32 alignmentBits_)
 
 	if(memory != nullptr)
 	{
-		ptr = memory;
+		top = memory;
 	}
 	else
 	{
@@ -30,21 +30,51 @@ StackedMemoryPool::StackedMemoryPool(PtrSize size_, U32 alignmentBits_)
 }
 
 //==============================================================================
-void* StackedMemoryPool::allocate(PtrSize size_) throw()
+StackMemoryPool::~StackMemoryPool()
 {
-	PtrSize size = calcAlignSize(size_ + sizeof(StackedMemoryPoolBlock));
+	if(memory != nullptr)
+	{
+		::free(memory);
+	}
+}
+
+//==============================================================================
+StackMemoryPool& StackMemoryPool::operator=(StackMemoryPool&& other)
+{
+	if(memory != nullptr)
+	{
+		::free(memory);
+	}
+
+	memory = other.memory;
+	memsize = other.memsize;
+	top.store(other.top.load());
+	alignmentBits = other.alignmentBits;
+
+	other.memory = nullptr;
+
+	return *this;
+}
+
+//==============================================================================
+void* StackMemoryPool::allocate(PtrSize size_) throw()
+{
+	// memory is nullptr if moved
+	ANKI_ASSERT(memory != nullptr);
+
+	PtrSize size = calcAlignSize(size_ + sizeof(MemoryBlockHeader));
 
 	ANKI_ASSERT(size < std::numeric_limits<U32>::max() && "Too big allocation");
 
-	U8* out = ptr.fetch_add(size);
+	U8* out = top.fetch_add(size);
 
 	if(out + size <= memory + memsize)
 	{
-		// Write the block
-		((StackedMemoryPoolBlock*)out)->size = size;
+		// Write the block header
+		((MemoryBlockHeader*)out)->size = size;
 
 		// Set the correct output
-		out += sizeof(StackedMemoryPoolBlock);
+		out += sizeof(MemoryBlockHeader);
 	}
 	else
 	{
@@ -56,25 +86,49 @@ void* StackedMemoryPool::allocate(PtrSize size_) throw()
 }
 
 //==============================================================================
-Bool StackedMemoryPool::free(void* p) throw()
+Bool StackMemoryPool::free(void* ptr) throw()
 {
+	// memory is nullptr if moved
+	ANKI_ASSERT(memory != nullptr);
+
 	// Correct the p
-	U8* realp = (U8*)p - sizeof(StackedMemoryPoolBlock);
+	U8* realptr = (U8*)ptr - sizeof(MemoryBlockHeader);
+
+	// realptr should be inside the pool's preallocated memory
+	ANKI_ASSERT(realptr >= memory && realptr < memory + memsize);
 
 	// Get block size
-	U32 size = ((StackedMemoryPoolBlock*)realp)->size;
+	U32 size = ((MemoryBlockHeader*)realptr)->size;
 
 	// Atomic stuff
-	U8* expected = realp + size;
-	U8* desired = realp;
-
-	// if(ptr == expected)
-	//     ptr = desired
-	// else
-	//     expected = ptr
-	Bool exchange = ptr.compare_exchange_strong(expected, desired);
+	U8* expected = realptr + size;
+	U8* desired = realptr;
+
+	// if(top == expected) {
+	//     top = desired;
+	//     exchange = true;
+	// } else {
+	//     expected = top;
+	//     exchange = false;
+	// }
+	Bool exchange = top.compare_exchange_strong(expected, desired);
 
 	return exchange;
 }
 
+//==============================================================================
+void StackMemoryPool::reset()
+{
+	// memory is nullptr if moved
+	ANKI_ASSERT(memory != nullptr);
+
+	top = memory;
+}
+
+//==============================================================================
+PtrSize StackMemoryPool::calcAlignSize(PtrSize size) const
+{
+	return size + (size % (alignmentBits / 8));
+}
+
 } // end namespace anki