Commit graph

101 commits

Author SHA1 Message Date
dd62240900 Workforce: terminate after excessive idle cycles
- count each consecutive idle cycle
- by default, terminate after 100 idle cycles (2 sec)
2023-09-09 01:47:15 +02:00
b493f15333 Workforce: configure and demonstrate idle-wait 2023-09-09 01:12:10 +02:00
ef5365057a Workforce: demonstrate standard behaviour
- can activate / scale up
- work functor invoked repeatedly
2023-09-08 14:07:23 +02:00
5e16ed11bd Workforce: detach terminating threads instead of joining
...which however brings the problem that we can no longer block the destructor
of WorkForce by simply joining on all joinable threads (there is a race
between testing joinable() and invoking join(), which does not tolerate
non-joinable state.

There is a second problem: we need to detect and clean-up terminated workers,
even for just finding out how many workers are still active. Fortunately
doing so also solves the waiting problem in the destructor
2023-09-08 04:26:29 +02:00
81cab9a675 Workforce: emergency brake
While in principle it would be possible (and desirable)
to control worker behaviour exclusively through the Work-Functor's return code,
in practice we must concede that Exceptions can always happen from situations
beyond our control. And while it is necessary for the WorkForce-dtor to
join and block (we can not just pull away the resources from running threads),
the same destructor (when called out of order) must somehow be able
at least to ask the running threads to terminate.

Especially for unit tests this becomes an obnoxious problem -- otherwise
each test failure would cause the test runner to hang.

Thus adding an emergency halt, and also improve setup for tests
with a convenience function to inject a work-function-λ
2023-09-08 02:48:30 +02:00
b8e52d008c Workforce: configuration and initialisation of workers
- use a template parameter to allow for hook into local facilities (Scheduler)
- pass config initialisation down through constructors
2023-09-07 17:15:25 +02:00
cf7c2d1327 Workforce: analysis and design
- investigate consistency guarantees through acquire-release
  ==> turns out we do not need a fence, but it is tantamount
      to have a guard variable and actually load and check
      the value to ensure we indeed get a happens-before

- elaborate design of the WorkForce
  + no shared control variables necessary
  + no ability to forcibly shut-down the WorkForce
  + rather, all control will be exerted through the return value
    of the Work-Functor
2023-09-06 19:18:37 +02:00
38ab5a6aa9 Workforce: draft simple usage
...start with an oversimplified implementation...
2023-09-05 00:24:33 +02:00
70cd8af806 Workforce: requirement analysis 2023-09-05 00:22:17 +02:00
2e28f5d278 Activity-Lang: abstracted execution framework complete and tested (closes: #1319) 2023-09-03 01:50:50 +02:00
95ae12bba1 Activity-Lang: complete handling of IO activities 2023-09-03 00:40:37 +02:00
b3b6f7524c Activity-Lang: outline for wiring async IO activities
...relies on the same building pattern, with the notable difference
that the chain is severed, providing an additional NOTIFY as re-entrance point
2023-09-02 22:36:02 +02:00
73a67886f0 Activity-Lang: wiring for internal/planning job
...uses just the minimal wiring and is thus already implemented :-)
2023-09-02 03:35:02 +02:00
f3cf178388 Activity-Lang: ability to hook in a fake implementation
Up to now, the DiagnosticFun mock in ActivityDetector only
created an EventLog entry on invocation and was able to retunr
a canned result value. Yet for the job invocation scenario test,
it would be desirable to hook-in a λ with a fake implementation
into the ExecutionContext. As a further convenience, the
return value is now default initialised, instead of being
marked as uninitialised until invocation of "returning(val)"
2023-09-01 21:59:25 +02:00
44e840f27c Activity-Lang: implement optional notification builders 2023-09-01 19:03:37 +02:00
963dc38088 Activity-Lang: introduce some shorthand notation
...regarding the kind of activity (the verb),
and also for some special case access of payload data;
deliberately asserting the correct verb, but no mandatory check,
since this whole Activity-Language is conceived as cohesive
and essentially sealed (not meant to be extended)
2023-09-01 17:41:40 +02:00
67c71725a4 Activity-Lang: access current scheduler time dynamically
It is not sufficient just to pass this "current time" as parameter
into the ActivityLang::dispatchChain(), since some Activities within
this chain will essentially be long-running (think rendering); thus
we need a real callback from within the chain. The obvious solution
is to make this part of the Execution Context, which is an abstraction
of the scheduler environment anyway
2023-09-01 02:44:29 +02:00
14effc2349 Activity-Lang: consider logic for dependency notification
...turns out there is still a lot of leeway in the possible implementation,
and seemingly it is too early to decide which case to consider the default.
Thus I'll proceed with the drafted preliminary solution...

- on primary-chain, an inhibited Gate dispatches itself into future for re-check
- on Notification, activation happens if and only if this very notification opens the Gate
- provide a specifically wired requireDirectActivation() to allow enforcing a minimal start time
2023-08-31 20:18:35 +02:00
32c08c0307 Activity-Lang: also dispatch notifications 2023-08-31 02:11:07 +02:00
900f46b1d5 Activity-Lang: framework to execute a chain of Activities
without and error or concurrency handling (which is the responsibility
of the Scheduler-Layer-2; just the sequencing of individual activations
2023-08-30 22:19:57 +02:00
cda1cdd975 Activity-Lang: verify memory allocation and connectivity 2023-08-29 18:46:37 +02:00
3bd4305dab Activity-Lang: create standard wiring for CALC-Term 2023-08-29 17:36:56 +02:00
80a48abcf4 Activity-Lang: determine role of the time window parameters 2023-08-29 16:40:52 +02:00
ae89831275 Activity-Lang: wire Job invocation in the activity::Term builder 2023-08-29 04:19:19 +02:00
e98fe1e78b Activity-Lang: scaffolding to create a simple Term 2023-08-29 03:18:47 +02:00
8e20fa6de1 Activity-Lang: framework for building an Activity-Term
While the ''general direction'' seems clear, some in-depth
analysis was required to find out what information can reasonably
be expected to be available at this point.

The decision was made to shift the actual deadline calculation
into the Job-Planning altogether, assuming that a preliminary solution
based on data implicitly available there will be enough to implement
simple linear playback, while precise management of job start times
can be added in later, when observation of actual timing behaviour
is available...
2023-08-29 01:41:17 +02:00
568957b75d Activity-Lang: prevent spurious activations after notification
Solved by special treatment of a notification, which happens
to decrement the latch to zero: in this case, the chain is
dispatched, but also the Gate is locked permanently to block
any further activations scheduled or forwareded otherwise
2023-08-23 01:03:11 +02:00
2f042ce6c0 Activity-Lang: cover all cases of Gate-behaviour
TODO: while correct as implemented, the handling of the
notification seems questionable, since re-scheduling the chain immediately
may lead to multiple invocations of the chain, since it might have been "spinned"
and thus re-scheduled already, and we have no way to find out about that
2023-08-22 20:13:13 +02:00
4fed0b8cd2 Activity-Lang: clarify and fix behaviour of POST
...can not take a shortcut here, since the timing information
embedded into the POST-Activity must somehow be transported
to the Scheduler; key point to note is that the chain will
be performed in »management mode« (single threaded)
2023-08-22 18:38:40 +02:00
108a5e7ca5 Activity-Lang: work out activation-dispatch-notification sequence
...attempt to get this intricate state machine sorted out

Notification turned out quite tricky, since it may emanate
from a concurrently executed phase and we try to avoid having
to protect the gate directly with a lock; rather we re-dispatch
the notification through the queue, which indirectly also ensures
that the worker de-queuing the NOTIFY-Activity operates in
management mode (single threaded, holding the GroomingToken)
2023-08-21 17:32:52 +02:00
b7641115fb Block-Flow: integrate actual Gate-implementation
Each Epoch in the memory manager holds a Gate in the first slot;
after the logic for Gate-activation is worked out now, we can switch
to using this actual logic to determine when an Epoch can be released
2023-08-21 17:20:47 +02:00
abc29eaa31 Activity-Lang: complete implementation for Gate (conditional)
Decision how to handle a failed Gate-check
- spin forward (re-scheduler) by some time amount
- this spin-offset parameter is retrieved from the Execution Context
- thus it will be some kind of engine parameter

With these determinations and the framework for the Execution Context
it is now possible to code up the logic for Gate check, which in turn
can then be verified by the watchGate diagnostics
2023-08-20 02:39:57 +02:00
7debaaca48 Activity-Lang: adaptor to watch existing Activity's activation
due to technical limitations this requires to wire the adaptor
as replacement for the subject Activity, so that it can capture
and log the activation, and then pass it on to its watched subject
2023-08-19 19:06:44 +02:00
49435c8aca Activity-Lang: investigate / fix string conversion
...turns out that util::toString does not explicitly handle pointers differently,
for very good reasons; this function must always work, always produce a simple and
compact representation, and it must be possible to instantiate the template
and take a function reference (which precludes adding an overload for pointers)
2023-08-19 02:27:06 +02:00
3784bd7252 Activity-Lang: build activation detector
...using a HOOK-Activity as prepended adaptor,
optionally forwarding the activation to the inferior
2023-08-18 19:37:44 +02:00
8c36e0a93b Activity-Lang: diagnostics for Activity and Execution Context 2023-08-18 16:25:09 +02:00
dd44373166 Activity-Lang: a way how to provide a faked Execution Context
...basically just delegated to DiagnosticFun instances,
yet the actual setup is somwewhat tricky to get right
2023-08-17 19:37:38 +02:00
ab7f506f4b Activity-Lang: failure will certainly not be signalled to the Job
doing so would contradict the fundamental architecture,
all kinds of failures and timeouts need to be handled within
Scheduler-Layer-2 rather.

Jobs are never aborted, nor do they need to know if and when they are invoked
2023-08-15 17:18:30 +02:00
111c05a1f9 Activity-Lang: introduce a callback hook
...primarily intended for testing,
but could be helpful as generic extension point later on...
2023-08-01 15:37:56 +02:00
26c2e835c3 Activity-Lang: setup skeleton of the activation function
- complete spec of Activity processing
- define the invocation structure
- implement basic cases of activation
2023-07-30 22:06:06 +02:00
4f29d436b3 Activity-Lang: draft patterns of execution
essentially define a concept how to ''perform'' render activities in the Scheduler.
This entails to specify the operation patterns for the four known base cases
and to establish a setup for the implementation.
2023-07-28 02:21:59 +02:00
28b3900284 Block-Flow: final adjustments from performance test (closes: #1311)
Further extensive testing with parameter variations,
using the test setup in `BlockFlow_test::storageFlow()`

- Tweaks to improve convergence under extreme overload;
  sudden load peaks are now accomodated typically < 5 sec

- Make the test definition parametric, to simplify variations

- Extract the generic microbenchmark helper function

- Documentation
2023-07-22 06:07:35 +02:00
049ca833a0 Block-Flow: optimise parameters for performance
There seems to be a ''sweet spot'' for somewhat larger Epoch sizes around 500 slots.
At least in the test setup used here, which works with a load of 200 Frames / sec,
which is significantly over the typical value of 50fps (video + audio) for simple playback.

The optimisation of averaged allocation times can not be much improved **below 30ns**.

Overall, this can be considered a good result,
since this allocation scheme does way more than just allocate memory,
it also provides a means to track dependencies and lifecycle.

__For context__:
 - we should strive at processing one frame in ~ 10ms
 - for 10 Activity records per Frame, we currently use < 0.5 µs for
   memory and dependency management in the scheduler
 - this leaves enough room for the further administrative efforts
   (priority queue, job planning, buffer management)
2023-07-21 04:34:04 +02:00
d557c540bf Block-Flow: tweaks to get down on par with the standard heap allocator
... while this a comparison of apples and oranges, since the standard
heap allocator does not offer any dependency and lifecycle managmenet,
while the BlockFlow scheme developed here is much more complex and
offers a lifetime and dependency control specifically tailored to
the needs of the Scheduler.

Anyway, with the latest tweaks and refactorings, the test case
now shows averaged times per allocation on a comparable level
(both in the range of ~30ns)
2023-07-21 01:52:07 +02:00
2977076b7f Block-Flow: switch to using the reworked config
BUT -> +50% runtime in -O3  (+20ns)

Investigation seems to indicate
 - that the increased (+1 Epochs, 10 -> 11) moving average
   caused the Algo to perform worse (strong effect)
 - that the Optimiser has problems with boost::rational, which however
   yields only a minute effect (+5ns), and only on the critical path

The access via Meyers Singleton has no adverse effect,
rather the new setup gives a tiny benefit (46ns -> 37ns).
Surprisingly, the increased pre-allocation has no observable effect.
2023-07-20 21:47:18 +02:00
ca502aa826 Block-Flow: introduce config through a policy mix-in
...measured running time reproduced unaltered for -O3
2023-07-20 19:28:20 +02:00
5803fed544 Block-Flow: draft for re-arranged configuration
On the long run, there will be a central Render Engine parametrisation;
some parameters can even be expected to be dynamic; thus prepare the
BlockFlow allocator to fit in with this expectation
2023-07-20 16:46:54 +02:00
14a5200cc0 Block-Flow: more runtime observation and fine-tuning
For comparison: use individual managment by refcount.
This supports the conclusion that BlockFlow is more than just a
custom allocator; it also supports a non-trivial lifetime management,
and this comes at a cost.

Playing around with various load patterns uncovers further weak spots
in the regulation mechanism. As a remedy, introduce a stronger feed-back
and especially set the target load factor from 100% -> 90%
to add some headroom to absorb intermittent load peaks

Presumably ''much more observation and fine-tuning'' will be necessary
under real-world load conditions (⟹ Ticket #1318 for later)
2023-07-19 03:29:09 +02:00
bf35ae030c Block-Flow: remove instrumentation of size-control
(!this changeset could be of importance for future investigation!)
2023-07-18 21:26:26 +02:00
c008858d8f Block-Flow: investigate, fix and fine-tune Epoch size control
- BUG: must prevent the Epoch size to become excessive low
- Problem: feedback signal should not be overly aggressive

Fine-Tuning:
- Dose for Overflow-compensation is delicate
- Moving average and Overflow should be balanced
- ideally the compensatory actions should be one order of magnitude
  slower than the characteristic regulation time

Improvement: perform Moving-Average calculations in doubles
2023-07-18 21:23:00 +02:00