diff --git a/src/lib/allocation-cluster.hpp b/src/lib/allocation-cluster.hpp index 3bf606879..c5c4555e7 100644 --- a/src/lib/allocation-cluster.hpp +++ b/src/lib/allocation-cluster.hpp @@ -352,7 +352,11 @@ namespace lib { { using Base = AllocationPolicy; using Bucket = typename Base::Bucket; - + + /** @warning allocation size is severely limited in AllocationCluster. */ + size_t static constexpr ALLOC_LIMIT = AllocationCluster::max_size(); + + Policy (AllocationCluster& clu) : AllocationPolicy (clu.getAllocator()) { } diff --git a/src/lib/several-builder.hpp b/src/lib/several-builder.hpp index 44adf2c92..5e1aa51b0 100644 --- a/src/lib/several-builder.hpp +++ b/src/lib/several-builder.hpp @@ -166,6 +166,12 @@ namespace lib { return req; } + size_t inline constexpr + alignRes (size_t alignment) + { + return positiveDiff (alignment, alignof(void*)); + } + template class ALO> class ElementFactory @@ -200,8 +206,7 @@ namespace lib { REQUIRE (cnt); REQUIRE (spread); size_t storageBytes = Bucket::storageOffset + cnt*spread; - if (alignment > alignof(void*)) // over-aligned data => reserve for alignment padding - storageBytes += (alignment - alignof(void*)); + storageBytes += alignRes (alignment); // over-aligned data => reserve for alignment padding // Step-1 : acquire the raw storage buffer std::byte* loc = AlloT::allocate (baseAllocator(), storageBytes); ENSURE (0 == size_t(loc) % alignof(void*)); @@ -278,6 +283,9 @@ namespace lib { using Fac::Fac; // pass-through ctor + /** by default assume that memory is practically unlimited... */ + size_t static constexpr ALLOC_LIMIT = size_t(-1) / sizeof(E); + bool canExpand(Bucket*, size_t){ return false; } Bucket* @@ -533,16 +541,20 @@ namespace lib { {// grow into exponentially expanded new allocation if (spread > Coll::spread()) cnt = max (cnt, buffSiz / Coll::spread()); // retain reserve + size_t overhead = sizeof(Bucket) + alignRes(alignof(E)); size_t safetyLim = LUMIERA_MAX_ORDINAL_NUMBER * Coll::spread(); - size_t expandAlloc = min (safetyLim + size_t expandAlloc = min (positiveDiff (min (safetyLim + ,POL::ALLOC_LIMIT) + ,overhead) ,max (2*buffSiz, cnt*spread)); + // round down to an even number of elements + size_t newCnt = expandAlloc / spread; + expandAlloc = newCnt * spread; if (expandAlloc < demand) throw err::State{_Fmt{"Storage expansion for Several-collection " "exceeds safety limit of %d bytes"} % safetyLim ,LERR_(SAFETY_LIMIT)}; // allocate new storage block... - size_t newCnt = expandAlloc / spread; - if (newCnt * spread < expandAlloc) ++newCnt; Coll::data_ = POL::realloc (Coll::data_, newCnt,spread); } ENSURE (Coll::data_); diff --git a/tests/library/several-builder-test.cpp b/tests/library/several-builder-test.cpp index 173e42c21..53d2c8b6d 100644 --- a/tests/library/several-builder-test.cpp +++ b/tests/library/several-builder-test.cpp @@ -556,7 +556,8 @@ namespace test{ /** @test TODO demonstrate integration with a custom allocator * - use the TrackingAllocator to verify balanced handling * of the underlying raw memory allocations - * @todo WIP 6/24 🔁 define ⟶ 🔁 implement + * - use an AllocationCluster instance to manage the storage + * @todo WIP 6/24 🔁 define ⟶ ✔ implement */ void check_CustomAllocator() @@ -610,38 +611,67 @@ namespace test{ { auto builder = makeSeveral() .withAllocator(clu) - .reserve(4) - .fillElm(4); + .reserve(4) // use a limited pre-reservation + .fillElm(4); // fill all the allocated space with 4 new elements size_t buffSiz = sizeof(Dummy) * builder.capacity(); size_t headerSiz = sizeof(ArrayBucket); expectedAlloc = headerSiz + buffSiz; SHOW_EXPR(expectedAlloc) SHOW_EXPR(builder.size()) + CHECK (4 == builder.size()); SHOW_EXPR(builder.capacity()) + CHECK (4 == builder.capacity()); SHOW_EXPR(clu.numExtents()) + CHECK (1 == clu.numExtents()); // AllocationCluster has only opened one extent thus far SHOW_EXPR(clu.numBytes()) - builder.append (Dummy{23}); + CHECK (expectedAlloc == clu.numBytes()); // and the allocated space matches the demand precisely + + builder.append (Dummy{23}); // now request to add just one further element SHOW_EXPR(builder.capacity()) + CHECK (8 == builder.capacity()); // ...which causes the builder to double up the reserve capacity buffSiz = sizeof(Dummy) * builder.capacity(); + expectedAlloc = headerSiz + buffSiz; SHOW_EXPR(buffSiz) SHOW_EXPR(buffSiz + expectedAlloc) SHOW_EXPR(buffSiz + headerSiz) SHOW_EXPR(builder.size()) SHOW_EXPR(builder.capacity()) SHOW_EXPR(clu.numExtents()) + CHECK (1 == clu.numExtents()); // However, AllocationCluster was able to adjust allocation in-place SHOW_EXPR(clu.numBytes()) + CHECK (expectedAlloc == clu.numBytes()); // and thus the new increased buffer is still placed in the first extent + // now perform another unrelated allocation Dummy& extraDummy = clu.create(55); SHOW_EXPR(clu.numExtents()) + CHECK (1 == clu.numExtents()); SHOW_EXPR(clu.numBytes()) - builder.reserve(9); + CHECK (clu.numBytes() > expectedAlloc + sizeof(Dummy)); // but now we've used some further space behind that point + + builder.reserve(9); // ...which means that the AllocationCluster can no longer adjust dynamically SHOW_EXPR(builder.size()) + CHECK (5 == builder.size()); // .....because this is only possible on the latest allocation opened SHOW_EXPR(builder.capacity()) + CHECK (9 <= builder.capacity()); // And while we still got out increased capacity, SHOW_EXPR(clu.numExtents()) + CHECK (2 == clu.numExtents()); // this was only possible by wasting space and copying into a new extent SHOW_EXPR(clu.numBytes()) - } + buffSiz = sizeof(Dummy) * builder.capacity(); + expectedAlloc = headerSiz + buffSiz; + CHECK (expectedAlloc <= AllocationCluster::max_size()); + CHECK (clu.numBytes() == AllocationCluster::max_size() + + expectedAlloc); + + elms = builder.build(); // Note: assigning to the existing front-end (which is storage agnostic) + CHECK (5 == elms.size()); + CHECK (23 == elms.back().getVal()); + CHECK (55 == extraDummy.getVal()); + } // Now the Builder and the ExtraDummy is gone... + CHECK (5 == elms.size()); // while all created elements are still there, sitting in the Allocationcluster + CHECK (23 == elms.back().getVal()); + CHECK (2 == clu.numExtents()); } }; diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index 8b9ea2bd3..bb3e25099 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -83011,6 +83011,42 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
+ + + + + + + + + +

+ gefunden in: new_allocator.h, Line 130 +

+ +
+ +
+
+ + + + + + +

+ zieht noch einen internen Admin-Overhead ab von der Extent-Size +

+ +
+
+
+ + + + + + @@ -83419,8 +83455,8 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - + + @@ -83430,6 +83466,11 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
+ + + + + @@ -83631,8 +83672,8 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - + + @@ -83695,9 +83736,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - - +

...ich hab diese Mechanik ganz bewußt janz domm implementiert — also muß praktisch alle Info von oben kommen @@ -84181,9 +84220,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - - +

...da AllocationCluster eine starke Größenbeschränkgung hat @@ -84194,18 +84231,17 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - - - + + + + + - - - +

denn das eine zusätzliche Element würde locker reinpassen — und zudem sollte ja nun der Block in den nächsten Extent migrieren @@ -84224,16 +84260,13 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - - +

einmal aus emplaceNewElm() und einmal aus reserve()

- -
+
@@ -84242,9 +84275,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - - +

das ist es nämlich, worauf die Implementierung hinausläuft, und dieser Grebrauch ist auch naheliegend und korrekt, sofern es sich um das Einfügen neuer Datenelemente handelt. Insofern muß sich der andere Gebrauch in reserve() daran anpassen @@ -84256,7 +84287,8 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + + @@ -84270,9 +84302,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - - +

...daß da immer noch high-level-Code darüber liegt, der eine inhaltliche Prüfung gemacht hat, so daß hier nur noch ein Konsistenzcheck vonnöten ist @@ -84283,9 +84313,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - - +

...aber realistisch betrachtet ist der AllocationCluster viel zu einfach zu verwenden, als daß man da überhaupt daran denkt, noch explizit zu prüfen — und durch den standard-Allocator-Adapter gibt es unvermeidbar einen direkten Eingang über allot(). @@ -84311,9 +84339,24 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
+ + + + + - - + + + + + + + + + + + + @@ -84777,9 +84820,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - - +

versteckte Inkonsistenz mit der Deleter-Behandlung aufgedeckt @@ -84793,26 +84834,12 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - - - - - -

- braucht ein API zum Übernehmen eines bereits konstruierten Objekts -

- -
- - - -

- zunächst hatte ich AllocationCluster dafür definiert, daß die Objekte direkt im Cluster-Speicher erzeugt werden; hier aber liegt ein valider Fall vor, in dem das Objekt bereits erzeugt wurde (nämlich über den Builder), und von dort per move/copy in den Cluster übernommen werden soll -

- -
+ + + + +
@@ -85087,8 +85114,6 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- -