Commit graph

4877 commits

Author SHA1 Message Date
3edf727c23 Library: simplify diagnostics using a transient list
since the calculation to find the current block start
has been recast as a private method, it is now possible to
calculate the allocation statistics without mutating the pos pointer.

To enable such usages, add a wrapper for `LinkedElements` to expose
an element-pointer temporarily as a immutable `LinkedElements` list,
allowing to iterate or use subscript and size information functions
2024-05-25 23:35:12 +02:00
08e0f52e61 Library: low-level implementation internals covered
...including overflow into new extents, alignment padding
and chaining and invocation of destructors.
2024-05-25 20:01:23 +02:00
be398e950a Library: better let C++ handle the destructors
...what I've implemented yesterday is effectively the same functionality
as provided automatically by the C++ object system when using a virtual destructor.
Thus a much cleaner solution is to turn `Destructor` into a interface
and let C++ do all the hard work.

Verified in test: works as intended
2024-05-25 19:27:17 +02:00
71d5851701 Library: implement optional invocation of destructors
This is the first draft, implementing the invocation explicitly
through a trampoline function. While it seems to work,
the formulation can probably be simplified....
2024-05-25 05:14:36 +02:00
e8d9dcd9bf Library: implement accounting of storage size
These diagnostics helpers must rely on low-level trickery,
since the implementation strives at avoiding unnecessary storage overhead.
Since `AllocationCluster` is move-only (for good reasons) and `StorageManager`
can not be constructed independently, a »backdoor« is created by
forced cast, relying on the known memory layout
2024-05-24 17:05:50 +02:00
13e22f315a Library: clarify details of the low-level allocation
- rather accept hard-wired limits than making the implementation excessively generic
- by exploiting the layout, the administrative overhead can be reduced significantly
- the trick with the "virtual managment overlay" allows to hand-off most of the
  clean-up work to C++ destructor invocation
- it is important to verify these low-level arrangements explicitly by unit-test
2024-05-19 17:53:51 +02:00
72aea53ac3 Library: idea for an extent management mechanism
* this is pure old-style low-level trickery
 * using a layout trick, the `AllocationCluster`
   can be operated with the bare minimum of overhead
 * this trick relies on the memory layout of `lib::LinkedElements`
2024-05-18 02:35:20 +02:00
c623298ac8 Library: draft standard case for actual memory allocation
- attempt to use the minimal possible storage for the management itself
- rely on the `std::align()` function to do the actual placement
2024-05-16 02:47:01 +02:00
eeda3aaa56 Library: remove elaborate allocation logic
...due to the decision to use a much simpler allocation scheme
to increase probability for actual savings, after switching the API
and removing all trading related aspects, a lot of further code is obsoleted
2024-05-16 01:46:41 +02:00
13f51c910c Library: work out ramifications of the changed design
Notably this raises the difficult question,
whether to ensure **invocation of destructors**.

Not invoking dtors ''breaks one of the most fundamental contracts''
of the C++ language — yet the infrastructure to invoke dtors in such
a heterogeneous cluster of allocations creates a hugely significant
overhead and is bound to poison the caches (objects to be deallocated
typically sit in cold memory pages).

What makes this decision especially daunting is the fact that the
low-level-Model can be expected to be one of the largest systemic
data structures (letting aside the media buffers).

I am leaning towards a compromise: turn down this decision
towards the user of the `AllocationCluster`
2024-05-15 19:59:05 +02:00
fd49c68096 Library: draft a simpler STL complient allocation interface
After some analysis, it became clear that the existing code for
`AllocationCluster` (while in itself valid) will likely miss the point
for the expected usage in the low-level Model: most segments of the
model will be rather small, and thus there is not enough potential for
amortisation when using such a per-type and per-segment scheme;
a rather simplistic linear allocator will be sufficient.

On the other hand, with the current C++ standard it is easy to provide
a complient allocator implementation for STL containers, and thus the
interface should be retro-fitted accordingly.
2024-05-15 01:38:16 +02:00
db30da90ce Invocation: consider storage and allocation of fan-in/fan-out
At the time of the initial design attempts, I naively created a
classic interface to describe an fixed container allocated ''elsewhere.''

Meanwhile the C++ language has evolved and this whole idea looks
much more as if it could be a ''Concept'' (C++20). Moreover, having
several implementations of such a container interface is deemed inadequate,
since it would necessitate ''at least two indirections'' — while
going the Concept + Template route would allow to work without any
indirection, given our current understanding that the `ProcNode` itself
is ''not an interface'' — rather a building block.
2024-05-13 18:34:42 +02:00
c0d5341b15 Invocation: capture idea for sharpened invocation structure
- the starting point is the idea to build a dedicated ''turnout system''
- `StateAdapter`, `BuffTable` ⟶ `FeedManifold` and _Invocation_ will be fused
- actually, the `TurnoutSystem` will be ''pulled'' and orchestrate the invocation
- the structure is assumed to be recursive

The essence of the Node-Invocation, as developed 2009 / 2011 remains intact,
yet it will be organised along a clearer structure
2024-05-12 17:27:07 +02:00
bd9527716a Invocation: segregate first and second buffer feed implementation
Within the existing body of code, there are two unfinished attempts
towards building a node invocation and management of data buffers.

The first attempt was entirely driven from the angle of invoking a
processing function, while the second one draws from a wider scope
and can be considered the solution to build upon regarding data buffers
in general. However, the results of the first approach are well suited
for their specific purpose, so both solutions will be combined.

Thus the arrangement of data feeds going in and out of the render node
shall be renamed into `BuffTable` -> `FeedManifold`
2024-05-11 17:06:12 +02:00
9a435a667e Invocation: start with some rename-refactorings
... to plot a clearer understanding of the intended usage
2024-05-11 16:39:58 +02:00
bb3d565436 Invocation: Reassessment of existing code
...which seems to be basically fine thus far
...beyond some renaming and rearranging

''it turns out that the final, crucial links,
necessary to tie all together, are yet to be developed''
2024-05-05 15:12:23 +02:00
47e26e2a65 Invocation: initial considerations...
Looks like some code archaeology is required
to sort apart the various effort to get this topic started....
2024-04-21 02:58:30 +02:00
c934e7f079 Scheduler-test: reduce impact of scale adjustments on breakpoint-search
the `BreakingPoint` tool conducts a binary search to find the ''stress factor''
where a given schedule breaks. There are some known deviations related to the
measurement setup, which unfortunately impact the interpretation of the
''stress factor'' scale. Earlier, an attempt was made, to watch those factors
empirically and work a ''form factor'' into the ''effective stress factor''
used to guide this measurement method.

Closer investigation with extended and elastic load patters now revealed
a strong tendency of the Scheduler to scale down the work resources when not
fully loaded. This may be mistaken by the above mentioned adjustments as a sign
of a structural limiation of the possible concurrency.

Thus, as a mitigation, those adjustments are now only performed at the
beginning of the measurement series, and also only when the stress factor
is high (implying that the scheduler is actually overloaded and thus has
no incentive for scaling down).

These observations indicate that the »Breaking Point« search must be taken
with a grain of salt: Especially when the test load does ''not'' contain
a high degree of inter dependencies, it will be ''stretched elastically''
rather than outright broken. And under such circumstances, this measurement
actually gauges the Scheduler's ability to comply to an established
load and computation goal.
2024-04-18 01:39:27 +02:00
55842a8d4f Scheduler-test: identify and fix bug in allocator
...well — more of a logical contradiction, not so much a bug.
The underlying problematic situation arises when meanwhile the
Extent storage has been expanded, and especially the active slots
are in »wrapped state«. In this case, the newly allocated extents
must be rotated in, which invalidates existing index numbers.

This problem was amended by exploting a chaching mechanism, allowing
to re-attach and validate an index position still stored in an old
iterator; especially this can happen when attempting to attach a
follow-up dependency onto a job planned earlier, but not yet scheduled.

The problem here was an assertion failure, which was triggered with a
high probability; the fix for the problem detailed above used the yield()
function, while it actually was only interested in retrieving the
Extent's address to probe if the extent matches an known storage location.

The solution is to provide a dedicated function for this check, which
can then skip the sanity check (because in this case we do not want
to use the Extent, and thus can touch obsoleted/inactive Extents
without problem)
2024-04-18 01:39:27 +02:00
5b62438eb4 Scheduler-test: investigate logic problem related to the »Tick« deadline
In the end, I decided that it ''is to early to decide anything'' in this respect...

The actual situation encountered is a **Catch-22**:
 * in its current form, the »Tick« handler detects compulsory jobs beyond deadline
 * since such a Job ''must not be touched anymore,'' there is no way scheduling can proceed
 * so this would constitute a ''Scheduler Emergency''
All fine — just the »Tick« handler ''itself is a compulsory job'' — and being a job, it can well be driven beyond its deadline. In fact this situation was encountered as part of stress testing.

Several mitigations or real solutions are conceivable, but in the end,
too little is known yet regarding the integration of the scheduler within the Engine
Thus I'll marked the problematic location and opened #1362
2024-04-12 02:23:31 +02:00
1316ee2c7f Scheduler-test: adjust contention mitigation as result of testing
Investigate the behaviour over a wider range of job loads,
job count and worker pool sizes. Seemingly the processing
can not fully utilise the available worker pool capacity.

By inspection of trace-dumps, one impeding mechanism could
be identified: the »stickiness« of the contention mitigation.
Whenever a worker encounters repeated contention, it steps up
and adds more and more wait cycles to remove pressure from the
schedule coordination. As such this is fine and prevents further
degradation of performance by repeated atomic synchronisation.
However, this throttling was kept up needlessly after further
successful work-pulls. Since job times of several milliseconds
can be expected on average in media processing, such a long
retention would spread a performance degradation over a duration
of several frames. Thus, the scheme for step-down was changed
to decrease the throttling by a power series rather than just
documenting the level.
2024-04-12 02:23:31 +02:00
6e7f9edf43 Scheduler-test: calculate linear model as test result
Use the statistic functions imported recently from Yoshimi-test
to compute a linear regression model as immediate test result.

Combining several measurement series, this allows to draw conclusions
about some generic traits and limitations of the scheduler.
2024-04-09 17:10:21 +02:00
3517ab6965 Scheduler-test: fine-tuning of result presentation (Gnuplot)
Visual tweaks specific to this measurement setup
 * include a numeric representation of the regression line
 * include descriptive axis labels
 * improve the key names to clarify their meaning
 * heuristic code for the x-ticks
Package these customisations as a helper function into the measurement tool
2024-04-08 18:45:02 +02:00
8e33194882 Scheduler-test: settle definition of specific test setup and data
After a lot of further tinkering, seemingly arriving at a
somewhat satisfactory solution for the layout and arrangement of
test definitions and especially the table for measurement series.

While the complete setup remains fragile indeed, and complexity is more
hidden than reduced — the pragmatic compromise established yesterday
at least allows to reduce the amount of boilerplate in the test or
measurement setup to make the actual specifics stand out clearly.

----

As an aside, the usage of the `DataFile` type imported from Yoshimi-test
recently was re-shaped more towards a generic handling of tabular data with
CSV storage option; thus renaming the type now into `DataTable`.
Persistent storage is now just one option, while another usage pattern
compounds observation data into table rows, which are then directly
rendered into a CSV string, e.g. for visualisation as Gnuplot graph.
2024-04-08 03:58:15 +02:00
0d3dc91584 Scheduler-test: rework ParameterRange tool for data visualisation
Rework the existing tool to capture the measurement series
into the newly integrated CSV-based data storage, allowing
to turn the results into a Gnuplot-visualisation.
2024-04-04 02:52:57 +02:00
55f8f229f1 Library: customisation of the generated Gnuplot diagram 2024-04-03 19:31:00 +02:00
96202f845a Library: example to schow the secondary diagram
...which is added automatically whenever additional data columns are present

Result can only be verified visually

 * the upper diagram should show the first fibonacci points
 * a (correct) linear regression line should be overlayed in red
 * below, a secondary diagram should appear, with aligned axis
 * the row "one" in this diagram should be shown as impulses
 * the further rows "two" and "three" should be drawn as
   green points, using the secondary Y-axis (values 100-250)
 * Gnuplot can handle missing data points
2024-04-03 00:29:27 +02:00
c997fc2341 Library: develop Gnuplot code for flexible scatter-regression
The idea is to build the Layout-branching into the generated Gnuplot script,
based on the number of data columns detected. If there is at least one further
data column, then the "mulitplot" layout will be used to feature this
additional data in a secondary diagram below with aligned axis;
if more than one additional data column is present, all further
visualisation will draw points, using the secondary Y-axis

Moreover, Gnuplot can calculate the linear regresssion line itself,
and the drawing will then be done using an `arrow` command,
defining a function regLine(x) based on the linear model.
2024-04-02 23:59:59 +02:00
13a6bba381 Library: some minimal test coverage
check some tokens to be expected in the generated Gnuplot script
2024-04-02 21:45:58 +02:00
f37e651b61 Library: add some mutual integration between DataFile and CSVData
...both are related to CSV, and it is conceivable
to create inline CSVData in a test case to populate a DataFile
2024-04-02 21:18:23 +02:00
03c2191649 Library: rearrange support for CSV notation
- `forElse` belongs to the metaprogramming utils

- have a CSVLine, which is a string with custom appending mechanism

- this in turn allows CSVData to accept arbitrary sized tuples,
  by rendering them into CSVLine
2024-04-01 22:33:55 +02:00
b029c308f9 Library: sharpen detection of string conversion cases
the metafunction `is_basically<X>` performed only an equality match,
while, given it's current usage, it should also include a subtype-interface-match.

This changes especially the `is_StringLike<S>` metafunction,
both on const references and on classes built on top of string
or string_view.
2024-04-01 19:44:21 +02:00
be4d809a23 Library: improve helper to deal with self-shadowing ctor (see: #963)
Whenever a class defines a single-arg templated constructor,
there is danger to shadow the auto-generated copy operations,
leading to insidious failures.

Some months ago, I did the ''obvious'' and added a tiny helper,
allowing to mask out the dangerous case when the ''single argument''
is actually the class itself (meaning, it is a copy invocation and
not meant to go through this templated ctor...


As this already turned out as tremendously helpful, I now extended
this helper to also cover cases where the problematic constructor
accepts variadic arguments, which is quite common with builder-helpers
2024-04-01 19:40:19 +02:00
fc084c1ca5 Library: find a better organisation of entrance points to plotting
The intention is to create a library of convenient building blocks;
providing a visualisation should be as simple as invoking a free function
with CSV data, yet with the ability to tweak some lables or display
variations if desired.

This can be achieved by..
 * having a series of ready-made standard visualisations
 * expose a function call for each, accepting a data-context builder
 * provide secondary convenience shortcuts, which add some of the expected bindings
 * notably a shortcut is provided to take the data as CSV-string
 * augmented by a wrapper/builder to allow defining data points inline
2024-03-31 19:12:43 +02:00
85e77f58a9 Library: allow to move the Rec::Mutator
Basically GenNode and the enclosed record were designed to be
immutable — yet some valid usage patterns emerged where gradually
building structure seems adequate — which can be accomodated by
entering a ''mutation mode'' explicitly through the Rec::Mutator.

Over time, a builder usage style came in widespread usage, especially
when building test data structures. There seems to be no deeper reason
preventing the Mutator from being ''moved'' — notwithstanding the fact
that using such a ''movable builder'' can be dangerous, especially
when digressing from the strict »fluent inline builder« usage style
and storing the mutator into a variable.

For populating the data context for a text template instantiation,
we have now a valid case where it seems helpful to partially populate
the context and then move it further down into an implementation
function, which does the bulk of the work.
2024-03-31 19:12:42 +02:00
a6084bd2d6 Library: implement generation of a simple data visualisation
CSV data -> Gnuplot script
2024-03-31 01:46:12 +01:00
db0838ddcc Library: draft invocation framework for generating a Gnuplot
Deliberately keep it unstructured and add dedicated functions
for each new emerging use case; hopefully some commen usage scheme
will emerge over time.

 * Data is to be handed in as an iterator over CSV-strings.
 * will have to find out about additional parametrisation on a case-by-case base
2024-03-31 01:45:23 +01:00
f627d42b6e Library: develop a suitable gnuplot layout and styling
The default visuals of gnuplot are simple,
yet tend to look cluttered and are not well suited for our purpose

We need the following presentation
 * a scatter diagram with a regression line
 * additionally a secondary diagram stacked below, with aligned axis

Thus 🠲 R-T-F-M
 * The [http://gnuplot.info/ Gnuplot docu] is exhaustive, yet hard to get into
 * Helpful was this collection of [http://gnuplotting.org/ example solutions for scientific plots]
 * and — Stackoverflow...
2024-03-30 04:08:45 +01:00
918f96bb6f Library: complete ETD data-source binding and test (closes #1359)
A minimalist `TextTemplate` engine is available for in-project use.

 * supports only the bare minimum of features (no programming language)
   * substitution of `${placeholder}` by key-name data access
   * conditional section `${if key}...${end if}`
   * iteration over a data sequence
 * other then most solutions available as library,
   this implementation does **not require** a specific data type,
   nor does it invent a dynamic object system or JSON backend;
   rather, a generic ''Data Source Adapter'' is used, which can
   be specialised to access any kind of ''structured data''
 * the following `DataSource` specialisations are provided
   * `std::map<string,string>`
   * Lumiera »External Tree Description« (based on `GenNode`)
   * a string-based spec for testing
2024-03-28 03:18:02 +01:00
cfe54a5070 Library: introduce compact textual representation for GenNode
This extension is required to use GenNode as data source for text-template instantiation.
I am aware that such a function could counter the design intent for GenNode,
because it could be (ab)used to "just get the damn value" and then
parse back the results...
2024-03-28 03:14:21 +01:00
597d8191c7 Library: code the DataSource template
...turns out challenging, since our intention here
is borderline to the intended design of the Lumiera ETD.
It ''should work'' though, when combined with a Variant-visitor...
2024-03-27 04:07:55 +01:00
64f60356b7 Library: prepare for a ETD binding
Document existing data binding logic and investigate in detail
what must be done to enable a similar binding backed by Lumiera's ETD structures.
This analysis highlights some tricky aspects, which can be accommodated by
slight adjustments and generalisations in the `TextTemplate` implementation
 * `GenNode` is not structured string data, rather binary data
 * thus exposing a std::string_view is not adequate, requiring to
   pick up the result type from the actual data binding
 * moreover, to allow for arbitrary nested scopes, a back-pointer
   to the parent scope must be maintained, which requires stable memory locations.
   This can best be solved within the InstanceCore itself, which manages
   the actual hierarchy of data source references.
 * the existing code happens already to fulfil this requirement, but
   for sake of clarity, handling of such a nested scope is now extracted
   into a dedicated operation, to highlight the guaranteed memory layout.
2024-03-27 01:24:41 +01:00
c0439b265c Library: verify proper working of logic constructs
uncovers some minor implementation bugs, as can be expected...
2024-03-26 06:30:23 +01:00
3711bf185c Library: allow quoted values for the test data binding
...hoped to keep it simple, but this is inevitable, since we
want to provide a CSV list as value within a list of key=value
bindings, and all packaged into a simple string for easy testing.

Thus the parsing RegExp just needs two branches for simple and quoted vals
2024-03-26 02:45:22 +01:00
4e26d655a8 Library: workaround for tricky problems with Template-Argument-Deduction
We use a DataSrc<DAT> template to access the actual data to be substituted.
However, when applying the Text-Template, we need to pick the right
specialisation, based on the type of the actual data provided.

Here we face several challenges:

 * Class-Template-Argument-Deduction starts from the *primary* template's constructors.
   Without that, the compiler will only try the copy constructor and will
   never see the constructors of partial specialisations.
   This can be fixed by providing a ''dummy constructor''.
 * The specifics of how to provide a custom CTAD deduction guide
   for a **nested template** are not well documented. I have found
   several bug reports, and seemingly one of these bugs failed my
   my various attempts. Moreover it is ''not clear if such a deduction
   guide can even be given outside of the class definition scope.''
   For the intended usage pattern this would be crucial, since users
   are expected to provide further specialisations of the DataSrc-template
 * Thus I resorted to the ''old school solution,'' which is to use
   a ''free builder function'' as an extension point. Thus users could
   provide further overloads for the `buildDataSrc()` function.
 * Unfortunately, SFINAE-Tricks are way more limited for function overload.
   Thus it seems impossible to have a generic and more specialised cases,
   unless all special cases are disjoint.

Thus the solution is far from perfect, ''yet for the current situation it seems
sufficient'' (and C++20 Concepts will greatly help to resolve this kind of problems)
2024-03-26 02:41:42 +01:00
a89e272e35 Library: supply a string-spec-binding for tests
...implemented by simply parsing the string into key=value pairs,
which are then stored into a shared map. The actual data binding
implementation can thus be inherited from the existing Map-binding
2024-03-25 18:26:17 +01:00
9b6fc3ebe5 Library: fix handling of escapes
While they were detected just fine, thy were passed-through
unaltered, which subverts the purpose of such an escape,
which is to allow for the tag syntax to be present in the
processed, substituted document (e.g. when generating a
shell script)

thus `\${escaped}` becomes `${escaped}`
2024-03-25 15:44:48 +01:00
c09f44e20f Library: complete implementation of Map-databinding
...using a ''special protocol'' to represent iterative data sequences
 * use an Index-Key with a CSV list of element prefixes
 * synthesise key-prefixes for each data element
 * perform lookup with the decorated key first

This allows to somehow ''emulate'' nested associations within a single, flat Map.
Obviously this is more like a proof-of-concept; actually the Map-databinding
is meant to handle the simple cases, where just placeholders are to be substituted.

The logic structures are much more relevant when binding to structural data,
most notably to the Lumiera _External Tree Description_ format, which is
used for model data and inter-layer communication.
2024-03-25 04:23:59 +01:00
58ffbac7f3 Library: implement logic-interpretation
- the basic interpretation of Action-tokens is already in place
 - add the interpretation of conditional and looping constructs
 - this includes helpers for
   * reset to another Action-token index
   * recursive interpretation of the next token
   * handling of nested loop evaluation context

In order to make this implementation compile, also the skeleton
of the Map-string-string data binding must be completed, including
a draft how to handle nested keys in a simple map
2024-03-25 03:30:16 +01:00
dd67b9f97b Library: cover some definition errors 2024-03-25 00:38:35 +01:00
8d432a6e0b Library: connect both parts of the engine
...gets the hello-world test to run
2024-03-25 00:37:58 +01:00
20f2b1b90a Library: complete implementation of code generation
...including the handling of cross jumps / links
...verified by one elaborate example in the tests
2024-03-24 21:42:38 +01:00
fd1ce5f0c1 Library: add special treatment for std::string_view
Sting-view is tricky, since it deliberately does not define a
conversion operator; rather, string has an explicit constructor.
This design was chosen on purpose, since creating a string will
„materialise“ the string-view, which could have severe performance
ramifications when done automatically.

Regarding Lumiera's string-conversion tooling, it seems indicated
thus to add std::string_view explicitly as a known conversion path,
even while this conversion does not happen implicitly.
2024-03-24 21:36:18 +01:00
aab446ce48 Library: further straighten iteration logic
playing the »fence post problem« the other way round
and abandoning the ''pull processing'' in favour of direct manipulation
leads to much clearer formulation of the code-generation logic
2024-03-24 15:51:38 +01:00
bc8e947f3c Library: remould compiler to active iteration
...turns out the ''pipeline design'' is not a good fit for the
Action compilation, since the compiler needs to refer to previous Actions;
better to let the compiler ''build'' the `ActionSeq`
2024-03-24 14:21:44 +01:00
b835d6a012 Library: get the template compiler basically operative
...implementation of bracketed constructs and cross references still omitted

...define a fairly elaborate test example for parsing
2024-03-24 00:48:04 +01:00
a9cbe7eb90 Library: define skeleton of TextTemplate compilation
...implemented as »custom processing layer« within a
demand-driven parsing pipeline, with the ability to
inject additional Action-tokens to represent the intermittent
constant text between tags; special handling to expose one
constant postfix after the last active tag.
2024-03-23 19:38:53 +01:00
5b53b53c4c Library: solution for ''trailing prefix'' in parser-context
* use a string-view embedded into the context-λ
 * on each match clip off some starting prefix from this string-view
2024-03-23 02:55:28 +01:00
c2df391f48 Library: draft how a pipelined-parser could work 2024-03-23 02:55:28 +01:00
2a60f77bdf Library: improve formulation of the parsing regexp
- allow additional leading and trailing whitespace within token
- more precise on the sequence of keywords
- clearer build-up of the regexp syntax
2024-03-23 02:55:28 +01:00
10bda3a400 Library: develop a token-parsing regular expression
oh my!
2024-03-23 02:55:28 +01:00
9790feb822 Library: remould MatchSeq into a _Lumiera Forward Iterator_
MatchSeq was imported recently from the Yoshimi-testsuite,
as supporting helper for the CSV table component.

Actually this is just a thin wrapper on top of std::regex_iterator,
which in turn has properties and behaviour very similar to Lumiera's
»Forward Iterator« concept (in fact, it was a source of inspiration to
generalise such a pattern).

So this is an obvious round out and cleanup, as it requires just some
minor additions and adjustments to allow processing a sequence of matches
through a for-loop or some elaborate pipelining setup.
2024-03-23 02:55:28 +01:00
a2749adbc9 Library: also cover the smart-ptr usage variant
The way I've written this helper template, as a byproduct
it is also possible to maintain the back-refrence to the container
through a smart-ptr. In this case, the iterator-handle also manages
the ownership automatically.
2024-03-21 19:57:34 +01:00
f716fb0bee Library: build a helper to encapsulate container access by index
...mostly we want the usual convenient handling pattern for iterators,
but with the proviso actually to perform an access by subscript,
and the ability to re-set to another current index
2024-03-21 19:57:34 +01:00
76bd9ba6ce Library: establish evaluation and iteration on application 2024-03-21 19:57:34 +01:00
7893cc99c3 Library: further work out the framework for TextTemplate instantiation 2024-03-21 19:57:34 +01:00
d2dcf6c163 Library: draft the interpretation of a compiled TextTemplate
will use an iteration-pipeline, based on the »State Core« concept
2024-03-21 19:57:34 +01:00
5881b014fe Library: work out a treatment for text template substitution (see: #1359)
* establish the feature set to provide
 * choose scheme for runtime representation
 * break down analysis to individual parsing and execution steps
 * conclude which actions to conduct and the necessary data
 * derive the abstract binding API required
2024-03-21 19:57:34 +01:00
af1f549190 Library: Assessment and plan for a text templating engine
Conducted an extended investigation regarding text templating
and the library solutions available and still maintained today.

The conclusion is
 * there are some mature and widely used solutions available for C++
 * all of these are considered a mismatch for the task at hand,
   which is to generate Gnuplot scripts for test data visualisation

Points of contention
 * all solutions offer a massive feature set, oriented towards web content generation
 * all solutions provide their own structured data type or custom property-tree framework

**Decision** 🠲  better to write a minimalistic templating engine from scratch rather
2024-03-21 19:57:34 +01:00
a90b9e5f16 Library: uniform definition scheme for error-IDs
In the Lumiera code base, we use C-String constants as unique error-IDs.
Basically this allows to create new unique error IDs anywhere in the code.

However, definition of such IDs in arbitrary namespaces tends to create
slight confusion and ambiguities, while maintaining the proper use statements
requires some manual work.

Thus I introduce a new **standard scheme**
 * Error-IDs for widespread use shall be defined _exclusively_ into `namespace lumiera::error`
 * The shorthand-Macro `LERR_()` can now be used to simplify inclusion and referral
 * (for local or single-usage errors, a local or even hidden definition is OK)
2024-03-21 19:57:34 +01:00
59390cd2f8 Library: reorder some pervasively used includes
reduce footprint of lib/util.hpp
 (Note: it is not possible to forward-declare std::string here)

define the shorthand "cStr()" in lib/symbol.hpp

reorder relevant includes to ensure std::hash is "hijacked" first
2024-03-21 19:57:34 +01:00
aa93bf9285 Library: cover statistic functions and linear regression 2024-03-16 03:05:49 +01:00
1f5518e2c8 Library: introduce functions for floating-point comparisons
- rough numeric comparisons (optionally given precision)
- epsilon comparison based on numeric_limits
2024-03-16 03:03:29 +01:00
95198c5f2a Library: need to exclude C++ stream sources from string conversion
In the Lumiera code base, a convenient string conversion is used
an many places, and is also ''magically'' integrated into the usual
C++ style output with `<<` operators.

However, there is a ''gotcha'' — in the ''rare cases'' when we
actually want to use the C++ input/output framework to copy stream
data from an input source into an output sink, obviously we do not want
the input source to be »string converted«....
2024-03-15 02:47:46 +01:00
599407deea Library: complete coverage of CSV data table including storage
also encompasses some coverage for the simplistic CSV format
implemented as storage backend for this data table
2024-03-15 02:45:45 +01:00
3b3600379a Library: introduce formating variants for decimal10
showDecimal -> decimal10 (maximal precision to survive round-trip through decimal representation=

showComplete -> max_decimal10 (enough decimal places to capture each possible distinct floating-point value)


Use these new functions to rewrite the format4csv() helper
2024-03-14 17:32:22 +01:00
01a098db99 Library: cover row handling of data table
...this uncovered one inconsistency: when directly adding values
into one of the embedded data vectors, the inconsistent size
was allowed to persist even when adding / removing lines.

This is in contradiction to the behavior for the CSV dump,
which uses index positions from the front of all vectors uniformely.

Thus changed the behaviour of adding a new row, so that it now
caps all vectors to a common size

also added function to clear the table
2024-03-14 17:29:16 +01:00
4a8364e422 Library: extend the DataFile to allow using it without storage
...seems obvious and does not compromise the simplistic design...
...we do check the file path anyway, just need to add saveAs()...
2024-03-13 18:57:48 +01:00
a6aad5261c Library: complete and verify temp-dir helper
verify also that clean-up happens in case of exceptions thrown;
as an aside, add Macro to check for ''any'' exception and match
on something in the message (as opposed to just a Lumiera Exception)
2024-03-13 02:50:13 +01:00
18b1d37a3d Library: also create unique temporary files
...using the same method for sake of uniformity

Also move the permissions helpers to the file.hpp support functions
and setup a separate unit test for these
2024-03-12 22:54:05 +01:00
a1832b1cb9 Library: base implementation of temp-dir creation
Inspired by https://stackoverflow.com/a/58454949

Verified behaviour of fs::create_directory
 --> it returns true only if it ''indeed could create'' a new directory
 --> it returns false if the directory exists already
 --> it throws when some other obstacle shows up

As an aside: the Header include/limits.h could be cleaned up,
and it is used solely from C++ code, thus could be typed, namespaced etc.
2024-03-12 20:14:29 +01:00
b426ea4921 Library: simple default implementation for random sequences
Since this is a much more complicated topic,
for now I decided to establish two instances through global variables:
 * a sequence seeded with a fixed starting value
 * another sequence seeded from a true entropy source

What we actually need however is some kind of execution framework
to define points of random-seeding and to capture seed values for
reproducible tests.
2024-03-12 02:34:19 +01:00
7a3e4098c8 Library: some first thoughts regarding random number generation
Relying on random numbers for verification and measurements is known to be problematic.
At some point we are bound to control the seed values -- and in the actual
application usage we want to record sequence seeding in the event log.

Some initial thoughts regarding this intricate topic.
 * a low-ceremony drop-in replacement for rand() is required
 * we want the ability to pick-up and control each and every usage eventually
 * however, some usages explicitly require true randomness
 * the ability to use separate streams of random-number generation is desirable
2024-03-12 00:48:11 +01:00
6e8c07ccd6 Library: draft tests to document the new features
Yesterday I decided to include some facilities I have written in 2022
for the Yoshimi-Testsuite. The intention is to use these as-is, and just
to adapt them stylistically to the Lumiera code base.

However — at least some basic documentation in the form of
very basic unit-tests can be considered »acceptance criteria«
2024-03-11 17:44:19 +01:00
0e88dec28a Library: integrate into the Lumiera code base
- reformat in Lumieara-GNU style
- use the Lumiera exceptions
- use Lumiera format-string frontend
- use lib/util

NOTE: I am the original author of the code introduced here,
and thus I can re-license it under GPL 2+
2024-03-11 17:38:30 +01:00
8c344b6a51 Library: bring in statistics helper from Yoshimi-test
[http://yoshimi.sourceforge.net/ Yoshimi] is a software sound synthesizer,
derived from `ZynAddSubFx` and developed by an OpenSource community.
The Repository [https://github.com/Ichthyostega/yoshimi-test/ Yoshimi-test]
is used by the Yoshimi developers to maintain a suite of automated
acceptance tests for the Yoshimi application.

This task involves watching execution times to detect long-term performance trends,
which in turn requires to maintain time-series data in CSV files and to perfrom some
simple statistic calculations, including linear regression. Requiring any external
statistics package as dependency was not deemed adequate for such a simple task,
and thus a set of self-contained helper functions was created as a byproduct.

This task attaches an excerpt of the Yoshimi-test history with those helpers.
2024-03-10 23:20:58 +01:00
605d747b8d Scheduler-test: attempt to find a viable Scheduler setup for this measurement
- better use a Test-Chain-Load without any dependencies
- schedule all at once
- employ instrumentation
- use the inner »overall time« as dependent result variable

The timing results now show an almost perfect linear dependency.
Also the inner overall time seems to omit the setup and tear-down time.
But other observed values (notably the avgConcurrency) do not line up
2024-03-08 01:30:12 +01:00
2d1bd2b765 Scheduler-test: fix deficiencies in search control mechanism
In binary search, in order to establish the invariant initially,
a loop is necessary, since a single step might not be sufficient.

Moreover, the ongoing adjustments jeopardise detection of the
statistical breaking point condition, by causing a negative delta
due to gradually approaching the point of convergence -- leading
to an ongoing search in a region beyond the actual breaking point.
2024-02-19 17:38:04 +01:00
96df8b20f9 Scheduler-test: introduce a form-factor to account for empiric adaptation
Relying on the new instrumentation facility, the actually effective
concurrency and cumulative run time of the test jobs can be established.
These can now be cast into a form-factor to represent actual excess expenses
in relation to the theoretical model.

By allowing to adjust the adapted schedule by this form factor,
it can be made to reflect more closely the actual empiric load,
hopefully leading to a more realistic effect of the stress-factor
and thus results better suited to conclude on generic behaviour.
2024-02-18 18:01:21 +01:00
27b34c4ed6 Scheduler-test: complements and fixes for the instrumentation
- supplement the pre-dimensioning for data capture; without that,
  sporadic memory corruption indeed happens (as expected, since
  concurrent re-allocation of the vector with an entry for each
  thread is not threadsafe, and this test shows much contention)

- add a top-level logging for better diagnostics of errors
  emanating from the test run
2024-02-15 20:33:28 +01:00
3e1239bd71 Scheduler-test: integrate instrumentation as optional feature
...can be activated on the Test-Chain-Load
...add a test case to validate its operation
2024-02-15 02:43:44 +01:00
580c1f1f68 Scheduler-test: complete instrumentation helper
Verify proper operation under pressure
using a multithreaded stress test
2024-02-15 00:52:59 +01:00
d0c1017580 Scheduler-test: resolve inconsistency in time accounting for instrumentation
Basically users are free to place the measurement calls to their liking.
This implies that bracketed measurement intervals can be defined overlapping
even within a single thread, thereby accounting the overlapping time interval
several times. However, for the time spent per thread, only actual thread
activity should be counted, disregarding overlaps. Thus introduce a
new aggregate, ''active time'', which is the sum of all thread times.

As an aside, do not need explicit randomness for the simple two-thread
test case — timings are random anyway...

+ bugfix for out-of-bounds access
2024-02-14 19:59:14 +01:00
9f0878f885 Scheduler-test: implement accounting for concurrency for instrumentation
...since we've established already an integration over the event timeline,
it is just one simple further step to determine the concurrency level
on each individual segment of the timeline. Based on this attribution

- the averaged concurrenty within the observation range can be computed as weighted mean
- moreover we can account for the precise cumulated time spent at each concurrency level
2024-02-14 04:18:43 +01:00
a1abed68f4 Scheduler-test: implement differentiated statistics counting for instrumentation
...break down the integration of the activation count over time
   into individual accounting
   - for each caseID
   - for each thread
2024-02-13 02:25:52 +01:00
08847ae283 Scheduler-test: implement the simplest case for the instrumentation
...which is to account for the cumulative time spent in code
marked by bracketed measurement calls ("enter" ... "leave")
2024-02-12 21:43:57 +01:00
754b3a2ea6 Scheduler-test: define storage for instrumentation helper
...using a simplistic allocation of next-slot based on initialisation
of a thread_local storage. This implies that this helper can not be
reset or reused, and that there can not be multiple or long-lived instances.

Keep-it-simple for now...
2024-02-12 20:26:38 +01:00
a68adb0364 Scheduler-test: need some instrumentation helper
...to sort out the interpretation of measurement results,
the actual duration and concurrency of ComputationLoad invocations
should be recorded, allowing to draw conclusions regarding the
Scheduler's performance as opposed to further system and thread
management effects due to concurrent operation under pressure.
2024-02-12 18:01:43 +01:00
54a91bcd5a Scheduler-test: investigation...
...and reflection about goals, methods of measurement and possible interpretation
2024-02-11 17:38:20 +01:00
602b7dbe3a Scheduler-test: continue investigation - combine base expense with stress factor
After an extended break due to "real life issues"....
Pick up the investigation, with the goal to ascertain a valid definition
and understanding of all test parameters. A first step is to establish
a baseline ''without using a computational load''; this might be some kind
of base overhead of the scheduler.

However -- the way the test scaffolding was built, it is difficult to
create a feedback loop for the statistical test setup with binary search,
since it is not really clear how the single control parameter of the test algorithm,
the so called "stress factor", shall be interpreted and how it can be
combined with a base load.

An extended series of tests, while watching the observed value patterns qualitatively,
seems to corroborate the former results, indicating that the base expense
in my test setup (using a debug build) is at ~200µs / Node / core.

Yet the difficulty to interpret this result and arrive at a logical and generic model
prevents me from translating this into a measurement scheme, which can
be executed independently from a specific test setup and hardware
2024-02-11 03:53:42 +01:00
3fb4baefd5 Scheduler-test: optionally allow to propagate immediately
This is just another (obvious) degree of freedom, which could be
interesting to explore in stress testing, while probably not of much
relevance in practice (if a job is expected to become runable earlier,
in can as well be just scheduled earlier).

Some experimentation shows that the timing measurements exhibit more
fluctuations, but also slightly better times when pressure is low, which
is pretty much what I'd expect. When raising pressure, the average
times converge towards the same time range as observed with time bound
propagation.

Note that enabling this variation requires to wire a boolean switch
over various layers of abstraction; arguably this is an unnecessary
complexity and could be retracted once the »experimentation phase«
is over.

This completes the preparation of a Scheduler Stress-Test setup.
2024-01-09 02:29:35 +01:00
f37b67f9bb Scheduler-test: ability to propagate solely by NOTIFY
...watching those dumps on the example Graph with excessive dependencies
made blatantly clear that we're dispatching a lot of unnecessary jobs,
since the actual continuation is /always/ triggered by the dependency-NOTIFY.
Before the rework of NOTIFY-Handling, this was rather obscured, but now,
since the NOTIFY trigger itself is also dispatched by the Scheduler,
it ''must be this job'' which actually continues the calculation, since
the main job ''can not pass the gate'' before the dependency notification
arrives.

Thus I've now added a variation to the test setup where all these duplicate
jobs are simply omitted. And, as expected, the computation runs faster
and with less signs of contention. Together with the other additional
parameter (the base expense) we might now actually be able to narrow down
on the observation of a ''expense socket'', which can then be
attributed to something like an ''inherent scheduler overhead''
2024-01-06 03:45:55 +01:00
032e4f6db5 Scheduler-test: extract search algo into lib 2024-01-06 01:23:00 +01:00
e52aed0b3c Scheduler-test: simplify binary search implementation
While the idea with capturing observation values is nice,
it definitively does not belong into a library impl of the
search algorithm, because this is usage specific and grossly
complicates the invocation.

Rather, observation data can be captured by side-effect
from the probe-λ holding the actual measurement run.
2024-01-04 02:37:05 +01:00
813f8721f7 Scheduler-test: build adapted schedule
...based on the adapted time-factor sequence
implemented yesterday in TestChainLoad itself

- in this case, the TimeBase from the computation load is used as level speed
- this »base beat« is then modulated by the timing factor sequence
- working in an additional stress factor to press the schedule uniformly
- actual start time will be added as offset once the actual test commences
2024-01-01 22:48:27 +01:00
409a60238a Scheduler-test: extract a generic grouping iterator
...so IterExplorer got yet another processing layer,
which uses the grouping mechanics developed yesterday,
but is freely configurable through λ-Functions.

At actual usage sit in TestChainLoad, now only the actual
aggregation computation must be supplied, and follow-up computations
can now be chained up easily as further transformation layers.
2023-12-31 00:41:01 +01:00
47ae4f237c Scheduler-test: investigate and fix further memory manager problem
In-depth investigation and reasoning highlighted another problem,
which could lead to memory corruption in rare cases; in the end
I found a solution by caching the ''address'' of the current Epoch
and re-validating this address on each Epoch-overflow.

After some difficulties getting any reliable measurement for a Release-build,
it turned out that this solution even ''improves performance by 22%''

Remark-1: the static blockFlow::Config prevents simple measurements by
  just recompiling one translation unit; it is necessary to build the
  relevant parts of Vault-layer with optimisation to get reliable numbers

Remark-2: performing a full non-DEBUG build highlighted two missing
  header-inclusions to allow for the necessary template specialisations.
2023-12-28 02:13:24 +01:00
3716a5b3d4 Scheduler-test: address defects in memory manager
...discovered by during investigation of latest Scheduler failures.
The root of the problems is that block overflow can potentially trigger
expansion of the allocation pool. Under some circumstances, this on-the fly
allocation requires a rotation of index slots, thereby invalidating
existing iterators.

While such behaviour is not uncommon with storage data structures (see std::vector),
in this case it turns out problematic because due to performance considerations,
a usage pattern emerged which exploits re-using existing storage »Slots« with known
deadline. This optimisation seems to have significant leverage on the
planning jobs, which happen to allocated and arrange a whole strike of
Activities with similar deadlines.

One of these problem situations can easily be fixed, since it is triggered
through the iterator itself, using a delegate function to request a storage expansion,
at which point the iterator is able to re-link and fix its internal index.
This solution also has no tangible performance implications in optimised code.

Unfortunately there remains one obscure corner case where such an pool expansion
could also have invalidated other iterators, which are then used later to
attach dependency relations; even a partial fix for that problem seems
to cause considerable performance cost of about -14% in optimised code.
2023-12-27 00:16:03 +01:00
09f0e92ea3 Scheduler-test: reorganise planning-job entrance and coordination
This amounts to a rather massive refactoring, prompted by the enduring problems
observed when pressing the scheduler. All the various glitches and (fixed) crashes
are related to the way how planning-jobs enter the schedule items,
which is also closely tied to the difficulties getting the locking
for planning-jobs correct.

The solution pursued hereby is to reorder the main avenues into the
scheduler implementation. There is now a streamlined main entrance,
which **always** enqueues only, allowing to omit most checks and
coordination. On the other hand, the complete coordination and dispatch
of the work capacity is now shifted down into the SchedulerCommutator,
thereby linking all coordination and access control close together
into a single implementation facility.

If this works out as intended
 - several repeated checks on the Grooming-Token could be omitted (performance)
 - the planning-job would no longer be able to loose / drop the Token,
   thereby running enforcedly single-threaded (as was the original intention)
 - since all planning effectively originates from planning-jobs, this
   would allow to omit many safety barriers and complexities at the
   scheduler entrance avenue, since now all entries just go into the queue.

WIP: tests pass compiler, but must be adapted / reworked
2023-12-26 03:06:30 +01:00
100252acdf Scheduler-test: bugfix - protect further allocation with guard
...whenever the planning falls behind schedule, it can happen that
the planner-worker immediately dispatches its own jobs; while the calculation
is broken anyway in this situation, especially this call scheme leads to
dropping the Grooming-Token prior to the calculation dispatched directly.

Since the dependency relation can only be established after creating
both predecessor and successor schedules, the corresponding allocation
of the NOTIFY-Activity is not protected against concurrent access,
which probably leads to the assertion failure due to corruption of
the allocator's internal data structures...
2023-12-23 21:38:53 +01:00
2cd51fa714 Scheduler-test: fix out-of-bound access
...causing the system to freeze due to excess memory allocation.

Fortunately it turned out this was not an error in the Scheduler core
or memory manager, but rather a sloppiness in the test scaffolding.
However, this incident highlights that the memory manager lacks some
sanity checks to prevent outright nonsensical allocation requests.

Moreover it became clear again that the allocation happens ''already before''
entering the Scheduler — and thus the existing sanity check comes too late.
Now I've used the same reasoning also for additional checks in the allocator,
limiting the Epoch increment to 3000 and the total memory allocation to 8GiB

Talking of Gibitbytes...
indeed we could use a shorthand notation for that purpose...
2023-12-21 20:25:43 +01:00
cca9787e08 Scheduler-test: increase capacity focus for neartime schedule
The scheduler implementation uses a randomised redistribution of
work capacity, taking into account the current ''scale'' of next pending event.
While this works surprisingly well overall, sometimes, in very tight and dense scheules
the workers seem to be spread somewhat too arbitrarily. Thus, if the scheduler
is working through a zone with several events as close as 1ms, often it takes
up to 3ms for another worker to show up.

With this change, the scattering range in the ''near zone'' (50µs ... 5ms)
is made dynamic, and now flexibly depends on current head time.
The closer the next event, the more tightly focussed will be the
capacity redistribution, if capacity becomes available just some 100µs
ahead of next demand, it is no longer „sent away“, but rather relocated
by roughly the same distance behind the next event.
2023-12-21 20:25:43 +01:00
707fbc2933 Scheduler-test: implement contention mitigation scheme
while my basic assessment is still that contention will not play a significant
role given the expected real world usage scenario — when testing with
tighter schedule and rather short jobs (500µs), some phases of massive contention
can be observed, leading to significant slow-down of the test.

The major problem seems to be that extended phases of contention will
effectively cause several workers to remain in an active spinning-loop for
multiple microseconds, while also permanently reading the atomic lock.

Thus an adaptive scheme is introduced: after some repeated contention events,
workers now throttle down by themselves, with polling delays increased
with exponential stepping up to 2ms. This turns out to be surprisingly
effective and completely removes any observed delays in the test setup.
2023-12-20 20:25:17 +01:00
84b92c2ee3 Scheduler-test: investigate and fix problem with the new Guard
...turns out to be a secondary problem (but must be fixed non the less).
Since the planning-job no longer drops the token now, the workers
have to wait; since they are waiting actively and contending on the token,
a significant slowdown can happen.

Sometimes the planning job gets behind its own scheduler and thus
enters dispatch, in which case it drops the GoomingToken, causing
an Assertion failure on return.

The **actual problem** however is the slowdown due to active spinning
2023-12-20 02:50:47 +01:00
b497980522 Scheduler-test: guard memory allocations by grooming-token
Turns out that we need to implemented fine grained and explicit handling logic
to ensure that Activity planning only ever happens protected by the Grooming-Token.
This is in accordance to the original design, which dictates that all management tasks
must be done in »management mode«, which can only be entered by a single thread at a time.
The underlying assumption is that the effort for management work is dwarfed in comparison
to any media calculation work.

However, in
5c6354882d
...I discovered an insidious border condition, an in an attempt to fix it,
I broke that fundamental assumpton. The problem arises from the fact that we
do want to expose a *public API* of the Scheduler. Even while this is only used
to ''seed'' a calculation stream, because any further planning- and management work
will be performed by the workers themselves (this is a design decision, we do not
employ a "scheduler thread")
Anyway, since the Scheduler API ''is'' public, ''someone from the outside'' could
invoke those functions, and — unaware of any Scheduler internals — will
automatically acquire the Grooming-Token, yet never release it,
leading to deadlock.

So we need a dedicated solution, which is hereby implemented as a
scoped guard: in the standard case, the caller is a management-job and
thus already holds the token (and nothing must be done). But in the
rare case of an »outsider«, this guard now ''transparently'' acquires
the token (possibly with a blocking wait) and ''drops it when leaving scope''
2023-12-19 23:38:57 +01:00
523586570f Scheduler-test: refactor and clarify λ-post
In the course of the last refactorings, a slight change in processing
order was introduced, which turned out to improve parallelisation considerably.

- Some further implementation logic can be relegated into the ActivationEvent
- the handling of start times now also incldues a check for sake of symmetry
- document the semantics change: λ-post no longer dispatches directly
2023-12-19 21:51:33 +01:00
f526360319 Scheduler-test: retract support for ''self-inhibition''
...this feature seems to be no longer necessary now;
leaving the actual implementation in-code for the time being,
but removed it from the public access API.
2023-12-19 21:07:33 +01:00
67036f45b0 Scheduler-test: Integration-test now running smoothly
The last round of refactorings yielded significant improvements
 - parallelisation now works as expected
 - processing progresses closer to the schedule
 - run time was reduced

The processing load for this test is tuned in a way to overload the
scheduler massively at the end -- the result must be correct non the less.

There was one notable glitch with an assertion failure from the memory manager.
Hopefully I can reproduce this by pressing and overloading the Scheduler more...
2023-12-18 23:34:10 +01:00
ba82a446fd Scheduler-test: address follow-up problem with depth-first
The rework from yesterday turned out to be effective ... unfortunately
a bit to much: since now late follow-up notifications take precedence,
a single worker tends to process the complete chain depth-first, because
the first chain will be followed and processed, even before the worker
was able to post the tasks for the other branches. Thus this single
worker is the only one to get a chance to proceed.

After some consideration, I am now leaning towards a fundamental change,
instead of just fixing some unfavourable behaviour pattern: while the
language semantics remains the same, the scheduler should no longer
directly dispatch into the next chain **from λ-post**. That is, whenever
a POST / NOTIFY is issued from the Activity-chain, the scheduler goes
through prioritisation.

This has further ramifications: we do not need a self-inhibition mechanism
any more (since now NOTIFY picks up the schedule time of the target).

With these changes, processing seems to proceed more smoothly,
albeit still with lots of contention on the Grooming token,
at least in the example structure tested here.
2023-12-17 23:46:44 +01:00
1cbb6b7371 Scheduler-test: rework handling of notifications in the Activity-Language
While the recent refactoring...
206c67cc

...was a step into the right direction, it pushed too hard,
overlooking the requirement to protect the scheduler contents
and thus all of the Activity-chains against concurrent modification.
Moreover, the recent solution still seems not quite orthogonal.

Thus the handling of notifications was thoroughly reworked:
- the explicit "double-dispatch" was removed, since actual usage
  of the language indicates that we only need notifications to
  Gate (and Hook), but not to any other conceivable Activity.
- thus it seems unnecessary to turn "notification" into some kind
  of secondary work mode. Rather, it is folded as special case
  into the regular dispatch.

This leads to new processing rules:
- a POST goes into λ-post (obviously... that's its meaning)
- a NOTIFY now passes its *target* into λ-post
- λ-post invokes ''dispatch''
- and **dispatching a Gate now implies to notify the Gate**

This greatly simplifies the »state machine« in the Activity-Language,
but also incurs some limitations (which seems adequate, since it is
now clear that we do not ''schedule'' or ''dispatch'' arbitrary
Activities — rather we'll do this only with POST and NOTIFY,
and all further processing happens by passing activation
along the chain, without involving the Scheduler)
2023-12-16 23:47:50 +01:00
75b5eea2d3 Scheduler-test: option to require activation by scheduler
use a feature of the Activity-Language prepared for this purpose:
self-Inhibition of the Chain. This prevents a prerequisite-NOTIFY
to trigger a complete chain of available tasks, before these tasks
have actually reached their nominal scheduling time.

This has the effect to align the computations much more strictly
with the defined schedule
2023-12-14 01:49:46 +01:00
3e84224f74 Scheduler-test: force dependency-wait to wake-up job
The main (test) thread is kept in a blocking wait until the
planned schedule is completed. If however the schedule overruns,
the wake-up job could just be triggered prematurely.

This can easily be prevented by adding a dependency from the last
computation job to the wake-up job. If the computation somehow
flounders, the SAFETY_TIMEOUT (5s) will eventually raise
an exception to let the test fail cleanly (shutting down
the Scheduler automatically)
2023-12-13 22:55:28 +01:00
206c67cc8a Scheduler-test: adapt λ-post to include deadline info
...it seems impossible to solve this conundrum other than by
opening a path to override a contextual deadline setting from
within the core Activity-Language logic.

This will be used in two cases
 - when processing a explicitly coded POST (using deadline from the POST)
 - after successfully opening a Gate by NOTIFY (using deadline from Gate)

All other cases can now supply Time::NEVER, thereby indicating that
the processing layer shall use contextual information (intersection
of the time intervals)
2023-12-13 19:42:38 +01:00
3bf3ca095b Scheduler-test: failure of extended cascading notifications
...this is an interesting test failure, which highlights inconsistencies
with handling of deadlines when processing follow-up from NOTIFY-triggers

There was also some fuzziness related to the ''meaning'' of λ-post,
leading to at least one superfluous POST invocation for each propagation;
fixing this does not solve the problem yet removes unnecessary overhead
and lock-contention
2023-12-13 19:27:45 +01:00
b987aa2446 Scheduler-test: single invocation of a computation load
...can now be assembled easily from existing parts
...use this setup as the simple introductory example in SchedulerService_test
2023-12-12 18:17:03 +01:00
b0bde3f0b2 Scheduler-test: fix bug in WorkForce scaling logic
this bug was there since the first draft, yet was covered
by another bug with the start-up logic.
And this latter one was fixed recently...

fa8622805

As a result, even when the COMPUTATION_CAPACITY is set to 0
still a single worker boots up (which should not be the case)

Solution: we do not need to "safeguard" against rounding errors,
since this is an internal implementation function, it is assumed
that the caller knows about its limitations...
2023-12-12 03:20:38 +01:00
69faaef8cd Scheduler-test: --- instrumentation ---
This partially reverts commit 72f11549e6.
"Chain-Load: Scheduler instrumentation for observation"

Hint: revert this changeset to re-introduce the print statements for diagnostic
2023-12-11 23:55:55 +01:00
9ef8e78459 Scheduler-test: implement memory-accessing load
...use an array of volatiles, and repeatedly add neighbouring cells
...bake the base allocation size configurable, and tie the alloc to the scale-step
2023-12-11 03:13:28 +01:00
df4ee5e9c1 Scheduler-test: implement pure computation load
..initial gauging is a tricky subject,
since existing computer's performance spans a wide scale

Allowing
 - pre calibration -98% .. +190%
 - single run ±20%
 - benchmark ±5%
2023-12-11 03:10:42 +01:00
beebf51ac7 Scheduler-test: draft a configurable CPU load component
...which can be deliberately attached (or not attached) to the
individual node invocation functor, allowing to study the effect
of actual load vs. zero-load and worker contention
2023-12-10 19:58:18 +01:00
fe6f2af7bb Chain-Load: combine all exit-hashes into a single global hash
...during development of the Chain-Load, it became clear that we'll often
need a collection of small trees rather than one huge graph. Thus a rule
for pruning nodes and finishing graphs was added. This has the consequence
that there might now be several exit nodes scattered all over the graph;
we still want one single global hash value to verify computations,
thus those exit hashes must now be picked up from the nodes and
combined into a single value.

All existing hash values hard coded into tests must be updated
2023-12-09 02:36:14 +01:00
1df328cfc1 Chain-Load: switch planning chunk-size from level to node
This is a trick to get much better scheduling and timing guesses.
Instead of targeting a specific level, rather a fixed number of nodes
is processed in each chunk, yet still always processing complete levels.

The final level number to expect can be retrieved from the chain-load graph.

With this refactoring, we can now schedule a wake-up job precisely
after the expected completion of the last level
2023-12-08 23:52:57 +01:00
7eca3ffe42 Scheduler-test: a helper for one-time operations
Invent a special JobFunctor...
 - can be created / bound from a λ
 - self-manages its storage on the heap
 - can be invoked once, then discards itself

Intention is to pass such one-time actions to the Scheduler
to cause some ad-hoc transitions tied to curren circumstances;
a notable example will be the callback after load-test completion.
2023-12-08 03:16:57 +01:00
030e9aa8a2 Scheduler / Activity-Lang: simplify handling of blocked Gate
In the first draft version, a blocked Gate was handled by
»polling« the Gate regularly by scheduling a re-invocation
repeatedly into the future (by a stepping defined through
ExecutionCtx::getWaitDelay()).

Yet the further development of the Activity-Language indicates
that the ''Notification mechanism'' is sufficient to handle all
foreseeable aspects of dependency management. Consequently this
''Gate poling is no longer necessary,'' since on Notification
the Gate is automatically checked and the activation impulse
is immediately passed on; thus the re-scheduled check would
never get an opportunity actually to trigger the Gate; such
an active polling would only be necessary if the count down
latch in the Gate is changed by "external forces".

Moreover, the first Scheduler integration tests with TestChainLoad
indicate that the rescheduled polling can create a considerable
additional load when longer dependency chains miss one early
prerequisite, and this additional load (albeit processed
comparatively fast by the Scheduler) will be shifted along
needlessly for quite some time, until all of the activities
from the failed chain have passed their deadline. And what
is even more concerning, these useless checks have a tendency
to miss-focus the capacity management, as it seems there is
much work to do in a near horizon, which in fact may not be
the case altogether.

Thus the Gate implementation is now *changed to just SKIP*
when blocked. This helped to drastically improve the behaviour
of the Scheduler immediately after start-up -- further observation
indicated another adjustment: the first Tick-duty-cycle is now
shortened, because (after the additional "noise" from gate-rescheduling
was removed), the newly scaled-up work capacity has the tendency
to focus in the time horizon directly behind the first jobs added
to the timeline, which typically is now the first »Tick«.

🡆 this leads to a recommendation, to arrange the first job-planning
chunk in such a way that the first actual work jobs appear in the area
between 5ms and 10ms after triggering the Scheduler start-up.Scheduler¡†
2023-12-07 22:12:41 +01:00
5abab5390d Scheduler: start-up working -- no need for pre-delay
Introducing a fixed pre-delay on each new Calc-Streem seemed like an obvious remedy,
yet on closer investigation it turned out that the start-up logic as such was contradictory,
which was only uncovered by some rather special schedule patterns.

After fixing the logic deficiencies, Scheduler starts up as intended
and the probabilistic capacity-control seems to work as designed.

Thus no need to introduce an artificial delay at begin, even while
this implies that typically the first round of job-planning will be
performed synchronous, in the invoking thread (which may be surprising,
but is completely within the limits of the architecture; we do not
employ specifically configured threads and planning should be done
in short chunks, thus the first chunk can well be done by the caller)
2023-12-07 21:02:39 +01:00
fa86228057 Scheduler: rework load-regulation
The first complete integration test with Chain-Load
highlighted some difficulties with the overall load regulation:
- it works well in the standard case (but is possibly to eager to scale up)
- the scale-up sometimes needs several cycles to get "off the ground"
- when the first job is dispatched immediately instead of going
  through the queue, the scheduler fails to boot up
2023-12-07 03:55:20 +01:00
72f11549e6 Chain-Load: Scheduler instrumentation for observation
- prime diagnostics with the first time invocation
- print timings relative to this first invocation
- DUMP output to watch the crucial scheduling operations
2023-12-06 23:54:33 +01:00
e761447a25 Chain-Load: setup simple integration test
- use a chain-load with 64 steps
- use a simple topology
- trigger test run with default stepping

TODO: Test hangs -> Timeout
2023-12-06 07:24:30 +01:00
481e35a597 Chain-Load: implement translation into Scheduler invocations
... so this (finally) is the missing cornerstone
... traverse the calculation graph and generate render jobs
... provide a chunk-wise pre-planning of the next batch
... use a future to block the (test) thread until completed
2023-12-06 01:54:35 +01:00
29ca3a485f Chain-Load: implement planning JobFunctor
- decided to abstract the scheduler invocations as λ
- so this functor contains the bare loop logic

Investigation regarding hash-framework:
It turns out that boost::hash uses a different hash_combine,
than what we have extracted/duplicated in lib/hash-value.hpp
(either this was a mistake, or boost::hash did use this weaker
 function at that time and supplied a dedicated 64bit implementation later)

Anyway, should use boost::hash for the time being
maybe also fix the duplicated impl in lib/hash-value.hpp
2023-12-04 16:29:57 +01:00
e0766f2262 Chain-Load: draft usage for Scheduler testing
- use a dedicated context "dropped off" the TestChainLoad instance
- encode the node-idx into the InvocationInstanceID
- build an invocation- and a planning-job-functor
- let planning progress over an lib::UninitialisedStorage array
- plant the ActivityTerm instances into that array as Scheduling progresses
2023-12-04 00:34:06 +01:00
c5679b0fd0 Library: Uninitialised-Storage array (see #1204)
Introduced as remedy for a long standing sloppiness:
Using a `char[]` together with `reinterpret_cast` in storage management helpers
bears danger of placing objects with wrong alignment; moreover, there are increasing
risks that modern code optimisers miss the ''backdoor access'' and might apply too
aggressive rewritings.

With C++17, there is a standard conformant way to express such a usage scheme.
 * `lib::UninitialisedStorage` can now be used in a situation (e.g. as in `ExtentFamily`)
   where a complete block of storage is allocated once and then subsequently used
   to plant objects one by one
 * moreover, I went over the code base and adapted the most relevant usages of
   ''placement-new into buffer'' to also include the `std::launder()` marker
2023-12-02 23:56:46 +01:00
229541859d Chain-Load: demonstrate use of reduction rule
... special rule to generate a fixed expansion on each seed
... consecutive reductions join everything back into one chain
... can counterbalance expansions and reductions
2023-11-30 03:20:23 +01:00
aafd277ebe Chain-Load: rework the pattern for dynamic rules
...as it turns out, the solution embraced first was the cleanest way
to handle dynamic configuration of parameters; just it did not work
at that time, due to the reference binding problem in the Lambdas.
Meanwhile, the latter has been resolved by relying on the LazyInit
mechanism. Thus it is now possible to abandon the manipulation by
side effect and rather require the dynamic rule to return a
''pristine instance''.

With these adjustments, it is now possible to install a rule
which expands only for some kinds of nodes; this is used here
to crate a starting point for a **reduction rule** to kick in.
2023-11-30 02:13:39 +01:00
3d5fdce1c7 Chain-Load: demonstrate use of the expansion rule
...played a lot with the parameters
...behaviour and DOT graphs look plausible
...document three typical examples with statistics
2023-11-29 02:58:55 +01:00
a780d696e5 Chain-Load: verify connectivity and recalculation
It seams indicated to verify the generated connectivity
and the hash calculation and recalculation explicitly
at least for one example topology; choosing a topology
comprised of several sub-graphs, to also verify the
propagation of seed values to further start-nodes.

In order to avoid addressing nodes directly by index number,
those sub-graphs can be processed by ''grouping of nodes'';
all parts are congruent because topology is determined by
the node hashes and thus a regular pattern can be exploited.

To allow for easy processing of groups, I have developed a
simplistic grouping device within the IterExplorer framework.
2023-11-27 21:58:37 +01:00
619a5173b0 Chain-Load: handle node seed and recalculation
- with the new pruning option, start-Nodes can now be anywhere
- introduce predicates to detect start-Nodes and exit-Nodes
- ensure each new seed node gets the global seed on graph construction
- provide functionality to re-propagate a seed and clear hashes
- provide functionality to recalculate the hashes over the graph
2023-11-26 22:28:12 +01:00
5af2279271 Chain-Load: ability to inject further shuffling
up to now, random values were completely determined by the
Node's hash, leading to completely symmetrical topology.
This is fine, but sometimes additional randomness is desirable,
while still keeping everything deterministic; the obvious solution
is to make the results optionally dependent on the invocation order,
which is simply to achieve with an additional state field. After some
tinkering, I decided to use the most simplistic solution, which is
just a multiplication with the state.
2023-11-26 19:46:48 +01:00
dbe71029b7 Chain-Load: now able to define RandomDraw rules
...all existing tests reproduced
...yet notation is hopefully more readable

Old:
  graph.expansionRule([](size_t h,double){ return Cap{8, h%16, 63}; })

New:
  graph.expansionRule(graph.rule().probability(0.5).maxVal(4))
2023-11-26 03:04:59 +01:00
f1c156b4cd Chain-Load: lazy init of functional configuration now complete
...so this was yet another digression, caused by the desire
somehow to salvage this problematic component design. Using a
DSL token fluently, while internally maintaining a complex and
totally open function based configuration is a bit of a stretch.
2023-11-25 23:47:20 +01:00