From 479f2a65a9fe493bda3398779e0100a76d933773 Mon Sep 17 00:00:00 2001 From: Ludovic 'Archivist' Lagouardette Date: Tue, 28 Apr 2020 10:18:49 +0200 Subject: [PATCH] Added aggregate allocator --- Makefile | 3 +- include/gp/algorithm/modifiers.hpp | 2 +- include/gp/algorithm/move.hpp | 11 +++ include/gp/allocator/aggregator.hpp | 104 ++++++++++++++++++++ include/gp/allocator/buddy.hpp | 10 +- include/gp/integer_math.hpp | 1 + include/gp/ring_list.hpp | 142 ++++++++++++++++++++++++++++ include/stored_indexed_array.hpp | 3 +- tests/gp_test.cpp | 102 +++++++++++++++++++- tests/test_scaffold.h | 4 - 10 files changed, 371 insertions(+), 11 deletions(-) create mode 100644 include/gp/allocator/aggregator.hpp create mode 100644 include/gp/ring_list.hpp diff --git a/Makefile b/Makefile index 6979335..c112d61 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,6 @@ CXX= clang++-8 -CXXFLAGS= --std=c++17 -O0 -pthread -DFUZZ_STRENGTH=4000000 \ +CXXFLAGS= --std=c++17 -O0 -pthread -DFUZZ_STRENGTH=500000 \ + -Wno-unknown-attributes \ -g -fprofile-instr-generate -fcoverage-mapping all: tests diff --git a/include/gp/algorithm/modifiers.hpp b/include/gp/algorithm/modifiers.hpp index 667eeb8..fc26b0a 100644 --- a/include/gp/algorithm/modifiers.hpp +++ b/include/gp/algorithm/modifiers.hpp @@ -9,7 +9,7 @@ namespace gp { template auto bind_front(F&& func, Args&&... arg_parent) { - return [&](auto&&... argv){ + return [=](auto&&... argv){ return func(arg_parent..., argv...); }; } diff --git a/include/gp/algorithm/move.hpp b/include/gp/algorithm/move.hpp index bf84193..c50fde4 100644 --- a/include/gp/algorithm/move.hpp +++ b/include/gp/algorithm/move.hpp @@ -32,6 +32,17 @@ namespace gp{ rhs = tmp; } + template + constexpr void swap( + T&& lhs, + T&& rhs + ) + { + auto tmp = lhs; + lhs = rhs; + rhs = tmp; + } + template nameless_range move(range_in src, range_out dest) { diff --git a/include/gp/allocator/aggregator.hpp b/include/gp/allocator/aggregator.hpp new file mode 100644 index 0000000..dd661d4 --- /dev/null +++ b/include/gp/allocator/aggregator.hpp @@ -0,0 +1,104 @@ +#pragma once +#include +#include "gp/ring_list.hpp" + +namespace gp { + class aggregator { + struct virtual_allocator + { + virtual ~virtual_allocator() = default; + virtual void* allocate(size_t) = 0; + virtual bool deallocate(void*) = 0; + virtual bool try_reallocate(void*, size_t) = 0; + }; + + template + class abstract_allocator final : public virtual_allocator{ + alloc internal_representation; + public: + abstract_allocator(abstract_allocator& v) + : internal_representation{v.internal_representation} + {} + + abstract_allocator(alloc& v) + : internal_representation{v} + {} + + virtual ~abstract_allocator() override = default; + virtual void* allocate(size_t sz) override { + return internal_representation.allocate(sz); + } + virtual bool deallocate(void* ptr) override { + return internal_representation.deallocate(ptr); + } + virtual bool try_reallocate(void* ptr, size_t sz) override { + return internal_representation.try_reallocate(ptr, sz); + } + }; + using local_container = ring_list; + + local_container contents; + local_container::explorer mark; + + public: + template + aggregator(bootstrapper&& allocator) + : contents{ + new(allocator.allocate(sizeof(local_container::node))) + local_container::node( + new(allocator.allocate(sizeof(bootstrapper))) abstract_allocator(allocator) + ), + *this + } + , mark{contents.explore()} + {} + + template + bool insert(allocator&& value) { + return contents.insert< + abstract_allocator< + std::remove_reference_t< + allocator + > + > + >(abstract_allocator(value)); + } + + + void* allocate(size_t sz) { + auto cpy = mark; + do{ + if(auto allocated = (*mark).allocate(sz)) + { + return allocated; + } + ++mark; + }while(cpy != mark); + return nullptr; + } + bool deallocate(void* ptr) { + auto cpy = mark; + do{ + if((*cpy).deallocate(ptr)) + { + return true; + } + --cpy; + }while(cpy != mark); + return false; + } + bool try_reallocate(void* ptr, size_t sz) { + auto cpy = mark; + do{ + if((*cpy).try_reallocate(ptr, sz)) + { + return true; + } + --cpy; + }while(cpy != mark); + return false; + } + }; + + +} \ No newline at end of file diff --git a/include/gp/allocator/buddy.hpp b/include/gp/allocator/buddy.hpp index 2bac2dd..05e9820 100644 --- a/include/gp/allocator/buddy.hpp +++ b/include/gp/allocator/buddy.hpp @@ -46,6 +46,12 @@ namespace gp{ static constexpr size_t span_size = required_twigs / 4 + (required_twigs % 4 != 0); gp::array stack; +/** + * This code has been manually hecked and will always return. + * If you find a case where it doesn't, please file an issue. +**/ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wreturn-type" twig get_twig(size_t idx) const { auto far = idx / 4; auto local = idx % 4; @@ -60,11 +66,13 @@ namespace gp{ return stack[far].d; } } +#pragma clang diagnostic pop void set_twig(size_t idx, twig v) { auto far = idx / 4; auto local = idx % 4; auto& group = stack[far]; + switch(local) { case 0: group.a = v; @@ -220,7 +228,7 @@ namespace gp{ } } - buddy(char* pos,size_t sz) + buddy(char* pos, size_t sz) : data(pos,pos+sz) , max_depth(gp::math::msb(sz)-gp::math::msb(align)) , twig_explore_length(1 << max_depth) diff --git a/include/gp/integer_math.hpp b/include/gp/integer_math.hpp index a50e010..6691f46 100644 --- a/include/gp/integer_math.hpp +++ b/include/gp/integer_math.hpp @@ -29,6 +29,7 @@ namespace gp { return MultiplyDeBruijnBitPosition[(uint32_t)(v * 0x07C4ACDDU) >> 27]; } + template<> constexpr size_t log2(uint64_t v) { diff --git a/include/gp/ring_list.hpp b/include/gp/ring_list.hpp new file mode 100644 index 0000000..37a98e8 --- /dev/null +++ b/include/gp/ring_list.hpp @@ -0,0 +1,142 @@ +#pragma once +#include +#include "gp/algorithm/tmp_manip.hpp" +#include "gp/algorithm/modifiers.hpp" +#include "gp_config.hpp" + +namespace gp { + template + class ring_list{ + public: + class explorer; + class node { + T* value; + node* prev; + node* next; + public: + node(T* p) + : value{p} + , prev{this} + , next{this} + {} + + friend class gp::ring_list::explorer; + friend class gp::ring_list; + }; + + class explorer { + node* pos; + public: + explorer(node* v) + : pos{v} + {} + + bool operator==(const explorer& oth) const { + return pos == oth.pos; + } + + bool operator!=(const explorer& oth) const { + return pos != oth.pos; + } + + explorer operator++() { + pos = pos->next; + return pos; + } + + explorer operator++(int) { + auto tmp = pos; + pos = pos->next; + return tmp; + } + + explorer operator--() { + pos = pos->prev; + return pos; + } + + explorer operator--(int) { + auto tmp = pos; + pos = pos->prev; + return tmp; + } + + T& operator*() { + return *(pos->value); + } + + friend class ring_list; + }; + + private: + node* any_node; + size_t sz; + typename gp::either< + copy_allocator, + allocator, + gp::reference_wrapper + >::type alloc; + + void stitch_around(node* n) { + n->prev->next = n->next; + n->next->prev = n->prev; + } + + public: + ring_list() + : any_node{nullptr} + , sz{0} + , alloc{} + {} + + ring_list(node* initial, allocator& _alloc) + : any_node{initial} + , sz{1} + , alloc{_alloc} + {} + + template + bool insert(Args&&... elem) { + allocator& used_allocator = alloc; + void* mem = used_allocator.allocate(sizeof(V)); + T* p = new(mem) V(elem...); + node* to_insert = nullptr; + if( + nullptr == (to_insert = reinterpret_cast(used_allocator.allocate(sizeof(node)))) + ) return false; + to_insert = new(to_insert) node(p); + [[unlikely]] if (any_node == nullptr) + { + any_node = to_insert; + } else { + to_insert->prev = any_node->prev; + to_insert->next = any_node; + to_insert->prev->next = to_insert; + any_node->prev = to_insert; + } + return true; + } + + explorer explore() { + return any_node; + } + + void remove(explorer& value) { + auto& v = *value; + if(v == explore()) { + if(v->next == v) { + any_node = nullptr; + } else { + stitch_around(any_node); + } + } else { + stitch_around(value.pos); + } + allocator& used_allocator = alloc; + v.value->~T(); + gp_config::assertion(used_allocator.deallocate(v.value), "Bad free of value"); + value.pos->~node(); + gp_config::assertion(used_allocator.deallocate(value.pos), "Bad free of node"); + } + }; +} diff --git a/include/stored_indexed_array.hpp b/include/stored_indexed_array.hpp index 3f59c93..7b9637e 100644 --- a/include/stored_indexed_array.hpp +++ b/include/stored_indexed_array.hpp @@ -1,2 +1 @@ -#pragma once - +#pragma once \ No newline at end of file diff --git a/tests/gp_test.cpp b/tests/gp_test.cpp index 7d12655..12ea7f4 100644 --- a/tests/gp_test.cpp +++ b/tests/gp_test.cpp @@ -1,8 +1,10 @@ #include "test_scaffold.h" #include "gp/array.hpp" +#include "gp/allocator/aggregator.hpp" #include "gp/allocator/buddy.hpp" #include "gp/allocator/dummy.hpp" #include "gp/algorithm/repeat.hpp" +#include "gp/ring_list.hpp" #include #include #include @@ -12,9 +14,15 @@ #include #include #include -#include +#include #include + + +#ifndef FUZZ_STRENGTH +#define FUZZ_STRENGTH 2048 +#endif + #define MACRO_STRGEN(X) #X #define MACRO_STR(X) MACRO_STRGEN(X) @@ -331,4 +339,94 @@ struct buddy_fuzz_test : public test_scaffold { }; append_test dummy_df987sd3(new buddy_fuzz_test{781017366}); -append_test dummy_df4sisd3(new buddy_fuzz_test{}); \ No newline at end of file +append_test dummy_df4sisd3(new buddy_fuzz_test{}); + +struct ring_list_test : public test_scaffold { + ring_list_test() { + name = __FILE__ ":5"; + } + + virtual int run() { + int res = 0; + alignas(8) gp::array store; + using local_allocator = gp::buddy(4096)>; + local_allocator bud{&*store.begin(), store.size()}; + { + using string_ring = gp::ring_list; + auto p = new(bud.allocate(sizeof(std::string))) std::string("Hello"); + auto orig = new(bud.allocate(sizeof(string_ring::node))) string_ring::node(p); + string_ring ring{orig, bud}; + ring.insert("World"); + auto it = ring.explore(); + std::string test = ""; + do{ + test += *it; + ++it; + }while(it != ring.explore()); + res += (test != "HelloWorld"); + } + return res; + } +}; + +append_test dummy_867fdrgsd3(new ring_list_test{}); + + +struct aggregator_test : public test_scaffold { + aggregator_test() { + name = std::string(__FILE__ "seed_") + std::to_string(seed) + ":6"; + rng.seed(seed); + } + + std::mt19937 rng{}; + int seed = std::random_device{}(); + + virtual int run() { + int res = 0; + alignas(8) gp::array store; + using local_allocator = gp::buddy(4096)>; + local_allocator bud{&*store.begin(), store.size()}; + alignas(8) gp::array store2; + local_allocator bud2{&*store2.begin(), store2.size()}; + + gp::aggregator allocator{bud}; + allocator.insert(bud2); + { + std::vector ptr_set; + auto get_random_mem_qt = [&]() -> size_t { + return 1+rng()%(store.size()-1); + }; + + auto start = std::chrono::steady_clock::now(); + { + gp::repeat(FUZZ_STRENGTH, [&](){ + void* ptr; + auto sz = get_random_mem_qt(); + size_t tries = 0; + std::shuffle( + ptr_set.begin(), + ptr_set.end(), + rng + ); + while(!(ptr = allocator.allocate(sz))) + { + void* free_ptr = ptr_set.back(); + ptr_set.pop_back(); + gp_config::assertion(allocator.deallocate(free_ptr), "could not free sample"); + gp_config::assertion(++tries <= store.size(), "infinite fuzzing"); + } + ptr_set.emplace_back(ptr); + }); + for(auto ptr : ptr_set) + { + bud.deallocate(ptr); + } + ptr_set.resize(0); + } + auto duration = std::chrono::steady_clock::now() - start; + } + return res; + } +}; + +append_test dummy_8ijfsd658(new aggregator_test{}); diff --git a/tests/test_scaffold.h b/tests/test_scaffold.h index bf855c5..db8a64e 100644 --- a/tests/test_scaffold.h +++ b/tests/test_scaffold.h @@ -3,10 +3,6 @@ #include #include -#ifndef FUZZ_STRENGTH -#define FUZZ_STRENGTH = 2048; -#endif - struct test_scaffold{ std::string name; virtual int run() = 0;