BufferProvider: add implementation for the asynchronous hand-over
...and define a test case, to perform the typical usage sequence within a lot of threads in parallel.
This commit is contained in:
parent
76542e873a
commit
0567633bd9
3 changed files with 151 additions and 10 deletions
|
|
@ -160,7 +160,7 @@ namespace mem {
|
|||
* @warning when the returned Alloc is [non-empty](\ref Alloc::empty),
|
||||
* the receiver is responsible for returning it after use.
|
||||
*/
|
||||
void async_requestAllocation (AllocRequest const&);
|
||||
void async_requestAllocation (AllocReceiver&, size_t sizRequest);
|
||||
|
||||
/**
|
||||
* Handle all enqueued asynchronous requests
|
||||
|
|
@ -181,9 +181,10 @@ namespace mem {
|
|||
|
||||
|
||||
inline void
|
||||
EngineBufferManager::async_requestAllocation (AllocRequest const& req)
|
||||
EngineBufferManager::async_requestAllocation (AllocReceiver& receiver, size_t sizRequest)
|
||||
{
|
||||
|
||||
REQUIRE (sizRequest);
|
||||
requestQueue_.push ({&receiver, sizRequest});
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -21,17 +21,26 @@
|
|||
//#include "vault/mem/engine-buffer-metadata.hpp"
|
||||
#include "vault/mem/engine-buffer-manager.hpp"
|
||||
#include "vault/mem/engine-buffer-allocator.hpp"
|
||||
#include "lib/scoped-collection.hpp"
|
||||
#include "lib/iter-explorer.hpp"
|
||||
//#include "lib/depend-inject.hpp"
|
||||
//#include "lib/thread.hpp"
|
||||
#include "lib/thread.hpp"
|
||||
//#include "lib/error.hpp"
|
||||
#include "lib/symbol.hpp"
|
||||
#include "test/diagnostic-output.hpp"////////////TODO
|
||||
|
||||
#include <algorithm>
|
||||
//#include <thread>
|
||||
#include <array>
|
||||
|
||||
//using std::this_thread::yield;
|
||||
//using lib::Thread;
|
||||
using lib::Thread;
|
||||
using lib::Literal;
|
||||
using lib::explore;
|
||||
//using lib::ScopedCollection;
|
||||
using std::this_thread::yield;
|
||||
using std::this_thread::sleep_for;
|
||||
using std::chrono::microseconds;
|
||||
using std::chrono_literals::operator ""ms;
|
||||
|
||||
namespace vault {
|
||||
namespace mem {
|
||||
|
|
@ -43,6 +52,8 @@ namespace test {
|
|||
|
||||
namespace { // Test helper
|
||||
|
||||
const size_t ALLOC_REQ = 1024*1024;
|
||||
const size_t NUM_THREADS = 100;
|
||||
// template<typename X>
|
||||
// Buff*
|
||||
// mark_as_Buffer(X& something)
|
||||
|
|
@ -66,7 +77,7 @@ namespace test {
|
|||
virtual void
|
||||
run (Arg)
|
||||
{
|
||||
// seedRand();
|
||||
seedRand();
|
||||
|
||||
demonstrate_AllocatorInterface();
|
||||
verify_syncRequest();
|
||||
|
|
@ -164,10 +175,87 @@ namespace test {
|
|||
|
||||
|
||||
|
||||
/** @test show that an working allocation request across thread boundaries */
|
||||
/** @test show that an working allocation request passes thread boundaries */
|
||||
void
|
||||
verify_asyncRequest()
|
||||
{
|
||||
struct LocalTestPool
|
||||
: AllocReceiver
|
||||
{
|
||||
Alloc
|
||||
retrieveAlloc()
|
||||
{
|
||||
Alloc received{nullptr, 0};
|
||||
inQueue_.pop (received); // Note: no-op if queue is empty
|
||||
return received;
|
||||
}
|
||||
|
||||
bool
|
||||
empty() const
|
||||
{
|
||||
return inQueue_.empty();
|
||||
}
|
||||
};
|
||||
|
||||
EngineBufferManager manager;
|
||||
|
||||
using WorkBuff = std::array<uint64_t, ALLOC_REQ / sizeof(uint64_t)>;
|
||||
|
||||
std::atomic_int cnt{0};
|
||||
auto workSeq = [&, gen = makeRandGen()]// local random generator per thread
|
||||
() mutable
|
||||
{// sequence of actions within a worker thread
|
||||
int i=++cnt;
|
||||
LocalTestPool myPool;
|
||||
CHECK (myPool.empty());
|
||||
|
||||
manager.async_requestAllocation (myPool, ALLOC_REQ);
|
||||
|
||||
// meanwhile the worker does some other "work"
|
||||
uint delay = 100 + gen.i(800);
|
||||
sleep_for (microseconds(delay));
|
||||
SHOW_EXPR(delay)
|
||||
|
||||
Alloc alloc = myPool.retrieveAlloc();
|
||||
if (alloc.empty())
|
||||
// they let us down ...
|
||||
// insist harder to be serviced thusly
|
||||
alloc = manager.requestAllocation (ALLOC_REQ);
|
||||
|
||||
CHECK (not alloc.empty());
|
||||
CHECK (ALLOC_REQ <= alloc.siz);
|
||||
// can use that allocation for some "serious work"...
|
||||
auto content = new(alloc.mem) WorkBuff;
|
||||
std::ranges::generate (*content, [&]{ return gen.u64(); });
|
||||
sleep_for (10ms);
|
||||
|
||||
// pass allocation back to the central hub
|
||||
manager.supply (alloc);
|
||||
SHOW_EXPR(i)
|
||||
};
|
||||
|
||||
using Threads = lib::ScopedCollection<Thread>;
|
||||
auto startThread = [&](auto& storage){ storage.template create<Thread> (workSeq); };
|
||||
|
||||
// Start a collection of workers....
|
||||
Threads threads{NUM_THREADS, startThread};
|
||||
|
||||
// wait for all threads to finish...
|
||||
while (explore(threads).has_any())
|
||||
yield();
|
||||
|
||||
// all the allocations sent back by the workers
|
||||
// should sit in the EngineBufferManager's input queue
|
||||
SHOW_EXPR(watch(manager).numAllocs());
|
||||
// CHECK (NUM_THREADS == watch(manager).numAllocs());
|
||||
SHOW_EXPR(watch(manager).bytesLeased());
|
||||
// CHECK (NUM_THREADS*ALLOC_REQ <= watch(manager).bytesLeased());
|
||||
|
||||
// process in-queue and retrieve all allocations..
|
||||
manager.processPendingRequests();
|
||||
SHOW_EXPR(watch(manager).numAllocs());
|
||||
SHOW_EXPR(watch(manager).bytesLeased());
|
||||
// CHECK (0 == watch(manager).bytesLeased());
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -146914,8 +146914,60 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension)
|
|||
<node CREATED="1778635075937" ID="ID_1005055901" MODIFIED="1778635080660" TEXT="alles wieder freigeben"/>
|
||||
<node CREATED="1778635083791" ID="ID_227876159" MODIFIED="1778635088455" TEXT="Diagnostik prüfen"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#d2beaf" COLOR="#5c4d6e" CREATED="1778599975152" ID="ID_1790536650" MODIFIED="1778599980968" TEXT="verify_asyncRequest">
|
||||
<icon BUILTIN="hourglass"/>
|
||||
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1778599975152" ID="ID_1790536650" MODIFIED="1778641424697" TEXT="verify_asyncRequest">
|
||||
<icon BUILTIN="pencil"/>
|
||||
<node CREATED="1778641426736" ID="ID_460139955" MODIFIED="1778641441187" TEXT="sollte hier gleich etwas »massives« machen"/>
|
||||
<node CREATED="1778641442270" ID="ID_1826595532" MODIFIED="1778641456169" TEXT="führe eine typische usage-sequence in jedem Thread aus">
|
||||
<node CREATED="1778641462236" ID="ID_51963277" MODIFIED="1778641469666" TEXT="fordere eine Allokation"/>
|
||||
<node CREATED="1778641470523" ID="ID_1338437091" MODIFIED="1778641482349" TEXT="— delay —"/>
|
||||
<node CREATED="1778641485269" ID="ID_1979457912" MODIFIED="1778641494779" TEXT="hole die Allokation">
|
||||
<node CREATED="1778641496587" ID="ID_818471680" MODIFIED="1778641501576" TEXT="entweder aus der Queue"/>
|
||||
<node CREATED="1778641502490" ID="ID_909654376" MODIFIED="1778641512097" TEXT="ansonsten fordere sie synchron an"/>
|
||||
</node>
|
||||
<node CREATED="1778641515186" ID="ID_1850061858" MODIFIED="1778641527135" TEXT="schreibe Zufallszahlen in die Allokation"/>
|
||||
<node CREATED="1778641528055" ID="ID_572369413" MODIFIED="1778641532894" TEXT="— delay —"/>
|
||||
<node CREATED="1778641533746" ID="ID_1255059129" MODIFIED="1778641540762" TEXT="schicke die Allokation zurück"/>
|
||||
</node>
|
||||
<node CREATED="1778641582324" ID="ID_723485846" MODIFIED="1778641604860" TEXT="danach sollten....">
|
||||
<node CREATED="1778641588959" ID="ID_857089658" MODIFIED="1778641600492" TEXT="NUM_THREADS Allokationen erfolgt sein"/>
|
||||
<node CREATED="1778641607696" ID="ID_514432880" MODIFIED="1778641618195" TEXT="alle diese Allokationen wieder in der Queue sitzen"/>
|
||||
<node BACKGROUND_COLOR="#fafe99" COLOR="#fa002a" CREATED="1778642703060" ID="ID_378615107" MODIFIED="1778642712701" TEXT="funktioniert nicht...">
|
||||
<icon BUILTIN="broken-line"/>
|
||||
<node CREATED="1778642715157" ID="ID_723545359" MODIFIED="1778642718343" TEXT="Beobachtungen">
|
||||
<node CREATED="1778642721100" ID="ID_454509509" MODIFIED="1778642737405" TEXT="einige Threads sind anscheinend schon fertig bevor andere überhaupt erst anfangen"/>
|
||||
<node CREATED="1778642739266" ID="ID_1618120303" MODIFIED="1778642753195" TEXT="damit werden natürlich einige Allokationen bereits weggeräumt"/>
|
||||
<node BACKGROUND_COLOR="#f0d5c5" COLOR="#990033" CREATED="1778642777488" ID="ID_735364209" MODIFIED="1778642799183" TEXT="unklar: warum sind nach processPending() noch Allocs offen?">
|
||||
<icon BUILTIN="help"/>
|
||||
</node>
|
||||
</node>
|
||||
<node CREATED="1778642807208" ID="ID_1933157461" MODIFIED="1778642812460" TEXT="genauere Analyse">
|
||||
<node BACKGROUND_COLOR="#f8f1cb" COLOR="#a50125" CREATED="1778642813695" ID="ID_241071013" MODIFIED="1778643259773" TEXT="Problem-1: brauche wirklich unabhängige Random-Generatoren">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
hatte diese Technik vom incidence-count-test kopiert; der Code dort ist aber <i>nicht ganz korrekt —</i> was dort aber nicht auffällt, da das thread-Microbenchmark seine test-Funktion (das »subjekt«) nochmal kopiert, nämlich in die closure des jeweiligen Thread. Diese Kopie bewirkt dann einfach, daß der Generator nochmal kopiert wird ... bin mir aber trotzdem nicht sicher, so wirklich verschiedene Zufallszahlen gezogen werden.
|
||||
</p>
|
||||
<p>
|
||||
|
||||
</p>
|
||||
<p>
|
||||
Jedenfalls in dem Setup, das ich jetzt hier für diesen neuen Test gebaut habe, verwenden alle Threads dasselbe Lamba für die Thread-Funktion <i>gemeinsam</i>. Das bedeutet, das makeRandomGen() wird tatsächlich nur einmal aufgerufen, und daher haben wir nur einen einzigen Seed (selbst wenn die Lambdas dann im Thread-Wrapper nochmal kopiert werden)
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
</richcontent>
|
||||
<icon BUILTIN="messagebox_warning"/>
|
||||
<node CREATED="1778643262156" ID="ID_46315611" MODIFIED="1778643274742" TEXT="sehe in allen Threads exakt den gleichen Delay"/>
|
||||
<node CREATED="1778643275676" ID="ID_284051539" MODIFIED="1778643284600" TEXT="daher wachen die auch alle gemeinsam auf"/>
|
||||
<node CREATED="1778643339650" ID="ID_1840909466" MODIFIED="1778643355371" TEXT="und dann sind die ersten schon fertig bevor die späteren überhaupt zum Zug kommen"/>
|
||||
<node CREATED="1778643357162" ID="ID_512680760" MODIFIED="1778643379727" TEXT="kann aber auch daran liegen, daß wir einfach nicht so viele Threads gleichzeitig laufen lassen können"/>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#f0d5c5" COLOR="#990033" CREATED="1777330585249" ID="ID_1415692101" MODIFIED="1777330598348" TEXT="Ergebnis / Einsichten">
|
||||
|
|
|
|||
Loading…
Reference in a new issue