BufferProvider: in preparation for a load test

To verify integration of this new engine-level facility,
it seems prudent to build a load test (without going into
extended stress- and performance testing).

For that we need
- random buffer demand
- a computational load that actually uses the buffer.

As part of the ''scheduler stress testing'', a self-calibrating
`ComputationalLoad` was developed, which even implements both a
merely computational load, but also a memory accessing computation.

This can be extended to optionally use an external buffer, instead
of allocating a precisely dimensioned memory block directly from heap.
Obviously this extension is ''dangerous'', since there is no way to
verify that the storage passed in can accommodated the storage required
based on the current calibration of the `ComputationalLoad`


As an aside: identified a timing check in `SchedulerService_test`
that showed the tendency to fail when invoked immediately after a build.
This commit is contained in:
Fischlurch 2026-05-27 17:08:23 +02:00
parent 7989e825b9
commit ae7c9e7889
3 changed files with 19 additions and 10 deletions

View file

@ -401,7 +401,7 @@ namespace test {
CHECK (activity::PASS == res); // instruction to check back once CHECK (activity::PASS == res); // instruction to check back once
pullWork(); pullWork();
CHECK (activity::WAIT == res); // but next call will send this thread to sleep right away CHECK (activity::WAIT == res); // but next call will send this thread to sleep right away
CHECK (delay_us < 40); CHECK (delay_us < 50);
cout << "follow-up with some distance => follow-up delay"<<endl; cout << "follow-up with some distance => follow-up delay"<<endl;

View file

@ -87,7 +87,7 @@ namespace test {
/** Register this test class... */ /** Register this test class... */
LAUNCHER (SchedulerUsage_test, "unit engine"); LAUNCHER (SchedulerUsage_test, "integration engine");

View file

@ -1324,6 +1324,7 @@ namespace test {
microseconds timeBase = LOAD_DEFAULT_TIME; microseconds timeBase = LOAD_DEFAULT_TIME;
size_t sizeBase = LOAD_DEFAULT_MEM_SIZE; size_t sizeBase = LOAD_DEFAULT_MEM_SIZE;
bool useAllocation = false; bool useAllocation = false;
size_t* externalBuff{nullptr};
/** cause a delay by computational load */ /** cause a delay by computational load */
double double
@ -1366,16 +1367,22 @@ namespace test {
return computationSpeed(false) != LOAD_SPEED_BASELINE; return computationSpeed(false) != LOAD_SPEED_BASELINE;
} }
size_t
requiredBuffSiz (uint scaleStep) const
{
return useAllocation? allocNeeded(scaleStep).first : 0;
}
private: private:
uint64_t uint64_t
roundsNeeded (uint scaleStep) roundsNeeded (uint scaleStep) const
{ {
auto desiredMicros = scaleStep*timeBase.count(); auto desiredMicros = scaleStep*timeBase.count();
return uint64_t(desiredMicros*computationSpeed(useAllocation)); return uint64_t(desiredMicros*computationSpeed(useAllocation));
} }
auto std::pair<size_t,size_t>
allocNeeded (uint scaleStep) allocNeeded (uint scaleStep) const
{ {
auto cnt = roundsNeeded(scaleStep); auto cnt = roundsNeeded(scaleStep);
auto siz = max (scaleStep * sizeBase, 1u); auto siz = max (scaleStep * sizeBase, 1u);
@ -1401,13 +1408,15 @@ namespace test {
causeMemProcessLoad (uint scaleStep) causeMemProcessLoad (uint scaleStep)
{ {
auto [siz,round] = allocNeeded (scaleStep); auto [siz,round] = allocNeeded (scaleStep);
lib::UninitialisedDynBlock<size_t> memBlock{siz}; lib::UninitialisedDynBlock<size_t> memBlock;
size_t* mem = externalBuff? externalBuff
: memBlock.allocate (siz);
Sink sink; Sink sink;
*memBlock.front() = sink+1; *mem = sink+1;
for ( ; 0 < round; --round) for ( ; 0 < round; --round)
for (size_t i=0; i<memBlock.size()-1; ++i) for (size_t i=0; i<siz-1; ++i)
memBlock[i+1] += memBlock[i]; mem[i+1] += mem[i];
sink = *memBlock.back(); sink = mem[siz-1];
sink = sink + 1; sink = sink + 1;
} }