Scheduler: look for ways to propagate a capacity-hint

...whenever a new CalcStream is seeded, it would be prudent
not only to step up the WorkForce (which is already implemented),
but also to provide a hint to the BlockFlow allocator regarding
the expected calculation density.

Such a hint would allow to set a more ample »epoch« spacing,
thereby avoiding to drive the allocator into overload first.
The allocator will cope anyway and re-balance in a matter of
about 2 seconds, but avoiding this kind of control oscillations
altogether will lead to better performance at calculation start.
This commit is contained in:
Fischlurch 2023-11-10 03:34:29 +01:00
parent ecf1a5a301
commit a2a960f544
5 changed files with 149 additions and 56 deletions

View file

@ -49,6 +49,7 @@
#include "lib/rational.hpp"
#include "lib/util-quant.hpp"
#include "lib/format-string.hpp"
#include "lib/util.hpp"
extern "C" {
#include "lib/tmpbuf.h"
@ -62,12 +63,15 @@ extern "C" {
#include <boost/lexical_cast.hpp>
using std::string;
using util::limited;
using util::floordiv;
using lib::time::FSecs;
using lib::time::FrameRate;
using boost::rational_cast;
using boost::lexical_cast;
#undef MAX
#undef MIN
namespace error = lumiera::error;
@ -263,6 +267,47 @@ namespace time {
return Duration (1, *this);
}
/** a rather arbitrary safety limit imposed on internal numbers used to represent a frame rate.
* @remark rational numbers bear the danger to overflow for quite ordinary computations;
* we stay away from the absolute maximum by an additional safety margin of 1/1000.
*/
const uint RATE_LIMIT{std::numeric_limits<uint>::max() / 1000};
/**
* @internal helper to work around the limitations of `uint`.
* @return a fractional number approximating the floating-point spec.
* @todo imposing a quite coarse limitation. If this turns out
* to be a problem: we can do better, use lib::reQuant (rational.hpp)
*/
boost::rational<uint>
__framerate_approximation (double fps)
{
double quantised{fabs(fps) * RATE_LIMIT};
uint num = uint(limited (1u, quantised + 0.5, 1000u*RATE_LIMIT));
uint den = RATE_LIMIT;
return {num, den};
}
/**
* @internal helper calculate the _count per time span_ approximately,
* to the precision possible to represent as fractional `uint`.
*/
boost::rational<uint>
__framerate_approximation (size_t cnt, Duration timeReference)
{
boost::rational<uint64_t> quot{cnt, _raw(timeReference)};
if (quot.denominator() < RATE_LIMIT
and quot.numerator() < RATE_LIMIT/1000)
return {uint(quot.numerator()) * uint(Time::SCALE)
,uint(quot.denominator())
};
// precise computation can not be handled numerically...
return __framerate_approximation (rational_cast<double>(quot) * Time::SCALE);
}

View file

@ -667,7 +667,11 @@ namespace time {
public:
FrameRate (uint fps) ;
FrameRate (uint num, uint denom);
FrameRate (boost::rational<uint> const& fractionalRate);
FrameRate (size_t count, Duration timeReference);
explicit
FrameRate (boost::rational<uint> fractionalRate);
static FrameRate approx(double fps);
// standard copy acceptable;
@ -806,10 +810,25 @@ namespace time {
{ }
inline
FrameRate::FrameRate (boost::rational<uint> const& fractionalRate)
FrameRate::FrameRate (boost::rational<uint> fractionalRate)
: boost::rational<uint> (__ensure_nonzero(fractionalRate))
{ }
boost::rational<uint> __framerate_approximation (size_t, Duration);
boost::rational<uint> __framerate_approximation (double);
inline
FrameRate::FrameRate (size_t count, Duration timeReference)
: FrameRate{__framerate_approximation (count, timeReference)}
{ }
inline FrameRate
FrameRate::approx (double fps)
{
return FrameRate{__framerate_approximation (fps)};
}
inline double
FrameRate::asDouble() const
{

View file

@ -104,6 +104,7 @@ namespace gear {
using lib::time::FSecs;
using lib::time::TimeVar;
using lib::time::Duration;
using lib::time::FrameRate;
namespace blockFlow {///< Parametrisation of Scheduler memory management scheme
@ -327,6 +328,7 @@ namespace gear {
* Scheduling entails to provide a chain of Activity definitions,
* which will then »flow« through the priority queue until invocation.
*
* @see ActivityLang
* @see SchedulerCommutator
* @see BlockFlow_test
*/
@ -385,7 +387,16 @@ namespace gear {
double stretched = _raw(epochStep_) * factor;
gavl_time_t microTicks(floor (stretched));
epochStep_ = TimeValue{microTicks};
}
/** provide a hint to the self-regulating allocation scheme */
void
announceAdditionalFlow (FrameRate additionalFps)
{
FrameRate currFps{config().framesPerEpoch(), epochStep_};
/////////////////////////////////////////////////////////////////////TODO need arithmetics on FrameRate
/////////////////////////////////////////////////////////////////////TODO -> then just add the new and calculate new stepping
/////////////////////////////////////////////////////////////////////TODO !! watch out for the minimum limit !!
}

View file

@ -229,6 +229,20 @@ namespace test{
CHECK (isnil (Duration (0, FrameRate::PAL)));
CHECK (isnil (Duration (0, FrameRate(123))));
CHECK (FrameRate::approx(2000) == "1000FPS"_expect); // limited
CHECK (FrameRate::approx(1e-5) == "43/4294967FPS"_expect);
CHECK (FrameRate::approx(1e-6) == "4/4294967FPS"_expect);
CHECK (FrameRate::approx(1e-7) == "1/4294967FPS"_expect); // limited
CHECK (FrameRate::approx(1e-8) == "1/4294967FPS"_expect); // limited
CHECK (FrameRate( 20'000, Duration{Time{0,10}}) == "2000FPS"_expect);
CHECK (FrameRate( 20'000, Duration{Time::MAX }) == "1/4294967FPS"_expect);// limited
CHECK (FrameRate(size_t(2e10), Duration{Time::MAX }) == "279397/4294967FPS"_expect);
CHECK (FrameRate(size_t(2e14), Duration{Time::MAX }) == "2793967531/4294967FPS"_expect);
CHECK (FrameRate(size_t(2e15), Duration{Time::MAX }) == "1000FPS"_expect); // limited
CHECK (FrameRate(size_t(2e16), Duration{Time::MAX }) == "1000FPS"_expect); // limited
}

View file

@ -78951,9 +78951,7 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
<icon BUILTIN="flag-yellow"/>
<node BACKGROUND_COLOR="#fdfdcf" COLOR="#ff0000" CREATED="1699563656019" ID="ID_1361396246" MODIFIED="1699566672294" TEXT="Job-Planung mu&#xdf; aus Konsistenzgr&#xfc;nden in einem Job laufen">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
...damit sie das Grooming-Token hat und damit Zugriff auf die Scheduler-Ressourcen. Au&#223;erdem ist damit ein konsitenter Rahmen sichergestellt: geplante Startzeiten sollten fr&#252;hestend 20ms danach beginnen
@ -80253,9 +80251,7 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
</node>
<node CREATED="1699563384878" ID="ID_28734392" LINK="#ID_394986223" MODIFIED="1699563462111" VSHIFT="6">
<richcontent TYPE="NODE"><html>
<head>
</head>
<head/>
<body>
<p style="text-align: center">
beide Probleme lassen sich nicht l&#246;sen &#8212;
@ -80441,9 +80437,7 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
</body>
</html></richcontent>
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
...da nun wirklich jeder Dispatch am Grooming-Token <i>&#8222;vorbei mu&#223;&#8220;&#160; &#8212; </i>theoretisch w&#228;re es sogar darstellbar, die eigentliche Job-Planung au&#223;erhalb und ohne Grooming-Token zu machen; die neuen Jobs gehen dann eben in die Instruction-Queue. Auch eine Notification (welche das Gate dekrementieren k&#246;nnte) geht durch das &#955;-post, welches direkt durch Layer2.postDispatch geht, und damit entweder versucht, das Grooming-Token zu erlangen, oder eben in die Instruct-Queue einstellt
@ -80483,9 +80477,7 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
</node>
<node CREATED="1699563536669" ID="ID_1878072938" MODIFIED="1699563756594" TEXT="dieser ist selbst ein (Meta)Job">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
und hat damit garantiert das Grooming-Token
@ -82110,9 +82102,7 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
<node CREATED="1698937110289" ID="ID_1837336933" MODIFIED="1698937115982" TEXT="reduziert Header-Includes"/>
<node CREATED="1698937116771" ID="ID_231850405" MODIFIED="1699573981078" TEXT="verbessert Verst&#xe4;ndlichkeit">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
<i>das</i>&#160;ist das ausschlaggebende Argument: Jemand, der erst mal nur den Scheduler-Header liest, w&#228;re sonst sofort erschlagen von technischen Details, und w&#252;rde den Wald vor lauter B&#228;umen nicht mehr sehen. Zumal Scheduler selber nochmal zerlegt wird in einen Header-Anteil und eine eigene translation-unit (scheduler.cpp)
@ -82151,29 +82141,23 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
<icon BUILTIN="button_cancel"/>
<node CREATED="1699573633992" ID="ID_1916986829" MODIFIED="1699573853826" TEXT="das l&#xe4;&#xdf;t sich in der Strenge nicht ohne Weiteres realisieren">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
...und zwar wegen der Builder-Notation; wegen der M&#246;glichkeit, sp&#228;ter noch Dependencies aufzuschalten, mu&#223; der activity::Term im Builder eingebettet sein, also bei der Definition der Builder-Klasse bekannt. Einziger Ausweg w&#228;re, in die &#228;u&#223;ere Builder-Klasse einen opaque buffer zu legen, und die Implementierung in scheduler.cpp nachzutragen. Diese L&#246;sung halte ich f&#252;r unn&#246;tig komplex (wiewohl sie keinen relevanten Performance-overhead erzeugt)
</p>
</body>
</html>
</richcontent>
</html></richcontent>
</node>
<node CREATED="1699573648854" ID="ID_716393193" MODIFIED="1699573724694" TEXT="ist aber auch nicht wirklich notwendig">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
...denn die betreffenden Header sind nicht wirklich <i>schwergewichtig</i>&#160; &#8212;<b>&#160;</b>im Besonderen da die typischen Clients des SchedulerService selber Implementierungs-Code sind
</p>
</body>
</html>
</richcontent>
</html></richcontent>
</node>
</node>
<node CREATED="1698936663551" ID="ID_1985217972" MODIFIED="1698937090405" TEXT="normalen Job &#xfc;ber abstrahiertes Builder-API einstellen">
@ -82191,29 +82175,23 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
<icon BUILTIN="flag-yellow"/>
<node CREATED="1699574037212" ID="ID_621021823" MODIFIED="1699574131476" TEXT="postChain w&#xe4;re ein Kandidat, ist aber zweifelhaft">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
denn dabei handelt es sich nun wirklich um eine zentrale Implementierungs-Funktion, die auch aus der Implementierung heraus aufgerufen wird. Selbst wenn der Optimizer wahrscheinlich die <i>monomorphic optimization </i>beherrscht, macht man sowas einfach nicht.
</p>
</body>
</html>
</richcontent>
</html></richcontent>
</node>
<node CREATED="1699574329467" ID="ID_1038685203" MODIFIED="1699574971826" TEXT="zumal die anderen high-level-Funktionen auch virtual sein m&#xfc;ssen">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
...so ziemlich jede von denen greift in der Implementierung auf die Komponenten im Scheduler zu.
</p>
</body>
</html>
</richcontent>
</html></richcontent>
</node>
</node>
</node>
@ -82429,37 +82407,35 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
<icon BUILTIN="idea"/>
</node>
<node CREATED="1699568058043" ID="ID_1348913314" MODIFIED="1699568067002" TEXT="daher ist hier kein expliziter Builder notwendig"/>
<node CREATED="1699568068327" ID="ID_1707263469" MODIFIED="1699568075312" TEXT="kann direkt auf die unterliegenden APIs durchgehen"/>
<node CREATED="1699568097669" ID="ID_669864822" MODIFIED="1699568115788" TEXT="einige Parameter werden fest vorgegeben">
<node COLOR="#338800" CREATED="1699568068327" ID="ID_1707263469" MODIFIED="1699588825322" TEXT="kann direkt auf die unterliegenden APIs durchgehen">
<icon BUILTIN="button_ok"/>
</node>
<node COLOR="#338800" CREATED="1699568097669" ID="ID_669864822" MODIFIED="1699588821465" TEXT="einige Parameter werden fest vorgegeben">
<icon BUILTIN="button_ok"/>
<node CREATED="1699568121776" ID="ID_33668425" MODIFIED="1699568155219" TEXT="die Deadline: analog zum &#xbb;Tick&#xab;">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
also sehr gro&#223;z&#252;gig
</p>
</body>
</html>
</richcontent>
</html></richcontent>
</node>
<node CREATED="1699568156544" ID="ID_635029922" MODIFIED="1699568164782" TEXT="es ist immer ein Meta-Job"/>
<node CREATED="1699568165537" ID="ID_788135445" MODIFIED="1699568185049">
<richcontent TYPE="NODE"><html>
<head>
</head>
<head/>
<body>
<p>
er ist stets <i>compulsory</i>
</p>
</body>
</html>
</richcontent>
</html></richcontent>
</node>
</node>
<node CREATED="1699568192950" ID="ID_46078853" MODIFIED="1699568201950" TEXT="weitere Parameter sind nur optional">
<node COLOR="#338800" CREATED="1699568192950" ID="ID_46078853" MODIFIED="1699588823469" TEXT="weitere Parameter sind nur optional">
<icon BUILTIN="button_ok"/>
<node CREATED="1699568225289" ID="ID_1953407534" MODIFIED="1699568238756" TEXT="brauche daher zwei Varianten"/>
<node CREATED="1699568239935" ID="ID_1918919265" MODIFIED="1699568264056" TEXT="Start-Offset ist per default &#xbb;now&#xab;"/>
<node CREATED="1699568273591" ID="ID_138761713" MODIFIED="1699568281494" TEXT="aber die ManifestationID wird man meist angeben">
@ -82467,6 +82443,13 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
<node CREATED="1699568305542" ID="ID_145276801" MODIFIED="1699568314497" TEXT="sie wird ja um eine erhebliche Zeit in die Zukunft geschoben"/>
</node>
</node>
<node COLOR="#338800" CREATED="1699588828420" ID="ID_291850353" MODIFIED="1699588844769" TEXT="zus&#xe4;tzlich die ManifestationID freischalten">
<icon BUILTIN="button_ok"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1699588845747" ID="ID_75294881" MODIFIED="1699589219892" TEXT="scheduler CalcStream: Kapazit&#xe4;ts-hint an den BlockFlow-Allocator weitergeben">
<arrowlink COLOR="#df1a3b" DESTINATION="ID_737137964" ENDARROW="Default" ENDINCLINATION="-1137;63;" ID="Arrow_ID_1003728898" STARTARROW="None" STARTINCLINATION="-998;36;"/>
<icon BUILTIN="flag-yellow"/>
</node>
</node>
<node COLOR="#338800" CREATED="1697757058002" ID="ID_603254465" MODIFIED="1698867045291" TEXT="terminateProcessing()">
<icon BUILTIN="button_ok"/>
@ -94226,6 +94209,30 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
</node>
</node>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1699588894575" ID="ID_737137964" MODIFIED="1699589215022" TEXT="BlockFlow: Hint zur Platz-Regulierung unterst&#xfc;tzen">
<linktarget COLOR="#df1a3b" DESTINATION="ID_737137964" ENDARROW="Default" ENDINCLINATION="-1137;63;" ID="Arrow_ID_1003728898" SOURCE="ID_75294881" STARTARROW="None" STARTINCLINATION="-998;36;"/>
<icon BUILTIN="flag-yellow"/>
<node CREATED="1699588945917" ID="ID_83511183" MODIFIED="1699588974776">
<richcontent TYPE="NODE"><html>
<head/>
<body>
<p>
Idee: Angabe in <i>&#187;zus&#228;tzlichen Frames-per-second&#171;</i>
</p>
</body>
</html></richcontent>
<icon BUILTIN="idea"/>
</node>
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1699588976977" ID="ID_290474610" MODIFIED="1699588993439" TEXT="dazu den FrameRate-Typ ausbauen">
<icon BUILTIN="pencil"/>
<node COLOR="#338800" CREATED="1699588995897" ID="ID_14216185" MODIFIED="1699589010757" TEXT="Errechnen aus count + Referenz-Zeit">
<icon BUILTIN="button_ok"/>
</node>
<node BACKGROUND_COLOR="#f8f1cb" COLOR="#a50125" CREATED="1699589011492" ID="ID_1793529885" MODIFIED="1699589032034" TEXT="Problem: &#xdc;berlauf-Gefahr der fractional-Arrithmetik">
<icon BUILTIN="messagebox_warning"/>
</node>
</node>
</node>
</node>
<node COLOR="#338800" CREATED="1689560454292" FOLDED="true" ID="ID_1170043916" MODIFIED="1689993985097" TEXT="Laufzeitverhalten beobachten">
<arrowlink COLOR="#3ba098" DESTINATION="ID_906364025" ENDARROW="Default" ENDINCLINATION="-1525;104;" ID="Arrow_ID_849510703" STARTARROW="None" STARTINCLINATION="455;-1256;"/>
@ -95627,16 +95634,13 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
<node CREATED="1699571882718" ID="ID_306783739" MODIFIED="1699571918305" TEXT="au&#xdf;erdem f&#xfc;hrt die Aktivierung via &#x3bb;-post wieder in den Dispatch"/>
<node CREATED="1699571920304" ID="ID_645005092" MODIFIED="1699571938972">
<richcontent TYPE="NODE"><html>
<head>
</head>
<head/>
<body>
<p>
allerdings haben wir jetzt praktisch immer einen<i>&#160;direkten Eingang</i>
</p>
</body>
</html>
</richcontent>
</html></richcontent>
</node>
<node CREATED="1699571939723" ID="ID_308661587" MODIFIED="1699571957595" TEXT="und dabei werden (start, deadline) jeweils nochmal explizit gegeben"/>
</node>