Commit graph

6340 commits

Author SHA1 Message Date
db1adb63a7 Activity-Lang: draft a diagnostic helper
...for coverage of the Activity-Language,
various invocations of unspecific functions must be verified,
with the additional twist that the implementation avoids indirections
and is thus hard to rig for tests.

Solution-Idea: provide a λ-mock to log any invocation into the
Event-Log helper, which was created some years ago to trace GUI communication...
2023-07-31 21:53:16 +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
c7d6f3e24c Block-Flow: Load-Test indicates problem in Epoch control
...leading to PATHETICALLY bad timing comparison
...it seems clear that the Epoch-Step went to zero
   (which was neither anticipated, nor protected against)

However, even individual heap allocations fare surprisingly well
under full optimisation; just they don't solve our problem with
tracking dependencies; the most simplest solution that would
also fulfil this requirement would be using shared_ptr
2023-07-18 01:59:17 +02:00
c1001064e3 Block-Flow: draft Load/Stress-Test
- use a midrange load scenario
- but play this at saturation level
2023-07-17 18:36:12 +02:00
a4365a24f8 Block-Flow: feed size regulation on clean-up
Generate a signal based on actual Epoch length and
observed fill ratio, assuming even distribution of load.
2023-07-17 04:32:10 +02:00
9d040dc49c Block-Flow: compute exponential moving average
..as a heuristic to regulate optimal Epoch duration;
when Epochs are discarded, the effective fill factor can be used
to guess an Epoch duration time, which would (in hindsight)
lead to perfect usage of storage space
2023-07-17 03:00:56 +02:00
bd353d768a Block-Flow: detect and react on Epoch overflow
..using a simplistic implementation for now: scale down the
Epoch-stepping by 0.9 to increase capacity accordingly.
This is done on each separate overflow event, and will be
counterbalanced by the observation of Epoch fill ratio
performed later on clean-up of completed Epochs
2023-07-16 20:47:39 +02:00
6d75a82932 Block-Flow: introduce backlink into AllocationHandle
further implementation makes clear that the AllocationHandle,
which is the primary usage front-end, has to rely both on
services of the underlying ExtentFamily allocator, as well
as on the BlockFlow itself for managing the Epoch spacing.
2023-07-16 18:03:27 +02:00
e4b74f3ae1 Block-Flow: handle Epoch overflow
...draft of control logic, does not work correct in all cases
2023-07-16 03:06:02 +02:00
dce65104aa Block-Flow: select suitable Epoch for new allocation 2023-07-15 21:37:58 +02:00
cb2ee9466b Block-Flow: add diagnostics and define further expectations
- fix a bug in IterExplorer: when iterating a »state core« directly,
  the helper CoreYield passed the detected type through ValueTypeBindings.
  This is logically wrong, because we never want to pick up some typedefs,
  rather we always want to use the type directly returned from CORE::yield()
  Here the iterator returns an Epoch&, which itself is again iterable
  (it inherits from std::array<Activity, N>). However, it is clear
  that we must not descent into such a "flatMap" style recursive expansion

- draft a simple scheme how to regulate Epoch lengths dynamically

- add diagnostics to pinpoint a given Activity and find out into which
  Epoch it has been allocated; used to cover the allocator behaviour
2023-07-15 18:54:59 +02:00
7167ad6d96 Block-Flow: define expected behaviour for Epoch association
...how new Activities are placed into Epochs, incl. overflow
2023-07-14 05:03:01 +02:00
d0fd7f32a9 Block-Flow: verify handling of Activity records within the Epoch 2023-07-14 01:51:00 +02:00
af8f84a72d Block-Flow: complete simple use case (see #1311)
- add preliminary deadline-check (directly instead of using the Activity)
- with this shortcut, now able to implement discarding obsoleted Epochs
- Iteration and use of the underlying `ExtentFamily` is also settled by now

💡 ''Implementation concept for the allocation scheme complete and validated''
2023-07-13 19:43:22 +02:00
5055ba7144 Block-Flow: rationalise iterator usage
...with the preceding IterableDecorator refactoring,
the navigation and access to the storage extents can now be
organised into a clear progression

Allocator::iterator -> EpochIter -> Epoch&

Convenience management and support functions can then be
pushed down into Epoch, while iteration control can be done
high-level in BlockFlow, based on the helpers in Epoch
2023-07-13 18:35:10 +02:00
5a8463acce Block-Flow: allow optionally to supply sanity checks
Especially for the BlockFlow allocator, sanity checks are elided
for performance reasons; yet, generally speaking, it can be a very bad idea
to "optimise" away sanity checks. Thus an additional adaptor is provided
to layer such checks on top of an existing core; and IterEplorer now
always wires in this additional adaptor, and so the original behaviour
is now restored in this respect (and for the largest part of the code base)
2023-07-13 16:46:43 +02:00
42ac55ea7b Block-Flow: promote IterableDecorator
While at first sight just a superficial variation of the existing IterStateWrapper,
it became clear with the evolution of the IterExplorer framework that
this setup represents a distinct concept, and especially lends itself
for complex and cohesive collaboration in a layered pipeline. Which
may, or may not be a good idea, depending on the circumstances.

Now, for the implementation of the scheduler memory allocation scheme,
another twist is added to the picture: we can not effort the sanity checks
on each access, even more so when layering / adapting iterators, where
it is essential that the optimiser can remove all unnecessary warts.
2023-07-13 16:29:06 +02:00
946f7c17f7 Block-Flow: implement opening a new Epoch
..this is the most simple case, where no Epochs are opened yet
..add diagnostics to inspect alloc count and deadlines
..add accessors for the first/last underlying Extent
2023-07-13 04:41:58 +02:00
180c6b8d84 Block-Flow: define next steps to construct
...continue to proceed test-driven
...scheduler internals turn out to be intricate and cohesive,
   and thus the only hope is to adhere to strict testing discipline
2023-07-13 01:51:21 +02:00
18904e5b58 Block-Flow: completed implementation of low-level cyclic extent storage
..verified boundary cases for expansion while retaining addresses
of currently active extents...
2023-07-12 21:55:50 +02:00
824a626c2e Block-Flow: investigate proper working of on-demand allocation
Library: add "obvious" utility to the IterExplorer, allowing to
         materialise all contents of the Pipeline into a container

...use this to take a snapshot of all currently active Extent addresses
2023-07-12 19:19:41 +02:00
f5813a1f29 Block-Flow: veryfy proper handling of extent reuse
- use a checksum to prove that ctor / dtor of "content" is not invoked
- let the usage of active extents "wrap around" so that the mem block is re-used
- verify that the same data is still there
2023-07-12 04:53:30 +02:00
6409e0eb36 Block-Flow: implement iteration and expansion of ExtentFamily
The low-level allocator is basically implemented now,
but we still need to check thoroughly that the tricky
wrap-around and expansion logic behaves sane...
(see #1311)
2023-07-11 03:52:24 +02:00
3b929cf014 Block-Flow: better setup for iterator implementation
Using a Storage* within a wrapper as "pos" will work,
but is borderline trickery, since it amounts to subverting
the idea behind IterAdapter (which is to encapsulate a target
pointer with some control-logic in the managing container).

Using the same storage size and implementation overhead,
it is much more straight-forward to package the complete
iteration logic into a »State Core«, which in this case
however maintains a back-link to the ExtentFamily.
2023-07-11 02:03:50 +02:00
3401f18c2c Block-Flow: consider usage in ActivityTerm and rectify iteration
Iteration should just yield an Reference to an Extent,
thereby hiding all details of the actual raw storage (char[]).
This can be achieved by usind a wrapper type around a pointer
into the managing vector; from this pointer we may convert
into a vector::iterator with the trick described here

https://stackoverflow.com/a/37101607/444796


Furthermore, continued planning of the Activity-Language,
basically clarified the complete usage scenario for now;
seems all implementable right away without further difficulties
2023-07-11 01:08:26 +02:00
e86cb017a5 Block-Flow: implement cyclic usage of an extent pool
..with the ability to grow on demand..
..possibly add the new extents in the middle, by first allocating at the end
  and then using the std::rotate() algo to bring them to the point
  in the middle where new extents are required
2023-07-10 05:40:50 +02:00
c1b16349f2 Block-Flow: define next steps for implementation of low-level allocator 2023-07-09 04:03:02 +02:00
ccf0710903 Block-Flow: maintain an »Epoch« within the raw allocation Extent
- the idea is to use slot-0 in each extent for administrative metadata
- to that end, a specialised GATE-Activity is placed into slot-0
- decision to use the next-pointer for managing the next free slot
- thus we need the help of the underlying ExtentFamily for navigating Extents

Decision to refrain from any attempt to "fix" excessive memory usage,
caused by Epochs still blocked by pending IO operations. Rather, we
assume the engine uses sane parametrisation (possibly with dynamic adjustment)
Yet still there will be some safety limit, but when exceeding this limit,
the allocator will just throw, thereby killing the playback/render process
2023-07-09 01:32:27 +02:00
533112a4b0 Block-Flow: provide specialised ctor notation
...now able to create instances for all the relevant Activity verbs
2023-07-07 03:41:30 +02:00
f34ecafa1a Block-Flow: consider data storage for render activities
- decision to favour small memory footprint
- rather use several Activity records to express invocation
- design Activity record as »POD with constructor«
- conceptually, Activity is polymorphic, but on implementation
  level, this is "folded down" into union-based data storage,
  layering accessor functions on top
2023-07-06 16:35:42 +02:00
4ac995548a Block-Flow: identify required API operations
- decision how to handle the Extent storage (by forced-cast)
- decision to place the administrative record directly into the Extent

TODO not clear yet how to handle the implicit limitation for future deadlines
2023-07-05 15:12:20 +02:00
022d40a8cf Block-Flow: initial draft of ExtentFamily storage
using a simple yet performant data structure.
Not clear yet if this approach is sustainable

- assuming that no value initialisation happens for POD payload
- performance trade-off growth when in wrapped-state vs using a list
2023-07-04 04:42:53 +02:00
23a6fbdf4f Scheduler: investigate modes of operation
- analysis of Activity usage
- derive possible memory management schemes
- research regarding asynchronous IO
- decision regarding the memory management scheme
2023-07-03 18:40:37 +02:00
4176576db0 Scheduler: consider what operations are necessary for layer-1
....still about to find out what kinds of Activities there are,
and what reasonably to implement on layer-2 vs. layer-1

It is clear that the worker will typically invoke a doWork()
operation on layer-2, which in turn will iterate layer-1.

Each worker pulls and performs internal managmenet tasks exclusively
until encountering the next real render task, at which point it will
drop an exclusion flag and then engage into performing the actual
extended work for rendering...
2023-06-27 03:21:10 +02:00
3b6519a7c0 Scheduler: pass activity marker (low-level)
- define a simple record to represent the Activity
- define a handle with an ordering function
- low-level functions to...
  + accept such a handle
  + pick it from the entrace queue
  + pass it for priorisation into the PriQueue
  + dequeue the top priority element
2023-06-26 02:16:50 +02:00
bdcfc94b57 Scheduler: implementation technology
- use Boost-Lockfree as entrance queue for instructions
- use the STL Heap-Algo and Priority-Queue adaptor for time order
2023-06-25 01:02:12 +02:00
3169ba88ad Scheduler: devise the arrangement of basic components
- define organisation of vault-layer namespaces
- define the ground plan of the scheduler implementation
2023-06-24 03:14:17 +02:00
130bc095d9 the new design takes the old name
The second design from 2017, based on a pipeline builder,
is now renamed `TreeExplorer` ⟼ `IterExplorer` and uses
the memorable entrance point `lib::explore(<seq>)`

✔
2023-06-22 20:23:55 +02:00
d109f5e1fb bye bye Monad (closes #1276)
after completing the recent clean-up and refactoring work,
the monad based framework for recursive tree expansion
can be abandoned and retracted.

This approach from functional programming leads to code,
which is ''cool to write'' yet ''hard to understand.''

A second design attempt was based on the pipeline and decorator pattern
and integrates the monadic expansion as a special case, used here to
discover the prerequisites for a render job. This turned out to be
more effective and prolific and became standard for several exploring
and backtracking algorithms in Lumiera.
2023-06-22 20:23:55 +02:00
42f4e403ac Job-Planning: rework of dispatcher and pipeline builder complete (see #1301)
An extended series of refactoring and partial rewrites resulted
in a new definition of the `Dispatcher` interface and completes
the buildup of a Job-Planning pipeline, including the ability
to discover prerequisites and compute scheduling deadlines.

At this point, I am about to ''switch to the topic'' of the `Scheduler`,
''postponing'' the completion of the `RenderDrive` until the related
questions regarding memory management and Scheduler interface are settled.
2023-06-22 03:55:09 +02:00