diff --git a/bitflags.h b/bitflags.h index 9a6050b..c9e4692 100644 --- a/bitflags.h +++ b/bitflags.h @@ -16,8 +16,8 @@ /////////////////////////////////////////////////////////////////////////////// -#ifndef GALLOC_BITFLAGS -#define GALLOC_BITFLAGS +#ifndef GCPP_BITFLAGS +#define GCPP_BITFLAGS #include "util.h" @@ -25,7 +25,7 @@ #include #include -namespace galloc { +namespace gcpp { //---------------------------------------------------------------------------- // diff --git a/deferred_allocator.cpp b/deferred_allocator.cpp deleted file mode 100644 index f0189cc..0000000 --- a/deferred_allocator.cpp +++ /dev/null @@ -1,29 +0,0 @@ - -/////////////////////////////////////////////////////////////////////////////// -// -// Copyright (c) 2016 Herb Sutter. All rights reserved. -// -// This code is licensed under the MIT License (MIT). -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// -/////////////////////////////////////////////////////////////////////////////// - - -#include "deferred_allocator.h" - -namespace galloc::detail { - - deferred_heap& global_deferred_heap() { - static deferred_heap the_arena; - return the_arena; - } - -} - diff --git a/deferred_allocator.h b/deferred_allocator.h index c26d0ab..aecf803 100644 --- a/deferred_allocator.h +++ b/deferred_allocator.h @@ -16,17 +16,24 @@ /////////////////////////////////////////////////////////////////////////////// -#ifndef GALLOC_DEFERRED_ALLOCATOR -#define GALLOC_DEFERRED_ALLOCATOR +#ifndef GCPP_DEFERRED_ALLOCATOR +#define GCPP_DEFERRED_ALLOCATOR #include "deferred_heap.h" -namespace galloc { +#include +#include +#include +#include +#include +#include + +namespace gcpp { //private: - namespace detail { - deferred_heap& global_deferred_heap(); - } + //namespace detail { + // deferred_heap& global_deferred_heap(); + //} //public: @@ -41,7 +48,13 @@ namespace galloc { template class deferred_allocator { + deferred_heap& h; public: + + deferred_heap& heap() const { + return h; + } + using value_type = T; using pointer = deferred_ptr; using const_pointer = deferred_ptr; @@ -56,18 +69,20 @@ namespace galloc { using other = deferred_allocator; }; - deferred_allocator() noexcept + deferred_allocator(deferred_heap& h_) noexcept + : h{ h_ } { } template - deferred_allocator(deferred_allocator const&) noexcept + deferred_allocator(deferred_allocator const& that) noexcept + : h{ that.heap() } { } pointer allocate(size_type n) { - return heap().allocate(n); + return h.allocate(n); } void deallocate(pointer, size_type) noexcept @@ -82,13 +97,13 @@ namespace galloc { template void construct(U* p, Args&& ...args) { - heap().construct(p, std::forward(args)...); + h.construct(p, std::forward(args)...); } template void destroy(U* p) noexcept { - heap().destroy(p); + h.destroy(p); } size_type max_size() const noexcept @@ -98,31 +113,64 @@ namespace galloc { deferred_allocator select_on_container_copy_construction() const { - return *this; // TODO deferred_heap is currently not copyable + return *this; // deferred_heap is not copyable } using propagate_on_container_copy_assignment = std::false_type; using propagate_on_container_move_assignment = std::true_type; using propagate_on_container_swap = std::true_type; using is_always_equal = std::true_type; - - static deferred_heap& heap() { - return detail::global_deferred_heap(); - } }; template - inline bool operator==(deferred_allocator const&, deferred_allocator const&) noexcept + inline bool operator==(deferred_allocator const& a, deferred_allocator const& b) noexcept { - return true; + return &a.h == &b.h; } template - inline bool operator!=(deferred_allocator const& x, deferred_allocator const& y) noexcept + inline bool operator!=(deferred_allocator const& a, deferred_allocator const& b) noexcept { - return !(x == y); + return !(a == b); } + + //---------------------------------------------------------------------------- + // + // Convenience aliases for containers + // + //---------------------------------------------------------------------------- + + template + using deferred_vector = std::vector>; + + template + using deferred_list = std::list>; + + template> + using deferred_set = std::set>; + + template> + using deferred_multiset = std::multiset>; + + template> + using deferred_map = std::map>>; + + template> + using deferred_multimap = std::multimap>>; + + template, class E = std::equal_to> + using deferred_unordered_set = std::unordered_set>; + + template, class E = std::equal_to> + using deferred_unordered_multiset = std::unordered_multiset>; + + template, class E = std::equal_to> + using deferred_unordered_map = std::unordered_map>>; + + template, class E = std::equal_to> + using deferred_unordered_multimap = std::unordered_multimap>>; + } #endif diff --git a/deferred_heap.h b/deferred_heap.h index 29953e8..575e4c2 100644 --- a/deferred_heap.h +++ b/deferred_heap.h @@ -16,8 +16,8 @@ /////////////////////////////////////////////////////////////////////////////// -#ifndef GALLOC_DEFERRED_HEAP -#define GALLOC_DEFERRED_HEAP +#ifndef GCPP_DEFERRED_HEAP +#define GCPP_DEFERRED_HEAP #include "gpage.h" @@ -29,7 +29,7 @@ #include #include -namespace galloc { +namespace gcpp { // destructor contains a pointer and type-correct-but-erased dtor call. // (Happily, a noncapturing lambda decays to a function pointer, which @@ -235,6 +235,8 @@ namespace galloc { } void reset() noexcept { p = nullptr; /* leave mypage alone so we can assign again */ } + + deferred_heap* get_heap() const noexcept { return mypage ? mypage->myheap : nullptr; } }; // For non-roots (deferred_ptrs that are in the deferred heap), we'll additionally @@ -263,7 +265,7 @@ namespace galloc { // template dhpage(const Hint* /*--*/, size_t n, deferred_heap* heap) - : page{ std::max(sizeof(Hint) * n * 2.62, 4096 /*good general default*/), + : page{ std::max(sizeof(Hint) * n * 2.62, 8192 /*good general default*/), std::max(sizeof(Hint), 4) } , live_starts{ page.locations(), false } , myheap{ heap } @@ -279,7 +281,7 @@ namespace galloc { destructors dtors; bool is_destroying = false; - bool collect_before_expand = false; // TODO is this worth keeping? + bool collect_before_expand = false; public: @@ -462,17 +464,17 @@ namespace galloc { } int compare3(const deferred_ptr& that) const { return get() < that.get() ? -1 : get() == that.get() ? 0 : 1; }; - GALLOC_TOTALLY_ORDERED_COMPARISON(deferred_ptr); // maybe someday this will be default + GCPP_TOTALLY_ORDERED_COMPARISON(deferred_ptr); // maybe someday this will be default // Checked pointer arithmetic -- TODO this should probably go into a separate array_deferred_ptr type // deferred_ptr& operator+=(int offset) noexcept { #ifndef NDEBUG - assert(get() != nullptr + assert(get() != nullptr && "bad deferred_ptr arithmetic: can't perform arithmetic on a null pointer"); - auto this_info = find_dhpage_info(get()); + auto this_info = get_heap()->find_dhpage_info(get()); assert(this_info.page != nullptr && "corrupt non-null deferred_ptr, not pointing into deferred heap"); @@ -481,9 +483,9 @@ namespace galloc { && "corrupt non-null deferred_ptr, pointing to unallocated memory"); auto temp = get() + offset; - auto temp_info = find_dhpage_info(temp); + auto temp_info = get_heap()->find_dhpage_info(temp); - assert(this_info.page == temp_info.page + assert(this_info.page == temp_info.page && "bad deferred_ptr arithmetic: attempt to leave dhpage"); assert( @@ -492,7 +494,7 @@ namespace galloc { // which covers one-past-the-end of single-element allocations ( ( this_info.info.found == gpage::in_range_allocated_start - && (offset == 0 || offset == 1) + && (offset == -1 || offset == 0 || offset == 1) ) // otherwise this and temp must point into the same allocation // which is covered for arrays by the extra byte we allocated @@ -554,8 +556,8 @@ namespace galloc { assert(get() != nullptr && that.get() != nullptr && "bad deferred_ptr arithmetic: can't subtract pointers when one is null"); - auto this_info = find_dhpage_info(get()); - auto that_info = find_dhpage_info(that.get()); + auto this_info = get_heap()->find_dhpage_info(get()); + auto that_info = get_heap()->find_dhpage_info(that.get()); assert(this_info.page != nullptr && that_info.page != nullptr @@ -1046,9 +1048,10 @@ namespace galloc { inline void deferred_heap::debug_print() const { + std::cout << "\n*** heap snapshot [" << (void*)this << "] ***********************************************\n\n"; for (auto& pg : pages) { pg.page.debug_print(); - std::cout << " this page's deferred_ptrs.size() is " << pg.deferred_ptrs.size() << "\n"; + std::cout << "\n this page's deferred_ptrs.size() is " << pg.deferred_ptrs.size() << "\n"; for (auto& dp : pg.deferred_ptrs) { std::cout << " " << (void*)dp.p << " -> " << dp.p->get() << ", level " << dp.level << "\n"; diff --git a/gpage.h b/gpage.h index f564de1..f68b553 100644 --- a/gpage.h +++ b/gpage.h @@ -16,8 +16,8 @@ /////////////////////////////////////////////////////////////////////////////// -#ifndef GALLOC_GPAGE -#define GALLOC_GPAGE +#ifndef GCPP_GPAGE +#define GCPP_GPAGE #include "bitflags.h" @@ -31,7 +31,7 @@ #include //#endif -namespace galloc { +namespace gcpp { //---------------------------------------------------------------------------- // @@ -165,9 +165,10 @@ namespace galloc { // alignment of location needed by a T const auto locations_step = 1 + (alignof(T)-1) / min_alloc; - // # contiguous locations needed total (note: array allocations get one - // extra location as a simple way to support one-past-the-end arithmetic) - const auto locations_needed = (1 + (bytes_needed - 1) / min_alloc) + (num > 1 ? 1 : 0); + // # contiguous locations needed total + // note: as a simplification, for now we just add an extra location to every + // allocation as a simple way to support one-past-the-end arithmetic + const auto locations_needed = (1 + (bytes_needed - 1) / min_alloc) + 1; const auto end = locations() - locations_needed; // intentionally omitting "+1" here in order to keep the @@ -327,6 +328,11 @@ namespace galloc { std::cout << "--- total_size " << total_size << " --- min_alloc " << min_alloc << " --- " << (void*)base << " ---------------------------\n "; + for (std::size_t i = 0; i < 64; i += 2) { + std::cout << lowest_hex_digits_of_address(base + i*min_alloc,2)[0] << ' '; + if (i % 8 == 6) { std::cout << ' '; } + } + std::cout << "\n "; for (std::size_t i = 0; i < 64; i += 2) { std::cout << lowest_hex_digits_of_address(base + i*min_alloc) << ' '; if (i % 8 == 6) { std::cout << ' '; } diff --git a/gpage_allocator.h b/gpage_allocator.h index 33b153d..b17c947 100644 --- a/gpage_allocator.h +++ b/gpage_allocator.h @@ -21,7 +21,7 @@ #include "gpage.h" -namespace galloc { +namespace gcpp { //---------------------------------------------------------------------------- // diff --git a/test.cpp b/test.cpp index 56586bf..dd2bb47 100644 --- a/test.cpp +++ b/test.cpp @@ -23,7 +23,7 @@ //---------------------------------------------------------------------------- #include "deferred_allocator.h" -using namespace galloc; +using namespace gcpp; #include #include @@ -61,7 +61,7 @@ struct widget { operator long() const { return v; } int compare3(const widget& that) const { return v < that.v ? -1 : v == that.v ? 0 : 1; }; - GALLOC_TOTALLY_ORDERED_COMPARISON(widget); // maybe someday this will be default + GCPP_TOTALLY_ORDERED_COMPARISON(widget); // maybe someday this will be default }; struct node { @@ -194,9 +194,10 @@ void time_deferred_heap() { //---------------------------------------------------------------------------- void test_deferred_allocator() { + deferred_heap heap; using X = std::allocator_traits>; - deferred_allocator x; + deferred_allocator x(heap); auto p = X::allocate(x, 1); X::construct(x, p.get(), 1); @@ -212,9 +213,9 @@ void test_deferred_allocator() { //---------------------------------------------------------------------------- void test_deferred_allocator_set() { - auto& heap = deferred_allocator::heap(); + deferred_heap heap; - set, deferred_allocator> s; + auto s = deferred_set(heap); s.insert(2); s.insert(1); s.insert(3); @@ -247,10 +248,11 @@ void test_deferred_allocator_set() { //---------------------------------------------------------------------------- void test_deferred_allocator_vector() { - auto& heap = deferred_allocator::heap(); + deferred_heap heap; + heap.set_collect_before_expand(true); { - vector> v; + auto v = deferred_vector(heap); auto iter = v.begin(); auto old_capacity = v.capacity(); @@ -293,8 +295,7 @@ void test_deferred_allocator_vector() { //---------------------------------------------------------------------------- template -void time_set(const char* sz, int N) { - Set s; +void time_set(Set s, const char* sz, int N) { auto start = std::chrono::high_resolution_clock::now(); for (int i = 0; i < N; ++i) s.insert(i); @@ -305,9 +306,13 @@ void time_set(const char* sz, int N) { } void time_deferred_allocator_set() { + deferred_heap heap; + auto s = set(); + auto s2 = deferred_set(heap); + for (int i = 10; i < 11000; i *= 2) { - time_set>("set", i); - time_set, deferred_allocator>>("set", i); + time_set(s, "set", i); + time_set(s2, "deferred_set", i); //heap.debug_print(); //heap.collect(); //heap.debug_print(); @@ -315,8 +320,7 @@ void time_deferred_allocator_set() { } template -void time_vec(const char* sz, int N) { - Vec v; +void time_vec(Vec v, const char* sz, int N) { auto start = std::chrono::high_resolution_clock::now(); for (int i = 0; i < N; ++i) v.push_back(i); @@ -327,11 +331,13 @@ void time_vec(const char* sz, int N) { } void time_deferred_allocator_vector() { - auto& heap = deferred_allocator::heap(); + deferred_heap heap; + auto v = vector(); + auto v2 = deferred_vector(heap); for (int i = 10; i < 11000; i *= 2) { - //time_vec>("vector", i); - time_vec>>("vector", i); + //time_vec(v, "vector", i); + time_vec(v, "deferred_vector", i); //heap.debug_print(); //heap.collect(); //heap.debug_print(); @@ -364,7 +370,7 @@ void test_deferred_array() { } -int main() { +int _main() { //test_page(); //test_deferred_heap(); @@ -372,15 +378,17 @@ int main() { //test_deferred_allocator(); - test_deferred_allocator_set(); + //test_deferred_allocator_set(); //time_deferred_allocator_set(); - //test_deferred_allocator_vector(); + test_deferred_allocator_vector(); //time_deferred_allocator_vector(); //test_deferred_array(); //heap.collect(); //heap.debug_print(); + + return 0; } diff --git a/test_graph.cpp b/test_graph.cpp new file mode 100644 index 0000000..3557aa7 --- /dev/null +++ b/test_graph.cpp @@ -0,0 +1,159 @@ + +#include "deferred_allocator.h" +using namespace gcpp; + +#include +#include +#include +#include +#include +using namespace std; + + +class Counter { +public: + Counter() { ++count_; } + ~Counter() { --count_; } + static int count() { return count_; } +private: + static int count_; +}; + +int Counter::count_ = 0; + +class MyGraph1 { +public: + class Node : public Counter { + vector> children; + public: + void AddChild(const shared_ptr& node) { + children.push_back(node); + } + void RemoveChild(const shared_ptr& node) { + auto it = find(children.begin(), children.end(), node); + assert(it != children.end() && "trying to remove a child that was never added"); + children.erase(it); + } + }; + + void SetRoot(const shared_ptr& node) { + root = node; + } + +private: + shared_ptr root; +}; + + +static deferred_heap heap; + +class MyGraph2 { +public: + + class Node : public Counter { + deferred_vector> children{ heap }; + public: + void AddChild(const deferred_ptr& node) { + children.push_back(node); + } + void RemoveChild(const deferred_ptr& node) { + auto it = find(children.begin(), children.end(), node); + assert(it != children.end() && "trying to remove a child that was never added"); + //children.erase(it); + *it = nullptr; + } + }; + + void SetRoot(const deferred_ptr& node) { + root = node; + } + +private: + deferred_ptr root; +}; + + +/* +using MyGraph = MyGraph1; +auto make() { return make_shared(); } +auto clean() { } +/*/ +using MyGraph = MyGraph2; +auto make() { return heap.make(); } +auto clean() { + //heap.debug_print(); + heap.collect(); + //heap.debug_print(); +} +//*/ + +bool TestCase1() { + clean(); + using Node = MyGraph::Node; + MyGraph g; + { + auto a = make(); + g.SetRoot(a); + auto b = make(); + a->AddChild(b); + auto c = make(); + b->AddChild(c); + a->RemoveChild(b); + } + clean(); + return Counter::count() == 1; +} + +bool TestCase2() { + clean(); + using Node = MyGraph::Node; + MyGraph g; + { + auto a = make(); + g.SetRoot(a); + auto b = make(); + a->AddChild(b); + auto c = make(); + b->AddChild(c); + auto d = make(); + b->AddChild(d); + d->AddChild(b); + a->RemoveChild(b); + } + clean(); + return Counter::count() == 1; +} + +bool TestCase3() { + clean(); + using Node = MyGraph::Node; + MyGraph g; + { + auto a = make(); + g.SetRoot(a); + auto b = make(); + a->AddChild(b); + auto c = make(); + b->AddChild(c); + auto d = make(); + b->AddChild(d); + d->AddChild(b); + } + clean(); + return Counter::count() == 4; +} + +int main() { + cout.setf(ios::boolalpha); + + bool passed1 = TestCase1(); + cout << passed1 << endl; + + bool passed2 = TestCase2(); + cout << passed2 << endl; + + bool passed3 = TestCase3(); + cout << passed3 << endl; + + return 0; +} diff --git a/util.h b/util.h index c4d1b91..73c98ad 100644 --- a/util.h +++ b/util.h @@ -16,13 +16,13 @@ /////////////////////////////////////////////////////////////////////////////// -#ifndef GALLOC_UTIL -#define GALLOC_UTIL +#ifndef GCPP_UTIL +#define GCPP_UTIL #define _ITERATOR_DEBUG_LEVEL 0 // this is the right way to do totally ordered comparisons; TODO propose again in ISO -#define GALLOC_TOTALLY_ORDERED_COMPARISON(Type) \ +#define GCPP_TOTALLY_ORDERED_COMPARISON(Type) \ bool operator==(const Type& that) const { return compare3(that) == 0; } \ bool operator!=(const Type& that) const { return compare3(that) != 0; } \ bool operator< (const Type& that) const { return compare3(that) < 0; } \ @@ -32,7 +32,7 @@ bool operator>=(const Type& that) const { return compare3(that) >= 0; } #include -namespace galloc { +namespace gcpp { using byte = std::uint8_t;