diff --git a/CMakeLists.txt b/CMakeLists.txt index 317e730..22f8a1e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,17 +1,34 @@ -cmake_minimum_required (VERSION 3.5) -project (memory-allocators LANGUAGES CXX) +cmake_minimum_required(VERSION 3.5) +project(memory-allocators LANGUAGES CXX) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) +set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) -set(SOURCES src/Allocator.cpp - src/CAllocator.cpp - src/LinearAllocator.cpp - src/StackAllocator.cpp - src/PoolAllocator.cpp - src/FreeListAllocator.cpp - src/Benchmark.cpp - src/main.cpp) +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Release) +endif() + +set(SOURCES + src/CAllocator.cpp + src/LinearAllocator.cpp + src/StackAllocator.cpp + src/PoolAllocator.cpp + src/FreeListAllocator.cpp + src/Benchmark.cpp + src/main.cpp) add_executable(main ${SOURCES}) + target_include_directories(main PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/includes) -target_compile_features(main PRIVATE cxx_std_11) + +target_compile_options(main PRIVATE + $<$: + -Wall -Wextra -Wpedantic -Wshadow -Wnon-virtual-dtor + -Wcast-align -Woverloaded-virtual -Wnull-dereference> + $<$:-O3 -march=native> + $<$:-O0 -g -fsanitize=address,undefined>) + +target_link_options(main PRIVATE + $<$:-fsanitize=address,undefined>) diff --git a/includes/Allocator.h b/includes/Allocator.h index 675c64c..05ff542 100644 --- a/includes/Allocator.h +++ b/includes/Allocator.h @@ -1,20 +1,25 @@ #ifndef ALLOCATOR_H #define ALLOCATOR_H -#include // size_t +#include class Allocator { protected: std::size_t m_totalSize; - std::size_t m_used; + std::size_t m_used; std::size_t m_peak; + public: - - Allocator(const std::size_t totalSize) : m_totalSize { totalSize }, m_used { 0 }, m_peak { 0 } { } + explicit Allocator(const std::size_t totalSize) noexcept : m_totalSize(totalSize), m_used(0), m_peak(0) {} + + virtual ~Allocator() noexcept { + m_totalSize = 0; + } - virtual ~Allocator() { m_totalSize = 0; } + Allocator(const Allocator&) = delete; + Allocator& operator=(const Allocator&) = delete; - virtual void* Allocate(const std::size_t size, const std::size_t alignment = 0) = 0; + virtual void* Allocate(std::size_t size, std::size_t alignment = 0) = 0; virtual void Free(void* ptr) = 0; @@ -23,5 +28,4 @@ class Allocator { friend class Benchmark; }; -#endif /* ALLOCATOR_H */ - +#endif /* ALLOCATOR_H */ \ No newline at end of file diff --git a/includes/Benchmark.h b/includes/Benchmark.h index f3592e2..349ad54 100644 --- a/includes/Benchmark.h +++ b/includes/Benchmark.h @@ -1,73 +1,59 @@ #ifndef BENCHMARK_H #define BENCHMARK_H -#include // timespec -#include // std::size_t #include -#include +#include // std::size_t +#include #include #include "Allocator.h" // base class allocator #include "IO.h" -#if 0 -#define OPERATIONS (m_nOperations) -#else -#define OPERATIONS (10) -#endif - struct BenchmarkResults { - std::size_t Operations; - std::chrono::milliseconds Milliseconds; - double OperationsPerSec; - double TimePerOperation; + std::size_t Operations; + std::chrono::nanoseconds Nanoseconds; + double OperationsPerSec; + double TimePerOperation; std::size_t MemoryPeak; }; class Benchmark { public: Benchmark() = delete; + explicit Benchmark(unsigned int nOperations) noexcept; - Benchmark(const unsigned int nOperations) : m_nOperations { nOperations } { } + void SingleAllocation(Allocator* allocator, std::size_t size, std::size_t alignment); + void SingleFree(Allocator* allocator, std::size_t size, std::size_t alignment); - void SingleAllocation(Allocator* allocator, const std::size_t size, const std::size_t alignment); - void SingleFree(Allocator* allocator, const std::size_t size, const std::size_t alignment); + void MultipleAllocation(Allocator* allocator, const std::vector& allocationSizes, const std::vector& alignments); + void MultipleFree(Allocator* allocator, const std::vector& allocationSizes, const std::vector& alignments); - void MultipleAllocation(Allocator* allocator, const std::vector& allocationSizes, const std::vector& alignments); - void MultipleFree(Allocator* allocator, const std::vector& allocationSizes, const std::vector& alignments); - - void RandomAllocation(Allocator* allocator, const std::vector& allocationSizes, const std::vector& alignments); - void RandomFree(Allocator* allocator, const std::vector& allocationSizes, const std::vector& alignments); + void RandomAllocation(Allocator* allocator, const std::vector& allocationSizes, const std::vector& alignments); + void RandomFree(Allocator* allocator, const std::vector& allocationSizes, const std::vector& alignments); private: - void PrintResults(const BenchmarkResults& results) const; - - void RandomAllocationAttr(const std::vector& allocationSizes, const std::vector& alignments, std::size_t & size, std::size_t & alignment); + void PrintResults(const BenchmarkResults& results) const; - const BenchmarkResults buildResults(std::size_t nOperations, std::chrono::milliseconds&& ellapsedTime, const std::size_t memoryUsed) const; - - void SetStartTime() noexcept { Start = std::chrono::high_resolution_clock::now(); } + void RandomAllocationAttr(const std::vector& allocationSizes, const std::vector& alignments, std::size_t& size, std::size_t& alignment) noexcept; - void SetFinishTime() noexcept { Finish = std::chrono::high_resolution_clock::now(); } + BenchmarkResults BuildResults(std::size_t nOperations, std::chrono::nanoseconds elapsedTime, std::size_t memoryPeak) const noexcept; - void SetElapsedTime() noexcept { TimeElapsed = std::chrono::duration_cast(Finish - Start); } - - void StartRound() noexcept { SetStartTime(); } - - void FinishRound() noexcept - { - SetFinishTime(); - SetElapsedTime(); + void StartRound() noexcept { + m_start = std::chrono::high_resolution_clock::now(); } -private: - std::size_t m_nOperations; + void FinishRound() noexcept { + m_finish = std::chrono::high_resolution_clock::now(); + m_elapsed = std::chrono::duration_cast(m_finish - m_start); + } - std::chrono::time_point Start; - std::chrono::time_point Finish; + unsigned int m_nOperations; + std::mt19937 m_rng; - std::chrono::milliseconds TimeElapsed; + std::chrono::time_point m_start; + std::chrono::time_point m_finish; + std::chrono::nanoseconds m_elapsed; }; #endif /* BENCHMARK_H */ \ No newline at end of file diff --git a/includes/CAllocator.h b/includes/CAllocator.h index 91a0687..1963776 100644 --- a/includes/CAllocator.h +++ b/includes/CAllocator.h @@ -4,17 +4,19 @@ #include "Allocator.h" class CAllocator : public Allocator { +private: + bool m_lastWasAligned; + public: - CAllocator(); + CAllocator() noexcept; - virtual ~CAllocator(); + virtual ~CAllocator() noexcept; - virtual void* Allocate(const std::size_t size, const std::size_t alignment = 0) override; + virtual void* Allocate(std::size_t size, std::size_t alignment = 0); - virtual void Free(void* ptr) override; + virtual void Free(void* ptr); - virtual void Init() override; + virtual void Init(); }; -#endif /* CALLOCATOR_H */ - +#endif /* CALLOCATOR_H */ \ No newline at end of file diff --git a/includes/DoublyLinkedList.h b/includes/DoublyLinkedList.h index 60dd90d..4194e3a 100644 --- a/includes/DoublyLinkedList.h +++ b/includes/DoublyLinkedList.h @@ -1,6 +1,8 @@ #ifndef DOUBLYLINKEDLIST_H #define DOUBLYLINKEDLIST_H +#include + template class DoublyLinkedList { public: @@ -10,16 +12,16 @@ class DoublyLinkedList { Node* next; }; Node* head; -public: - DoublyLinkedList(); - void insert(Node* previousNode, Node* newNode); - void remove(Node* deleteNode); -private: - DoublyLinkedList(DoublyLinkedList &doublyLinkedList); + DoublyLinkedList() : head(NULL) {} + + DoublyLinkedList(const DoublyLinkedList&) = delete; + DoublyLinkedList& operator=(const DoublyLinkedList&) = delete; + + void insert(Node* previousNode, Node* newNode) noexcept; + void remove(Node* deleteNode) noexcept; }; #include "DoublyLinkedListImpl.h" -#endif /* DOUBLYLINKEDLIST_H */ - +#endif /* DOUBLYLINKEDLIST_H */ \ No newline at end of file diff --git a/includes/DoublyLinkedListImpl.h b/includes/DoublyLinkedListImpl.h index 8fdf152..3bbd8bb 100644 --- a/includes/DoublyLinkedListImpl.h +++ b/includes/DoublyLinkedListImpl.h @@ -1,60 +1,37 @@ #include "DoublyLinkedList.h" template -DoublyLinkedList::DoublyLinkedList() { +void DoublyLinkedList::insert(Node* previousNode, Node* newNode) noexcept { + if (previousNode == NULL) { + newNode->previous = NULL; + newNode->next = head; -} + if (head != NULL) + head->previous = newNode; -template -void DoublyLinkedList::insert(Node* previousNode, Node* newNode) { - if (previousNode == nullptr) { - // Is the first node - if (head != nullptr) { - // The list has more elements - newNode->next = head; - newNode->next->previous = newNode; - }else { - newNode->next = nullptr; - } head = newNode; - head->previous = nullptr; - } else { - if (previousNode->next == nullptr){ - // Is the last node - previousNode->next = newNode; - newNode->next = nullptr; - }else { - // Is a middle node - newNode->next = previousNode->next; - if (newNode->next != nullptr){ - newNode->next->previous = newNode; - } - previousNode->next = newNode; - newNode->previous = previousNode; - } + } + else { + newNode->previous = previousNode; + newNode->next = previousNode->next; + + if (previousNode->next != NULL) + previousNode->next->previous = newNode; + + previousNode->next = newNode; } } template -void DoublyLinkedList::remove(Node* deleteNode) { - if (deleteNode->previous == nullptr){ - // Is the first node - if (deleteNode->next == nullptr){ - // List only has one element - head = nullptr; - }else { - // List has more elements - head = deleteNode->next; - head->previous = nullptr; - } - }else { - if (deleteNode->next == nullptr){ - // Is the last node - deleteNode->previous->next = nullptr; - }else { - // Middle node - deleteNode->previous->next = deleteNode->next; - deleteNode->next->previous = deleteNode->previous; - } - } +void DoublyLinkedList::remove(Node* deleteNode) noexcept { + if (deleteNode->previous != NULL) + deleteNode->previous->next = deleteNode->next; + else + head = deleteNode->next; + + if (deleteNode->next != NULL) + deleteNode->next->previous = deleteNode->previous; + + deleteNode->previous = NULL; + deleteNode->next = NULL; } \ No newline at end of file diff --git a/includes/FreeListAllocator.h b/includes/FreeListAllocator.h index 6b5a9fe..a76e8a0 100644 --- a/includes/FreeListAllocator.h +++ b/includes/FreeListAllocator.h @@ -6,7 +6,7 @@ class FreeListAllocator : public Allocator { public: - enum PlacementPolicy { + enum class PlacementPolicy { FIND_FIRST, FIND_BEST }; @@ -17,37 +17,33 @@ class FreeListAllocator : public Allocator { }; struct AllocationHeader { std::size_t blockSize; - char padding; + std::size_t padding; }; - + typedef SinglyLinkedList::Node Node; - - void* m_start_ptr = nullptr; + void* m_start_ptr; PlacementPolicy m_pPolicy; SinglyLinkedList m_freeList; public: - FreeListAllocator(const std::size_t totalSize, const PlacementPolicy pPolicy); + FreeListAllocator(std::size_t totalSize, PlacementPolicy pPolicy) noexcept; + virtual ~FreeListAllocator() noexcept; - virtual ~FreeListAllocator(); + virtual void* Allocate(std::size_t size, std::size_t alignment = 0); - virtual void* Allocate(const std::size_t size, const std::size_t alignment = 0) override; + virtual void Free(void* ptr); - virtual void Free(void* ptr) override; + virtual void Init(); - virtual void Init() override; + void Reset() noexcept; - virtual void Reset(); private: - FreeListAllocator(FreeListAllocator &freeListAllocator); - - void Coalescence(Node* prevBlock, Node * freeBlock); + void Coalescence(Node* prevBlock, Node* freeBlock) noexcept; - void Find(const std::size_t size, const std::size_t alignment, std::size_t& padding, Node*& previousNode, Node*& foundNode); - void FindBest(const std::size_t size, const std::size_t alignment, std::size_t& padding, Node*& previousNode, Node*& foundNode); - void FindFirst(const std::size_t size, const std::size_t alignment, std::size_t& padding, Node*& previousNode, Node*& foundNode); + void Find(std::size_t size, std::size_t alignment, std::size_t& padding, Node*& previousNode, Node*& foundNode) noexcept; + void FindFirst(std::size_t size, std::size_t alignment, std::size_t& padding, Node*& previousNode, Node*& foundNode) noexcept; + void FindBest(std::size_t size, std::size_t alignment, std::size_t& padding, Node*& previousNode, Node*& foundNode) noexcept; }; -#endif /* FREELISTALLOCATOR_H */ - +#endif /* FREELISTALLOCATOR_H */ \ No newline at end of file diff --git a/includes/IO.h b/includes/IO.h index 86520a4..68e0350 100644 --- a/includes/IO.h +++ b/includes/IO.h @@ -21,4 +21,4 @@ namespace IO } } -#endif // MEMORY_ALLOCATORS_IO_H_INCLUDED +#endif // MEMORY_ALLOCATORS_IO_H_INCLUDED \ No newline at end of file diff --git a/includes/LinearAllocator.h b/includes/LinearAllocator.h index 624398b..91be7e7 100644 --- a/includes/LinearAllocator.h +++ b/includes/LinearAllocator.h @@ -5,22 +5,20 @@ class LinearAllocator : public Allocator { protected: - void* m_start_ptr = nullptr; - std::size_t m_offset; -public: - LinearAllocator(const std::size_t totalSize); + void* m_start_ptr; + std::size_t m_offset; - virtual ~LinearAllocator(); +public: + explicit LinearAllocator(std::size_t totalSize) noexcept; + virtual ~LinearAllocator() noexcept; - virtual void* Allocate(const std::size_t size, const std::size_t alignment = 0) override; - - virtual void Free(void* ptr) override; + virtual void* Allocate(std::size_t size, std::size_t alignment = 0); - virtual void Init() override; + virtual void Free(void* ptr); - virtual void Reset(); -private: - LinearAllocator(LinearAllocator &linearAllocator); + virtual void Init(); + + void Reset() noexcept; }; -#endif /* LINEARALLOCATOR_H */ +#endif /* LINEARALLOCATOR_H */ \ No newline at end of file diff --git a/includes/PoolAllocator.h b/includes/PoolAllocator.h index 9807482..901f241 100644 --- a/includes/PoolAllocator.h +++ b/includes/PoolAllocator.h @@ -1,29 +1,29 @@ +#ifndef POOLALLOCATOR_H +#define POOLALLOCATOR_H + #include "Allocator.h" #include "StackLinkedList.h" class PoolAllocator : public Allocator { private: - struct FreeHeader{ - }; - using Node = StackLinkedList::Node; - StackLinkedList m_freeList; + struct FreeHeader {}; + typedef StackLinkedList::Node Node; - void * m_start_ptr = nullptr; + StackLinkedList m_freeList; + void* m_start_ptr; std::size_t m_chunkSize; - std::size_t m_offset = 0; -public: - PoolAllocator(const std::size_t totalSize, const std::size_t chunkSize); - virtual ~PoolAllocator(); +public: + PoolAllocator(std::size_t totalSize, std::size_t chunkSize) noexcept; + virtual ~PoolAllocator() noexcept; - virtual void* Allocate(const std::size_t size, const std::size_t alignment = 0) override; + virtual void* Allocate(std::size_t size, std::size_t alignment = 0); - virtual void Free(void* ptr) override; + virtual void Free(void* ptr); - virtual void Init() override; + virtual void Init(); - virtual void Reset(); -private: - PoolAllocator(PoolAllocator &poolAllocator); + void Reset() noexcept; +}; -}; \ No newline at end of file +#endif \ No newline at end of file diff --git a/includes/SinglyLinkedList.h b/includes/SinglyLinkedList.h index 670d1b6..d67e73e 100644 --- a/includes/SinglyLinkedList.h +++ b/includes/SinglyLinkedList.h @@ -1,24 +1,27 @@ #ifndef SINGLYLINKEDLIST_H #define SINGLYLINKEDLIST_H +#include + template class SinglyLinkedList { public: struct Node { T data; - Node * next; + Node* next; }; - - Node * head; - -public: - SinglyLinkedList(); - void insert(Node * previousNode, Node * newNode); - void remove(Node * previousNode, Node * deleteNode); + Node* head; + + SinglyLinkedList() : head(NULL) {} + + SinglyLinkedList(const SinglyLinkedList&) = delete; + SinglyLinkedList& operator=(const SinglyLinkedList&) = delete; + + void insert(Node* previousNode, Node* newNode) noexcept; + void remove(Node* previousNode, Node* deleteNode) noexcept; }; #include "SinglyLinkedListImpl.h" -#endif /* SINGLYLINKEDLIST_H */ - +#endif /* SINGLYLINKEDLIST_H */ \ No newline at end of file diff --git a/includes/SinglyLinkedListImpl.h b/includes/SinglyLinkedListImpl.h index 487963c..47902a6 100644 --- a/includes/SinglyLinkedListImpl.h +++ b/includes/SinglyLinkedListImpl.h @@ -1,46 +1,25 @@ #include "SinglyLinkedList.h" template -SinglyLinkedList::SinglyLinkedList(){ - -} - -template -void SinglyLinkedList::insert(Node* previousNode, Node* newNode){ - if (previousNode == nullptr) { - // Is the first node - if (head != nullptr) { - // The list has more elements - newNode->next = head; - }else { - newNode->next = nullptr; - } +void SinglyLinkedList::insert(Node* previousNode, Node* newNode) noexcept { + if (previousNode == NULL) { + newNode->next = head; head = newNode; - } else { - if (previousNode->next == nullptr){ - // Is the last node - previousNode->next = newNode; - newNode->next = nullptr; - }else { - // Is a middle node - newNode->next = previousNode->next; - previousNode->next = newNode; - } + } + else if (previousNode->next == NULL) { + previousNode->next = newNode; + newNode->next = NULL; + } + else { + newNode->next = previousNode->next; + previousNode->next = newNode; } } template -void SinglyLinkedList::remove(Node* previousNode, Node* deleteNode){ - if (previousNode == nullptr){ - // Is the first node - if (deleteNode->next == nullptr){ - // List only has one element - head = nullptr; - }else { - // List has more elements - head = deleteNode->next; - } - }else { +void SinglyLinkedList::remove(Node* previousNode, Node* deleteNode) noexcept { + if (previousNode == NULL) + head = deleteNode->next; + else previousNode->next = deleteNode->next; - } } \ No newline at end of file diff --git a/includes/StackAllocator.h b/includes/StackAllocator.h index f06eb31..b95f4ae 100644 --- a/includes/StackAllocator.h +++ b/includes/StackAllocator.h @@ -6,27 +6,22 @@ class StackAllocator : public Allocator { protected: - void* m_start_ptr = nullptr; - std::size_t m_offset = 0; + void* m_start_ptr; + std::size_t m_offset; std::vector m_markers; - std::vector m_checkpoints; -public: - StackAllocator(const std::size_t totalSize); - - virtual ~StackAllocator(); - virtual void* Allocate(const std::size_t size, const std::size_t alignment = 0) override; +public: + explicit StackAllocator(std::size_t totalSize) noexcept; + virtual ~StackAllocator() noexcept; - virtual void Free(void* ptr) override; + virtual void* Allocate(std::size_t size, std::size_t alignment = 0); - virtual void Init() override; + virtual void Free(void* ptr); - virtual void Reset(); - std::size_t Push(); - void Pop(const std::size_t marker); -private: - StackAllocator(StackAllocator &stackAllocator); + virtual void Init(); + void Reset() noexcept; + void Pop(std::size_t marker) noexcept; }; #endif /* STACKALLOCATOR_H */ \ No newline at end of file diff --git a/includes/StackLinkedList.h b/includes/StackLinkedList.h index 1d4a682..0d6c30c 100644 --- a/includes/StackLinkedList.h +++ b/includes/StackLinkedList.h @@ -1,6 +1,8 @@ #ifndef STACKLINKEDLIST_H #define STACKLINKEDLIST_H +#include + template class StackLinkedList { public: @@ -8,16 +10,22 @@ class StackLinkedList { T data; Node* next; }; - + Node* head; -public: - StackLinkedList() = default; - StackLinkedList(StackLinkedList &stackLinkedList) = delete; - void push(Node * newNode); - Node* pop(); + + StackLinkedList() : head(NULL) {} + + StackLinkedList(const StackLinkedList&) = delete; + StackLinkedList& operator=(const StackLinkedList&) = delete; + + void push(Node* newNode) noexcept; + Node* pop() noexcept; + + bool Empty() const noexcept { + return head == NULL; + } }; #include "StackLinkedListImpl.h" -#endif /* STACKLINKEDLIST_H */ - +#endif /* STACKLINKEDLIST_H */ \ No newline at end of file diff --git a/includes/StackLinkedListImpl.h b/includes/StackLinkedListImpl.h index a259263..b9276b4 100644 --- a/includes/StackLinkedListImpl.h +++ b/includes/StackLinkedListImpl.h @@ -1,14 +1,17 @@ #include "StackLinkedList.h" template -void StackLinkedList::push(Node * newNode) { +void StackLinkedList::push(Node* newNode) noexcept { newNode->next = head; head = newNode; } template -typename StackLinkedList::Node* StackLinkedList::pop() { - Node * top = head; +typename StackLinkedList::Node* StackLinkedList::pop() noexcept { + assert(head != NULL && "Pop on empty 'StackLinkedList'."); + + Node* top = head; head = head->next; + return top; } \ No newline at end of file diff --git a/includes/Utils.h b/includes/Utils.h index 8a041f4..9bd2daf 100644 --- a/includes/Utils.h +++ b/includes/Utils.h @@ -1,33 +1,31 @@ #ifndef UTILS_H #define UTILS_H +#include +#include + class Utils { public: - static const std::size_t CalculatePadding(const std::size_t baseAddress, const std::size_t alignment) { - const std::size_t multiplier = (baseAddress / alignment) + 1; - const std::size_t alignedAddress = multiplier * alignment; - const std::size_t padding = alignedAddress - baseAddress; - return padding; - } - - static const std::size_t CalculatePaddingWithHeader(const std::size_t baseAddress, const std::size_t alignment, const std::size_t headerSize) { - std::size_t padding = CalculatePadding(baseAddress, alignment); - std::size_t neededSpace = headerSize; - - if (padding < neededSpace){ - // Header does not fit - Calculate next aligned address that header fits - neededSpace -= padding; - - // How many alignments I need to fit the header - if(neededSpace % alignment > 0){ - padding += alignment * (1+(neededSpace / alignment)); - }else { - padding += alignment * (neededSpace / alignment); - } - } - - return padding; - } + static std::size_t CalculatePadding(const std::size_t baseAddress, const std::size_t alignment) noexcept { + assert(alignment > 0 && (alignment & (alignment - 1)) == 0 && "Alignment must be a non-zero power of 2."); + + const std::size_t aligned = (baseAddress + alignment - 1) & ~(alignment - 1); + + return aligned - baseAddress; + } + + static std::size_t CalculatePaddingWithHeader(const std::size_t baseAddress, const std::size_t alignment, const std::size_t headerSize) noexcept { + assert(alignment > 0 && (alignment & (alignment - 1)) == 0 && "Alignment must be a non-zero power of 2."); + + std::size_t padding = CalculatePadding(baseAddress, alignment); + + if (padding < headerSize) { + std::size_t needed = headerSize - padding; + padding += alignment * ((needed + alignment - 1) / alignment); + } + + return padding; + } }; #endif /* UTILS_H */ \ No newline at end of file diff --git a/src/Allocator.cpp b/src/Allocator.cpp index d8bb047..9d861af 100644 --- a/src/Allocator.cpp +++ b/src/Allocator.cpp @@ -1,2 +1 @@ -#include "Allocator.h" -#include //assert \ No newline at end of file +#include "Allocator.h" \ No newline at end of file diff --git a/src/Benchmark.cpp b/src/Benchmark.cpp index 0f74e31..4826dce 100644 --- a/src/Benchmark.cpp +++ b/src/Benchmark.cpp @@ -1,170 +1,135 @@ - #include "Benchmark.h" #include -#include /* srand, rand */ #include +Benchmark::Benchmark(const unsigned int nOperations) noexcept : m_nOperations(nOperations), m_rng(42), m_elapsed(0) {} + void Benchmark::SingleAllocation(Allocator* allocator, const std::size_t size, const std::size_t alignment) { std::cout << "BENCHMARK: ALLOCATION" << IO::endl; - std::cout << "\tSize: \t" << size << IO::endl; - std::cout << "\tAlignment\t" << alignment << IO::endl; - - StartRound(); + std::cout << "\tSize: \t" << size << IO::endl; + std::cout << "\tAlignment: \t" << alignment << IO::endl; allocator->Init(); + StartRound(); - auto operations = 0u; - - while (operations < m_nOperations) { + for (unsigned int i = 0; i < m_nOperations; ++i) allocator->Allocate(size, alignment); - ++operations; - } - - FinishRound(); - BenchmarkResults results = buildResults(m_nOperations, std::move(TimeElapsed), allocator->m_peak); - - PrintResults(results); + FinishRound(); + PrintResults(BuildResults(m_nOperations, m_elapsed, allocator->m_peak)); } void Benchmark::SingleFree(Allocator* allocator, const std::size_t size, const std::size_t alignment) { std::cout << "BENCHMARK: ALLOCATION/FREE" << IO::endl; - std::cout << "\tSize: \t" << size << IO::endl; - std::cout << "\tAlignment\t" << alignment << IO::endl; + std::cout << "\tSize: \t" << size << IO::endl; + std::cout << "\tAlignment: \t" << alignment << IO::endl; - // BUG: (https://github.com/mtrebi/memory-allocators/issues/6) - // void* addresses[m_nOperations]; - void* addresses[OPERATIONS]; + std::vector addresses(m_nOperations); + allocator->Init(); StartRound(); - allocator->Init(); + for (unsigned int i = 0; i < m_nOperations; ++i) + addresses[i] = allocator->Allocate(size, alignment); - auto operations = 0u; - - while (operations < m_nOperations) { - addresses[operations] = allocator->Allocate(size, alignment); - ++operations; - } - - while (operations) { - allocator->Free(addresses[--operations]); - } + for (unsigned int i = m_nOperations; i > 0; --i) + allocator->Free(addresses[i - 1]); FinishRound(); - - BenchmarkResults results = buildResults(m_nOperations, std::move(TimeElapsed), allocator->m_peak); - - PrintResults(results); + PrintResults(BuildResults(m_nOperations, m_elapsed, allocator->m_peak)); } void Benchmark::MultipleAllocation(Allocator* allocator, const std::vector& allocationSizes, const std::vector& alignments) { - assert(allocationSizes.size() == alignments.size() && "Allocation sizes and Alignments must have same length"); + assert(allocationSizes.size() == alignments.size() && "Allocation sizes and alignments must have the same length"); - for (auto i = 0u; i < allocationSizes.size(); ++i) { + for (std::size_t i = 0; i < allocationSizes.size(); ++i) { SingleAllocation(allocator, allocationSizes[i], alignments[i]); } } void Benchmark::MultipleFree(Allocator* allocator, const std::vector& allocationSizes, const std::vector& alignments) { - assert(allocationSizes.size() == alignments.size() && "Allocation sizes and Alignments must have same length"); + assert(allocationSizes.size() == alignments.size() && "Allocation sizes and alignments must have the same length"); - for (auto i = 0u; i < allocationSizes.size(); ++i) { + for (std::size_t i = 0; i < allocationSizes.size(); ++i) { SingleFree(allocator, allocationSizes[i], alignments[i]); } } void Benchmark::RandomAllocation(Allocator* allocator, const std::vector& allocationSizes, const std::vector& alignments) { - - // NOTE: Is this actually initializing the RNG? Jose Fernando Lopez Fernandez 11/07/2018 @ 12:54am (UTC) - srand(1); - - std::cout << "\tBENCHMARK: ALLOCATION" << IO::endl; + std::cout << "\tBENCHMARK: RANDOM ALLOCATION" << IO::endl; + allocator->Init(); StartRound(); std::size_t allocation_size; std::size_t alignment; - allocator->Init(); - - auto operations = 0u; + for (unsigned int i = 0; i < m_nOperations; ++i) { + RandomAllocationAttr(allocationSizes, alignments, allocation_size, alignment); - while (operations < m_nOperations) { - this->RandomAllocationAttr(allocationSizes, alignments, allocation_size, alignment); allocator->Allocate(allocation_size, alignment); - ++operations; } - - FinishRound(); - BenchmarkResults results = buildResults(m_nOperations, std::move(TimeElapsed), allocator->m_peak); - - PrintResults(results); + FinishRound(); + PrintResults(BuildResults(m_nOperations, m_elapsed, allocator->m_peak)); } void Benchmark::RandomFree(Allocator* allocator, const std::vector& allocationSizes, const std::vector& alignments) { - - // NOTE: Is this actually initializing the RNG? Jose Fernando Lopez Fernandez 11/07/2018 @ 1:51am (UTC) - srand(1); - - std::cout << "\tBENCHMARK: ALLOCATION/FREE" << IO::endl; - - StartRound(); - - void* addresses[OPERATIONS]; + std::cout << "\tBENCHMARK: RANDOM ALLOCATION/FREE" << IO::endl; + std::vector addresses(m_nOperations); std::size_t allocation_size; std::size_t alignment; allocator->Init(); + StartRound(); - auto operations = 0u; + for (unsigned int i = 0; i < m_nOperations; ++i) { + RandomAllocationAttr(allocationSizes, alignments, allocation_size, alignment); - while (operations < m_nOperations) { - this->RandomAllocationAttr(allocationSizes, alignments, allocation_size, alignment); - addresses[operations] = allocator->Allocate(allocation_size, alignment); - ++operations; + addresses[i] = allocator->Allocate(allocation_size, alignment); } - while (operations) { - allocator->Free(addresses[--operations]); - } + for (unsigned int i = m_nOperations; i > 0; --i) + allocator->Free(addresses[i - 1]); FinishRound(); - - BenchmarkResults results = buildResults(m_nOperations, std::move(TimeElapsed), allocator->m_peak); - - PrintResults(results); - + PrintResults(BuildResults(m_nOperations, m_elapsed, allocator->m_peak)); } void Benchmark::PrintResults(const BenchmarkResults& results) const { + const double ns = static_cast(results.Nanoseconds.count()); + std::cout << "\tRESULTS:" << IO::endl; std::cout << "\t\tOperations: \t" << results.Operations << IO::endl; - std::cout << "\t\tTime elapsed: \t" << results.Milliseconds.count() << " ms" << IO::endl; - std::cout << "\t\tOp per sec: \t" << results.OperationsPerSec << " ops/ms" << IO::endl; - std::cout << "\t\tTimer per op: \t" << results.TimePerOperation << " ms/ops" << IO::endl; + std::cout << "\t\tTime elapsed: \t" << ns / 1e6 << " ms" << IO::endl; + std::cout << "\t\tOp per sec: \t" << results.OperationsPerSec << " ops/ns" << IO::endl; + std::cout << "\t\tTime per op: \t" << results.TimePerOperation << " ns/op" << IO::endl; std::cout << "\t\tMemory peak: \t" << results.MemoryPeak << " bytes" << IO::endl; std::cout << IO::endl; } -const BenchmarkResults Benchmark::buildResults(std::size_t nOperations, std::chrono::milliseconds&& elapsedTime, const std::size_t memoryPeak) const { +BenchmarkResults Benchmark::BuildResults(const std::size_t nOperations, const std::chrono::nanoseconds elapsedTime, const std::size_t memoryPeak) const noexcept { BenchmarkResults results; results.Operations = nOperations; - results.Milliseconds = std::move(elapsedTime); - results.OperationsPerSec = results.Operations / static_cast(results.Milliseconds.count()); - results.TimePerOperation = static_cast(results.Milliseconds.count()) / static_cast(results.Operations); + results.Nanoseconds = elapsedTime; + + const double ns = static_cast(elapsedTime.count()); + + results.OperationsPerSec = ns > 0.0 ? static_cast(nOperations) / ns : 0.0; + results.TimePerOperation = ns > 0.0 ? ns / static_cast(nOperations) : 0.0; results.MemoryPeak = memoryPeak; return results; } -void Benchmark::RandomAllocationAttr(const std::vector& allocationSizes, const std::vector& alignments, std::size_t & size, std::size_t & alignment) { - const int r = rand() % allocationSizes.size(); - size = allocationSizes[r]; - alignment = alignments[r]; -} +void Benchmark::RandomAllocationAttr(const std::vector& allocationSizes, const std::vector& alignments, std::size_t& size, std::size_t& alignment) noexcept { + std::uniform_int_distribution dist(0, allocationSizes.size() - 1); + + const std::size_t idx = dist(m_rng); + size = allocationSizes[idx]; + alignment = alignments[idx]; +} \ No newline at end of file diff --git a/src/CAllocator.cpp b/src/CAllocator.cpp index 29ef42c..161ae24 100644 --- a/src/CAllocator.cpp +++ b/src/CAllocator.cpp @@ -1,25 +1,46 @@ #include "CAllocator.h" -#include /* malloc, free */ +#include -CAllocator::CAllocator() - : Allocator(0) { +#if defined(_WIN32) || defined(_WIN64) + #include -} + #define PLATFORM_ALIGNED_ALLOC(ptr, align, size) (((ptr) = _aligned_malloc((size), (align))) == NULL ? -1 : 0) + #define PLATFORM_ALIGNED_FREE(ptr) _aligned_free((ptr)) +#else + #include -void CAllocator::Init() { + #define PLATFORM_ALIGNED_ALLOC(ptr, align, size) posix_memalign(&(ptr), (align), (size)) + #define PLATFORM_ALIGNED_FREE(ptr) std::free((ptr)) +#endif -} +CAllocator::CAllocator() noexcept : Allocator(0), m_lastWasAligned(false) {} -CAllocator::~CAllocator(){ - -} +CAllocator::~CAllocator() noexcept {} + +void CAllocator::Init() {} void* CAllocator::Allocate(const std::size_t size, const std::size_t alignment) { - return malloc(size); -} + if (alignment > 1) { + void* ptr = NULL; + const std::size_t align = (alignment < sizeof(void*)) ? sizeof(void*) : alignment; + const std::size_t aligned_size = (size + align - 1) & ~(align - 1); -void CAllocator::Free(void* ptr) { - free(ptr); -} + if (PLATFORM_ALIGNED_ALLOC(ptr, align, aligned_size) != 0) + return NULL; + + m_lastWasAligned = true; + + return ptr; + } + m_lastWasAligned = false; + return std::malloc(size); +} + +void CAllocator::Free(void* ptr) { + if (m_lastWasAligned) + PLATFORM_ALIGNED_FREE(ptr); + else + std::free(ptr); +} \ No newline at end of file diff --git a/src/FreeListAllocator.cpp b/src/FreeListAllocator.cpp index fb84853..697f896 100644 --- a/src/FreeListAllocator.cpp +++ b/src/FreeListAllocator.cpp @@ -1,6 +1,6 @@ #include "FreeListAllocator.h" #include "Utils.h" /* CalculatePaddingWithHeader */ -#include /* malloc, free */ +#include #include /* assert */ #include /* limits_max */ #include // std::max @@ -9,174 +9,187 @@ #include #endif -FreeListAllocator::FreeListAllocator(const std::size_t totalSize, const PlacementPolicy pPolicy) -: Allocator(totalSize) { - m_pPolicy = pPolicy; -} +FreeListAllocator::FreeListAllocator(const std::size_t totalSize, const PlacementPolicy pPolicy) noexcept : Allocator(totalSize), m_start_ptr(NULL), m_pPolicy(pPolicy) {} void FreeListAllocator::Init() { - if (m_start_ptr != nullptr) { - free(m_start_ptr); - m_start_ptr = nullptr; + if (m_start_ptr != NULL) { + std::free(m_start_ptr); + m_start_ptr = NULL; } - m_start_ptr = malloc(m_totalSize); + m_start_ptr = std::malloc(m_totalSize); + + assert(m_start_ptr != NULL && "FreeListAllocator: malloc failed."); - this->Reset(); + Reset(); } -FreeListAllocator::~FreeListAllocator() { - free(m_start_ptr); - m_start_ptr = nullptr; +FreeListAllocator::~FreeListAllocator() noexcept { + std::free(m_start_ptr); + m_start_ptr = NULL; } void* FreeListAllocator::Allocate(const std::size_t size, const std::size_t alignment) { - const std::size_t allocationHeaderSize = sizeof(FreeListAllocator::AllocationHeader); - const std::size_t freeHeaderSize = sizeof(FreeListAllocator::FreeHeader); - assert("Allocation size must be bigger" && size >= sizeof(Node)); - assert("Alignment must be 8 at least" && alignment >= 8); + assert(m_start_ptr != NULL && "FreeListAllocator::Allocate called before 'Init()'."); + assert(size >= sizeof(Node) && "Allocation size must be '>= sizeof(Node)'."); + assert(alignment >= 8 && "Alignment must be at least 8."); + assert((alignment & (alignment - 1)) == 0 && "Alignment must be a power of 2."); - // Search through the free list for a free block that has enough space to allocate our data - std::size_t padding; - Node * affectedNode, - * previousNode; - this->Find(size, alignment, padding, previousNode, affectedNode); - assert (affectedNode != nullptr && "Not enough memory"); + const std::size_t allocationHeaderSize = sizeof(AllocationHeader); + std::size_t padding = 0; + Node* affectedNode = NULL; + Node* previousNode = NULL; + Find(size, alignment, padding, previousNode, affectedNode); - const std::size_t alignmentPadding = padding - allocationHeaderSize; - const std::size_t requiredSize = size + padding; + assert(affectedNode != NULL && "FreeListAllocator: not enough memory."); + const std::size_t alignmentPadding = padding - allocationHeaderSize; + const std::size_t requiredSize = size + padding; const std::size_t rest = affectedNode->data.blockSize - requiredSize; - if (rest > 0) { - // We have to split the block into the data block and a free block of size 'rest' - Node * newFreeNode = (Node *)((std::size_t) affectedNode + requiredSize); + if (rest >= sizeof(Node)) { + Node* newFreeNode = reinterpret_cast(reinterpret_cast(affectedNode) + requiredSize); + newFreeNode->data.blockSize = rest; m_freeList.insert(affectedNode, newFreeNode); } m_freeList.remove(previousNode, affectedNode); - // Setup data block - const std::size_t headerAddress = (std::size_t) affectedNode + alignmentPadding; + const std::size_t headerAddress = reinterpret_cast(affectedNode) + alignmentPadding; const std::size_t dataAddress = headerAddress + allocationHeaderSize; - ((FreeListAllocator::AllocationHeader *) headerAddress)->blockSize = requiredSize; - ((FreeListAllocator::AllocationHeader *) headerAddress)->padding = alignmentPadding; - m_used += requiredSize; + AllocationHeader* header = reinterpret_cast(headerAddress); + + header->blockSize = requiredSize + (rest < sizeof(Node) ? rest : 0); + header->padding = alignmentPadding; + + m_used += header->blockSize; m_peak = std::max(m_peak, m_used); #ifdef _DEBUG - std::cout << "A" << "\t@H " << (void*) headerAddress << "\tD@ " <<(void*) dataAddress << "\tS " << ((FreeListAllocator::AllocationHeader *) headerAddress)->blockSize << "\tAP " << alignmentPadding << "\tP " << padding << "\tM " << m_used << "\tR " << rest << std::endl; + std::cout << "A\t@H " << reinterpret_cast(headerAddress) << "\tD@ " << reinterpret_cast(dataAddress) << "\tS " << header->blockSize << "\tAP " << alignmentPadding << "\tP " << padding << "\tM " << m_used << "\tR " << rest << '\n'; #endif - return (void*) dataAddress; + return reinterpret_cast(dataAddress); } -void FreeListAllocator::Find(const std::size_t size, const std::size_t alignment, std::size_t& padding, Node *& previousNode, Node *& foundNode) { +void FreeListAllocator::Find(const std::size_t size, const std::size_t alignment, std::size_t& padding, Node*& previousNode, Node*& foundNode) noexcept { switch (m_pPolicy) { - case FIND_FIRST: + case PlacementPolicy::FIND_FIRST: FindFirst(size, alignment, padding, previousNode, foundNode); break; - case FIND_BEST: + case PlacementPolicy::FIND_BEST: FindBest(size, alignment, padding, previousNode, foundNode); break; } } -void FreeListAllocator::FindFirst(const std::size_t size, const std::size_t alignment, std::size_t& padding, Node *& previousNode, Node *& foundNode) { - //Iterate list and return the first free block with a size >= than given size - Node * it = m_freeList.head, - * itPrev = nullptr; - - while (it != nullptr) { - padding = Utils::CalculatePaddingWithHeader((std::size_t)it, alignment, sizeof (FreeListAllocator::AllocationHeader)); - const std::size_t requiredSpace = size + padding; - if (it->data.blockSize >= requiredSpace) { +void FreeListAllocator::FindFirst(const std::size_t size, const std::size_t alignment, std::size_t& padding, Node*& previousNode, Node*& foundNode) noexcept { + Node* it = m_freeList.head; + Node* itPrev = NULL; + + while (it != NULL) { + padding = Utils::CalculatePaddingWithHeader(reinterpret_cast(it), alignment, sizeof(AllocationHeader)); + + if (it->data.blockSize >= size + padding) break; - } + itPrev = it; it = it->next; } + previousNode = itPrev; foundNode = it; } -void FreeListAllocator::FindBest(const std::size_t size, const std::size_t alignment, std::size_t& padding, Node *& previousNode, Node *& foundNode) { - // Iterate WHOLE list keeping a pointer to the best fit +void FreeListAllocator::FindBest(const std::size_t size, const std::size_t alignment, std::size_t& padding, Node*& previousNode, Node*& foundNode) noexcept { std::size_t smallestDiff = std::numeric_limits::max(); - Node * bestBlock = nullptr; - Node * it = m_freeList.head, - * itPrev = nullptr; - while (it != nullptr) { - padding = Utils::CalculatePaddingWithHeader((std::size_t)it, alignment, sizeof (FreeListAllocator::AllocationHeader)); - const std::size_t requiredSpace = size + padding; - if (it->data.blockSize >= requiredSpace && (it->data.blockSize - requiredSpace < smallestDiff)) { - bestBlock = it; + Node* bestBlock = NULL; + Node* bestPrev = NULL; + std::size_t bestPadding = 0; + + Node* it = m_freeList.head; + Node* itPrev = NULL; + + while (it != NULL) { + const std::size_t currentPadding = Utils::CalculatePaddingWithHeader(reinterpret_cast(it), alignment, sizeof(AllocationHeader)); + const std::size_t requiredSpace = size + currentPadding; + + if (it->data.blockSize >= requiredSpace) { + const std::size_t diff = it->data.blockSize - requiredSpace; + + if (diff < smallestDiff) { + smallestDiff = diff; + bestBlock = it; + bestPrev = itPrev; + bestPadding = currentPadding; + } } + itPrev = it; it = it->next; } - previousNode = itPrev; + + padding = bestPadding; + previousNode = bestPrev; foundNode = bestBlock; } void FreeListAllocator::Free(void* ptr) { - // Insert it in a sorted position by the address number - const std::size_t currentAddress = (std::size_t) ptr; - const std::size_t headerAddress = currentAddress - sizeof (FreeListAllocator::AllocationHeader); - const FreeListAllocator::AllocationHeader * allocationHeader{ (FreeListAllocator::AllocationHeader *) headerAddress}; + assert(ptr != NULL && "FreeListAllocator::Free: null pointer."); + + const std::size_t currentAddress = reinterpret_cast(ptr); + const std::size_t headerAddress = currentAddress - sizeof(AllocationHeader); + const AllocationHeader* allocationHeader = reinterpret_cast(headerAddress); - Node * freeNode = (Node *) (headerAddress); + Node* freeNode = reinterpret_cast(headerAddress - allocationHeader->padding); freeNode->data.blockSize = allocationHeader->blockSize + allocationHeader->padding; - freeNode->next = nullptr; + freeNode->next = NULL; - Node * it = m_freeList.head; - Node * itPrev = nullptr; - while (it != nullptr) { - if (ptr < it) { - m_freeList.insert(itPrev, freeNode); - break; - } + Node* it = m_freeList.head; + Node* itPrev = NULL; + + while (it != NULL && reinterpret_cast(it) < reinterpret_cast(freeNode)) { itPrev = it; it = it->next; } - + + m_freeList.insert(itPrev, freeNode); + m_used -= freeNode->data.blockSize; // Merge contiguous nodes - Coalescence(itPrev, freeNode); + Coalescence(itPrev, freeNode); #ifdef _DEBUG - std::cout << "F" << "\t@ptr " << ptr <<"\tH@ " << (void*) freeNode << "\tS " << freeNode->data.blockSize << "\tM " << m_used << std::endl; + std::cout << "F\t@ptr " << ptr << "\tH@ " << static_cast(freeNode) << "\tS " << freeNode->data.blockSize << "\tM " << m_used << '\n'; #endif } -void FreeListAllocator::Coalescence(Node* previousNode, Node * freeNode) { - if (freeNode->next != nullptr && - (std::size_t) freeNode + freeNode->data.blockSize == (std::size_t) freeNode->next) { +void FreeListAllocator::Coalescence(Node* previousNode, Node* freeNode) noexcept { + if (freeNode->next != NULL && reinterpret_cast(freeNode) + freeNode->data.blockSize == reinterpret_cast(freeNode->next)) { freeNode->data.blockSize += freeNode->next->data.blockSize; m_freeList.remove(freeNode, freeNode->next); #ifdef _DEBUG - std::cout << "\tMerging(n) " << (void*) freeNode << " & " << (void*) freeNode->next << "\tS " << freeNode->data.blockSize << std::endl; + std::cout << "\tMerging(n) " << static_cast(freeNode) << " S " << freeNode->data.blockSize << '\n'; #endif } - - if (previousNode != nullptr && - (std::size_t) previousNode + previousNode->data.blockSize == (std::size_t) freeNode) { + + if (previousNode != NULL && reinterpret_cast(previousNode) + previousNode->data.blockSize == reinterpret_cast(freeNode)) { previousNode->data.blockSize += freeNode->data.blockSize; m_freeList.remove(previousNode, freeNode); #ifdef _DEBUG - std::cout << "\tMerging(p) " << (void*) previousNode << " & " << (void*) freeNode << "\tS " << previousNode->data.blockSize << std::endl; + std::cout << "\tMerging(p) " << static_cast(previousNode) << " S " << previousNode->data.blockSize << '\n'; #endif } } -void FreeListAllocator::Reset() { +void FreeListAllocator::Reset() noexcept { m_used = 0; m_peak = 0; - Node * firstNode = (Node *) m_start_ptr; + m_freeList.head = NULL; + Node* firstNode = reinterpret_cast(m_start_ptr); firstNode->data.blockSize = m_totalSize; - firstNode->next = nullptr; - m_freeList.head = nullptr; - m_freeList.insert(nullptr, firstNode); + firstNode->next = NULL; + m_freeList.insert(NULL, firstNode); } \ No newline at end of file diff --git a/src/LinearAllocator.cpp b/src/LinearAllocator.cpp index f306f83..736caf8 100644 --- a/src/LinearAllocator.cpp +++ b/src/LinearAllocator.cpp @@ -1,63 +1,63 @@ #include "LinearAllocator.h" #include "Utils.h" /* CalculatePadding */ -#include /* malloc, free */ +#include #include /*assert */ #include // max #ifdef _DEBUG #include #endif -LinearAllocator::LinearAllocator(const std::size_t totalSize) -: Allocator(totalSize) { -} +LinearAllocator::LinearAllocator(const std::size_t totalSize) noexcept : Allocator(totalSize), m_start_ptr(NULL), m_offset(0) {} void LinearAllocator::Init() { - if (m_start_ptr != nullptr) { - free(m_start_ptr); - } - m_start_ptr = malloc(m_totalSize); + if (m_start_ptr != NULL) + std::free(m_start_ptr); + + m_start_ptr = std::malloc(m_totalSize); + + assert(m_start_ptr != NULL && "LinearAllocator: malloc failed."); + m_offset = 0; } -LinearAllocator::~LinearAllocator() { - free(m_start_ptr); - m_start_ptr = nullptr; +LinearAllocator::~LinearAllocator() noexcept { + std::free(m_start_ptr); + m_start_ptr = NULL; } void* LinearAllocator::Allocate(const std::size_t size, const std::size_t alignment) { + assert(m_start_ptr != NULL && "LinearAllocator::Allocate called before 'Init()'."); + assert(size > 0 && "Allocation size must be greater than 0."); + + const std::size_t currentAddress = reinterpret_cast(m_start_ptr) + m_offset; std::size_t padding = 0; - std::size_t paddedAddress = 0; - const std::size_t currentAddress = (std::size_t)m_start_ptr + m_offset; - if (alignment != 0 && m_offset % alignment != 0) { - // Alignment is required. Find the next aligned memory address and update offset + if (alignment > 1 && (currentAddress & (alignment - 1)) != 0) padding = Utils::CalculatePadding(currentAddress, alignment); - } - if (m_offset + padding + size > m_totalSize) { - return nullptr; - } + if (m_offset + padding + size > m_totalSize) + return NULL; + + m_offset += padding + size; + m_used = m_offset; + m_peak = std::max(m_peak, m_used); - m_offset += padding; const std::size_t nextAddress = currentAddress + padding; - m_offset += size; #ifdef _DEBUG - std::cout << "A" << "\t@C " << (void*) currentAddress << "\t@R " << (void*) nextAddress << "\tO " << m_offset << "\tP " << padding << std::endl; + std::cout << "A\t@C " << reinterpret_cast(currentAddress) << "\t@R " << reinterpret_cast(nextAddress) << "\tO " << m_offset << "\tP " << padding << '\n'; #endif - m_used = m_offset; - m_peak = std::max(m_peak, m_used); - - return (void*) nextAddress; + return reinterpret_cast(nextAddress); } void LinearAllocator::Free(void* ptr) { - assert(false && "Use Reset() method"); + (void)ptr; + + assert(false && "LinearAllocator does not support individual frees - use 'Reset()'."); } -void LinearAllocator::Reset() { +void LinearAllocator::Reset() noexcept { m_offset = 0; m_used = 0; - m_peak = 0; -} +} \ No newline at end of file diff --git a/src/PoolAllocator.cpp b/src/PoolAllocator.cpp index 2b4cecf..04acb44 100644 --- a/src/PoolAllocator.cpp +++ b/src/PoolAllocator.cpp @@ -1,62 +1,75 @@ #include "PoolAllocator.h" -#include -#include -#include /* malloc, free */ +#include +#include #include //max #ifdef _DEBUG #include #endif -PoolAllocator::PoolAllocator(const std::size_t totalSize, const std::size_t chunkSize) -: Allocator(totalSize) { - assert(chunkSize >= 8 && "Chunk size must be greater or equal to 8"); - assert(totalSize % chunkSize == 0 && "Total Size must be a multiple of Chunk Size"); - this->m_chunkSize = chunkSize; +PoolAllocator::PoolAllocator(const std::size_t totalSize, const std::size_t chunkSize) noexcept : Allocator(totalSize), m_start_ptr(NULL), m_chunkSize(chunkSize) { + assert(chunkSize >= sizeof(void*) && "Chunk size must be '>= sizeof(void*)'."); + assert(totalSize % chunkSize == 0 && "Total size must be a multiple of chunk size."); } void PoolAllocator::Init() { - m_start_ptr = malloc(m_totalSize); - this->Reset(); -} + if (m_start_ptr != NULL) { + std::free(m_start_ptr); + m_start_ptr = NULL; + } + + m_start_ptr = std::malloc(m_totalSize); + + assert(m_start_ptr != NULL && "PoolAllocator: malloc failed."); -PoolAllocator::~PoolAllocator() { - free(m_start_ptr); + Reset(); } -void *PoolAllocator::Allocate(const std::size_t allocationSize, const std::size_t alignment) { - assert(allocationSize == this->m_chunkSize && "Allocation size must be equal to chunk size"); +PoolAllocator::~PoolAllocator() noexcept { + std::free(m_start_ptr); + m_start_ptr = NULL; +} - Node * freePosition = m_freeList.pop(); +void* PoolAllocator::Allocate(const std::size_t size, const std::size_t alignment) { + (void)size; + (void)alignment; - // If no freed chunks available, allocate from the pool using offset - if (freePosition == nullptr) { - assert(m_offset < m_totalSize / m_chunkSize && "The pool allocator is full"); - std::size_t address = (std::size_t) m_start_ptr + m_offset * m_chunkSize; - freePosition = (Node *) address; - m_offset++; - } + assert(m_start_ptr != NULL && "PoolAllocator::Allocate called before 'Init()'."); + assert(size == m_chunkSize && "PoolAllocator: allocation size must equal chunk size."); + assert(!m_freeList.Empty() && "PoolAllocator is full."); + Node* freeNode = m_freeList.pop(); m_used += m_chunkSize; m_peak = std::max(m_peak, m_used); + #ifdef _DEBUG - std::cout << "A" << "\t@S " << m_start_ptr << "\t@R " << (void*) freePosition << "\tM " << m_used << std::endl; + std::cout << "A\t@S " << m_start_ptr << "\t@R " << static_cast(freeNode) << "\tM " << m_used << '\n'; #endif - return (void*) freePosition; + return static_cast(freeNode); } -void PoolAllocator::Free(void * ptr) { - m_used -= m_chunkSize; +void PoolAllocator::Free(void* ptr) { + assert(ptr != NULL && "PoolAllocator::Free: null pointer."); + assert(m_used >= m_chunkSize && "PoolAllocator::Free: underflow."); - m_freeList.push((Node *) ptr); + m_used -= m_chunkSize; + m_freeList.push(static_cast(ptr)); #ifdef _DEBUG - std::cout << "F" << "\t@S " << m_start_ptr << "\t@F " << ptr << "\tM " << m_used << std::endl; + std::cout << "F\t@S " << m_start_ptr << "\t@F " << ptr << "\tM " << m_used << '\n'; #endif } -void PoolAllocator::Reset() { +void PoolAllocator::Reset() noexcept { m_used = 0; m_peak = 0; - m_offset = 0; + m_freeList.head = NULL; + + const std::size_t nChunks = m_totalSize / m_chunkSize; + + for (std::size_t i = 0; i < nChunks; ++i) { + Node* node = reinterpret_cast(reinterpret_cast(m_start_ptr) + i * m_chunkSize); + + m_freeList.push(node); + } } \ No newline at end of file diff --git a/src/StackAllocator.cpp b/src/StackAllocator.cpp index 9f2ed1d..0741743 100644 --- a/src/StackAllocator.cpp +++ b/src/StackAllocator.cpp @@ -1,88 +1,85 @@ #include "StackAllocator.h" -#include "Utils.h" /* CalculatePadding */ -#include /* malloc, free */ -#include /* max */ +#include "Utils.h" +#include +#include #include #ifdef _DEBUG #include #endif -StackAllocator::StackAllocator(const std::size_t totalSize) -: Allocator(totalSize) { - -} +StackAllocator::StackAllocator(const std::size_t totalSize) noexcept : Allocator(totalSize), m_start_ptr(NULL), m_offset(0) {} void StackAllocator::Init() { - if (m_start_ptr != nullptr) { - free(m_start_ptr); - m_start_ptr = nullptr; + if (m_start_ptr != NULL) { + std::free(m_start_ptr); + m_start_ptr = NULL; } - m_start_ptr = malloc(m_totalSize); + m_start_ptr = std::malloc(m_totalSize); + + assert(m_start_ptr != NULL && "StackAllocator: malloc failed."); + Reset(); + + m_markers.reserve(64); } -StackAllocator::~StackAllocator() { - free(m_start_ptr); - m_start_ptr = nullptr; +StackAllocator::~StackAllocator() noexcept { + std::free(m_start_ptr); + m_start_ptr = NULL; } void* StackAllocator::Allocate(const std::size_t size, const std::size_t alignment) { - const std::size_t currentAddress = (std::size_t)m_start_ptr + m_offset; + assert(m_start_ptr != NULL && "StackAllocator::Allocate called before 'Init()'."); + assert(size > 0 && "Allocation size must be greater than 0."); + const std::size_t currentAddress = reinterpret_cast(m_start_ptr) + m_offset; std::size_t padding = 0; - if (alignment != 0) { - padding = Utils::CalculatePadding(currentAddress, alignment); - } - if (m_offset + padding + size > m_totalSize) { - return nullptr; - } + if (alignment > 1) + padding = Utils::CalculatePadding(currentAddress, alignment); + + if (m_offset + padding + size > m_totalSize) + return NULL; m_markers.push_back(m_offset); const std::size_t nextAddress = currentAddress + padding; m_offset += padding + size; + m_used = m_offset; + m_peak = std::max(m_peak, m_used); #ifdef _DEBUG - std::cout << "A" << "\t@C " << (void*) currentAddress << "\t@R " << (void*) nextAddress << "\tO " << m_offset << "\tP " << padding << std::endl; + std::cout << "A\t@C " << reinterpret_cast(currentAddress) << "\t@R " << reinterpret_cast(nextAddress) << "\tO " << m_offset << "\tP " << padding << '\n'; #endif - m_used = m_offset; - m_peak = std::max(m_peak, m_used); - return (void*) nextAddress; + return reinterpret_cast(nextAddress); } -void StackAllocator::Free(void *ptr) { +void StackAllocator::Free(void* ptr) { (void)ptr; - assert(!m_markers.empty() && "StackAllocator::Free must follow LIFO order"); + + assert(!m_markers.empty() && "StackAllocator::Free called with no active allocations (LIFO violation)."); m_offset = m_markers.back(); m_markers.pop_back(); m_used = m_offset; #ifdef _DEBUG - std::cout << "F" << "\t@F " << ptr << "\tO " << m_offset << std::endl; + std::cout << "F\t@F " << ptr << "\tO " << m_offset << '\n'; #endif } -void StackAllocator::Reset() { +void StackAllocator::Reset() noexcept { m_offset = 0; m_used = 0; m_peak = 0; m_markers.clear(); - m_checkpoints.clear(); -} - -std::size_t StackAllocator::Push() { - m_checkpoints.push_back(m_offset); - return m_offset; } -void StackAllocator::Pop(const std::size_t marker) { - assert(marker <= m_offset && "StackAllocator::Pop marker must be <= current offset"); +void StackAllocator::Pop(const std::size_t marker) noexcept { + assert(marker <= m_offset && "StackAllocator::Pop: marker exceeds current offset."); - while (!m_checkpoints.empty() && m_checkpoints.back() >= marker) { - m_checkpoints.pop_back(); - } + while (!m_markers.empty() && m_markers.back() > marker) + m_markers.pop_back(); m_offset = marker; m_used = m_offset; diff --git a/src/main.cpp b/src/main.cpp index c668187..28f7d2b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -12,54 +12,51 @@ int main() { - const std::size_t A = static_cast(1e9); - const std::size_t B = static_cast(1e8); - - const std::vector ALLOCATION_SIZES {32, 64, 256, 512, 1024, 2048, 4096}; - const std::vector ALIGNMENTS {8, 8, 8, 8, 8, 8, 8}; - - Allocator * cAllocator = new CAllocator(); - Allocator * linearAllocator = new LinearAllocator(A); - Allocator * stackAllocator = new StackAllocator(A); - Allocator * poolAllocator = new PoolAllocator(16777216, 4096); - Allocator * freeListAllocator = new FreeListAllocator(B, FreeListAllocator::PlacementPolicy::FIND_FIRST); - - Benchmark benchmark(OPERATIONS); - - std::cout << "C" << std::endl; - benchmark.MultipleAllocation(cAllocator, ALLOCATION_SIZES, ALIGNMENTS); - benchmark.MultipleFree(cAllocator, ALLOCATION_SIZES, ALIGNMENTS); - benchmark.RandomAllocation(cAllocator, ALLOCATION_SIZES, ALIGNMENTS); - benchmark.RandomFree(cAllocator, ALLOCATION_SIZES, ALIGNMENTS); - - std::cout << "LINEAR" << std::endl; - benchmark.MultipleAllocation(linearAllocator, ALLOCATION_SIZES, ALIGNMENTS); - benchmark.RandomAllocation(linearAllocator, ALLOCATION_SIZES, ALIGNMENTS); - - std::cout << "STACK" << std::endl; - benchmark.MultipleAllocation(stackAllocator, ALLOCATION_SIZES, ALIGNMENTS); - benchmark.MultipleFree(stackAllocator, ALLOCATION_SIZES, ALIGNMENTS); - benchmark.RandomAllocation(stackAllocator, ALLOCATION_SIZES, ALIGNMENTS); - benchmark.RandomFree(stackAllocator, ALLOCATION_SIZES, ALIGNMENTS); - - std::cout << "POOL" << std::endl; - benchmark.SingleAllocation(poolAllocator, 4096, 8); - benchmark.SingleFree(poolAllocator, 4096, 8); - - std::cout << "FREE LIST" << std::endl; - benchmark.MultipleAllocation(freeListAllocator, ALLOCATION_SIZES, ALIGNMENTS); - benchmark.MultipleFree(freeListAllocator, ALLOCATION_SIZES, ALIGNMENTS); - benchmark.RandomAllocation(freeListAllocator, ALLOCATION_SIZES, ALIGNMENTS); - benchmark.RandomFree(freeListAllocator, ALLOCATION_SIZES, ALIGNMENTS); - - delete cAllocator; - delete linearAllocator; - delete stackAllocator; - delete poolAllocator; - - return 1; -} - - - - + const std::size_t GB = static_cast(1e9); + const std::size_t MB = static_cast(1e8); + const std::size_t POOL_SIZE = 16777216; + const std::size_t CHUNK_SIZE = 4096; + + const std::size_t sizes_arr[] = { 32, 64, 256, 512, 1024, 2048, 4096 }; + const std::size_t alignments_arr[] = { 8, 8, 8, 8, 8, 8, 8 }; + + const std::vector ALLOCATION_SIZES(sizes_arr, sizes_arr + 7); + const std::vector ALIGNMENTS(alignments_arr, alignments_arr + 7); + + CAllocator cAllocator; + LinearAllocator linearAllocator(GB); + StackAllocator stackAllocator(GB); + PoolAllocator poolAllocator(POOL_SIZE, CHUNK_SIZE); + FreeListAllocator freeListAllocator(MB, FreeListAllocator::PlacementPolicy::FIND_FIRST); + + const unsigned int N_OPERATIONS = 1000; + Benchmark benchmark(N_OPERATIONS); + + std::cout << "=== C ALLOCATOR ===" << IO::endl; + benchmark.MultipleAllocation(&cAllocator, ALLOCATION_SIZES, ALIGNMENTS); + benchmark.MultipleFree(&cAllocator, ALLOCATION_SIZES, ALIGNMENTS); + benchmark.RandomAllocation(&cAllocator, ALLOCATION_SIZES, ALIGNMENTS); + benchmark.RandomFree(&cAllocator, ALLOCATION_SIZES, ALIGNMENTS); + + std::cout << "=== LINEAR ALLOCATOR ===" << IO::endl; + benchmark.MultipleAllocation(&linearAllocator, ALLOCATION_SIZES, ALIGNMENTS); + benchmark.RandomAllocation(&linearAllocator, ALLOCATION_SIZES, ALIGNMENTS); + + std::cout << "=== STACK ALLOCATOR ===" << IO::endl; + benchmark.MultipleAllocation(&stackAllocator, ALLOCATION_SIZES, ALIGNMENTS); + benchmark.MultipleFree(&stackAllocator, ALLOCATION_SIZES, ALIGNMENTS); + benchmark.RandomAllocation(&stackAllocator, ALLOCATION_SIZES, ALIGNMENTS); + benchmark.RandomFree(&stackAllocator, ALLOCATION_SIZES, ALIGNMENTS); + + std::cout << "=== POOL ALLOCATOR ===" << IO::endl; + benchmark.SingleAllocation(&poolAllocator, CHUNK_SIZE, 8); + benchmark.SingleFree(&poolAllocator, CHUNK_SIZE, 8); + + std::cout << "=== FREE LIST ALLOCATOR ===" << IO::endl; + benchmark.MultipleAllocation(&freeListAllocator, ALLOCATION_SIZES, ALIGNMENTS); + benchmark.MultipleFree(&freeListAllocator, ALLOCATION_SIZES, ALIGNMENTS); + benchmark.RandomAllocation(&freeListAllocator, ALLOCATION_SIZES, ALIGNMENTS); + benchmark.RandomFree(&freeListAllocator, ALLOCATION_SIZES, ALIGNMENTS); + + return 0; +} \ No newline at end of file