diff --git a/include/DataFrame/Utils/FixedSizeAllocator.h b/include/DataFrame/Utils/FixedSizeAllocator.h index 7c89913a..07b96dde 100644 --- a/include/DataFrame/Utils/FixedSizeAllocator.h +++ b/include/DataFrame/Utils/FixedSizeAllocator.h @@ -34,99 +34,423 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include #include +#include +#include +#include +#include // ---------------------------------------------------------------------------- namespace hmdf { -struct AllocatorFlags { - inline static constexpr unsigned char FREE_ { 0 }; - inline static constexpr unsigned char USED_ { 1 }; +template +struct StaticStorage { + + using value_type = T; + using size_type = std::size_t; + + inline static constexpr size_type max_size = + MAX_SIZE * sizeof(value_type); + inline static constexpr bool is_static = true; + + StaticStorage() = default; + StaticStorage(const StaticStorage &) = delete; + StaticStorage(StaticStorage &&) = delete; + ~StaticStorage() = default; + + StaticStorage &operator =(const StaticStorage &) = delete; + StaticStorage &operator =(StaticStorage &&) = delete; + + // Main allocation space + // + alignas(value_type[]) + inline static unsigned char buffer_[max_size]; }; // ---------------------------------------------------------------------------- template -struct StaticAllocStorage : public AllocatorFlags { +struct StackStorage { using value_type = T; - using Base = AllocatorFlags; + using size_type = std::size_t; - StaticAllocStorage() { + inline static constexpr size_type max_size = + MAX_SIZE * sizeof(value_type); + inline static constexpr bool is_static = false; - // This is guaranteed to execute only once even in multithreading - // - [[maybe_unused]] static auto slug = std::invoke([]() -> int { - std::memset(in_use_, Base::FREE_, MAX_SIZE); - return (0); - }); - } - StaticAllocStorage(const StaticAllocStorage &) = default; - StaticAllocStorage(StaticAllocStorage &&) = delete; - ~StaticAllocStorage() = default; + StackStorage() = default; + StackStorage(const StackStorage &) = delete; + StackStorage(StackStorage &&) = delete; + ~StackStorage() = default; - StaticAllocStorage &operator =(const StaticAllocStorage &) = delete; - StaticAllocStorage &operator =(StaticAllocStorage &&) = delete; + StackStorage &operator =(const StackStorage &) = delete; + StackStorage &operator =(StackStorage &&) = delete; // Main allocation space // alignas(value_type[]) - inline static unsigned char buffer_[MAX_SIZE * sizeof(value_type)]; + unsigned char buffer_[max_size]; +}; + +// ---------------------------------------------------------------------------- + +struct BestFitMemoryBlock { + + using value_type = unsigned char *; + using size_type = std::size_t; + + value_type address { nullptr }; + size_type size { 0 }; + + inline value_type + get_end() const { return (address + size); } + inline value_type + get_start() const { return (address - size); } + + // Hash function + // + inline size_type + operator() (const BestFitMemoryBlock &mb) const { + + return (std::hash{ }(mb.address)); + } + + inline friend bool + operator < (const BestFitMemoryBlock &lhs, + const BestFitMemoryBlock &rhs) { + + return (lhs.size < rhs.size); + } + inline friend bool + operator > (const BestFitMemoryBlock &lhs, + const BestFitMemoryBlock &rhs) { + + return (lhs.size > rhs.size); + } + inline friend bool + operator == (const BestFitMemoryBlock &lhs, + const BestFitMemoryBlock &rhs) { + + return (lhs.address == rhs.address); + } +}; + +// ---------------------------------------------------------------------------- + +template // Storage class +struct BestFitAlgo : public S { + + using Base = S; + using size_type = Base::size_type; + using pointer = unsigned char *; + + BestFitAlgo() : Base() { + + free_blocks_start_.insert({ Base::buffer_, Base::max_size }); + free_blocks_assist_.insert( + std::make_pair(Base::buffer_, free_blocks_start_.begin())); + free_blocks_end_.insert(std::make_pair(Base::buffer_ + Base::max_size, + Base::max_size)); + } + ~BestFitAlgo() = default; + + // Like malloc + // + [[nodiscard]] pointer + get_space (size_type requested_size) { + + auto free_iter = + free_blocks_start_.lower_bound({ nullptr, requested_size }); + + if (free_iter != free_blocks_start_.end()) { + auto found_end = free_iter->get_end(); + + if (free_iter->size > requested_size) { + auto remaining = free_iter->size - requested_size; + auto new_address = free_iter->address + requested_size; + const auto insert_ret = + free_blocks_start_.insert({ new_address, remaining }); + + free_blocks_assist_.insert( + std::make_pair(new_address, insert_ret)); + free_blocks_end_[found_end] = remaining; + } + else // Exact size match + free_blocks_end_.erase(found_end); + + auto ret = free_iter->address; + + free_blocks_assist_.erase(free_iter->address); + free_blocks_start_.erase(free_iter); + used_blocks_.insert({ ret, requested_size }); + return (ret); + } + throw std::bad_alloc(); + } + + // Like free + // + void + put_space (pointer to_be_freed, size_type) { + + auto used_iter = used_blocks_.find({ to_be_freed, 0 }); + + if (used_iter != used_blocks_.end()) { + const pointer tail_ptr = to_be_freed + used_iter->size; + bool found_tail = false; + const auto tail_block = free_blocks_assist_.find(tail_ptr); + + // Try to find a free block that starts where to_be_freed block + // ends. If there is such a free block, join it with to_be_freed + // block + // + if (tail_block != free_blocks_assist_.end()) { + const size_type new_len = + used_iter->size + tail_block->second->size; + const BestFitMemoryBlock to_insert { to_be_freed, new_len }; + + free_blocks_start_.erase(tail_block->second); + free_blocks_assist_.erase(tail_block); + + const auto insert_ret = free_blocks_start_.insert(to_insert); + + free_blocks_assist_.insert( + std::make_pair(to_be_freed, insert_ret)); + free_blocks_end_[to_insert.get_end()] = new_len; + found_tail = true; + } + + // Try to find a free block that ends where to_be_freed block + // starts. If there is such a free block, join it with to_be_freed + // block + // + const auto end_iter = free_blocks_end_.find(to_be_freed); + bool found_head = false; + + if (end_iter != free_blocks_end_.end()) { + const pointer head_ptr = + end_iter->first - end_iter->second; + const auto head_block = free_blocks_assist_.find(head_ptr); + + if (head_block != free_blocks_assist_.end()) { + const size_type new_len = + used_iter->size + head_block->second->size; + const auto new_head = head_block->second->address; + const auto new_end = + end_iter->first + used_iter->size; + + free_blocks_start_.erase(head_block->second); + free_blocks_assist_.erase(head_block); + + const auto insert_ret = + free_blocks_start_.insert({ new_head, new_len }); + + free_blocks_assist_.insert( + std::make_pair(new_head, insert_ret)); + free_blocks_end_.erase(end_iter); + free_blocks_end_[new_end] = new_len; + found_head = true; + } + } + + // If we could not join with any other adjacent free blocks, + // process it as a stand alone + // + if (! (found_tail || found_head)) { + const pointer end_address = + used_iter->address + used_iter->size; + const auto insert_ret = + free_blocks_start_.insert( + { used_iter->address, used_iter->size }); + + free_blocks_assist_.insert( + std::make_pair(used_iter->address, insert_ret)); + free_blocks_end_[end_address] = used_iter->size; + } + + // Finally remove the block from used blocks map + // + used_blocks_.erase(used_iter); + } + // else // This is undefined behavior in delete operator + // throw std::invalid_argument("BestFitAlgo::put_space()"); + } + +private: + + // It is based on size, so it must be multi-set + // + using blk_set = std::multiset; + using blk_uoset = + std::unordered_set; + using blk_uomap = std::unordered_map; + using blk_assist = std::unordered_map; + + // Set of free blocks, keyed by size of the block. There could be multiple + // blocks with the same size. + // + blk_set free_blocks_start_ { }; + + // Map of free blocks to size, keyed by the pointer to the end of the block. + // + blk_uomap free_blocks_end_ { }; + + // Hash set of used blocks, keyed by the pointer to the beginning of + // the block. + // + blk_uoset used_blocks_ { }; + + // Map of free blocks to iterators of free_blocks_start_, keyed pointers + // to the beginning of free blocks + // + blk_assist free_blocks_assist_ { }; +}; + +// ---------------------------------------------------------------------------- + +template +struct FirstFitStaticBase : public StaticStorage { + + using Base = StaticStorage; + using value_type = Base::value_type; + using size_type = Base::size_type; + + inline static constexpr unsigned char FREE_ { 0 }; + inline static constexpr unsigned char USED_ { 1 }; + inline static constexpr std::size_t max_size = MAX_SIZE; // The bitmap to indicate which slots are in use. // - alignas(64) + alignas(value_type[]) inline static unsigned char in_use_[MAX_SIZE]; // Pointer to the first free slot. // alignas(value_type *) inline static unsigned char *first_free_ptr_ { in_use_ }; + + FirstFitStaticBase() : Base() { + + // This is guaranteed to execute only once even in multithreading + // + [[maybe_unused]] static auto slug = std::invoke([]() -> int { + std::memset(in_use_, FREE_, MAX_SIZE); + return (0); + }); + } }; // ---------------------------------------------------------------------------- template -struct StackAllocStorage : public AllocatorFlags { - - using value_type = T; - using Base = AllocatorFlags; +struct FirstFitStackBase : public StackStorage { - StackAllocStorage() { std::memset(in_use_, Base::FREE_, MAX_SIZE); } - StackAllocStorage(const StackAllocStorage &) { + using Base = StackStorage; + using value_type = Base::value_type; + using size_type = Base::size_type; - std::memset(in_use_, Base::FREE_, MAX_SIZE); - } - StackAllocStorage(StackAllocStorage &&) = delete; - ~StackAllocStorage() = default; - - StackAllocStorage &operator =(const StackAllocStorage &) = delete; - StackAllocStorage &operator =(StackAllocStorage &&) = delete; - - // Main allocation space - // - alignas(value_type[]) - unsigned char buffer_[MAX_SIZE * sizeof(value_type)]; + inline static constexpr unsigned char FREE_ { 0 }; + inline static constexpr unsigned char USED_ { 1 }; + inline static constexpr std::size_t max_size = MAX_SIZE; // The bitmap to indicate which slots are in use. // - alignas(64) + alignas(value_type[]) unsigned char in_use_[MAX_SIZE]; // Pointer to the first free slot. // alignas(value_type *) unsigned char *first_free_ptr_ { in_use_ }; + + FirstFitStackBase() : Base() { std::memset(in_use_, FREE_, MAX_SIZE); } +}; + +// ---------------------------------------------------------------------------- + +template // Storage class +struct FirstFitAlgo : public S { + + using Base = S; + using value_type = Base::value_type; + using size_type = Base::size_type; + using pointer = unsigned char *; + + FirstFitAlgo() : Base() { } + ~FirstFitAlgo() = default; + + // Like malloc + // + [[nodiscard]] pointer + get_space (size_type requested_size) { + + // Pointers to the "in use" bitmap. + // + unsigned char *first_ptr = Base::first_free_ptr_; + unsigned char *const end_ptr = &(Base::in_use_[Base::max_size]); + const size_type n_items = requested_size / sizeof(value_type); + + // Find first fit allocation algorithm, starting from the first + // free slot. + // Search for a big enough range of free slots. + // + first_ptr = std::search_n(first_ptr, end_ptr, n_items, Base::FREE_); + + // Not enough space found? + // + if (first_ptr == end_ptr) + throw std::bad_alloc(); + + // Mark the range as used + // + std::memset(first_ptr, Base::USED_, n_items); + + // Update the "first free" pointer if necessary. + // + if (first_ptr == Base::first_free_ptr_) // Find the next free slot + Base::first_free_ptr_ = + std::find(first_ptr + n_items, end_ptr, Base::FREE_); + + // Return the memory allocation. + // + const size_type offset = + std::distance(Base::in_use_, first_ptr) * sizeof(value_type); + + return (&(Base::buffer_[offset])); + } + + // Like free + // + void + put_space (pointer to_be_freed, size_type space_size) { + + // Find the start of the range. + // + const size_type index = // Find the start of the range. + std::distance(Base::buffer_, to_be_freed) / sizeof(value_type); + unsigned char *first_ptr = &(Base::in_use_[index]); + + // Mark the range as free. + // + std::memset(first_ptr, Base::FREE_, space_size / sizeof(value_type)); + + // Update the "first free" pointer if necessary. + // + if (first_ptr < Base::first_free_ptr_) + Base::first_free_ptr_ = first_ptr; + } }; // ---------------------------------------------------------------------------- template typename STORE> -class FixedSizeAllocator : private STORE { + template typename STORAGE, + template typename ALGO> +class FixedSizeAllocator : ALGO> { - using Base = STORE; + using AlgoBase = ALGO>; public: @@ -139,7 +463,7 @@ class FixedSizeAllocator : private STORE { using const_void_pointer = const void *; using reference = T &; using const_reference = const T &; - using size_type = std::size_t; + using size_type = AlgoBase::size_type; using difference_type = ptrdiff_t; using propagate_on_container_copy_assignment = std::false_type; using propagate_on_container_move_assignment = std::false_type; @@ -151,27 +475,33 @@ class FixedSizeAllocator : private STORE { // std::allocator_traits implementation fail during compilation. // template - struct rebind { using other = FixedSizeAllocator; }; + struct rebind { + using other = FixedSizeAllocator; + }; [[nodiscard]] pointer address(reference r) const { return (std::addressof(r)); } [[nodiscard]] const_pointer address(const_reference cr) const { return (std::addressof(cr)); } - [[nodiscard]] constexpr size_type max_size() const { return (MAX_SIZE); } + [[nodiscard]] constexpr size_type + max_size() const { return (MAX_SIZE); } public: - FixedSizeAllocator() : Base() { } - FixedSizeAllocator(const FixedSizeAllocator &that) : Base(that) { } + FixedSizeAllocator() : AlgoBase() { } + FixedSizeAllocator(const FixedSizeAllocator &that) = delete; FixedSizeAllocator(FixedSizeAllocator &&) = delete; ~FixedSizeAllocator() = default; - FixedSizeAllocator &operator =(FixedSizeAllocator &&) = delete; - FixedSizeAllocator &operator =(const FixedSizeAllocator &) = delete; + FixedSizeAllocator & + operator =(FixedSizeAllocator &&) = delete; + FixedSizeAllocator & + operator =(const FixedSizeAllocator &) = delete; template - FixedSizeAllocator(const FixedSizeAllocator &) { } + FixedSizeAllocator(const FixedSizeAllocator &) { + } // Always return true for stateless allocators. // @@ -182,97 +512,72 @@ class FixedSizeAllocator : private STORE { public: - inline void construct(pointer p, const_reference val) const { + inline void + construct(pointer p, const_reference val) const { new (static_cast(p)) value_type(val); } - inline void construct(pointer p) const { + inline void + construct(pointer p) const { new (static_cast(p)) value_type(); } - new (static_cast(p)) value_type(); - } + template + inline void + construct(U *p, As && ... args) const { - template - inline void construct(U *p, Ts && ... args) const { - - new (static_cast(p)) U(std::forward(args) ...); + new (static_cast(p)) U(std::forward(args) ...); } - inline void destroy(pointer p) const { p->~value_type(); } + inline void + destroy(pointer p) const { p->~value_type(); } template - inline void destroy(U *p) const { p->~U(); } + inline void + destroy(U *p) const { p->~U(); } [[nodiscard]] inline pointer allocate(size_type n_items, [[maybe_unused]] const_pointer cp) { - // Pointers to the "in use" bitmap. - // - unsigned char *first_ptr = Base::first_free_ptr_; - unsigned char *const end_ptr = &(Base::in_use_[MAX_SIZE]); - - // Find first fit allocation algorithm, starting from the first - // free slot. - - // Search for a big enough range of free slots. - // - first_ptr = std::search_n(first_ptr, end_ptr, n_items, Base::FREE_); - - // Not enough space found? - // - if (first_ptr == end_ptr) - throw std::bad_alloc(); - - // Mark the range as used - // - std::memset(first_ptr, Base::USED_, n_items); - - // Update the "first free" pointer if necessary. - // - if (first_ptr == Base::first_free_ptr_) // Find the next free slot - Base::first_free_ptr_ = - std::find(first_ptr + n_items, end_ptr, Base::FREE_); - - // Return the memory allocation. - // - const size_type offset = - std::distance(Base::in_use_, first_ptr) * sizeof(value_type); + auto memory_ptr = this->get_space(n_items * sizeof(value_type)); - return (reinterpret_cast(&(Base::buffer_[offset]))); + return (reinterpret_cast(memory_ptr)); } - [[nodiscard]] inline pointer allocate(size_type n_items) { - - return (allocate(n_items, nullptr)); - } - - void deallocate(pointer p, size_type n_items) { - - // Find the start of the range. - // - const size_type index = - std::distance(Base::buffer_, reinterpret_cast(p)) / - sizeof(value_type); - - unsigned char *first_ptr = &(Base::in_use_[index]); + [[nodiscard]] inline pointer + allocate(size_type n_items) { return (allocate(n_items, nullptr)); } - // Mark the range as free. - // - std::memset(first_ptr, Base::FREE_, n_items); + void + deallocate(pointer p, size_type n_items) { - // Update the "first free" pointer if necessary. - // - if (first_ptr < Base::first_free_ptr_) - Base::first_free_ptr_ = first_ptr; + this->put_space(reinterpret_cast(p), + n_items * sizeof(value_type)); } }; // ---------------------------------------------------------------------------- +// This is slower than first-fit, but it causes a lot less fragmentations. +// +template +using StaticBestFitAllocator = + FixedSizeAllocator; + +// This is slower than first-fit, but it causes a lot less fragmentations. +// +template +using StackBestFitAllocator = + FixedSizeAllocator; + +// This is faster than best-fit, but it causes more fragmentations. +// template -using StaticAllocator = FixedSizeAllocator; +using StaticFirstFitAllocator = + FixedSizeAllocator; +// This is faster than best-fit, but it causes more fragmentations. +// template -using StackAllocator = FixedSizeAllocator; +using StackFirstFitAllocator = + FixedSizeAllocator; } // namespace std diff --git a/test/allocator_tester.cc b/test/allocator_tester.cc index 342fb42d..70f2e9b2 100644 --- a/test/allocator_tester.cc +++ b/test/allocator_tester.cc @@ -28,9 +28,12 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include +#include #include +#include #include #include +#include #include #include @@ -163,11 +166,11 @@ static void test_aligned_allocator() { // ----------------------------------------------------------------------------- -static void test_static_allocator() { +static void test_first_fit_static_allocator() { - std::cout << "\nTesting StaticAllocator ..." << std::endl; + std::cout << "\nTesting StaticFirstFitAllocator ..." << std::endl; - std::vector> vec1; + std::vector> vec1; vec1.reserve(100000); for (std::size_t i = 0; i < 100000; ++i) @@ -175,7 +178,7 @@ static void test_static_allocator() { for (std::size_t i = 0; i < 100000; ++i) assert(vec1[i] == int(i)); - std::vector> vec2; + std::vector> vec2; for (std::size_t i = 0; i < 10000; ++i) vec2.push_back(int(i)); @@ -183,7 +186,7 @@ static void test_static_allocator() { assert(vec2[i] == int(i)); { - std::vector> vec3; + std::vector> vec3; for (std::size_t i = 0; i < 10000; ++i) vec3.push_back(double(i)); @@ -191,7 +194,7 @@ static void test_static_allocator() { assert(vec3[i] == double(i)); } - std::vector> vec4; + std::vector> vec4; for (std::size_t i = 0; i < 10000; ++i) vec4.push_back(double(i)); @@ -199,7 +202,7 @@ static void test_static_allocator() { assert(vec4[i] == double(i)); using MyString = std::basic_string, - StaticAllocator>; + StaticFirstFitAllocator>; { MyString str1 = @@ -223,7 +226,7 @@ static void test_static_allocator() { str3 += ". Adding more stuff"; std::cout << str3 << std::endl; - std::vector> vec5; + std::vector> vec5; vec5.reserve(1); for (std::size_t i = 0; i < 2; ++i) @@ -231,8 +234,9 @@ static void test_static_allocator() { for (std::size_t i = 0; i < 2; ++i) assert(vec5[i] == double(i)); - using map_t = std::map, - StaticAllocator, 1000>>; + using map_t = + std::map, + StaticFirstFitAllocator, 1000>>; map_t my_map; @@ -240,15 +244,241 @@ static void test_static_allocator() { my_map.insert({ i, i * 10 }); for (int i = 0; i < 1000; ++i) assert((my_map.find(i)->second == i * 10)); + + // Test how badly it fragments the memory + // + FirstFitAlgo> allocator; + std::vector> ptr_vec; + std::mt19937 gen { 98 }; + + std::srand(98); + for (std::size_t i = 0; i < 1000; ++i) { + for (std::size_t j = 0; j < 10; ++j) { + const std::size_t size = (std::rand() % 100) * sizeof(int); + const auto ptr = allocator.get_space(size); + + ptr_vec.push_back(std::make_pair(ptr, size)); + } + + std::shuffle(ptr_vec.begin(), ptr_vec.end(), gen); + + if ((i % 10) == 0) { + for (std::size_t w = 0; w < (ptr_vec.size() / 3); ++w) + allocator.put_space(ptr_vec[w].first, ptr_vec[w].second); + ptr_vec.erase(ptr_vec.begin(), + ptr_vec.begin() + (ptr_vec.size() / 3)); + } + else { + for (std::size_t w = 0; w < (ptr_vec.size() / 2); ++w) + allocator.put_space(ptr_vec[w].first, ptr_vec[w].second); + ptr_vec.erase(ptr_vec.begin(), + ptr_vec.begin() + (ptr_vec.size() / 2)); + } + } +} + +// ----------------------------------------------------------------------------- + +static void test_first_fit_stack_allocator() { + + std::cout << "\nTesting StackFirstFitAllocator ..." << std::endl; + + std::vector> vec1; + + vec1.reserve(1000); + for (std::size_t i = 0; i < 1000; ++i) + vec1.push_back(int(i)); + for (std::size_t i = 0; i < 1000; ++i) + assert(vec1[i] == int(i)); + + std::vector> vec2; + + for (std::size_t i = 0; i < 100; ++i) + vec2.push_back(int(i)); + for (std::size_t i = 0; i < 100; ++i) + assert(vec2[i] == int(i)); + + { + std::vector> vec3; + + for (std::size_t i = 0; i < 100; ++i) + vec3.push_back(double(i)); + for (std::size_t i = 0; i < 100; ++i) + assert(vec3[i] == double(i)); + } + + std::vector> vec4; + + for (std::size_t i = 0; i < 100; ++i) + vec4.push_back(double(i)); + for (std::size_t i = 0; i < 100; ++i) + assert(vec4[i] == double(i)); + + using MyString = std::basic_string, + StackFirstFitAllocator>; + + { + MyString str1 = + "This is the first strig xo xo xo xo xo xo xo xo xo xo xo xo xo xo"; + + str1 += ". Adding more stuff xo xo xo xo xo xo xo xo xo xo"; + + { + MyString str2 = + "This is the second strig"; + + str2 += ". Again, adding more stuff wd wd wd wd wd wd wd wd wd wd"; + + std::cout << str2 << std::endl; + } + std::cout << str1 << std::endl; + } + + MyString str3 = "This is the third strig"; + + str3 += ". Adding more stuff"; + std::cout << str3 << std::endl; + + std::vector> vec5; + + vec5.reserve(1); + for (std::size_t i = 0; i < 2; ++i) + vec5.push_back(double(i)); + for (std::size_t i = 0; i < 2; ++i) + assert(vec5[i] == double(i)); + + using map_t = + std::map, + StackFirstFitAllocator, 1000>>; + + map_t my_map; + + for (int i = 0; i < 1000; ++i) + my_map.insert({ i, i * 10 }); + for (int i = 0; i < 1000; ++i) + assert((my_map.find(i)->second == i * 10)); +} + +// ----------------------------------------------------------------------------- + +static void test_best_fit_static_allocator() { + + std::cout << "\nTesting StaticBestFitAllocator ..." << std::endl; + + std::vector> vec1; + + vec1.reserve(100000); + for (std::size_t i = 0; i < 100000; ++i) + vec1.push_back(int(i)); + for (std::size_t i = 0; i < 100000; ++i) + assert(vec1[i] == int(i)); + + std::vector> vec2; + + for (std::size_t i = 0; i < 10000; ++i) + vec2.push_back(int(i)); + for (std::size_t i = 0; i < 10000; ++i) + assert(vec2[i] == int(i)); + + { + std::vector> vec3; + + for (std::size_t i = 0; i < 10000; ++i) + vec3.push_back(double(i)); + for (std::size_t i = 0; i < 10000; ++i) + assert(vec3[i] == double(i)); + } + + std::vector> vec4; + + for (std::size_t i = 0; i < 10000; ++i) + vec4.push_back(double(i)); + for (std::size_t i = 0; i < 10000; ++i) + assert(vec4[i] == double(i)); + + using MyString = std::basic_string, + StaticBestFitAllocator>; + + { + MyString str1 = + "This is the first strig xo xo xo xo xo xo xo xo xo xo xo xo xo xo"; + + str1 += ". Adding more stuff xo xo xo xo xo xo xo xo xo xo"; + + { + MyString str2 = + "This is the second strig"; + + str2 += ". Again, adding more stuff wd wd wd wd wd wd wd wd wd wd"; + + std::cout << str2 << std::endl; + } + std::cout << str1 << std::endl; + } + + MyString str3 = "This is the third strig"; + + str3 += ". Adding more stuff"; + std::cout << str3 << std::endl; + + std::vector> vec5; + + vec5.reserve(1); + for (std::size_t i = 0; i < 2; ++i) + vec5.push_back(double(i)); + for (std::size_t i = 0; i < 2; ++i) + assert(vec5[i] == double(i)); + + using map_t = + std::map, + StaticBestFitAllocator, 1000>>; + + map_t my_map; + + for (int i = 0; i < 1000; ++i) + my_map.insert({ i, i * 10 }); + for (int i = 0; i < 1000; ++i) + assert((my_map.find(i)->second == i * 10)); + + // Test how badly it fragments the memory + // + BestFitAlgo> allocator; + std::vector> ptr_vec; + std::mt19937 gen { 98 }; + + std::srand(98); + for (std::size_t i = 0; i < 10000; ++i) { + for (std::size_t j = 0; j < 100; ++j) { + const std::size_t size = (std::rand() % 100) * sizeof(int); + const auto ptr = allocator.get_space(size); + + ptr_vec.push_back(std::make_pair(ptr, size)); + } + + std::shuffle(ptr_vec.begin(), ptr_vec.end(), gen); + + if ((i % 10) == 0) { + for (std::size_t w = 0; w < (ptr_vec.size() / 3); ++w) + allocator.put_space(ptr_vec[w].first, ptr_vec[w].second); + ptr_vec.erase(ptr_vec.begin(), + ptr_vec.begin() + (ptr_vec.size() / 3)); + } + else { + for (std::size_t w = 0; w < (ptr_vec.size() / 2); ++w) + allocator.put_space(ptr_vec[w].first, ptr_vec[w].second); + ptr_vec.erase(ptr_vec.begin(), + ptr_vec.begin() + (ptr_vec.size() / 2)); + } + } } // ----------------------------------------------------------------------------- -static void test_stack_allocator() { +static void test_best_fit_stack_allocator() { - std::cout << "\nTesting StackAllocator ..." << std::endl; + std::cout << "\nTesting StackBestFitAllocator ..." << std::endl; - std::vector> vec1; + std::vector> vec1; vec1.reserve(1000); for (std::size_t i = 0; i < 1000; ++i) @@ -256,7 +486,7 @@ static void test_stack_allocator() { for (std::size_t i = 0; i < 1000; ++i) assert(vec1[i] == int(i)); - std::vector> vec2; + std::vector> vec2; for (std::size_t i = 0; i < 100; ++i) vec2.push_back(int(i)); @@ -264,7 +494,7 @@ static void test_stack_allocator() { assert(vec2[i] == int(i)); { - std::vector> vec3; + std::vector> vec3; for (std::size_t i = 0; i < 100; ++i) vec3.push_back(double(i)); @@ -272,7 +502,7 @@ static void test_stack_allocator() { assert(vec3[i] == double(i)); } - std::vector> vec4; + std::vector> vec4; for (std::size_t i = 0; i < 100; ++i) vec4.push_back(double(i)); @@ -280,7 +510,7 @@ static void test_stack_allocator() { assert(vec4[i] == double(i)); using MyString = std::basic_string, - StackAllocator>; + StackBestFitAllocator>; { MyString str1 = @@ -304,7 +534,7 @@ static void test_stack_allocator() { str3 += ". Adding more stuff"; std::cout << str3 << std::endl; - std::vector> vec5; + std::vector> vec5; vec5.reserve(1); for (std::size_t i = 0; i < 2; ++i) @@ -312,8 +542,9 @@ static void test_stack_allocator() { for (std::size_t i = 0; i < 2; ++i) assert(vec5[i] == double(i)); - using map_t = std::map, - StackAllocator, 1000>>; + using map_t = + std::map, + StackBestFitAllocator, 1000>>; map_t my_map; @@ -328,8 +559,10 @@ static void test_stack_allocator() { int main(int, char *[]) { test_aligned_allocator(); - test_static_allocator(); - test_stack_allocator(); + test_first_fit_static_allocator(); + test_first_fit_stack_allocator(); + test_best_fit_static_allocator(); + test_best_fit_stack_allocator(); return (0); }