Browse Source

Refactoring and optimizing the memory classes

Panagiotis Christopoulos Charitos 12 years ago
parent
commit
18b46f3bc0

+ 18 - 214
include/anki/util/Allocator.h

@@ -21,214 +21,6 @@ namespace anki {
 /// @addtogroup memory
 /// @{
 
-namespace detail {
-
-/// Internal methods for the Allocator class
-class HeapAllocatorInternal
-{
-public:
-	/// Print a few debugging messages
-	static void dump();
-
-protected:
-	/// Keep track of the allocated size. Relevant only when debugging
-	static PtrSize allocatedSize;
-};
-
-} // end namespace detail
-
-/// The default allocator. It uses malloc and free for 
-/// allocations/deallocations. It's STL compatible
-template<typename T>
-class HeapAllocator: public detail::HeapAllocatorInternal
-{
-public:
-	// STL Typedefs
-	typedef size_t size_type;
-	typedef ptrdiff_t difference_type;
-	typedef T* pointer;
-	typedef const T* const_pointer;
-	typedef T& reference;
-	typedef const T& const_reference;
-	typedef T value_type;
-
-	/// Default constructor
-	HeapAllocator() throw()
-	{}
-	/// Copy constructor
-	HeapAllocator(const HeapAllocator&) throw()
-	{}
-	/// Copy constructor with another type
-	template<typename U>
-	HeapAllocator(const HeapAllocator<U>&) throw()
-	{}
-
-	/// Destructor
-	~HeapAllocator()
-	{}
-
-	/// Copy
-	HeapAllocator<T>& operator=(const HeapAllocator&)
-	{
-		return *this;
-	}
-	/// Copy with another type
-	template<typename U>
-	HeapAllocator& operator=(const HeapAllocator<U>&) 
-	{
-		return *this;
-	}
-
-	/// Get address of reference
-	pointer address(reference x) const 
-	{
-		return &x; 
-	}
-	/// Get const address of const reference
-	const_pointer address(const_reference x) const 
-	{
-		return &x;
-	}
-
-	/// Allocate memory
-	pointer allocate(size_type n, const void* = 0)
-	{
-		// operator new doesn't respect alignment (in GCC at least) so use 
-		// custom mem allocation function
-		PtrSize size = n * sizeof(T);
-		allocatedSize += size;
-		return (pointer)mallocAligned(size, alignof(T));
-	}
-
-	/// Deallocate memory
-	void deallocate(void* p, size_type n)
-	{
-		freeAligned(p);
-		allocatedSize -= n * sizeof(T);
-	}
-
-	/// Call constructor
-	void construct(pointer p, const T& val)
-	{
-		// Placement new
-		new ((T*)p) T(val); 
-	}
-	/// Call constructor with more arguments
-	template<typename U, typename... Args>
-	void construct(U* p, Args&&... args)
-	{
-		// Placement new
-		::new((void*)p) U(std::forward<Args>(args)...);
-	}
-
-	/// Call the destructor of p
-	void destroy(pointer p) 
-	{
-		p->~T();
-	}
-	/// Call the destructor of p of type U
-	template<typename U>
-	void destroy(U* p)
-	{
-		p->~U();
-	}
-
-	/// Get the max allocation size
-	size_type max_size() const 
-	{
-		return size_type(-1); 
-	}
-
-	/// A struct to rebind the allocator to another allocator of type U
-	template<typename U>
-	struct rebind
-	{ 
-		typedef HeapAllocator<U> other; 
-	};
-
-	/// Allocate a new object and call it's constructor
-	/// @note This is AnKi specific
-	template<typename U, typename... Args>
-	U* newInstance(Args&&... args)
-	{
-		typename rebind<U>::other alloc(*this);
-
-		U* x = alloc.allocate(1);
-		alloc.construct(x, std::forward<Args>(args)...);
-		return x;
-	}
-
-	/// Allocate a new array of objects and call their constructor
-	/// @note This is AnKi specific
-	template<typename U, typename... Args>
-	U* newArray(size_type n, Args&&... args)
-	{
-		typename rebind<U>::other alloc(*this);
-
-		U* x = alloc.allocate(n);
-		// Call the constuctors
-		for(size_type i = 0; i < n; i++)
-		{
-			alloc.construct(&x[i], std::forward<Args>(args)...);
-		}
-		return x;
-	}
-
-	/// Call the destructor and deallocate an object
-	/// @note This is AnKi specific
-	template<typename U>
-	void deleteInstance(U* x)
-	{
-		typename rebind<U>::other alloc(*this);
-
-		alloc.destroy(x);
-		alloc.deallocate(x, 1);
-	}
-
-	/// Call the destructor and deallocate an array of objects
-	/// @note This is AnKi specific
-	template<typename U>
-	void deleteArray(U* x, size_type n)
-	{
-		typename rebind<U>::other alloc(*this);
-
-		// Call the destructors
-		for(size_type i = 0; i < n; i++)
-		{
-			alloc.destroy(&x[i]);
-		}
-		alloc.deallocate(x, n);
-	}
-};
-
-/// Another allocator of the same type can deallocate from this one
-template<typename T1, typename T2>
-inline bool operator==(const HeapAllocator<T1>&, const HeapAllocator<T2>&)
-{
-	return true;
-}
-
-/// Another allocator of the another type cannot deallocate from this one
-template<typename T1, typename AnotherAllocator>
-inline bool operator==(const HeapAllocator<T1>&, const AnotherAllocator&)
-{
-	return false;
-}
-
-/// Another allocator of the same type can deallocate from this one
-template<typename T1, typename T2>
-inline bool operator!=(const HeapAllocator<T1>&, const HeapAllocator<T2>&)
-{
-	return false;
-}
-
-/// Another allocator of the another type cannot deallocate from this one
-template<typename T1, typename AnotherAllocator>
-inline bool operator!=(const HeapAllocator<T1>&, const AnotherAllocator&)
-{
-	return true;
-}
-
 /// Pool based allocator
 ///
 /// This is a template that accepts memory pools with a specific interface
@@ -318,9 +110,12 @@ public:
 	pointer allocate(size_type n, const void* hint = 0)
 	{
 		(void)hint;
+
 		size_type size = n * sizeof(value_type);
 		
-		void* out = mpool.allocate(size);
+		// Operator new doesn't respect alignment (in GCC at least) so use 
+		// the allocation
+		void* out = mpool.allocate(size, alignof(value_type));
 
 		if(out != nullptr)
 		{
@@ -383,7 +178,7 @@ public:
 	/// Get the max allocation size
 	size_type max_size() const
 	{
-		return mpool.getTotalSize();
+		return MAX_PTR_SIZE;
 	}
 
 	/// A struct to rebind the allocator to another allocator of type U
@@ -393,13 +188,16 @@ public:
 		typedef GenericPoolAllocator<U, TPool, deallocationFlag> other;
 	};
 
-	/// Reinit the allocator. All existing allocated memory will be lost
-	void reset()
+	/// Get the memory pool
+	/// @note This is AnKi specific
+	const TPool& getMemoryPool() const
 	{
-		mpool.reset();
+		return mpool;
 	}
 
-	const TPool& getMemoryPool() const
+	/// Get the memory pool
+	/// @note This is AnKi specific
+	TPool& getMemoryPool()
 	{
 		return mpool;
 	}
@@ -500,6 +298,12 @@ inline bool operator!=(
 	return true;
 }
 
+/// Heap based allocator. The default allocator. It uses malloc and free for 
+/// allocations/deallocations
+template<typename T>
+using HeapAllocator = 
+	GenericPoolAllocator<T, HeapMemoryPool, true>;
+
 /// Allocator that uses a StackMemoryPool
 template<typename T, Bool deallocationFlag = false>
 using StackAllocator = 

+ 25 - 6
include/anki/util/Memory.h

@@ -12,10 +12,27 @@ namespace anki {
 /// @{
 
 /// Allocate aligned memory
-void* mallocAligned(PtrSize size, PtrSize alignmentBytes);
+void* mallocAligned(PtrSize size, PtrSize alignmentBytes) throw();
 
 /// Free aligned memory
-void freeAligned(void* ptr);
+void freeAligned(void* ptr) throw();
+
+/// A dummy interface to match the StackMemoryPool and ChainMemoryPool 
+/// interfaces in order to be used by the same allocator template
+class HeapMemoryPool
+{
+public:
+	static void* allocate(PtrSize size, PtrSize alignment) throw()
+	{
+		return mallocAligned(size, alignment);
+	}
+
+	static Bool free(void* ptr) throw()
+	{
+		freeAligned(ptr);
+		return true;
+	}
+};
 
 /// Thread safe memory pool. It's a preallocated memory pool that is used for 
 /// memory allocations on top of that preallocated memory. It is mainly used by 
@@ -57,7 +74,7 @@ public:
 
 	/// Allocate memory
 	/// @return The allocated memory or nullptr on failure
-	void* allocate(PtrSize size) throw();
+	void* allocate(PtrSize size, PtrSize alignmentBytes) throw();
 
 	/// Free memory in StackMemoryPool. If the ptr is not the last allocation
 	/// then nothing happens and the method returns false
@@ -70,7 +87,8 @@ public:
 	void reset();
 
 private:
-	// Forward
+	// Forward. Hide the implementation because Memory.h is the base of other
+	// files and should not include them
 	class Implementation;
 
 	/// The actual implementation
@@ -124,7 +142,7 @@ public:
 
 	/// Allocate memory. This operation is thread safe
 	/// @return The allocated memory or nullptr on failure
-	void* allocate(PtrSize size) throw();
+	void* allocate(PtrSize size, PtrSize alignmentBytes) throw();
 
 	/// Free memory. If the ptr is not the last allocation of the chunk
 	/// then nothing happens and the method returns false
@@ -139,7 +157,8 @@ public:
 	/// @}
 
 private:
-	// Forward
+	// Forward. Hide the implementation because Memory.h is the base of other
+	// files and should not include them
 	class Implementation;
 
 	/// The actual implementation

+ 1 - 1
src/scene/SceneGraph.cpp

@@ -200,7 +200,7 @@ void SceneGraph::update(F32 prevUpdateTime, F32 crntTime, Renderer& renderer)
 	//
 
 	// Reset the framepool
-	frameAlloc.reset();
+	frameAlloc.getMemoryPool().reset();
 
 	// Delete nodes
 	deleteNodesMarkedForDeletion();

+ 0 - 37
src/util/Allocator.cpp

@@ -1,37 +0,0 @@
-#include "anki/util/Allocator.h"
-#include <cstring>
-
-namespace anki {
-
-#if ANKI_PRINT_ALLOCATOR_MESSAGES
-#	define ANKI_ALLOCATOR_PRINT(x_) \
-		std::cout << "(" << ANKI_FILE << ":" << __LINE__ << ") " << x_ \
-		<< std::endl;
-#else
-#	define ANKI_ALLOCATOR_PRINT(x) ((void)0)
-#endif
-
-//==============================================================================
-// AllocatorInternal                                                           =
-//==============================================================================
-
-namespace detail {
-
-//==============================================================================
-PtrSize HeapAllocatorInternal::allocatedSize = 0;
-
-//==============================================================================
-void HeapAllocatorInternal::dump()
-{
-#if ANKI_DEBUG_ALLOCATORS
-	if(allocatedSize > 0)
-	{
-		ANKI_ALLOCATOR_PRINT("You have memory leak of " << allocatedSize 
-			<< " bytes");
-	}
-#endif
-}
-
-} // end namespace detail
-
-} // end namespace anki

+ 1 - 1
src/util/CMakeLists.txt

@@ -1,4 +1,4 @@
-set(ANKI_UTIL_SOURCES Assert.cpp Exception.cpp Functions.cpp StringList.cpp File.cpp Allocator.cpp Memory.cpp System.cpp HighRezTimer.cpp Thread.cpp)
+set(ANKI_UTIL_SOURCES Assert.cpp Exception.cpp Functions.cpp StringList.cpp File.cpp Memory.cpp System.cpp HighRezTimer.cpp Thread.cpp)
 
 if(LINUX OR ANDROID OR MACOS)
 	set(ANKI_UTIL_SOURCES ${ANKI_UTIL_SOURCES} HighRezTimerPosix.cpp FilePosix.cpp)

+ 36 - 25
src/util/Memory.cpp

@@ -15,7 +15,7 @@ namespace anki {
 //==============================================================================
 
 //==============================================================================
-void* mallocAligned(PtrSize size, PtrSize alignmentBytes)
+void* mallocAligned(PtrSize size, PtrSize alignmentBytes) throw()
 {
 #if ANKI_POSIX 
 #	if ANKI_OS != ANKI_OS_ANDROID
@@ -31,7 +31,7 @@ void* mallocAligned(PtrSize size, PtrSize alignmentBytes)
 	}
 	else
 	{
-		throw ANKI_EXCEPTION("mallocAligned() failed");
+		ANKI_ASSERT(0 && "mallocAligned() failed");
 		return nullptr;
 	}
 #	else
@@ -46,7 +46,7 @@ void* mallocAligned(PtrSize size, PtrSize alignmentBytes)
 	}
 	else
 	{
-		throw ANKI_EXCEPTION("mallocAligned() failed");
+		ANKI_ASSERT(0 && "mallocAligned() failed");
 		return nullptr;
 	}
 #	endif
@@ -56,7 +56,7 @@ void* mallocAligned(PtrSize size, PtrSize alignmentBytes)
 }
 
 //==============================================================================
-void freeAligned(void* ptr)
+void freeAligned(void* ptr) throw()
 {
 #if ANKI_POSIX
 	::free(ptr);
@@ -76,12 +76,17 @@ public:
 	/// The header of each allocation
 	struct MemoryBlockHeader
 	{
-		U32 size;
+		U8 size[4]; ///< It's U8 to allow whatever alignment
 	};
 
+	static_assert(alignof(MemoryBlockHeader) == 1, "Alignment error");
+
 	/// Alignment of allocations
 	U32 alignmentBytes;
 
+	/// Aligned size of MemoryBlockHeader
+	U32 headerSize;
+
 	/// Pre-allocated memory chunk
 	U8* memory = nullptr;
 
@@ -97,12 +102,17 @@ public:
 			memsize(getAlignedRoundUp(alignmentBytes, size))
 	{
 		ANKI_ASSERT(memsize > 0);
+		ANKI_ASSERT(alignmentBytes > 0);
 		memory = (U8*)mallocAligned(memsize, alignmentBytes);
 
 		if(memory != nullptr)
 		{
 			// Align allocated memory
 			top = memory;
+
+			// Calc header size
+			headerSize = 
+				getAlignedRoundUp(alignmentBytes, sizeof(MemoryBlockHeader));
 		}
 		else
 		{
@@ -137,15 +147,13 @@ public:
 	}
 
 	/// Allocate
-	void* allocate(PtrSize size_) throw()
+	void* allocate(PtrSize size, PtrSize alignment) throw()
 	{
-		// memory is nullptr if moved
 		ANKI_ASSERT(memory != nullptr);
+		ANKI_ASSERT(alignment <= alignmentBytes);
+		(void)alignment;
 
-		PtrSize headerSize = 
-			getAlignedRoundUp(alignmentBytes, sizeof(MemoryBlockHeader));
-		PtrSize size = 
-			getAlignedRoundUp(alignmentBytes, size_ + headerSize);
+		size = getAlignedRoundUp(alignmentBytes, size + headerSize);
 
 		ANKI_ASSERT(size < MAX_U32 && "Too big allocation");
 
@@ -154,7 +162,9 @@ public:
 		if(out + size <= memory + memsize)
 		{
 			// Write the block header
-			((MemoryBlockHeader*)out)->size = size;
+			MemoryBlockHeader* header = (MemoryBlockHeader*)out;
+			U32 size32 = size;
+			memcpy(&header->size[0], &size32, sizeof(U32));
 
 			// Set the correct output
 			out += headerSize;
@@ -182,17 +192,17 @@ public:
 		ANKI_ASSERT(memory != nullptr);
 
 		// Correct the p
-		PtrSize headerSize = 
-			getAlignedRoundUp(alignmentBytes, sizeof(MemoryBlockHeader));
 		U8* realptr = (U8*)ptr - headerSize;
 
 		// realptr should be inside the pool's preallocated memory
 		ANKI_ASSERT(realptr >= memory);
 
 		// Get block size
-		U32 size = ((MemoryBlockHeader*)realptr)->size;
+		MemoryBlockHeader* header = (MemoryBlockHeader*)realptr;
+		U32 size;
+		memcpy(&size, &header->size[0], sizeof(U32));
 
-		// Check if the size is ok
+		// Check if the size is within limits
 		ANKI_ASSERT(realptr + size <= memory + memsize);
 
 		// Atomic stuff
@@ -251,10 +261,10 @@ PtrSize StackMemoryPool::getAllocatedSize() const
 }
 
 //==============================================================================
-void* StackMemoryPool::allocate(PtrSize size) throw()
+void* StackMemoryPool::allocate(PtrSize size, PtrSize alignment) throw()
 {
 	ANKI_ASSERT(impl.get() != nullptr);
-	return impl->allocate(size);
+	return impl->allocate(size, alignment);
 }
 
 //==============================================================================
@@ -419,11 +429,11 @@ public:
 	}
 
 	/// Allocate from chunk
-	void* allocateFromChunk(Chunk* ch, PtrSize size) throw()
+	void* allocateFromChunk(Chunk* ch, PtrSize size, PtrSize alignment) throw()
 	{
 		ANKI_ASSERT(ch);
 		ANKI_ASSERT(size <= maxSize);
-		void* mem = ch->pool.allocate(size);
+		void* mem = ch->pool.allocate(size, alignment);
 
 		if(mem)
 		{
@@ -434,7 +444,7 @@ public:
 	}
 
 	/// Allocate memory
-	void* allocate(PtrSize size) throw()
+	void* allocate(PtrSize size, PtrSize alignment) throw()
 	{
 		Chunk* ch;
 		void* mem = nullptr;
@@ -445,7 +455,8 @@ public:
 		ch = crntChunk;
 
 		// Create new chunk if needed
-		if(ch == nullptr || (mem = allocateFromChunk(ch, size)) == nullptr)
+		if(ch == nullptr 
+			|| (mem = allocateFromChunk(ch, size, alignment)) == nullptr)
 		{
 			// Create new chunk
 			crntChunk = createNewChunk(size);
@@ -461,7 +472,7 @@ public:
 
 		if(mem == nullptr)
 		{
-			mem = allocateFromChunk(ch, size);
+			mem = allocateFromChunk(ch, size, alignment);
 			ANKI_ASSERT(mem != nullptr && "The chunk should have space");
 		}
 
@@ -547,9 +558,9 @@ ChainMemoryPool::~ChainMemoryPool()
 {}
 
 //==============================================================================
-void* ChainMemoryPool::allocate(PtrSize size) throw()
+void* ChainMemoryPool::allocate(PtrSize size, PtrSize alignment) throw()
 {
-	return impl->allocate(size);
+	return impl->allocate(size, alignment);
 }
 
 //==============================================================================

+ 4 - 4
tests/util/Memory.cpp

@@ -40,11 +40,11 @@ ANKI_TEST(Memory, ChainMemoryPool)
 		ChainMemoryPool pool(
 			size, size, ChainMemoryPool::MULTIPLY, 2, 1);
 
-		void* mem = pool.allocate(1);
+		void* mem = pool.allocate(1, 1);
 		ANKI_TEST_EXPECT_NEQ(mem, nullptr);
 		ANKI_TEST_EXPECT_EQ(pool.getChunksCount(), 1);
 
-		void* mem1 = pool.allocate(10);
+		void* mem1 = pool.allocate(10, 1);
 		ANKI_TEST_EXPECT_NEQ(mem1, nullptr);
 		ANKI_TEST_EXPECT_EQ(pool.getChunksCount(), 2);
 
@@ -59,11 +59,11 @@ ANKI_TEST(Memory, ChainMemoryPool)
 		ChainMemoryPool pool(
 			size, size, ChainMemoryPool::MULTIPLY, 2, 1);
 
-		void* mem = pool.allocate(1);
+		void* mem = pool.allocate(1, 1);
 		ANKI_TEST_EXPECT_NEQ(mem, nullptr);
 		ANKI_TEST_EXPECT_EQ(pool.getChunksCount(), 1);
 
-		void* mem1 = pool.allocate(10);
+		void* mem1 = pool.allocate(10, 1);
 		ANKI_TEST_EXPECT_NEQ(mem1, nullptr);
 		ANKI_TEST_EXPECT_EQ(pool.getChunksCount(), 2);