From 7d5242f6040561c58409fac88add3b09187f5176 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Mon, 4 Dec 2023 03:15:46 +0100 Subject: [PATCH] Chain-Load: remove excess template argument The number of nodes was just defined as template argument to get a cheap implementation through std::array... But actually this number of nodes is ''not a characteristics of the type;'' we'd end up with a distinct JobFunctor type for each different test size, which is plain nonsensical. Usage analysis reveals, now that the implementation is ''basically complete,'' that all of the topology generation and statistic calculation code does not integrate deeply with the node storage, but rather just iterates over all nodes and uses the ''first'' and ''last'' node. This can actually be achieved very easy with a heap-allocated plain array, relying on the magic of lib::IterExplorer for all iteration and transformation. --- tests/vault/gear/test-chain-load-test.cpp | 24 +- tests/vault/gear/test-chain-load.hpp | 70 +++--- wiki/thinkPad.ichthyo.mm | 271 ++++++++++++++++++---- 3 files changed, 277 insertions(+), 88 deletions(-) diff --git a/tests/vault/gear/test-chain-load-test.cpp b/tests/vault/gear/test-chain-load-test.cpp index d72ef9f1c..0975694f0 100644 --- a/tests/vault/gear/test-chain-load-test.cpp +++ b/tests/vault/gear/test-chain-load-test.cpp @@ -54,8 +54,8 @@ namespace test { namespace { // shorthands and parameters for test... /** shorthand for specific parameters employed by the following tests */ - using ChainLoad32 = TestChainLoad<32,16>; - using Node = ChainLoad32::Node; + using ChainLoad16 = TestChainLoad<16>; + using Node = ChainLoad16::Node; auto isStartNode = [](Node& n){ return isStart(n); }; auto isInnerNode = [](Node& n){ return isInner(n); }; auto isExitNode = [](Node& n){ return isExit(n); }; @@ -106,8 +106,6 @@ namespace test { void verify_Node() { - using Node = TestChainLoad<>::Node; - Node n0; // Default-created empty Node CHECK (n0.hash == 0); CHECK (n0.level == 0); @@ -194,7 +192,7 @@ namespace test { void verify_Topology() { - auto graph = ChainLoad32{} + auto graph = ChainLoad16{32} .buildToplolgy(); CHECK (graph.topLevel() == 31); @@ -249,7 +247,7 @@ namespace test { void showcase_Expansion() { - ChainLoad32 graph; + ChainLoad16 graph{32}; // moderate symmetrical expansion with 40% probability and maximal +2 links graph.expansionRule(graph.rule().probability(0.4).maxVal(2)) @@ -289,7 +287,7 @@ namespace test { // if the generation is allowed to run for longer, // while more constrained in width... - TestChainLoad<256,8> gra_2; + TestChainLoad<8> gra_2{256}; gra_2.expansionRule(gra_2.rule().probability(0.4).maxVal(2).shuffle(23)) .buildToplolgy() // .printTopologyDOT() @@ -319,7 +317,7 @@ namespace test { void showcase_Reduction() { - ChainLoad32 graph; + ChainLoad16 graph{32}; // expand immediately at start and then gradually reduce / join chains graph.expansionRule(graph.rule_atStart(8)) @@ -389,7 +387,7 @@ namespace test { void showcase_SeedChains() { - ChainLoad32 graph; + ChainLoad16 graph{32}; // randomly start new chains, to be carried-on linearly graph.seedingRule(graph.rule().probability(0.2).maxVal(3).shuffle()) @@ -448,7 +446,7 @@ namespace test { void showcase_PruneChains() { - ChainLoad32 graph; + ChainLoad16 graph{32}; // terminate chains randomly graph.pruningRule(graph.rule().probability(0.2)) @@ -589,7 +587,7 @@ namespace test { void showcase_StablePattern() { - TestChainLoad<256> graph; + ChainLoad16 graph{256}; // This example creates a repetitive, non-expanding stable pattern // comprised of four small graph segments, generated interleaved @@ -852,7 +850,7 @@ namespace test { void verify_reseed_recalculate() { - ChainLoad32 graph; + ChainLoad16 graph{32}; graph.expansionRule(graph.rule().probability(0.8).maxVal(1)) .pruningRule(graph.rule().probability(0.6)) .buildToplolgy(); @@ -961,7 +959,7 @@ namespace test { p2.level = 1; e.level = 2; - RandomChainCalcFunctor<32,16> chainJob{nodes[0]}; + RandomChainCalcFunctor<16> chainJob{nodes[0]}; Job job0{chainJob ,chainJob.encodeNodeID(0) ,chainJob.encodeLevel(0)}; diff --git a/tests/vault/gear/test-chain-load.hpp b/tests/vault/gear/test-chain-load.hpp index 7c9b06a09..6d4097f47 100644 --- a/tests/vault/gear/test-chain-load.hpp +++ b/tests/vault/gear/test-chain-load.hpp @@ -155,7 +155,7 @@ namespace test { * @tparam maxFan maximal fan-in/out from a node, also limits maximal parallel strands. * @see TestChainLoad_test */ - template + template class TestChainLoad : util::MoveOnly { @@ -276,33 +276,40 @@ namespace test { private: using NodeTab = typename Node::Tab; - using NodeStorage = std::array; + using NodeIT = lib::RangeIter; - std::unique_ptr nodes_; + std::unique_ptr nodes_; + size_t numNodes_; Rule seedingRule_ {}; Rule expansionRule_{}; Rule reductionRule_{}; Rule pruningRule_ {}; + Node* frontNode() { return &nodes_[0]; } + Node* afterNode() { return &nodes_[numNodes_]; } + Node* backNode() { return &nodes_[numNodes_-1];} + public: - TestChainLoad() - : nodes_{new NodeStorage} - { } + explicit + TestChainLoad(size_t nodeCnt =DEFAULT_SIZ) + : nodes_{new Node[nodeCnt]} + , numNodes_{nodeCnt} + { + REQUIRE (1 < nodeCnt); + } - size_t size() const { return nodes_->size(); } - size_t topLevel() const { return nodes_->back().level; } - size_t getSeed() const { return nodes_->front().hash; } - size_t getHash() const { return nodes_->back().hash; } + size_t size() const { return afterNode() - frontNode(); } + size_t topLevel() const { return unConst(this)->backNode()->level; } + size_t getSeed() const { return unConst(this)->frontNode()->hash; } + size_t getHash() const { return unConst(this)->backNode()->hash; } /////////////////////TODO combine hash of all exit nodes - using NodeIter = decltype(lib::explore (std::declval())); - - NodeIter + auto allNodes() { - return lib::explore (*nodes_); + return lib::explore (NodeIT{frontNode(),afterNode()}); } auto allNodePtr() @@ -311,7 +318,7 @@ namespace test { } /** @return the node's index number, based on its storage location */ - size_t nodeID(Node const* n){ return size_t(n - &nodes_->front()); }; + size_t nodeID(Node const* n){ return n - frontNode(); }; size_t nodeID(Node const& n){ return nodeID (&n); }; @@ -406,7 +413,7 @@ namespace test { NodeTab a,b, // working data for generation *curr{&a}, // the current set of nodes to carry on *next{&b}; // the next set of nodes connected to current - Node* node = &nodes_->front(); + Node* node = frontNode(); size_t level{0}; // transient snapshot of rules (non-copyable, once engaged) @@ -417,7 +424,7 @@ namespace test { // prepare building blocks for the topology generation... auto moreNext = [&]{ return next->size() < maxFan; }; - auto moreNodes = [&]{ return node < &nodes_->back(); }; + auto moreNodes = [&]{ return node < backNode(); }; auto spaceLeft = [&]{ return moreNext() and moreNodes(); }; auto addNode = [&](size_t seed =0) { @@ -480,7 +487,7 @@ namespace test { ENSURE (not next->empty()); ++level; } - ENSURE (node == &nodes_->back()); + ENSURE (node == backNode()); // connect ends of all remaining chains to top-Node node->clear(); node->level = level; @@ -502,7 +509,7 @@ namespace test { TestChainLoad&& setSeed (size_t seed = rand()) { - nodes_->front().hash = seed; + frontNode()->hash = seed; return move(*this); } @@ -611,8 +618,8 @@ namespace test { * and pruning._ The RandomDraw component used to implement those rules provides a builder-DSL * and accepts λ-bindings in various forms to influence mapping of Node hash into result parameters. */ - template - class TestChainLoad::NodeControlBinding + template + class TestChainLoad::NodeControlBinding : public std::function { protected: @@ -631,8 +638,8 @@ namespace test { static double guessHeight (size_t level) - { // heuristic guess, typically too low - double expectedHeight = max (1u, numNodes/maxFan); + { // heuristic guess for a »fully stable state« + double expectedHeight = 2*maxFan; return level / expectedHeight; } @@ -662,7 +669,8 @@ namespace test { }; /** allow rules additionally involving the height of the graph, - * which also represents time. 1.0 refers to (guessed) _full height. */ + * which also represents time. 1.0 refers to _stable state generation,_ + * guessed as height Level ≡ 2·maxFan . */ template struct Adaptor { @@ -879,9 +887,9 @@ namespace test { * - the weight centre of this category members * - the weight centre of according to density */ - template + template inline Statistic - TestChainLoad::computeGraphStatistics() + TestChainLoad::computeGraphStatistics() { auto totalLevels = uint(topLevel()); auto classify = prepareEvaluaions(); @@ -962,9 +970,9 @@ namespace test { * *no reliable indication of subgraphs*. The `SEGS` statistics may be misleading, * since these count only completely severed and restarted graphs. */ - template - inline TestChainLoad&& - TestChainLoad::printTopologyStatistics() + template + inline TestChainLoad&& + TestChainLoad::printTopologyStatistics() { cout << "INDI: cnt frac ∅pS ∅pL ∅pLW γL◆ γLW◆ γL⬙ γLW⬙\n"; _Fmt line{"%4s: %3d %3.0f%% %5.1f %5.2f %4.2f %4.2f %4.2f %4.2f %4.2f\n"}; @@ -1000,11 +1008,11 @@ namespace test { /* ========= Render Job generation and Scheduling ========= */ - template + template class RandomChainCalcFunctor : public NopJobFunctor { - using Node = typename TestChainLoad::Node; + using Node = typename TestChainLoad::Node; public: RandomChainCalcFunctor(Node& startNode) diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index ddaf8016f..966c40164 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -79737,9 +79737,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - - +

⟹ man könnte Argumente encodieren @@ -79763,9 +79761,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - - +

denn damit wäre nicht nur die Segmentation zu erhalten, sondern auch der Play-Prozess-Translator-Record — solange bis kein Job mehr „anrufen“ kann @@ -79783,9 +79779,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - - +

Fazit: nein und YAGNI @@ -99371,9 +99365,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - - +

stattet jede seed-Node sofort mit einem festen Branch-Faktor aus @@ -100350,9 +100342,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - - +

Stand: noch nicht volltändig umgesetzt @@ -100382,9 +100372,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - - +

für den eigentlichen Aufruf genügt ein Node* @@ -100397,9 +100385,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - - +

....und das hängt vom Template-Parameter der TestChainLoad ab (size_t maxFan) @@ -100421,9 +100407,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - - +

weiß ja noch nicht, wie das für ProcNode implementiert wird @@ -100439,9 +100423,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - - +

formal-Logisch entspräche der Node-Index dem srcRef-Parameter @@ -100489,9 +100471,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - - +

also nicht um den gesamten Playback-Prozeß. Es müssen keinerlei Deadline-Planungen gemacht werden; das kommt alles Fix per Test-Setup @@ -100502,9 +100482,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - - +

das könnten Varianten des Test-Setup sein; weiß noch nicht was hier gebraucht wird. Rein intuitiv würde ich erst mal nur nach dem Level vorgehen @@ -100514,9 +100492,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - - +

das paßt doch hervorragend; LevelNr ≡ nominal time @@ -100596,16 +100572,13 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - - +

wir wissen, daß es keine concurrent Aufrufe geben wird, und auch Allokation stellt kein Problem dar; wir können einfach State im »Test-Rahmen« liegen lassen

- -
+
@@ -100624,6 +100597,218 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
+ + + + + + + +

+ die Node-Anzahl ist inhaltlich keine Typ-Eigenschaft +

+ +
+ + + +

+ ...sie ist rein aus Bequemlichkeit der Implementierung zum Template-Parameter geworden: ich hab nämlich ein std::array genommen +

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

+ aber dann muß er unter zwei Namen auftreten +

+ +
+ + + +

+ ...und zwar, weil ich im Klassen-Scope einen Template-Namen nicht verdecken darf. Leider verteilt sich der Node-bezogene Code zu ziemlich gleichen Teilen auf die Node-Klasse selber, und den umschließenen Scope +

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

+ hier ist zwar die maxCapacity festgelegt, aber nur zur Erstellungs-Zeit. Das wäre ja genau, was hier gebraucht wird +

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

+ weil wir aufbauen auf can_STL_ForEach. +

+
    +
  • + hat nested-type "iterator" +
  • +
  • + hat member-Funktionen begin() und end() +
  • +
+

+ ....außerdem wird dann das Hilfs-Template + StlRange auch noch + initialisiert mit
+ RangeIter{begin(con), end(con} + .... �� +

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

+ ...darauf deuten die Komentare zur Typ-Inferenz hin: +

+

+ "when IT is just a pointer, we use the pointee as value type" +

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

+ Template-Parameter numNodes zurückbauen +

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

+ wird in den Adaptoren verwendet — und die sind statisch +

+ +
+
+
+ + + + + +

+ das war mal eine offensichtlich einfache Idee +

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

+ man könnte das anders interpretieren +

+ +
+ + + + + + + + +
+ + + +
+
+
@@ -111121,9 +111306,7 @@ class Something - - - +

Da alignof(char) ≡ 1, ist es gradezu eine »Steilvorlage« für Probleme, wenn man einen Allocation-Buffer per char[] deklariert. GCC macht das nicht (er fängt Allokationen immer an 64bit-Grenzen an), aber grundsätzlich dürfte ein Compiler ein char[] anfangen, wo er grad' lustig ist. Besonders gefährlich, wenn das Array in ein anderes Objekt eingebettet wird. Nur den zuletzt genannten Fall habe ich 2019 abgeklärt; gegen alle sonstigen Schnaps-Ideen gibt es keinen Schutz — es sei denn, man hält sich an die Sprachmittel