diff --git a/src/lib/allocation-cluster.hpp b/src/lib/allocation-cluster.hpp index 6095fc85e..2b5a9c99c 100644 --- a/src/lib/allocation-cluster.hpp +++ b/src/lib/allocation-cluster.hpp @@ -29,14 +29,9 @@ ** as a whole. AllocationCluster implements memory management to ** support this usage pattern. ** - ** @note this file is organised in a way which doesn't bind the - ** client code to the memory manager implementation. Parts of the - ** interface depending on the usage situation are implemented using - ** templates, and thus need to be in the header. This way they can - ** exploit the type information available in call context. This - ** information is passed to generic implementation functions - ** defined in allocation-cluster.cpp . In a similar vein, the - ** AllocationCluster::MemoryManger is just forward declared. + ** @warning in rework 5/2024 — with the goal to simplify the logic, + ** remove all thread safety and make the implementation + ** usable as standard conformant allocator for STL. ** ** @see allocation-cluster-test.cpp ** @see builder::ToolFactory @@ -89,9 +84,22 @@ namespace lib { * Is this issue worth the hassle? //////////////////////////////TICKET #169 */ class AllocationCluster - : util::NonCopyable + : util::MoveOnly { + template + struct Allocator + { + using value_type = X; + + [[nodiscard]] X* allocate (size_t n) { return mother_->allot(n); } + void deallocate (X*, size_t) noexcept { /* rejoice */ } + + Allocator(AllocationCluster* m) : mother_{m} { } + private: + AllocationCluster* mother_; + }; + public: AllocationCluster (); ~AllocationCluster () noexcept; @@ -101,8 +109,14 @@ namespace lib { TY& create (ARGS&& ...args) { - TY* obj = new(allocation()) TY (std::forward (args)...); - return commit(obj); + return * new(allotMemory (sizeof(TY))) TY (std::forward (args)...); + } + + template + Allocator + getAllocator() + { + return Allocator{this}; } @@ -115,6 +129,24 @@ namespace lib { private: + /** + * portion out the requested amount of memory, + * possibly claiming a new pool block. + */ + void* + allotMemory (size_t bytes) + { + UNIMPLEMENTED ("actual memory management"); + } + + template + X* + allot (size_t cnt =1) + { + return static_cast (allotMemory (cnt * sizeof(X))); + } + + /** initiate an allocation for the given type */ template void* diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index c9d335bd5..c0152bb5c 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -80681,6 +80681,37 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
+ + + + + + + + + +

+ An der Stelle habe ich nicht weiter analysiert, sondern einfach Heap-Allokationen gemacht; der Grund seinerzeit war, daß Christian den »Mempool« überall einführen wollte — ein Ansatz, den ich grundsätzlich unterstützte, wenngleich auch seine Implementierung zu einfach war, und ich damit diesen use-Case nicht sauber realisieren konnte. Damit unterblieben aber weitere Überlegungen zum Allocation-Trend +

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

+ damals hatte ich als Vorbild den small-objects pool allocator von Alexandrescu im Kopf; deshalb habe ich auch »Familien« von Objekten vorgesehen — ohne jedoch zu klären, ob und wie sich daraus ein Amortisierungs-Effekt ergibt. Nach gründlicherer Überlegung erscheint mir das als ein Widerspruch im Konzept, denn diese small-objects-Pools laufen ja auf ein Tiling mit fortlaufend stattfindedenden Allokationen hinaus; das ist exakt das Gegenteil von dem, was mir hier vorschwebt. Damit würden die Einzelpools nur Administrations-Overhead verursachen, der seine Vorteile überhaupt nicht ausspielen kann; stattdessen sollte besser in Betracht gezogen werden, alles heterogen, so wie es kommt, in größere Blöcke zu packen. Das Tiling würde damit auf einem größeren Level stattfinden, und wäre in den Basis-Allocator verlagert... +

+ + +
+
+
@@ -80987,6 +81018,29 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
+ + + + + + + + + + + + + + + + + + + + + + + @@ -81067,7 +81121,292 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
+ + + + + + + + +

+ Sorge: +

+
    +
  • + ich erwarte ~10k Nodes im Modell +
  • +
  • + die meisten Nodes haben nur einen Vorläufer / Nachfolger +
  • +
+

+ ⟹ bläht die Storage um 30% auf +

+ + +
+ + + + + + +

+ Da der Heap-Allokator inzwischen ziemlich performant ist, könnte man damit durchkommen... +

+

+ Sorgen: +

+
    +
  • + wenn es dann doch Probleme gibt, ist der Zug bereits abgefahren (weil Allokationen überall im Code passieren) +
  • +
  • + auf anderen Plattformen kann die Performance vom Heap-Allocator ganz anders sein, und wir haben darauf keinerlei Einfluß +
  • +
+ + +
+
+ + + + + + + +

+ ...man verwendet nur speziell im produktiven Einsatz im Node-Graph  einen besonderen Allocator, der zwar den Destruktor aufruf, aber den Speicher nicht freigibt; alloziert wird immer in einen kompakten Block hinein, der dann auf der Basis der Prozeß-Kenntnis als Ganzes verworfen und neu verwendet wird. +

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

+ ...weil std::vector zwar bereits alles bietet, aber eingebettet in sehr komplexen Code — im Besonderen dürfte es schwierig werden, das Thema on-demand-growth vs non-copyable zu umschiffen +

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

+ ...das heißt, ich gehe mal davon aus, daß ich mit einer einzigen, dedizierten Implementierung erst mal den aktuellen Bedarf decken kann; daraus könnte allerdings später immer noch ein Concept gemacht werden, welches dann alternativ auch durch ScopedCollection oder durch eine embedded-storage-Lösung erfüllt werden kann. +

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

+ heterogene Allokation in eine Sequenz größerer Blöcke; keinerlei de-Allokation und kein Locking +

+ + +
+
+ + + + + + +

+ ...sie ist ja fertig und getestet, und wartet seit Jahren auf ihren Einsatz; allerdings wäre ein solches Vorgehen erklärungsbedürftig +

+
    +
  • + man tut so, als wäre etwas in Ordnung, das nicht in Ordnung ist +
  • +
  • + die bestehende Implementierung ist sogar maximal-dämlich +
  • +
  • + und bietet zudem keinen guten Pfad zur Weiterentwicklung, sondern müßte irgendwann ersetzt werden +
  • +
  • + allerdings spielt das Thema vermutlich lange Zeit gar keine Rolle (solange wir nicht sehr große Modelle bauen) +
  • +
+ + +
+
+ + + + + + +

+ ...wenn man schon die bestehenden Implementierung nutzt (wohl wissend, daß ihre inhärenten Probleme erst mal nicht relevant sind), dann kann man genausogut ganz auf blöd sich auf den KISS-Standpunkt stellen und einfach Heap-Allokationen machen, denn die sind heutzutage verdammt effizient geworden +

+ + +
+
+ + + + + + +

+ ...und alles das läuft auf weitere technische Schulden hinaus +

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

+ ...die allesamt mit dem Model + Player zu tun haben; einzige externe Verkoppelung ist der LinkedElements_test, und auch dieser stellt explizit einen Vorgriff auf die Verwendung im low-level-Model dar. +

+ + +
+
+ + + + + + +

+ ...da der davon abhängende Code effektiv nur compilierbar ist, aber nicht lauffähig +

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

+ let it crash — wenn tatsächlich eine Exception fliegt, ist es ziemlich wahrscheinlich, daß der ganze Cluster sowiso weggeworfen wird; wenn nicht, dann akzeptieren wir einfach toten Speicher. +

+ + +
+
+ + + + + + +

+ die Bedeutung ist geringer geworden +

+ + +
+ + + + + +

+ seinerzeit habe ich im AllocationCluster etwas gesehen, daß pervasiv überall im Code verwendet wird, analog zum Mempool. Inzwischen stehe ich auf dem Standpunkt, daß für die meisten Allokationen der Standard-Heap-Allokator sowiso gut genug ist (oder man nutzt ohnehin den Stack oder eine statische Variable); spezielle Allokatoren sind nach meinem heutigen Verständnis nur noch sinnvoll, wenn sie extrem spezifisch sind +

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

+ ...oder zumindest könnte man ein limitiertes Teil-Konzept umsetzen; mir fällt auf, daß diverse Methoden im Standard-Allocator inzwischen durch Traits ersetzt wurden. +

+ + +
+ + + + + + + + + +
+ + + + + + + + + + + + + @@ -81100,7 +81439,8 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + + @@ -81317,6 +81657,35 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -81326,6 +81695,10 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
+ + + + @@ -81378,7 +81751,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + @@ -81387,6 +81760,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200

+
@@ -81471,12 +81845,19 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- +
- + + + + + + + + @@ -121028,7 +121409,11 @@ std::cout << tmpl.render({"what", "World"}) << s - + + + + + @@ -125755,6 +126140,99 @@ std::cout << tmpl.render({"what", "World"}) << s + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ viel Geplänkel cool-getue; tatsächlich hat er schon einen eigenständigen Gedanken, braucht aber sehr lange, ihn auszuformulieren; und zu den schwierigen praktischen Fragen mit STL-Containern sagt er gar nichts +

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

+ dann skizziert er alle wesentlichen Standard-Allocator-Patterns als  composable allocators +

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

+ Begründung: es ist sinnlos, weil das einzige, was man dabei machen kann, ist Fehler machen. Der Standard ist extrem genau und elaboriert für dieses Thema, und wenn man sich an wirklich alle Vorgaben hält, hat man praktisch keinen Spielraum mehr.... +

+ +
+
+ + + + + + +

+ ...bei Bedarf kann man dort sogar einige optionale Methoden zusätzlich implementieren, z.B. construct (und das wird dann auch verwendet, anstatt der Standard-Implementierung in den Traits) +

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