BufferProvider: establish a setup for the load test
- provide mock instances of the relevant global services - use a base config record for the worker thread(s) - pregenerate ''Buffer Descriptors'' commonly used by the workers - further access to the buffer manager should then be possible by locking one of these descriptors...
This commit is contained in:
parent
9f0d7cc796
commit
f868a7462a
2 changed files with 173 additions and 75 deletions
|
|
@ -18,25 +18,34 @@
|
|||
|
||||
#include "test/run.hpp"
|
||||
#include "test/test-helper.hpp"
|
||||
#include "vault/mem/buffer-provider-setup.hpp"
|
||||
#include "vault/mem/engine-buffer-metadata.hpp"
|
||||
#include "vault/mem/engine-buffer-manager.hpp"
|
||||
#include "vault/mem/engine-buffer-allocator.hpp"
|
||||
#include "vault/mem/local-buffer-stage.hpp"
|
||||
#include "vault/mem/local-buffer-store.hpp"
|
||||
#include "vault/gear/test-chain-load.hpp"
|
||||
#include "lib/scoped-collection.hpp"
|
||||
#include "lib/depend-inject.hpp"
|
||||
#include "lib/iter-explorer.hpp"
|
||||
#include "lib/sync-barrier.hpp"
|
||||
#include "lib/iter-stack.hpp"
|
||||
#include "lib/thread.hpp"
|
||||
#include "lib/symbol.hpp"
|
||||
#include "lib/random.hpp"
|
||||
#include "test/diagnostic-output.hpp"////////////TODO
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <array>
|
||||
|
||||
using lib::explore;
|
||||
using lib::Depend;
|
||||
using lib::DependInject;
|
||||
using lib::Random;
|
||||
using lib::Literal;
|
||||
using lib::Thread;
|
||||
using lib::SyncBarrier;
|
||||
using lib::ScopedCollection;
|
||||
using vault::gear::test::ComputationalLoad;
|
||||
using std::this_thread::yield;
|
||||
using std::this_thread::sleep_for;
|
||||
|
|
@ -49,13 +58,132 @@ namespace mem {
|
|||
namespace test {
|
||||
|
||||
namespace { // Test parameters......
|
||||
const size_t ALLOC_REQ = 1024*1024; ///< use test-allocation request of 1 MiB size
|
||||
const size_t NUM_THREADS = 100; ///< press concurrently
|
||||
|
||||
const size_t MAX_SCALE = 5; ///< maximum scale step for randomised resources
|
||||
|
||||
|
||||
/** seed a per-thread random generator */
|
||||
inline Random
|
||||
makeRandGen()
|
||||
{
|
||||
return Random{lib::seedFromDefaultGen()};
|
||||
}
|
||||
|
||||
class MultithreadBufferSetup
|
||||
: public BufferProviderSetup
|
||||
{
|
||||
public:
|
||||
MultithreadBufferSetup()
|
||||
: BufferProviderSetup{*this}
|
||||
{ }
|
||||
|
||||
auto buildStage() { return std::make_unique<LocalBufferStage>("BufferProviderLoad_test"); }
|
||||
auto buildStore() { return std::make_unique<LocalBufferStore>(); }
|
||||
};
|
||||
|
||||
|
||||
using BuffTypes = ScopedCollection<BuffDescr>;
|
||||
|
||||
/**
|
||||
* Mock dependency injection context
|
||||
* to emulate a Render Engine setup
|
||||
*/
|
||||
struct Ctx
|
||||
{
|
||||
static Depend<MultithreadBufferSetup> bufferProvider;
|
||||
static BuffTypes buffType_;
|
||||
|
||||
// install mock instances for the relevant services
|
||||
DependInject<EngineBufferMetadata>::Local<EngineBufferMetadata> metaHub;
|
||||
DependInject<EngineBufferManager>::Local<EngineBufferManager> globalPool;
|
||||
DependInject<BufferProvider>::Local<MultithreadBufferSetup> frontEnd;
|
||||
};
|
||||
|
||||
// static storage for the mock-engine entrance point
|
||||
Depend<MultithreadBufferSetup> Ctx::bufferProvider;
|
||||
BuffTypes Ctx::buffType_{MAX_SCALE};
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
struct WorkSetup
|
||||
{
|
||||
static constexpr uint NUM_JOBS = 10;
|
||||
static constexpr size_t BASE_BUFFSIZ = 100_KiB;
|
||||
|
||||
/// local random generator per thread
|
||||
Random rand_;
|
||||
ComputationalLoad load_;
|
||||
|
||||
WorkSetup()
|
||||
: rand_{makeRandGen()}
|
||||
{
|
||||
load_.timeBase = 1ms;
|
||||
load_.sizeBase = BASE_BUFFSIZ;
|
||||
load_.useAllocation = true;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Emulation of the load produced by a worker thread
|
||||
*/
|
||||
template<class CONF = WorkSetup>
|
||||
class DummyWorkerThread
|
||||
: CONF
|
||||
, public Thread
|
||||
{
|
||||
|
||||
|
||||
void
|
||||
workActivity()
|
||||
{
|
||||
for (uint i=0; i<CONF::NUM_JOBS; ++i)
|
||||
{
|
||||
uint delay = CONF::rand_.i(1000);
|
||||
SHOW_EXPR(delay)
|
||||
sleep_for (microseconds(delay));
|
||||
jobActivity();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
jobActivity()
|
||||
{
|
||||
///////OOO Idea : use an IterQueue of BuffHandles, fill these by a random-walk
|
||||
static uint ivo{0};
|
||||
SHOW_EXPR(++ivo)
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
registerBufferTypes()
|
||||
{
|
||||
if (Ctx::buffType_.empty()
|
||||
or Ctx::buffType_[0].buffSize() != CONF::BASE_BUFFSIZ )
|
||||
{
|
||||
Ctx::buffType_.clear();
|
||||
Ctx::buffType_.populate_by(
|
||||
BuffTypes::invoke(
|
||||
[&, i=0]() mutable
|
||||
{
|
||||
return Ctx::bufferProvider().getDescriptorFor (++i * CONF::BASE_BUFFSIZ);
|
||||
}));
|
||||
|
||||
}
|
||||
}
|
||||
public:
|
||||
DummyWorkerThread()
|
||||
: Thread{"Load-Test-Worker"
|
||||
,[this]{ workActivity(); }}
|
||||
{
|
||||
registerBufferTypes();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
/*****************************************************************************//**
|
||||
* @test Show how the central buffer manager of the Render Engine can be accessed.
|
||||
*/
|
||||
|
|
@ -78,63 +206,21 @@ namespace test {
|
|||
void
|
||||
demonstrate_EngineSetup()
|
||||
{
|
||||
Ctx mockEngine;
|
||||
|
||||
DummyWorkerThread worker;
|
||||
|
||||
while (worker)
|
||||
sleep_for (200us);
|
||||
|
||||
SHOW_EXPR("hurgha")
|
||||
SHOW_EXPR(bool(mockEngine.globalPool))
|
||||
SHOW_EXPR(bool(mockEngine.metaHub))
|
||||
SHOW_EXPR(bool(mockEngine.frontEnd))
|
||||
}
|
||||
|
||||
|
||||
|
||||
struct DefaultSetup
|
||||
{
|
||||
static constexpr uint NUM_JOBS = 10;
|
||||
static constexpr size_t BASE_BUFFSIZ = 100_KiB;
|
||||
|
||||
/// local random generator per thread
|
||||
Random rand_;
|
||||
ComputationalLoad load_;
|
||||
|
||||
DefaultSetup()
|
||||
: rand_{makeRandGen()}
|
||||
{
|
||||
load_.timeBase = 1ms;
|
||||
load_.sizeBase = BASE_BUFFSIZ;
|
||||
load_.useAllocation = true;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Emulation of the load produced by a worker thread
|
||||
*/
|
||||
template<class CONF = DefaultSetup>
|
||||
class DummyWorkerThread
|
||||
: CONF
|
||||
, public Thread
|
||||
{
|
||||
|
||||
|
||||
void
|
||||
workActivity()
|
||||
{
|
||||
for (uint i=0; i<CONF::NUM_JOBS; ++i)
|
||||
{
|
||||
uint delay = CONF::rand_.i(1000);
|
||||
sleep_for (microseconds(delay));
|
||||
jobActivity();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
jobActivity()
|
||||
{
|
||||
///////OOO Idea : use an IterQueue of BuffHandles, fill these by a random-walk
|
||||
}
|
||||
|
||||
public:
|
||||
DummyWorkerThread()
|
||||
: Thread{"Load-Test-Worker"
|
||||
,[this]{ workActivity(); }}
|
||||
{ }
|
||||
};
|
||||
|
||||
|
||||
/** @test
|
||||
*
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -147095,8 +147095,8 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension)
|
|||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#a4c8cf" COLOR="#22695a" CREATED="1776819109351" ID="ID_1998897998" MODIFIED="1779579198844" TEXT="EngineBufferManager">
|
||||
<linktarget COLOR="#1b1ac0" DESTINATION="ID_1998897998" ENDARROW="Default" ENDINCLINATION="15;-85;" ID="Arrow_ID_1964764365" SOURCE="ID_1791813360" STARTARROW="None" STARTINCLINATION="-438;24;"/>
|
||||
<linktarget COLOR="#2270cc" DESTINATION="ID_1998897998" ENDARROW="Default" ENDINCLINATION="389;-2271;" ID="Arrow_ID_58701695" SOURCE="ID_853514765" STARTARROW="None" STARTINCLINATION="-1483;82;"/>
|
||||
<linktarget COLOR="#1b1ac0" DESTINATION="ID_1998897998" ENDARROW="Default" ENDINCLINATION="15;-85;" ID="Arrow_ID_1964764365" SOURCE="ID_1791813360" STARTARROW="None" STARTINCLINATION="-438;24;"/>
|
||||
<icon BUILTIN="yes"/>
|
||||
<node COLOR="#338800" CREATED="1776819156872" ID="ID_1476925601" MODIFIED="1778788622379" TEXT="Grundkonzeption schaffen">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
|
|
@ -148526,8 +148526,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension)
|
|||
konkret müssen giftige Situationen provoziert werden, um zu sehen daß der Code selbst dann nicht entgleist, wenn man ihn an die Grenzen treibt. Diese Grenzen sind jedoch ohne eine vertiefte Analyse nicht bekannt, und diese Analyse möchte ich (jetzt aus Zeitgründen) nicht machen. Daher der Ansatz, möglichst viel relevante Unregelmäßigkeiten reinzubringen
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
</richcontent>
|
||||
</html></richcontent>
|
||||
</node>
|
||||
<node CREATED="1779844392008" ID="ID_31203292" MODIFIED="1779844470562" TEXT="an später denken: man möchte vielleicht mal den Einfluß des CPU-cache betrachten">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
|
|
@ -148563,8 +148562,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension)
|
|||
</li>
|
||||
</ul>
|
||||
</body>
|
||||
</html>
|
||||
</richcontent>
|
||||
</html></richcontent>
|
||||
</node>
|
||||
<node CREATED="1779896398730" ID="ID_1701977765" MODIFIED="1779896412644" TEXT="diese Aufruf-Ordnung erfolgt im Main-Thread und ist deterministisch"/>
|
||||
</node>
|
||||
|
|
@ -148579,8 +148577,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension)
|
|||
der default-Wert sind 1KiB — aber die zu erwartenden Repetitionen für 100µs sind bereits so ca 10k. Deshalb wird mit diesen Standard-Settings der Buffer x mal bespielt, und für etwas größere Last-Zeiten (1ms und mehr) fällt die Granularität des Buffers kaum noch ins Gewicht
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
</richcontent>
|
||||
</html></richcontent>
|
||||
</node>
|
||||
<node CREATED="1779928703245" ID="ID_1631634789" MODIFIED="1779928962463" TEXT="ein großer Buffer wird mindestens einmal bespielt">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
|
|
@ -148600,8 +148597,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension)
|
|||
Extrembeispiel: angenommen, die Kalibrierung ist nahe am default, also 100 Schritte pro µs, dann brauchen wir 10000 Schritte für 100µs. Wenn der Buffer aber 1 MiB groß ist, machen wir 1 Mio Schritte. Das bedeutet, egal was wir vorgeben, wir landen bei ~10ms. Die Kalibrierung wird sich zwar »auf den Kopf stellen«, aber das Aufrunden verhindert, das die Kalibrierung zum Tragen kommt.
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
</richcontent>
|
||||
</html></richcontent>
|
||||
</node>
|
||||
<node COLOR="#338800" CREATED="1779929352367" ID="ID_381566945" MODIFIED="1779932443069" TEXT="man könnte das mit einer Modulus-Rechnung korrekt lösen">
|
||||
<icon BUILTIN="idea"/>
|
||||
|
|
@ -148622,8 +148618,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension)
|
|||
Aber nun lasse ich die Schleife stets bis zur kalibrierten Rundenzahl laufen, und lediglich die Schreib-Position rotiert, gesteuert durch einen modulus auf (size - 1), da wir ja eine zusätzliche Speicherzelle brauchen, um jeweils die summe mit dem Vorgänger reinzuschreiben.
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
</richcontent>
|
||||
</html></richcontent>
|
||||
</node>
|
||||
</node>
|
||||
<node COLOR="#435e98" CREATED="1779932445997" ID="ID_1079860125" MODIFIED="1779933985655" TEXT="Test ComputationalLoad">
|
||||
|
|
@ -148690,16 +148685,21 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension)
|
|||
<node CREATED="1779938411204" ID="ID_1370099969" MODIFIED="1779938428022" TEXT="sollte sich an der Verwendung in der Engine orientieren..."/>
|
||||
<node CREATED="1779938428956" ID="ID_1626444519" MODIFIED="1779938438163" TEXT="also in der Vorbereitungsphase">
|
||||
<node CREATED="1779938470147" ID="ID_1823680981" MODIFIED="1779938483599" TEXT="konkret: im Worker-Init"/>
|
||||
<node CREATED="1779938484443" ID="ID_355256150" MODIFIED="1779938501748" TEXT="per EngineCtx::access().mem auf den BufferProvider zugreifen"/>
|
||||
<node COLOR="#5b280f" CREATED="1779938484443" ID="ID_355256150" MODIFIED="1779982781115" TEXT="per EngineCtx::access().mem auf den BufferProvider zugreifen">
|
||||
<icon BUILTIN="closed"/>
|
||||
<node CREATED="1779982783249" ID="ID_68750997" MODIFIED="1779982786710" TEXT="geht so nicht"/>
|
||||
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1779982787666" ID="ID_431944041" MODIFIED="1779982801101" TEXT="»Engine« liegt im Steam-Layer">
|
||||
<icon BUILTIN="messagebox_warning"/>
|
||||
</node>
|
||||
</node>
|
||||
<node CREATED="1779938502773" ID="ID_794046860" MODIFIED="1779938532040" TEXT="von diesem die Typ-Deskriptoren geben lassen"/>
|
||||
<node CREATED="1779938532878" ID="ID_17738" MODIFIED="1779938548302" TEXT="passiert effektiv sequenziert im Main-Thread"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1779938554185" ID="ID_609113292" MODIFIED="1779938666502" TEXT="⟹ muß also Depend<EngineFacilities> instrumentieren">
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1779938554185" ID="ID_609113292" MODIFIED="1779982844157" TEXT="⟹ muß daher lib::Depend instrumentieren...">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
<node CREATED="1779938710204" ID="ID_1736771261" MODIFIED="1779938720856" TEXT="brauche eine Subklasse von EngineFacilities"/>
|
||||
<node CREATED="1779938736586" ID="ID_452153689" MODIFIED="1779938768255" TEXT="diese liefert die produktiv-Konfiguration des BufferProviders"/>
|
||||
<node BACKGROUND_COLOR="#fdfdcf" COLOR="#ff0000" CREATED="1779982847525" ID="ID_1455958640" MODIFIED="1779982879491" TEXT="eine produktiv-Konfiguration des BufferProviders">
|
||||
<icon BUILTIN="flag-pink"/>
|
||||
</node>
|
||||
<node CREATED="1779938777523" ID="ID_792406911" MODIFIED="1779938786046" TEXT="außerdem...">
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1779938787106" ID="ID_1927878895" MODIFIED="1779938806832" TEXT="EngineMetadata instrumentieren">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
</node>
|
||||
|
|
@ -148707,9 +148707,11 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension)
|
|||
<icon BUILTIN="flag-yellow"/>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#f8f1cb" COLOR="#a50125" CREATED="1779938827316" ID="ID_1745334491" MODIFIED="1779938944463" TEXT="der Service-Zugriff allerdings erfolgt »kanonisch« (ohne Rücksicht auf die Instrumentierung)">
|
||||
<node BACKGROUND_COLOR="#f8f1cb" COLOR="#a50125" CREATED="1779938827316" ID="ID_1745334491" MODIFIED="1779982883437" TEXT="der Service-Zugriff allerdings erfolgt »kanonisch« (ohne Rücksicht auf die Instrumentierung)">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head/>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
...das ist sogar wichtig, damit wir uns ähnlich wie in der echten Engine verhalten, incl. Zugriff über double-checked locking;
|
||||
|
|
@ -148721,9 +148723,19 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension)
|
|||
Für das korrekte Test-Setup ist es <i>lediglich erforderlich, daß effektiv</i> die instrumentierung stattfindet <i>bevor</i> der jeweilige Test-Worker gestartet wird.
|
||||
</p>
|
||||
</body>
|
||||
</html></richcontent>
|
||||
</html>
|
||||
</richcontent>
|
||||
<icon BUILTIN="messagebox_warning"/>
|
||||
</node>
|
||||
<node CREATED="1779988096766" ID="ID_505434940" MODIFIED="1779988102193" TEXT="Anordnung im Test-Code">
|
||||
<node CREATED="1779988104069" ID="ID_197563801" MODIFIED="1779988109480" TEXT="verwende einen Mock-Ctx"/>
|
||||
<node CREATED="1779988112724" ID="ID_1508494695" MODIFIED="1779988129476" TEXT="dieser hält die drei instrumentierten Mock-Instanzen"/>
|
||||
<node CREATED="1779988130330" ID="ID_743202526" MODIFIED="1779988137709" TEXT="sowie den globalen API-Zugang"/>
|
||||
<node CREATED="1779988138597" ID="ID_1934175122" MODIFIED="1779988148072" TEXT="und prekonfigurierte Buffer-Descriptoren"/>
|
||||
<node CREATED="1779988156254" ID="ID_1478428488" MODIFIED="1779988193895" TEXT="definiere die TestWorker-Klasse daneben (⟶ lokaler Zugriff in der translation-Unit)">
|
||||
<icon BUILTIN="idea"/>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node CREATED="1779937246315" ID="ID_863725806" MODIFIED="1779937253926" TEXT="Job-Laststruktur erzeugen">
|
||||
<node CREATED="1779937260262" ID="ID_26328925" MODIFIED="1779937450294" TEXT="wird doch etwas knifflig, denn es müssen Buffer-Descriptor verwendet werden">
|
||||
|
|
@ -153351,8 +153363,8 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension)
|
|||
</node>
|
||||
</node>
|
||||
<node COLOR="#338800" CREATED="1702180165409" ID="ID_168687387" MODIFIED="1779842195670" TEXT="verify_computation_load">
|
||||
<linktarget COLOR="#c26a36" DESTINATION="ID_168687387" ENDARROW="Default" ENDINCLINATION="-214;715;" ID="Arrow_ID_917319622" SOURCE="ID_257805938" STARTARROW="None" STARTINCLINATION="-169;-474;"/>
|
||||
<linktarget COLOR="#0f90ad" DESTINATION="ID_168687387" ENDARROW="Default" ENDINCLINATION="561;-1397;" ID="Arrow_ID_1543255992" SOURCE="ID_734254741" STARTARROW="None" STARTINCLINATION="-843;54;"/>
|
||||
<linktarget COLOR="#c26a36" DESTINATION="ID_168687387" ENDARROW="Default" ENDINCLINATION="-214;715;" ID="Arrow_ID_917319622" SOURCE="ID_257805938" STARTARROW="None" STARTINCLINATION="-169;-474;"/>
|
||||
<icon BUILTIN="button_ok"/>
|
||||
<node COLOR="#338800" CREATED="1702180170904" ID="ID_1257116598" MODIFIED="1702317797128" TEXT="prüft die konfigurierbare CPU-Last">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
|
|
|
|||
Loading…
Reference in a new issue