diff --git a/src/vault/gear/block-flow.hpp b/src/vault/gear/block-flow.hpp index 39561177a..943cd7f79 100644 --- a/src/vault/gear/block-flow.hpp +++ b/src/vault/gear/block-flow.hpp @@ -54,6 +54,8 @@ ** obsolete goals is pointless and can be just side stepped. Once the scheduling has ** passed a defined deadline (and no further pending IO operations are around), the ** Epoch can be abandoned as a whole and the storage extent can be re-used. + ** @warning this implies any access after the deadline is _undefined behaviour,_ + ** spanning also further use of the AllocatorHandle obtained for a deadline. ** ** Dynamic adjustments are necessary to keep this scheme running efficiently. ** Ideally, the temporal stepping between subsequent Epochs should be chosen such @@ -516,6 +518,8 @@ namespace gear { /** * initiate allocations for activities to happen until some deadline * @return opaque handle allowing to perform several allocations. + * @warning should not be used after the deadline has passed, + * since memory will then be re-used and overwritten. */ AllocatorHandle until (Time deadline) diff --git a/src/vault/mem/extent-family.hpp b/src/vault/mem/extent-family.hpp index aca7e9ca8..22d678be4 100644 --- a/src/vault/mem/extent-family.hpp +++ b/src/vault/mem/extent-family.hpp @@ -185,12 +185,12 @@ namespace mem { void validatePos (Extent* knownTarget) { - if (knownTarget == & yield()) + if (exFam->matchPos (index,knownTarget)) return; size_t prevIdx = index; do{ iterNext(); - if (knownTarget == & yield()) + if (exFam->matchPos (index,knownTarget)) return; } while (index != prevIdx); @@ -350,6 +350,13 @@ namespace mem { : (start_ <= idx and idx < after_); } + bool + matchPos (size_t idx, void* address) + { + REQUIRE (idx < slotCnt()); + return address == extents_[idx].get(); + } + Extent& access (size_t idx) const { diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index d85c924bc..ba6746799 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -86672,6 +86672,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
+ @@ -87693,6 +87694,33 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
+ + + + + + +

+ ...ich hatte diesen Fix nur oberflächlich getestet, und dabei übersehen, daß eine Assertion ansprechen kann (sogar sehr wahrscheinlich einmal ansprechen wird, sobald der Reparatur-Mechanismus eine größtere Strecke zurücklegt). Das ist aber kein Bug im eigentlichen Reparatur-/reLink-Mechanismus; dieser funktioniert präzise, wie ich nochmals im einzelnen mit dem Debugger nachvollziehen konnte. +

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

+ wenn eine Deadline überfahren wurde, ist ein weiterer Zugriff auf den Extend als _undefined behaviour_ zu betrachten. Das gilt auch für das AllocatorHandle, das man früher mal für eine bestimmte Deadline bekommen hat; dieses kann man sehr wohl weiterhin verwenden (solange die Deadline noch in der Zukunft liegt). Konkreter Fall: später noch eine Dependency anhängen. Wenn der Anker dieser Dependecy zu dem Zeitpunkt bereits ausgeführt oder invalidiert wurde, ist man selber schuld! +

+ +
+ +
@@ -96194,11 +96222,15 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + - - + + + + + + @@ -110731,7 +110763,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + @@ -111019,7 +111051,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + @@ -111032,8 +111064,9 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + + @@ -111125,10 +111158,18 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
+ - - + + + + + + + + + @@ -115153,6 +115194,7 @@ std::cout << tmpl.render({"what", "World"}) << s + @@ -116210,6 +116252,420 @@ std::cout << tmpl.render({"what", "World"}) << s + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ und damit nicht über eine Abweichung der Job-Zeiten in den Formfaktor eingehen +

+ +
+
+ + + + + + + +

+ naja... die ist unterirdisch +

+ +
+
+ + + + + + +

+ zur Erinnerung: in einer Serie machen wir ja eine Art Konvergenz auf einen effektiven Form-Faktor hin. Mit den Ergebnissen eines Laufes wird für den nächsten Lauf nachjustiert; der von außen vorgegebene (nominelle) Streß-Faktor bleibt, aber die tatsächliche Dichte wird so optimiert, daß die dann effektiv diesem Faktor entspricht. Im Zuge dieser Anpassung wird anscheinend das Schedule jeweils etwas verdichtet, und die erreichte Concurency fällt (von etwas über 2 auf 1.6 zuletzt) +

+ +
+ +
+ + + + + + +

+ zumindest nach den inzwischen vorliegenden Beobachtungen aus dem Param-Range-Setup +

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

+ extent-family.hpp:356 +

+ +
+ + + + + + + +

+ ...damals allerdings aus einem ganz anderen Kontext, der inzwischen durch einen Umbau im Scheduler behoben ist/sein sollte. +

+ +
+
+ + + + + + + +

+ »investigateWorkProcessing« +

+

+ ∅conc:7.29344 +

+

+ ∅conc:6.53128 +

+

+ ∅conc:6.24495 +

+

+ ∅conc:6.082 +

+

+ PRECONDITION: extent-family.hpp:356: thread_9: access: (isValidPos (idx)) +

+ +
+
+
+ + + + + + +

+ 0000000609: PRECONDITION: extent-family.hpp:356: thread_9: access: (isValidPos (idx)) +

+

+ vault::mem::ExtentFamily<vault::gear::Activity, 500ul>::access(unsigned long) const+0xdd +

+

+ vault::mem::ExtentFamily<vault::gear::Activity, 500ul>::IdxLink::yield() const+0x26 +

+

+ vault::mem::ExtentFamily<vault::gear::Activity, 500ul>::IdxLink::validatePos(vault::mem::ExtentFamily<vault::gear::Activity, 500ul>::Extent*)+0x52 +

+

+ vault::gear::BlockFlow<vault::gear::blockFlow::RenderConfig>::StorageAdaptor::iterNext()+0x23 +

+

+ lib::IterableDecorator<vault::gear::blockFlow::Epoch<vault::mem::ExtentFamily<vault::gear::Activity, 500ul> >, vault::gear::BlockFlow<vault::gear::blockFlow::RenderConfig>::StorageAdaptor>::operator++()+0x20 +

+

+ vault::gear::BlockFlow<vault::gear::blockFlow::RenderConfig>::AllocatorHandle::claimSlot()+0x228 +

+

+ vault::gear::Activity& vault::gear::BlockFlow<vault::gear::blockFlow::RenderConfig>::AllocatorHandle::create<vault::gear::Activity::Verb>(vault::gear::Activity::Verb&&)+0x2e +

+

+ vault::gear::activity::Term::appendNotificationTo(vault::gear::activity::Term&, bool)+0x2f +

+

+ vault::gear::ScheduleSpec::linkToPredecessor(vault::gear::ScheduleSpec&, bool)+0x68 +

+

+ vault::gear::test::TestChainLoad<8ul>::ScheduleCtx::continuation(unsigned long, unsigned long, unsigned long, bool)+0x2ef +

+

+ vault::gear::test::TestChainLoad<8ul>::ScheduleCtx::performRun()::{lambda(unsigned long, unsigned long, unsigned long, bool)#3}::operator()(unsigned long, unsigned long, unsigned long, bool) const+0x40 +

+

+ std::_Function_handler<void (unsigned long, unsigned long, unsigned long, bool), vault::gear::test::TestChainLoad<8ul>::ScheduleCtx::performRun()::{lambda(unsigned long, unsigned long, unsigned long, bool)#3}>::_M_invoke(std::_Any_data const&, unsigned long&&, std::_Any_data const&, std::_Any_data const&, bool&&)+0x86 +

+

+ std::function<void (unsigned long, unsigned long, unsigned long, bool)>::operator()(unsigned long, unsigned long, unsigned long, bool) const+0x90 +

+

+ vault::gear::test::RandomChainPlanFunctor<8ul>::invokeJobOperation(lumiera_jobParameter_struct const&)+0x2ca +

+

+ vault::gear::Activity::invokeFunktor(lib::time::Time)+0x740 +

+

+ vault::gear::activity::Proc vault::gear::Activity::activate<vault::gear::Scheduler::ExecutionCtx>(lib::time::Time, vault::gear::Scheduler::ExecutionCtx&)+0x70 +

+

+ vault::gear::activity::Proc vault::gear::ActivityLang::activateChain<vault::gear::Scheduler::ExecutionCtx>(vault::gear::Activity*, vault::gear::Scheduler::ExecutionCtx&)+0x4e +

+

+ vault::gear::activity::Proc vault::gear::ActivityLang::dispatchChain<vault::gear::Scheduler::ExecutionCtx>(vault::gear::Activity*, vault::gear::Scheduler::ExecutionCtx&)+0x68 +

+

+ vault::gear::Scheduler::doWork()::{lambda(vault::gear::ActivationEvent)#1}::operator()(vault::gear::ActivationEvent) const+0x4d +

+

+ vault::gear::SchedulerCommutator::dispatchCapacity<vault::gear::Scheduler::doWork()::{lambda(vault::gear::ActivationEvent)#1}, vault::gear::Scheduler::doWork()::{lambda()#2}>(vault::gear::SchedulerInvocation&, vault::gear::LoadController&, vault::gear::Scheduler::doWork()::{lambda(vault::gear::ActivationEvent)#1}, vault::gear::Scheduler::doWork()::{lambda()#2})::{lambda()#2}::operator()() const+0x8f +

+

+ vault::gear::SchedulerCommutator::WorkerInstruction vault::gear::SchedulerCommutator::WorkerInstruction::performStep<vault::gear::SchedulerCommutator::dispatchCapacity<vault::gear::Scheduler::doWork()::{lambda(vault::gear::ActivationEvent)#1}, vault::gear::Scheduler::doWork()::{lambda()#2}>(vault::gear::SchedulerInvocation&, vault::gear::LoadController&, vault::gear::Scheduler::doWork()::{lambda(vault::gear::ActivationEvent)#1}, vault::gear::Scheduler::doWork()::{lambda()#2})::{lambda()#2}>(vault::gear::LoadController)+0x1f +

+

+ vault::gear::activity::Proc vault::gear::SchedulerCommutator::dispatchCapacity<vault::gear::Scheduler::doWork()::{lambda(vault::gear::ActivationEvent)#1}, vault::gear::Scheduler::doWork()::{lambda()#2}>(vault::gear::SchedulerInvocation&, vault::gear::LoadController&, vault::gear::Scheduler::doWork()::{lambda(vault::gear::ActivationEvent)#1}, vault::gear::Scheduler::doWork()::{lambda()#2})+0xc5 +

+

+ vault::gear::Scheduler::doWork()+0x3d +

+

+ vault::gear::Scheduler::Setup::doWork()+0x1c +

+

+ vault::gear::work::Worker<vault::gear::Scheduler::Setup>::pullWork()+0x26 +

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

+ linkToPredecessor() +

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

+ tritt reproduzierbar auf ab Load=4ms +

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

+ alloziert wurde auf dem predecessor-Term +

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

+ ...das ist ohnehin etwas kreativ +

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

+ dieser hängt an der Deadline +

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

+ Der Allokator in sich ist robust; die Deadline beschreibt nur einen Nutzungs-Kontrakt; sie ist zwar im Gate des Blocks gespeichert, aber für den Allokator nur maßgeblich zur Suche des passenden Blocks. Das weitere Nutzungs-Muster muß auf einer höheren Ebene gewährleistet sein. +

+ + +
+
+ + + + + + + + + +
+
+
@@ -116227,8 +116683,7 @@ std::cout << tmpl.render({"what", "World"}) << s Und zwar unabhängig davon, ob die Kalibrierung mit kurzen oder langen Zeiten und single- oder multithreaded erfolgte. Die Abweichtung tritt nur im realen Last-Kontext auf, und ist (visuell. den Diagrammen nach zu urteilen) korreliert mit dem Grad an contention und irregularität im Ablauf. Tendentiell nimmt sie für längere Testläufe ab, konvergiert aber — auch für ganz große Lasten und sehr lange Läufe — typischerweise zu einem Offset von ~ +1ms

- - +
@@ -116240,8 +116695,7 @@ std::cout << tmpl.render({"what", "World"}) << s Und das ist schon aus rein-logischen Gründen so zu erwarten. Bewußt habe ich beim Aufstellen der Heuristig für das Test-Schedule auf jedwede optimale Anordnung der Rechenwege verzichtet (kein Box-stacking problem lösen!). Hinzu kommen die tatsächlichen Beschränkungen des Worker-Pools. Daraus ergibt sich eine charakteristische Abweichung zwischen einem theoretisch berechneten concurrency-speed-up (wie er in's Schedule eingerechnet ist) und der empirisch beobachteten durchschnittlichen concurrency. Das wird als Form-Faktor gedeutet.

- -
+
@@ -116257,8 +116711,7 @@ std::cout << tmpl.render({"what", "World"}) << s zwischen Load-Size und Laufzeit zum kompletten Abarbeiten der erzeugten Lastspitze

- - +
@@ -116272,8 +116725,7 @@ std::cout << tmpl.render({"what", "World"}) << s Gradient sehr nah am zu erwartenden Wert

- - + @@ -116283,8 +116735,7 @@ std::cout << tmpl.render({"what", "World"}) << s wenn man die empirisch beobachtete, effektive Concurrency und reale durchschnittliche Job-Zeit ansetzt

- -
+
@@ -116296,8 +116747,7 @@ std::cout << tmpl.render({"what", "World"}) << s das bedeutet: der tatsächlich beobachtete Sockel hängt von der Länge der Job-Last und der Concurrency ab: Grundsätzlich muß man einmal die ganze Worker-Pool-size zu Beginn und am Ende aufschlagen — mit reduzierter Concurrency. Das ergibt sich bereits aus einer rein logischen Überlegung: »Voll-Last« kann erst konstituiert werden, wenn der erste Worker sich den zweiten Job holt. Analog beginnt der spin-down, wenn der erste worker idle fällt.

- -
+
@@ -124818,7 +125268,7 @@ unsigned int ThreadIdAsInt = *static_cast<unsigned int*>(static_cast<vo size="10!"
ratio="expand"

-

Beachte das Ausrufezeichen; auch expand ist wichtig, um die Apsect-Ratio zu erhalten. Wenn man stark verkleinert, sollte man ein generisches node-Template hinzufügen mit node[fontsize="16"]... dann Rendern mit -Tsvg

+

Beachte das Ausrufezeichen; auch expand ist wichtig, um die Apsect-Ratio zu erhalten. Wenn man stark verkleinert, sollte man ein generisches node-Template hinzufügen mit node[fontsize="16"]... dann Rendern mit -Tsvg