diff --git a/src/mem/slab.h b/src/mem/slab.h index 860bb6f..d64eaf3 100644 --- a/src/mem/slab.h +++ b/src/mem/slab.h @@ -32,8 +32,7 @@ namespace snmalloc static SNMALLOC_FAST_PATH void alloc_new_list(void*& bumpptr, FreeListIter& fast_free_list, size_t rsize) { - void* slab_end = - pointer_align_up(pointer_offset(bumpptr, rsize)); + void* slab_end = pointer_align_up(pointer_offset(bumpptr, 1)); FreeListBuilder b; SNMALLOC_ASSERT(b.empty()); diff --git a/src/mem/superslab.h b/src/mem/superslab.h index d689d09..ea35a71 100644 --- a/src/mem/superslab.h +++ b/src/mem/superslab.h @@ -4,14 +4,32 @@ #include "allocslab.h" #include "metaslab.h" +#include #include namespace snmalloc { + /** + * Superslabs are, to first approximation, a `CHUNK_SIZE`-sized and -aligned + * region of address space, internally composed of a header (a `Superslab` + * structure) followed by an array of `Slab`s, each `SLAB_SIZE`-sized and + * -aligned. Each active `Slab` holds an array of identically sized + * allocations strung on an invasive free list, which is lazily constructed + * from a bump-pointer allocator (see `Metaslab::alloc_new_list`). + * + * In order to minimize overheads, Slab metadata is held externally, in + * `Metaslab` structures; all `Metaslab`s for the Slabs within a Superslab are + * densely packed within the `Superslab` structure itself. Moreover, as the + * `Superslab` structure is typically much smaller than `SLAB_SIZE`, a "short + * Slab" is overlaid with the `Superslab`. This short Slab can hold only + * allocations that are smaller than the `SLAB_SIZE - sizeof(Superslab)` + * bytes; see `Superslab::is_short_sizeclass`. The Metaslab state for a short + * slabs is constructed in a way that avoids branches on fast paths; + * effectively, the object slots that overlay the `Superslab` at the start are + * omitted from consideration. + */ class Superslab : public Allocslab { - // This is the view of a 16 mb superslab when it is being used to allocate - // 64 kb slabs. private: friend DLList; @@ -73,8 +91,17 @@ namespace snmalloc static bool is_short_sizeclass(sizeclass_t sizeclass) { - constexpr sizeclass_t h = size_to_sizeclass_const(sizeof(Superslab)); - return sizeclass <= h; + static_assert(SLAB_SIZE > sizeof(Superslab), "Meta data requires this."); + /* + * size_to_sizeclass_const rounds *up* and returns the smallest class that + * could contain (and so may be larger than) the free space available for + * the short slab. While we could detect the exact fit case and compare + * `<= h` therein, it's simpler to just treat this class as a strict upper + * bound and only permit strictly smaller classes in short slabs. + */ + constexpr sizeclass_t h = + size_to_sizeclass_const(SLAB_SIZE - sizeof(Superslab)); + return sizeclass < h; } void init(RemoteAllocator* alloc) diff --git a/src/test/measuretime.h b/src/test/measuretime.h index 250191d..a67a65e 100644 --- a/src/test/measuretime.h +++ b/src/test/measuretime.h @@ -3,12 +3,19 @@ #include #include #include +#include -#define DO_TIME(name, code) \ - { \ - auto start__ = std::chrono::high_resolution_clock::now(); \ - code auto finish__ = std::chrono::high_resolution_clock::now(); \ - auto diff__ = finish__ - start__; \ - std::cout << name << ": " << std::setw(12) << diff__.count() << " ns" \ - << std::endl; \ +class MeasureTime : public std::stringstream +{ + std::chrono::time_point start = + std::chrono::high_resolution_clock::now(); + +public: + ~MeasureTime() + { + auto finish = std::chrono::high_resolution_clock::now(); + auto diff = finish - start; + std::cout << str() << ": " << std::setw(12) << diff.count() << " ns" + << std::endl; } +}; \ No newline at end of file diff --git a/src/test/perf/contention/contention.cc b/src/test/perf/contention/contention.cc index 7506052..bcc66d3 100644 --- a/src/test/perf/contention/contention.cc +++ b/src/test/perf/contention/contention.cc @@ -1,4 +1,3 @@ -#include "test/measuretime.h" #include "test/opt.h" #include "test/setup.h" #include "test/usage.h" diff --git a/src/test/perf/external_pointer/externalpointer.cc b/src/test/perf/external_pointer/externalpointer.cc index e750922..0a88ea5 100644 --- a/src/test/perf/external_pointer/externalpointer.cc +++ b/src/test/perf/external_pointer/externalpointer.cc @@ -64,7 +64,9 @@ namespace test #endif setup(r, alloc); - DO_TIME("External pointer queries ", { + { + MeasureTime m; + m << "External pointer queries "; for (size_t i = 0; i < iterations; i++) { size_t rand = (size_t)r.next(); @@ -77,7 +79,7 @@ namespace test if (calced_external != external_ptr) abort(); } - }); + } teardown(alloc); } diff --git a/src/test/perf/low_memory/low-memory.cc b/src/test/perf/low_memory/low-memory.cc index 9bdd90b..22ba0ba 100644 --- a/src/test/perf/low_memory/low-memory.cc +++ b/src/test/perf/low_memory/low-memory.cc @@ -1,6 +1,5 @@ #include #include -#include #include #include #include diff --git a/src/test/perf/singlethread/singlethread.cc b/src/test/perf/singlethread/singlethread.cc index 0088028..61dee2f 100644 --- a/src/test/perf/singlethread/singlethread.cc +++ b/src/test/perf/singlethread/singlethread.cc @@ -10,54 +10,55 @@ void test_alloc_dealloc(size_t count, size_t size, bool write) { auto* alloc = ThreadAlloc::get(); - DO_TIME( - "Count: " << std::setw(6) << count << ", Size: " << std::setw(6) << size - << ", ZeroMem: " << (zero_mem == YesZero) << ", Write: " << write, + { + MeasureTime m; + m << "Count: " << std::setw(6) << count << ", Size: " << std::setw(6) + << size << ", ZeroMem: " << (zero_mem == YesZero) << ", Write: " << write; + + std::unordered_set set; + + // alloc 1.5x objects + for (size_t i = 0; i < ((count * 3) / 2); i++) { - std::unordered_set set; + void* p = alloc->alloc(size); + SNMALLOC_CHECK(set.find(p) == set.end()); - // alloc 1.5x objects - for (size_t i = 0; i < ((count * 3) / 2); i++) - { - void* p = alloc->alloc(size); - SNMALLOC_CHECK(set.find(p) == set.end()); + if (write) + *(int*)p = 4; - if (write) - *(int*)p = 4; + set.insert(p); + } - set.insert(p); - } + // free 0.25x of the objects + for (size_t i = 0; i < (count / 4); i++) + { + auto it = set.begin(); + void* p = *it; + alloc->dealloc(p, size); + set.erase(it); + SNMALLOC_CHECK(set.find(p) == set.end()); + } - // free 0.25x of the objects - for (size_t i = 0; i < (count / 4); i++) - { - auto it = set.begin(); - void* p = *it; - alloc->dealloc(p, size); - set.erase(it); - SNMALLOC_CHECK(set.find(p) == set.end()); - } + // alloc 1x objects + for (size_t i = 0; i < count; i++) + { + void* p = alloc->alloc(size); + SNMALLOC_CHECK(set.find(p) == set.end()); - // alloc 1x objects - for (size_t i = 0; i < count; i++) - { - void* p = alloc->alloc(size); - SNMALLOC_CHECK(set.find(p) == set.end()); + if (write) + *(int*)p = 4; - if (write) - *(int*)p = 4; + set.insert(p); + } - set.insert(p); - } - - // free everything - while (!set.empty()) - { - auto it = set.begin(); - alloc->dealloc(*it, size); - set.erase(it); - } - }); + // free everything + while (!set.empty()) + { + auto it = set.begin(); + alloc->dealloc(*it, size); + set.erase(it); + } + } current_alloc_pool()->debug_check_empty(); }