BufferProvider: validate (and fix) concurrent access
The test performs a massively concurrent scenario that presses with 100 threads and 1 MiB allocation for each. This highlighted my still limited understanding of the situation and leads to a much better solution, where, on-demand, some worker performs all the accrued allocation and management work. So effectively we do not even need to use the synchronous API
This commit is contained in:
parent
0567633bd9
commit
d94bfe64b5
4 changed files with 401 additions and 91 deletions
|
|
@ -75,7 +75,18 @@ namespace mem {
|
||||||
Buff* mem;
|
Buff* mem;
|
||||||
size_t siz;
|
size_t siz;
|
||||||
|
|
||||||
bool empty() const { return not bool(mem); }
|
bool
|
||||||
|
empty() const
|
||||||
|
{
|
||||||
|
return not bool(mem);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
discard()
|
||||||
|
{
|
||||||
|
mem = nullptr;
|
||||||
|
siz = 0;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -100,9 +111,10 @@ namespace mem {
|
||||||
* implementation code from a subclass will have to
|
* implementation code from a subclass will have to
|
||||||
* receive and dispatch those entries */
|
* receive and dispatch those entries */
|
||||||
void
|
void
|
||||||
supply (Alloc const& alloc)
|
supply (Alloc& alloc)
|
||||||
{
|
{
|
||||||
inQueue_.push (alloc);
|
inQueue_.push (alloc);
|
||||||
|
alloc.discard();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -227,7 +227,8 @@ namespace mem {
|
||||||
void
|
void
|
||||||
supply (Buff* mem, size_t siz)
|
supply (Buff* mem, size_t siz)
|
||||||
{
|
{
|
||||||
AllocReceiver::supply ({mem,siz});
|
Alloc alloc{mem,siz};
|
||||||
|
AllocReceiver::supply (alloc);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -18,25 +18,22 @@
|
||||||
|
|
||||||
#include "test/run.hpp"
|
#include "test/run.hpp"
|
||||||
#include "test/test-helper.hpp"
|
#include "test/test-helper.hpp"
|
||||||
//#include "vault/mem/engine-buffer-metadata.hpp"
|
|
||||||
#include "vault/mem/engine-buffer-manager.hpp"
|
#include "vault/mem/engine-buffer-manager.hpp"
|
||||||
#include "vault/mem/engine-buffer-allocator.hpp"
|
#include "vault/mem/engine-buffer-allocator.hpp"
|
||||||
#include "lib/scoped-collection.hpp"
|
#include "lib/scoped-collection.hpp"
|
||||||
#include "lib/iter-explorer.hpp"
|
#include "lib/iter-explorer.hpp"
|
||||||
//#include "lib/depend-inject.hpp"
|
#include "lib/sync-barrier.hpp"
|
||||||
#include "lib/thread.hpp"
|
#include "lib/thread.hpp"
|
||||||
//#include "lib/error.hpp"
|
|
||||||
#include "lib/symbol.hpp"
|
#include "lib/symbol.hpp"
|
||||||
#include "test/diagnostic-output.hpp"////////////TODO
|
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
//#include <thread>
|
#include <string>
|
||||||
#include <array>
|
#include <array>
|
||||||
|
|
||||||
using lib::Thread;
|
|
||||||
using lib::Literal;
|
|
||||||
using lib::explore;
|
using lib::explore;
|
||||||
//using lib::ScopedCollection;
|
using lib::Literal;
|
||||||
|
using lib::Thread;
|
||||||
|
using lib::SyncBarrier;
|
||||||
using std::this_thread::yield;
|
using std::this_thread::yield;
|
||||||
using std::this_thread::sleep_for;
|
using std::this_thread::sleep_for;
|
||||||
using std::chrono::microseconds;
|
using std::chrono::microseconds;
|
||||||
|
|
@ -46,28 +43,14 @@ namespace vault {
|
||||||
namespace mem {
|
namespace mem {
|
||||||
namespace test {
|
namespace test {
|
||||||
|
|
||||||
// using LERR_(LOGIC);
|
namespace { // Test parameters......
|
||||||
// using LERR_(LIFECYCLE);
|
const size_t ALLOC_REQ = 1024*1024; ///< use test-allocation request of 1 MiB size
|
||||||
|
const size_t NUM_THREADS = 100; ///< press concurrently
|
||||||
|
|
||||||
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)
|
|
||||||
// {
|
|
||||||
// return reinterpret_cast<Buff*> (std::addressof(something));
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*****************************************************************************//**
|
/*****************************************************************************//**
|
||||||
* @test Show how the central buffer manager of the Render Engine can be accessed.
|
* @test Show how the central buffer manager of the Render Engine can be accessed.
|
||||||
*/
|
*/
|
||||||
|
|
@ -77,12 +60,9 @@ namespace test {
|
||||||
virtual void
|
virtual void
|
||||||
run (Arg)
|
run (Arg)
|
||||||
{
|
{
|
||||||
seedRand();
|
|
||||||
|
|
||||||
demonstrate_AllocatorInterface();
|
demonstrate_AllocatorInterface();
|
||||||
verify_syncRequest();
|
verify_syncRequest();
|
||||||
verify_asyncRequest();
|
verify_asyncRequest();
|
||||||
verify_lulz();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -150,7 +130,7 @@ namespace test {
|
||||||
|
|
||||||
// can use that memory
|
// can use that memory
|
||||||
char* buf = reinterpret_cast<char*> (alloc.mem);
|
char* buf = reinterpret_cast<char*> (alloc.mem);
|
||||||
string ranS{lib::randStr(555-1)};
|
std::string ranS{lib::randStr(555-1)};
|
||||||
auto* p = std::copy (ranS.begin(), ranS.end(), buf);
|
auto* p = std::copy (ranS.begin(), ranS.end(), buf);
|
||||||
CHECK (ranS.length() == size_t(p - buf));
|
CHECK (ranS.length() == size_t(p - buf));
|
||||||
*p = '\0';
|
*p = '\0';
|
||||||
|
|
@ -175,7 +155,16 @@ namespace test {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/** @test show that an working allocation request passes thread boundaries */
|
/** @test show a working allocation request passed over thread boundaries...
|
||||||
|
* - use a simplified »local pool« that actually just provides an in-queue
|
||||||
|
* - start a lot of threads that perform the typical allocation sequence
|
||||||
|
* + first send an asynchronous allocation request
|
||||||
|
* + some delay while doing other stuff (here: sleep)
|
||||||
|
* + check if allocation has been serviced
|
||||||
|
* + if not, then perform global allocation servicing in this worker
|
||||||
|
* + allocation must be available now, can use it
|
||||||
|
* + when done, send allocation back to the global pool
|
||||||
|
*/
|
||||||
void
|
void
|
||||||
verify_asyncRequest()
|
verify_asyncRequest()
|
||||||
{
|
{
|
||||||
|
|
@ -196,42 +185,44 @@ namespace test {
|
||||||
return inQueue_.empty();
|
return inQueue_.empty();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
EngineBufferManager manager;
|
EngineBufferManager manager;
|
||||||
|
|
||||||
using WorkBuff = std::array<uint64_t, ALLOC_REQ / sizeof(uint64_t)>;
|
using WorkBuff = std::array<uint64_t, ALLOC_REQ / sizeof(uint64_t)>;
|
||||||
|
|
||||||
std::atomic_int cnt{0};
|
SyncBarrier afterThread{NUM_THREADS+1};
|
||||||
auto workSeq = [&, gen = makeRandGen()]// local random generator per thread
|
auto workSeq = [&]{ // sequence-of-actions-within-worker-thread-------------------
|
||||||
() mutable
|
|
||||||
{// sequence of actions within a worker thread
|
|
||||||
int i=++cnt;
|
|
||||||
LocalTestPool myPool;
|
LocalTestPool myPool;
|
||||||
CHECK (myPool.empty());
|
CHECK (myPool.empty());
|
||||||
|
|
||||||
manager.async_requestAllocation (myPool, ALLOC_REQ);
|
manager.async_requestAllocation (myPool, ALLOC_REQ);
|
||||||
|
|
||||||
// meanwhile the worker does some other "work"
|
// meanwhile the worker does some other "work"...
|
||||||
uint delay = 100 + gen.i(800);
|
auto gen = makeRandGen(); // local random generator per thread
|
||||||
|
uint delay = 100 + gen.i(900);
|
||||||
sleep_for (microseconds(delay));
|
sleep_for (microseconds(delay));
|
||||||
SHOW_EXPR(delay)
|
|
||||||
|
|
||||||
Alloc alloc = myPool.retrieveAlloc();
|
Alloc alloc = myPool.retrieveAlloc();
|
||||||
if (alloc.empty())
|
if (alloc.empty())
|
||||||
// they let us down ...
|
{ // they let us down ...
|
||||||
// insist harder to be serviced thusly
|
// insist harder to be serviced thusly
|
||||||
alloc = manager.requestAllocation (ALLOC_REQ);
|
manager.processPendingRequests();
|
||||||
|
alloc = myPool.retrieveAlloc();
|
||||||
|
// Explanation: since the async_requestAllocation() »happens-before« the processPendingRequests()
|
||||||
|
} // our request *must* have been processed and dispatched now, unless there was a failure
|
||||||
|
|
||||||
CHECK (not alloc.empty());
|
CHECK (not alloc.empty());
|
||||||
CHECK (ALLOC_REQ <= alloc.siz);
|
CHECK (ALLOC_REQ <= alloc.siz);
|
||||||
// can use that allocation for some "serious work"...
|
// can use that allocation for some "serious work"...
|
||||||
auto content = new(alloc.mem) WorkBuff;
|
auto content = new(alloc.mem) WorkBuff;
|
||||||
std::ranges::generate (*content, [&]{ return gen.u64(); });
|
std::ranges::generate (*content, [&]{ return gen.u64(); });
|
||||||
sleep_for (10ms);
|
|
||||||
|
// coordinate end of all threads
|
||||||
|
afterThread.sync();
|
||||||
|
|
||||||
// pass allocation back to the central hub
|
// pass allocation back to the central hub
|
||||||
manager.supply (alloc);
|
manager.supply (alloc);
|
||||||
SHOW_EXPR(i)
|
CHECK (myPool.empty()); // no further pending / duplicate Alloc in-queue
|
||||||
};
|
};
|
||||||
|
|
||||||
using Threads = lib::ScopedCollection<Thread>;
|
using Threads = lib::ScopedCollection<Thread>;
|
||||||
|
|
@ -240,31 +231,21 @@ SHOW_EXPR(i)
|
||||||
// Start a collection of workers....
|
// Start a collection of workers....
|
||||||
Threads threads{NUM_THREADS, startThread};
|
Threads threads{NUM_THREADS, startThread};
|
||||||
|
|
||||||
|
// after that barrier, all threads send back their allocation
|
||||||
|
afterThread.sync();
|
||||||
|
|
||||||
// wait for all threads to finish...
|
// wait for all threads to finish...
|
||||||
while (explore(threads).has_any())
|
while (explore(threads).has_any())
|
||||||
yield();
|
yield();
|
||||||
|
|
||||||
// all the allocations sent back by the workers
|
// all the allocations sent back by the workers
|
||||||
// should sit in the EngineBufferManager's input queue
|
// should sit now in the EngineBufferManager's input queue
|
||||||
SHOW_EXPR(watch(manager).numAllocs());
|
CHECK (NUM_THREADS == watch(manager).numAllocs());
|
||||||
// CHECK (NUM_THREADS == watch(manager).numAllocs());
|
CHECK (NUM_THREADS*ALLOC_REQ <= watch(manager).bytesLeased());
|
||||||
SHOW_EXPR(watch(manager).bytesLeased());
|
|
||||||
// CHECK (NUM_THREADS*ALLOC_REQ <= watch(manager).bytesLeased());
|
|
||||||
|
|
||||||
// process in-queue and retrieve all allocations..
|
// process in-queue and retrieve all allocations..
|
||||||
manager.processPendingRequests();
|
manager.processPendingRequests();
|
||||||
SHOW_EXPR(watch(manager).numAllocs());
|
CHECK (0 == watch(manager).bytesLeased());
|
||||||
SHOW_EXPR(watch(manager).bytesLeased());
|
|
||||||
// CHECK (0 == watch(manager).bytesLeased());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/** @test
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
verify_lulz()
|
|
||||||
{
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -146679,23 +146679,34 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension)
|
||||||
</node>
|
</node>
|
||||||
</node>
|
</node>
|
||||||
</node>
|
</node>
|
||||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1776819169143" ID="ID_1367745941" MODIFIED="1777050391595" TEXT="Schnittstelle für asynchrones Messaging bedenken">
|
<node COLOR="#338800" CREATED="1776819169143" ID="ID_1367745941" MODIFIED="1778694413443" TEXT="Schnittstelle für asynchrones Messaging bedenken">
|
||||||
<arrowlink COLOR="#fefab1" DESTINATION="ID_1503539674" ENDARROW="Default" ENDINCLINATION="-233;10;" ID="Arrow_ID_451160831" STARTARROW="None" STARTINCLINATION="92;5;"/>
|
<arrowlink COLOR="#fefab1" DESTINATION="ID_1503539674" ENDARROW="Default" ENDINCLINATION="-233;10;" ID="Arrow_ID_451160831" STARTARROW="None" STARTINCLINATION="92;5;"/>
|
||||||
<icon BUILTIN="flag-yellow"/>
|
<icon BUILTIN="button_ok"/>
|
||||||
<node CREATED="1778593688734" HGAP="35" ID="ID_730263664" MODIFIED="1778593713957" TEXT="Kern: die Verarbeitungs-Funktionalität" VSHIFT="-9">
|
<node CREATED="1778593688734" HGAP="35" ID="ID_730263664" MODIFIED="1778593713957" TEXT="Kern: die Verarbeitungs-Funktionalität" VSHIFT="-9">
|
||||||
<node CREATED="1778593747927" ID="ID_1315127845" MODIFIED="1778593770426" TEXT="sollte auf alle Allokator- und Aufräum-Arbeit kaskadieren"/>
|
<node CREATED="1778593747927" ID="ID_1315127845" MODIFIED="1778593770426" TEXT="sollte auf alle Allokator- und Aufräum-Arbeit kaskadieren"/>
|
||||||
<node CREATED="1778593720983" ID="ID_1339362365" MODIFIED="1778594032492" TEXT="Trick: diesen Zugang explizit auch public aufrufbar machen">
|
<node CREATED="1778593720983" ID="ID_1339362365" MODIFIED="1778594032492" TEXT="Trick: diesen Zugang explizit auch public aufrufbar machen">
|
||||||
<linktarget COLOR="#fefbd6" DESTINATION="ID_1339362365" ENDARROW="Default" ENDINCLINATION="-30;50;" ID="Arrow_ID_1537514504" SOURCE="ID_308671982" STARTARROW="None" STARTINCLINATION="57;3;"/>
|
<linktarget COLOR="#fefbd6" DESTINATION="ID_1339362365" ENDARROW="Default" ENDINCLINATION="-30;50;" ID="Arrow_ID_1537514504" SOURCE="ID_308671982" STARTARROW="None" STARTINCLINATION="57;3;"/>
|
||||||
<linktarget COLOR="#e7f3c5" DESTINATION="ID_1339362365" ENDARROW="Default" ENDINCLINATION="273;14;" ID="Arrow_ID_74341588" SOURCE="ID_1723782536" STARTARROW="None" STARTINCLINATION="226;-11;"/>
|
<linktarget COLOR="#e7f3c5" DESTINATION="ID_1339362365" ENDARROW="Default" ENDINCLINATION="273;14;" ID="Arrow_ID_74341588" SOURCE="ID_1723782536" STARTARROW="None" STARTINCLINATION="226;-11;"/>
|
||||||
<icon BUILTIN="idea"/>
|
<icon BUILTIN="idea"/>
|
||||||
|
<node BACKGROUND_COLOR="#f0d5c5" COLOR="#990033" CREATED="1778684644901" HGAP="82" ID="ID_494756239" LINK="#ID_1946083526" MODIFIED="1778684756894" TEXT="dieser Kniff geht nicht auf" VSHIFT="2">
|
||||||
|
<arrowlink COLOR="#dd0609" DESTINATION="ID_314091864" ENDARROW="Default" ENDINCLINATION="21;-45;" ID="Arrow_ID_425697321" STARTARROW="None" STARTINCLINATION="-13;40;"/>
|
||||||
|
<icon BUILTIN="stop-sign"/>
|
||||||
</node>
|
</node>
|
||||||
</node>
|
</node>
|
||||||
</node>
|
</node>
|
||||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1776819181261" ID="ID_1026628741" MODIFIED="1776819956356" TEXT="alternative synchrone Schnittstelle bereitstellen">
|
</node>
|
||||||
<icon BUILTIN="flag-yellow"/>
|
<node COLOR="#338800" CREATED="1776819181261" ID="ID_1026628741" MODIFIED="1778694419297" TEXT="alternative synchrone Verbindung bereitstellen">
|
||||||
|
<icon BUILTIN="button_ok"/>
|
||||||
<node CREATED="1777324291441" ID="ID_336948545" MODIFIED="1777324316333" TEXT="ist relevant für »sofort«-Allokation (Fallback)"/>
|
<node CREATED="1777324291441" ID="ID_336948545" MODIFIED="1777324316333" TEXT="ist relevant für »sofort«-Allokation (Fallback)"/>
|
||||||
|
<node COLOR="#5b280f" CREATED="1778692651060" ID="ID_526425164" MODIFIED="1778692684555" TEXT="erst mal eine synchrone API-Funktion bauen">
|
||||||
|
<icon BUILTIN="button_cancel"/>
|
||||||
|
<node CREATED="1778692661635" ID="ID_971709459" MODIFIED="1778692666072" TEXT="nett für Tests"/>
|
||||||
|
<node CREATED="1778692671532" ID="ID_1297893109" MODIFIED="1778692716537" TEXT="löst aber irgendwie nicht das Problem">
|
||||||
|
<arrowlink COLOR="#fe590e" DESTINATION="ID_985414238" ENDARROW="Default" ENDINCLINATION="0;-19;" ID="Arrow_ID_928400072" STARTARROW="None" STARTINCLINATION="64;21;"/>
|
||||||
|
</node>
|
||||||
|
</node>
|
||||||
<node CREATED="1776819226503" ID="ID_1059062619" MODIFIED="1777051865655" TEXT="sollte kongruent zum Messaging sein"/>
|
<node CREATED="1776819226503" ID="ID_1059062619" MODIFIED="1777051865655" TEXT="sollte kongruent zum Messaging sein"/>
|
||||||
<node CREATED="1778593890737" ID="ID_308671982" MODIFIED="1778594041691" TEXT="sollte idealerweise die asynchrone Lösung inkludieren">
|
<node CREATED="1778593890737" ID="ID_308671982" MODIFIED="1778692684564" TEXT="sollte idealerweise die asynchrone Lösung inkludieren">
|
||||||
<richcontent TYPE="NOTE"><html>
|
<richcontent TYPE="NOTE"><html>
|
||||||
<head/>
|
<head/>
|
||||||
<body>
|
<body>
|
||||||
|
|
@ -146705,11 +146716,113 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension)
|
||||||
</body>
|
</body>
|
||||||
</html></richcontent>
|
</html></richcontent>
|
||||||
<arrowlink COLOR="#fefbd6" DESTINATION="ID_1339362365" ENDARROW="Default" ENDINCLINATION="-30;50;" ID="Arrow_ID_1537514504" STARTARROW="None" STARTINCLINATION="57;3;"/>
|
<arrowlink COLOR="#fefbd6" DESTINATION="ID_1339362365" ENDARROW="Default" ENDINCLINATION="-30;50;" ID="Arrow_ID_1537514504" STARTARROW="None" STARTINCLINATION="57;3;"/>
|
||||||
|
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1778683283418" HGAP="55" ID="ID_985414238" MODIFIED="1778692707890" TEXT="das ist aber problematisch..." VSHIFT="13">
|
||||||
|
<richcontent TYPE="NOTE"><html>
|
||||||
|
<head/>
|
||||||
|
<body>
|
||||||
|
<p>
|
||||||
|
bezüglich Code-Struktur, da Teile des asynchronen Verarbeitungs-Pfades im LocalMemPool liegen
|
||||||
|
</p>
|
||||||
|
</body>
|
||||||
|
</html></richcontent>
|
||||||
|
<linktarget COLOR="#b9104e" DESTINATION="ID_985414238" ENDARROW="Default" ENDINCLINATION="653;64;" ID="Arrow_ID_649016741" SOURCE="ID_398699629" STARTARROW="Default" STARTINCLINATION="646;0;"/>
|
||||||
|
<linktarget COLOR="#fe590e" DESTINATION="ID_985414238" ENDARROW="Default" ENDINCLINATION="0;-19;" ID="Arrow_ID_928400072" SOURCE="ID_1297893109" STARTARROW="None" STARTINCLINATION="64;21;"/>
|
||||||
|
<icon BUILTIN="broken-line"/>
|
||||||
|
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1778684714648" HGAP="44" ID="ID_314091864" MODIFIED="1778692175930" TEXT="genaue Anforderungs-Analyse" VSHIFT="2">
|
||||||
|
<linktarget COLOR="#dd0609" DESTINATION="ID_314091864" ENDARROW="Default" ENDINCLINATION="21;-45;" ID="Arrow_ID_425697321" SOURCE="ID_494756239" STARTARROW="None" STARTINCLINATION="-13;40;"/>
|
||||||
|
<icon BUILTIN="yes"/>
|
||||||
|
<node CREATED="1778684814094" ID="ID_495303541" MODIFIED="1778684863789" TEXT="synchrone Anforderung ⟹ bediene mich jetzt sofort und verbindlich"/>
|
||||||
|
<node CREATED="1778684880184" ID="ID_1007933032" MODIFIED="1778684907582" TEXT="Duplikat vermeiden ⟹ atomar feststellen ob bedient wurde"/>
|
||||||
|
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1778684917792" ID="ID_705215696" MODIFIED="1778684931903" TEXT="der Prüf-Punkt für Beides liegt im LocalMemPool">
|
||||||
|
<icon BUILTIN="messagebox_warning"/>
|
||||||
|
</node>
|
||||||
|
<node BACKGROUND_COLOR="#f1f1a5" COLOR="#351d75" CREATED="1778692148968" ID="ID_335112254" MODIFIED="1778692984253" TEXT="Einsicht: Unterscheidung zwischen »Schnittstelle« und »Code-Pfad«">
|
||||||
|
<arrowlink COLOR="#fff5b4" DESTINATION="ID_404163400" ENDARROW="Default" ENDINCLINATION="62;-198;" ID="Arrow_ID_750368441" STARTARROW="None" STARTINCLINATION="-477;19;"/>
|
||||||
|
<font NAME="SansSerif" SIZE="12"/>
|
||||||
|
<icon BUILTIN="idea"/>
|
||||||
|
<node CREATED="1778692209288" ID="ID_1212177224" MODIFIED="1778692337700" TEXT="gebraucht wird der »synchrone Code-Pfad«">
|
||||||
|
<richcontent TYPE="NOTE"><html>
|
||||||
|
<head/>
|
||||||
|
<body>
|
||||||
|
<p>
|
||||||
|
und zwar als zweiter Code-Pfad, nachdem zunächst eine asynchrone Anfrage abgesetzt wurde, und dennoch an der Stelle des Bedarfs die Allokation noch nicht bereitgestellt wurde. Deshalb dient diser »synchrone Code-Pfad« dazu, JETZT eine DEFINITIVE Antwort zu bekommen (ggfs. einen Fehler)
|
||||||
|
</p>
|
||||||
|
</body>
|
||||||
|
</html></richcontent>
|
||||||
|
</node>
|
||||||
|
<node CREATED="1778692223446" ID="ID_286335522" MODIFIED="1778692386533" TEXT="das impliziert nicht, daß ich eine synchrone Methode aufrufe">
|
||||||
|
<richcontent TYPE="NOTE"><html>
|
||||||
|
<head/>
|
||||||
|
<body>
|
||||||
|
<p>
|
||||||
|
...das wäre zwar der naheliegende Ansatz, würde hier aber die Sache sehr komplex machen, da ich nicht nur die definitive Antwort möchte, sondern <i>zusätzlich auch noch Duplikate vermeiden.</i>
|
||||||
|
</p>
|
||||||
|
</body>
|
||||||
|
</html></richcontent>
|
||||||
|
</node>
|
||||||
|
<node CREATED="1778692404305" ID="ID_430379303" MODIFIED="1778692564453">
|
||||||
|
<richcontent TYPE="NODE"><html>
|
||||||
|
<head/>
|
||||||
|
<body>
|
||||||
|
<p>
|
||||||
|
ich hatte bereits die <i>zutreffende Antwort</i> gefunden, diese aber nicht »realisiert«
|
||||||
|
</p>
|
||||||
|
</body>
|
||||||
|
</html></richcontent>
|
||||||
|
<richcontent TYPE="NOTE"><html>
|
||||||
|
<head/>
|
||||||
|
<body>
|
||||||
|
<p>
|
||||||
|
Nochmal: die Lösung besteht darin, daß der asynchrone Code-Pfad in die synchrone Feststellung eingeschlossen wird. Aber eben nicht so, daß ich (am asynchronen Codepfad vorbei) einen synchronen API-Aufruf mache.
|
||||||
|
</p>
|
||||||
|
</body>
|
||||||
|
</html></richcontent>
|
||||||
|
<icon BUILTIN="forward"/>
|
||||||
|
</node>
|
||||||
|
</node>
|
||||||
|
</node>
|
||||||
|
</node>
|
||||||
</node>
|
</node>
|
||||||
<node CREATED="1776819244365" ID="ID_933815031" MODIFIED="1778593872888" TEXT="erfolgt stets als »pull« vom lokalen Manager"/>
|
<node CREATED="1776819244365" ID="ID_933815031" MODIFIED="1778593872888" TEXT="erfolgt stets als »pull« vom lokalen Manager"/>
|
||||||
|
<node CREATED="1778692733047" ID="ID_404163400" MODIFIED="1778692984253" TEXT="Spezifikation: synchrone Verbindung">
|
||||||
|
<linktarget COLOR="#fff5b4" DESTINATION="ID_404163400" ENDARROW="Default" ENDINCLINATION="62;-198;" ID="Arrow_ID_750368441" SOURCE="ID_335112254" STARTARROW="None" STARTINCLINATION="-477;19;"/>
|
||||||
|
<icon BUILTIN="list"/>
|
||||||
|
<node CREATED="1778692764885" ID="ID_202708583" MODIFIED="1778692781959" TEXT="(lokal) feststellen ob Allokation bedient werden kann"/>
|
||||||
|
<node CREATED="1778692783354" ID="ID_255495997" MODIFIED="1778692800030" TEXT="(synchron) alles anstehende globale Processing ausführen"/>
|
||||||
|
<node CREATED="1778692808951" ID="ID_471309892" MODIFIED="1778692818145" TEXT="(lokal) feststellen ob Allokation nun doch bedient werden kann"/>
|
||||||
|
<node CREATED="1778692821014" ID="ID_819345575" MODIFIED="1778692825913" TEXT="sonst: Fehler">
|
||||||
|
<node COLOR="#5b280f" CREATED="1778692856948" ID="ID_1878910282" MODIFIED="1778692888621" TEXT="man könnte an der Stelle auch das synchrone API aufrufen">
|
||||||
|
<icon BUILTIN="button_cancel"/>
|
||||||
</node>
|
</node>
|
||||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1777324327301" ID="ID_1965528564" MODIFIED="1777324340058" TEXT="lege zunächst einen »naiven« Memory-Manager an">
|
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1778692867807" ID="ID_1843603861" MODIFIED="1778692893324" TEXT="Aber: diese Situation kann nur auftreten, wenn etwas defekt ist">
|
||||||
<icon BUILTIN="flag-yellow"/>
|
<icon BUILTIN="messagebox_warning"/>
|
||||||
|
<node CREATED="1778692897100" ID="ID_1787182653" MODIFIED="1778692907839" TEXT="unter der Annahme daß vorher ein async-Request gestellt wurde"/>
|
||||||
|
<node CREATED="1778692908762" ID="ID_707657939" MODIFIED="1778692926163" TEXT="dann muß das globale Processing diesen behandelt und zugestellt haben"/>
|
||||||
|
<node CREATED="1778692929071" ID="ID_467967428" MODIFIED="1778692935363" TEXT="und sei es, als leere Alloc"/>
|
||||||
|
</node>
|
||||||
|
</node>
|
||||||
|
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#540f69" CREATED="1778693019244" ID="ID_1545731490" MODIFIED="1778693042226" TEXT="beachte: gar kein direkter Request">
|
||||||
|
<icon BUILTIN="forward"/>
|
||||||
|
<node CREATED="1778693079023" ID="ID_1817887106" MODIFIED="1778693099150" TEXT="beruht auf der Idee der atomaren Ordnung"/>
|
||||||
|
<node CREATED="1778693100696" ID="ID_72040121" MODIFIED="1778693128459">
|
||||||
|
<richcontent TYPE="NODE"><html>
|
||||||
|
<head/>
|
||||||
|
<body>
|
||||||
|
<p>
|
||||||
|
der async-Request <b>happens-before</b> dem synchronen globalen Processing
|
||||||
|
</p>
|
||||||
|
</body>
|
||||||
|
</html></richcontent>
|
||||||
|
</node>
|
||||||
|
</node>
|
||||||
|
</node>
|
||||||
|
<node BACKGROUND_COLOR="#a9d8a7" COLOR="#338800" CREATED="1778694426989" ID="ID_1722546045" MODIFIED="1778694529807" TEXT="funktioniert im Test">
|
||||||
|
<arrowlink COLOR="#12d25a" DESTINATION="ID_389422946" ENDARROW="Default" ENDINCLINATION="-296;-885;" ID="Arrow_ID_533922798" STARTARROW="None" STARTINCLINATION="158;20;"/>
|
||||||
|
<icon BUILTIN="forward"/>
|
||||||
|
</node>
|
||||||
|
</node>
|
||||||
|
<node COLOR="#338800" CREATED="1777324327301" ID="ID_1965528564" MODIFIED="1778694422162" TEXT="lege zunächst einen »naiven« Memory-Manager an">
|
||||||
|
<icon BUILTIN="button_ok"/>
|
||||||
<node CREATED="1777324341484" ID="ID_269815037" MODIFIED="1777324543018" TEXT="einzig sinnvoll: direkt vom/zum Heap">
|
<node CREATED="1777324341484" ID="ID_269815037" MODIFIED="1777324543018" TEXT="einzig sinnvoll: direkt vom/zum Heap">
|
||||||
<richcontent TYPE="NOTE"><html>
|
<richcontent TYPE="NOTE"><html>
|
||||||
<head/>
|
<head/>
|
||||||
|
|
@ -146819,9 +146932,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension)
|
||||||
<node CREATED="1778630852336" ID="ID_1688369877" MODIFIED="1778630859835" TEXT="zwei mögliche Ansätze">
|
<node CREATED="1778630852336" ID="ID_1688369877" MODIFIED="1778630859835" TEXT="zwei mögliche Ansätze">
|
||||||
<node CREATED="1778630861078" ID="ID_692489731" MODIFIED="1778630977002" TEXT="möglichst kurz/lokal">
|
<node CREATED="1778630861078" ID="ID_692489731" MODIFIED="1778630977002" TEXT="möglichst kurz/lokal">
|
||||||
<richcontent TYPE="NOTE"><html>
|
<richcontent TYPE="NOTE"><html>
|
||||||
<head>
|
<head/>
|
||||||
|
|
||||||
</head>
|
|
||||||
<body>
|
<body>
|
||||||
<p>
|
<p>
|
||||||
im Grunde müßte man nur den Zugang zum Memory-Manager selber locken, denn die ganzen Queue-Operationen sind ja schon lock-free und threadsafe; auch könnten diese Locks dann non-recursive sein
|
im Grunde müßte man nur den Zugang zum Memory-Manager selber locken, denn die ganzen Queue-Operationen sind ja schon lock-free und threadsafe; auch könnten diese Locks dann non-recursive sein
|
||||||
|
|
@ -146832,9 +146943,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension)
|
||||||
</node>
|
</node>
|
||||||
<node CREATED="1778630869382" ID="ID_1688710426" MODIFIED="1778631446175" TEXT="möglichst wenig Locks">
|
<node CREATED="1778630869382" ID="ID_1688710426" MODIFIED="1778631446175" TEXT="möglichst wenig Locks">
|
||||||
<richcontent TYPE="NOTE"><html>
|
<richcontent TYPE="NOTE"><html>
|
||||||
<head>
|
<head/>
|
||||||
|
|
||||||
</head>
|
|
||||||
<body>
|
<body>
|
||||||
<p>
|
<p>
|
||||||
...andererseits ist jeder (Mutex)-Lock ein Kernel-Aufruf, und wir erwarten durchaus ganze Bündel an Requests. Außerdem wird mit wenigen Locks auf API-Level viel klarer, daß das Locking vollständig, und der Zustand konsistent ist.
|
...andererseits ist jeder (Mutex)-Lock ein Kernel-Aufruf, und wir erwarten durchaus ganze Bündel an Requests. Außerdem wird mit wenigen Locks auf API-Level viel klarer, daß das Locking vollständig, und der Zustand konsistent ist.
|
||||||
|
|
@ -146846,9 +146955,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension)
|
||||||
</node>
|
</node>
|
||||||
<node BACKGROUND_COLOR="#ccb59b" COLOR="#6e2a38" CREATED="1778631447430" ID="ID_1196517656" MODIFIED="1778631809891" TEXT="entscheide mich (nach Bauchgefühl) für »wenig Locks«">
|
<node BACKGROUND_COLOR="#ccb59b" COLOR="#6e2a38" CREATED="1778631447430" ID="ID_1196517656" MODIFIED="1778631809891" TEXT="entscheide mich (nach Bauchgefühl) für »wenig Locks«">
|
||||||
<richcontent TYPE="NOTE"><html>
|
<richcontent TYPE="NOTE"><html>
|
||||||
<head>
|
<head/>
|
||||||
|
|
||||||
</head>
|
|
||||||
<body>
|
<body>
|
||||||
<p>
|
<p>
|
||||||
...und zwar sagt mir mein Bauchgefühl, daß wir hier keine Situation mit starker Contention haben; das hat sich auch bereits im Zusammenhang mit dem Scheduler gezeigt. Im Regelfall erwarte ich, daß die Anfragen durch die Queue gehen, und damit ohne Interferenz aneinander vorbeikommen. Wenn dann tatsächlich mal ein Thread den synchronen Pfad einschlägt, erscheint es mir sinnvoller, alle Arbeit in einem Schwung zu erledigen, selbst wenn dadruch (ohenhin nur ausnahmsweise!) ein anderer Thread mal geblockt wird. Letztlich gilt hier immer: die Jobs sind <i>vergleichsweise groß. </i>
|
...und zwar sagt mir mein Bauchgefühl, daß wir hier keine Situation mit starker Contention haben; das hat sich auch bereits im Zusammenhang mit dem Scheduler gezeigt. Im Regelfall erwarte ich, daß die Anfragen durch die Queue gehen, und damit ohne Interferenz aneinander vorbeikommen. Wenn dann tatsächlich mal ein Thread den synchronen Pfad einschlägt, erscheint es mir sinnvoller, alle Arbeit in einem Schwung zu erledigen, selbst wenn dadruch (ohenhin nur ausnahmsweise!) ein anderer Thread mal geblockt wird. Letztlich gilt hier immer: die Jobs sind <i>vergleichsweise groß. </i>
|
||||||
|
|
@ -146931,9 +147038,10 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension)
|
||||||
<node CREATED="1778641582324" ID="ID_723485846" MODIFIED="1778641604860" TEXT="danach sollten....">
|
<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="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 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...">
|
<node COLOR="#435e98" CREATED="1778642703060" ID="ID_378615107" MODIFIED="1778694539272" TEXT="funktioniert nicht...">
|
||||||
<icon BUILTIN="broken-line"/>
|
<icon BUILTIN="broken-line"/>
|
||||||
<node CREATED="1778642715157" ID="ID_723545359" MODIFIED="1778642718343" TEXT="Beobachtungen">
|
<node CREATED="1778642715157" ID="ID_723545359" MODIFIED="1778694786375" TEXT="Beobachtungen">
|
||||||
|
<icon BUILTIN="info"/>
|
||||||
<node CREATED="1778642721100" ID="ID_454509509" MODIFIED="1778642737405" TEXT="einige Threads sind anscheinend schon fertig bevor andere überhaupt erst anfangen"/>
|
<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 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?">
|
<node BACKGROUND_COLOR="#f0d5c5" COLOR="#990033" CREATED="1778642777488" ID="ID_735364209" MODIFIED="1778642799183" TEXT="unklar: warum sind nach processPending() noch Allocs offen?">
|
||||||
|
|
@ -146943,9 +147051,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension)
|
||||||
<node CREATED="1778642807208" ID="ID_1933157461" MODIFIED="1778642812460" TEXT="genauere Analyse">
|
<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">
|
<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>
|
<richcontent TYPE="NOTE"><html>
|
||||||
<head>
|
<head/>
|
||||||
|
|
||||||
</head>
|
|
||||||
<body>
|
<body>
|
||||||
<p>
|
<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.
|
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.
|
||||||
|
|
@ -146963,11 +147069,223 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension)
|
||||||
<node CREATED="1778643262156" ID="ID_46315611" MODIFIED="1778643274742" TEXT="sehe in allen Threads exakt den gleichen Delay"/>
|
<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="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="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 BACKGROUND_COLOR="#f8f1cb" COLOR="#a50125" CREATED="1778679838089" ID="ID_437100940" MODIFIED="1778679860382" TEXT="Problem-2: erste Theads terminieren bevor letzte starten">
|
||||||
|
<icon BUILTIN="messagebox_warning"/>
|
||||||
|
<node COLOR="#970133" CREATED="1778679865901" ID="ID_621751054" MODIFIED="1778680065876" TEXT="das ist das eigentliche Problem"/>
|
||||||
|
<node CREATED="1778679898083" ID="ID_1944064033" MODIFIED="1778679919434" TEXT="wobei — weiß nicht ... kann ja auch ein tatsächlicher Bug sein">
|
||||||
|
<icon BUILTIN="messagebox_warning"/>
|
||||||
|
</node>
|
||||||
|
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1778679922322" ID="ID_857836397" MODIFIED="1778680049795" TEXT="zumindest ist wegen diesem Problem das Test-Setup korrupt">
|
||||||
|
<richcontent TYPE="NOTE"><html>
|
||||||
|
<head/>
|
||||||
|
<body>
|
||||||
|
<p>
|
||||||
|
Genauer: das Szenario ist nicht so, wie von mir geplant. Es ist zwar tatsächlich massiv concurrent, und das müßte das Subject auch verkraften; aber das Verhalten ist dadurch nicht mehr verifzierbar
|
||||||
|
</p>
|
||||||
|
</body>
|
||||||
|
</html></richcontent>
|
||||||
|
<icon BUILTIN="yes"/>
|
||||||
|
</node>
|
||||||
|
</node>
|
||||||
|
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1778681131699" ID="ID_725298648" MODIFIED="1778681149966" TEXT="nebenbei bemerkt: das API für ScopedCollection ist unvollständig">
|
||||||
|
<icon BUILTIN="broken-line"/>
|
||||||
|
<node CREATED="1778681154739" ID="ID_1527328731" MODIFIED="1778681164103" TEXT="das Erzeugen der Instanz ist zu umständlich"/>
|
||||||
|
<node CREATED="1778681181873" ID="ID_262040421" MODIFIED="1778709901385" TEXT="es fehlt das Hinterlegen fester Argumente">
|
||||||
|
<node CREATED="1778709995012" ID="ID_1485556701" MODIFIED="1778710018906" TEXT="wäre möglich durch Erweiterung von FillWith">
|
||||||
|
<node CREATED="1778710037003" ID="ID_730979662" MODIFIED="1778710085583" TEXT="müßte dazu ein Argument-Tupel materialisieren"/>
|
||||||
|
<node CREATED="1778710769213" ID="ID_410726998" MODIFIED="1778710789818" TEXT="bisher wird <tuple> nicht includiert"/>
|
||||||
|
</node>
|
||||||
|
<node CREATED="1778711002374" ID="ID_839332664" MODIFIED="1778711024630" TEXT="könnte man auch als λ-builder neu formulieren">
|
||||||
|
<node CREATED="1778711483235" ID="ID_1590205155" LINK="https://stackoverflow.com/a/49902823/444796" MODIFIED="1778711541326" TEXT="in C++20 gibt es perfect λ-capture"/>
|
||||||
|
<node CREATED="1778711904212" ID="ID_1775952563" LINK="https://en.cppreference.com/cpp/language/lambda#Lambda_capture" MODIFIED="1778711918406" TEXT="ist möglich sowohl für Referenz, alsauch Copy-Capture"/>
|
||||||
|
</node>
|
||||||
|
<node COLOR="#435e98" CREATED="1778727867774" ID="ID_833107791" MODIFIED="1778727892750" TEXT="kann damit die nested-Funcors durch Builder-Funktionen ersetzen">
|
||||||
|
<icon BUILTIN="idea"/>
|
||||||
|
</node>
|
||||||
|
</node>
|
||||||
|
<node CREATED="1778681164947" ID="ID_281208279" MODIFIED="1778681175454" TEXT="es fehlt das Aufrufen eines Lambda"/>
|
||||||
|
<node CREATED="1778727780762" ID="ID_1017169041" MODIFIED="1778727790117" TEXT="Iterator-pull ist limitiert">
|
||||||
|
<node CREATED="1778727795148" ID="ID_699461971" MODIFIED="1778727812229" TEXT="könnte sowohl copy-Konstruktion, alsauch Value-Konstruktion machen"/>
|
||||||
|
<node CREATED="1778727813246" ID="ID_791306906" MODIFIED="1778727831783" TEXT="sollte mit dem Populieren aufhören, wenn der Iterator erschöpft ist"/>
|
||||||
|
<node COLOR="#435e98" CREATED="1778727839622" ID="ID_576015934" MODIFIED="1778727896555" TEXT="Mit einem Concept könnte das direkt eine Konstruktor-Variante sein">
|
||||||
|
<icon BUILTIN="idea"/>
|
||||||
</node>
|
</node>
|
||||||
</node>
|
</node>
|
||||||
</node>
|
</node>
|
||||||
</node>
|
</node>
|
||||||
|
<node COLOR="#435e98" CREATED="1778680084927" ID="ID_831309457" MODIFIED="1778694776376" TEXT="Anpassungen">
|
||||||
|
<icon BUILTIN="yes"/>
|
||||||
|
<node CREATED="1778680089194" ID="ID_197674581" MODIFIED="1778680092854" TEXT="Random-Generierung">
|
||||||
|
<node CREATED="1778680096265" ID="ID_1358593920" MODIFIED="1778680104920" TEXT="Erinnerung...">
|
||||||
|
<node CREATED="1778680106055" ID="ID_750869602" MODIFIED="1778680120858" TEXT="Nucleus ≙ Quelle für den Seed"/>
|
||||||
|
<node CREATED="1778680128125" ID="ID_472547204" MODIFIED="1778680162028" TEXT="Random::Seed ≙ ein Nucleus der den Seed aus einem anderen Generator zieht"/>
|
||||||
|
<node CREATED="1778680175271" ID="ID_733278339" MODIFIED="1778680199983" TEXT="der Seed passiert in dem Moment, wo der Generator konstruiert wird"/>
|
||||||
|
</node>
|
||||||
|
<node CREATED="1778680209583" ID="ID_1798555768" MODIFIED="1778680229555" TEXT="Problem hier: verwende eine gemeinsame λ-Instanz für alle Threads">
|
||||||
|
<node CREATED="1778680231725" ID="ID_231244196" MODIFIED="1778680237418" TEXT="kann man machen"/>
|
||||||
|
<node CREATED="1778680238246" ID="ID_257727363" MODIFIED="1778680251465" TEXT="wird verwirrend, sofern die Closure mutable ist"/>
|
||||||
|
</node>
|
||||||
|
<node CREATED="1778680371181" ID="ID_189533548" MODIFIED="1778680499023">
|
||||||
|
<richcontent TYPE="NODE"><html>
|
||||||
|
<head/>
|
||||||
|
<body>
|
||||||
|
<p>
|
||||||
|
Randomisierung ist hier <b>grundsätzlich nicht reproduzierbar</b>
|
||||||
|
</p>
|
||||||
|
</body>
|
||||||
|
</html></richcontent>
|
||||||
|
<richcontent TYPE="NOTE"><html>
|
||||||
|
<head/>
|
||||||
|
<body>
|
||||||
|
<p>
|
||||||
|
Das liegt daran, daß das relative Verhalten der Threads zueinander eine Rolle spielt. Solche zeitlichen Verschiebungen enthalten ein Element echter Entropie, wenngleich auch die Qualität der Entropie unterschiedlich sein kann. Man kann zwar versuchen, diese Entropie zu reduzieren, und ein solches Unterfangen ist gefährlich, weil es einen Grad der Kontrolle suggeriert, der letztlich nicht gegeben ist.
|
||||||
|
</p>
|
||||||
|
</body>
|
||||||
|
</html></richcontent>
|
||||||
|
<node BACKGROUND_COLOR="#ccb59b" COLOR="#6e2a38" CREATED="1778680507732" ID="ID_1577851272" MODIFIED="1778680636069" TEXT="dann besser gleich auf Reproduzierbarkeit verzichten">
|
||||||
|
<font ITALIC="true" NAME="SansSerif" SIZE="14"/>
|
||||||
|
<icon BUILTIN="yes"/>
|
||||||
|
</node>
|
||||||
|
<node CREATED="1778680521745" ID="ID_1202354696" MODIFIED="1778680538048" TEXT="ja dann ⟹ ist auch ein korrupter Generator-State egal">
|
||||||
|
<node CREATED="1778680541142" ID="ID_1203688363" MODIFIED="1778680549382" TEXT="siehe random-concurrent-test.cpp"/>
|
||||||
|
<node CREATED="1778680551508" ID="ID_214413089" MODIFIED="1778680561399" TEXT="Generatoren sind nicht threadsafe"/>
|
||||||
|
<node CREATED="1778680562252" ID="ID_1130838275" MODIFIED="1778680576390" TEXT="mögliche Effekte">
|
||||||
|
<node CREATED="1778680576916" ID="ID_1824606428" MODIFIED="1778680584349" TEXT="Bias in der Verteilung"/>
|
||||||
|
<node CREATED="1778680585424" ID="ID_1894957394" MODIFIED="1778680598203" TEXT="Werte außerhalb des Wertbereichs"/>
|
||||||
|
</node>
|
||||||
|
<node CREATED="1778680599902" ID="ID_926765859" MODIFIED="1778680628723">
|
||||||
|
<richcontent TYPE="NODE"><html>
|
||||||
|
<head/>
|
||||||
|
<body>
|
||||||
|
<p>
|
||||||
|
beides ist hier <b>komplett egal</b>
|
||||||
|
</p>
|
||||||
|
</body>
|
||||||
|
</html></richcontent>
|
||||||
|
<richcontent TYPE="NOTE"><html>
|
||||||
|
<head/>
|
||||||
|
<body>
|
||||||
|
<p>
|
||||||
|
solange wir <i>irgendwie verschiedene Werte</i> in den Threads bekommen, is gut
|
||||||
|
</p>
|
||||||
|
</body>
|
||||||
|
</html></richcontent>
|
||||||
|
</node>
|
||||||
|
</node>
|
||||||
|
<node CREATED="1778680640757" ID="ID_1394659905" MODIFIED="1778680760973" TEXT="also einfach einen Generator im Thread erzeugen">
|
||||||
|
<richcontent TYPE="NOTE"><html>
|
||||||
|
<head/>
|
||||||
|
<body>
|
||||||
|
<p>
|
||||||
|
Da der Seed irgend ein 64bit-uint ist, ist das Schlimmste, was passieren kann, daß zwei Threads einen lokalen Generator mit dem gleichen Seed und damit der gleichen Sequenz haben. Dieser Fall <b>ist aber nicht die Regel</b>
|
||||||
|
</p>
|
||||||
|
</body>
|
||||||
|
</html></richcontent>
|
||||||
|
</node>
|
||||||
|
</node>
|
||||||
|
</node>
|
||||||
|
<node CREATED="1778681240809" ID="ID_1531241320" MODIFIED="1778681252059" TEXT="kontrolliertes Szenario herstellen">
|
||||||
|
<node CREATED="1778681253391" ID="ID_1862800880" MODIFIED="1778681315111" TEXT="selbst 100ms reichen nicht aus">
|
||||||
|
<richcontent TYPE="NOTE"><html>
|
||||||
|
<head/>
|
||||||
|
<body>
|
||||||
|
<p>
|
||||||
|
...was auch eigentlich nicht verwunderlich ist, denn wir haben zwar einen Sleep, aber auch eine signifikante CPU-Load (1 Million Speicherzugriffe)
|
||||||
|
</p>
|
||||||
|
</body>
|
||||||
|
</html></richcontent>
|
||||||
|
</node>
|
||||||
|
<node CREATED="1778681346331" ID="ID_1939471461" MODIFIED="1778681362397" TEXT="sollte dann eine N-fach-Barriere verwenden"/>
|
||||||
|
</node>
|
||||||
|
</node>
|
||||||
|
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1778681773306" ID="ID_609909829" MODIFIED="1778694543005" TEXT="Problem auch mit dem korrekten Szenario">
|
||||||
|
<icon BUILTIN="broken-line"/>
|
||||||
|
<node CREATED="1778681862806" ID="ID_443478205" MODIFIED="1778681870089" TEXT="es passieren zu viele Allokationen"/>
|
||||||
|
<node CREATED="1778681887129" ID="ID_1998517831" MODIFIED="1778681907304" TEXT="exakt die überschüssigen Allokationen > 100 (numThreds) bleiben übrig"/>
|
||||||
|
<node CREATED="1778682005163" ID="ID_796657228" MODIFIED="1778682023905" TEXT="Vermutung: Race in der synchronen Zuteilung">
|
||||||
|
<node CREATED="1778682357350" ID="ID_797392472" MODIFIED="1778682405357" TEXT="bestätigt: queues im Thread nicht leer">
|
||||||
|
<richcontent TYPE="NOTE"><html>
|
||||||
|
<head/>
|
||||||
|
<body>
|
||||||
|
<p>
|
||||||
|
wenn ich am Ende in jedem Thread die Queue leere, dann stimmt die Bilanz am Ende
|
||||||
|
</p>
|
||||||
|
</body>
|
||||||
|
</html></richcontent>
|
||||||
|
</node>
|
||||||
|
<node CREATED="1778682408073" ID="ID_1038446915" MODIFIED="1778682446918" TEXT="es ist maximal eine zusätzliche Allokation in der Queue">
|
||||||
|
<richcontent TYPE="NOTE"><html>
|
||||||
|
<head/>
|
||||||
|
<body>
|
||||||
|
<p>
|
||||||
|
Typische Werte schwanken zwischen 40 und 80 zusätzlichen Allokationen
|
||||||
|
</p>
|
||||||
|
</body>
|
||||||
|
</html></richcontent>
|
||||||
|
</node>
|
||||||
|
<node CREATED="1778682682880" ID="ID_499008534" MODIFIED="1778682807751" TEXT="im Testcode ist ein offensichtlicher Race">
|
||||||
|
<richcontent TYPE="NOTE"><html>
|
||||||
|
<head/>
|
||||||
|
<body>
|
||||||
|
<p>
|
||||||
|
zwischen dem Prüfen auf die leere Queue und dem synchronen Aufruf für eine Zuteilung
|
||||||
|
</p>
|
||||||
|
</body>
|
||||||
|
</html></richcontent>
|
||||||
|
<node CREATED="1778682880797" ID="ID_127419517" MODIFIED="1778682888786" TEXT="tritt aber überraschend häufig auf"/>
|
||||||
|
<node CREATED="1778682889494" ID="ID_1958360361" MODIFIED="1778683124733" TEXT="Vermututng: Race wird durch Contention verlängert">
|
||||||
|
<richcontent TYPE="NOTE"><html>
|
||||||
|
<head/>
|
||||||
|
<body>
|
||||||
|
<p>
|
||||||
|
...da man das Lock erlangen muß, um die synchrone Anfrage zu tätigen, verstärkt sich eine einzige zufällige Kollision: der benachteiligte Thread wartet, erlangt das Lock und bringt dadruch andere Threads zum Warten. <b>Immer wenn ein Thread auf das Lock wartet</b>, ist danach auch sicher seine asynchrone Anfrage bereits bedient
|
||||||
|
</p>
|
||||||
|
</body>
|
||||||
|
</html></richcontent>
|
||||||
|
</node>
|
||||||
|
</node>
|
||||||
|
</node>
|
||||||
|
<node BACKGROUND_COLOR="#f2d3b5" COLOR="#990033" CREATED="1778683132230" ID="ID_1946083526" MODIFIED="1778683164074" TEXT="dahinter verbirgt sich ein Problem der Code-Struktur">
|
||||||
|
<icon BUILTIN="broken-line"/>
|
||||||
|
<node CREATED="1778683193686" ID="ID_398699629" MODIFIED="1778683443874" TEXT="problematisch ist exakt der Kniff, der Doppel-Zuteilungen verhindern soll">
|
||||||
|
<arrowlink COLOR="#b9104e" DESTINATION="ID_985414238" ENDARROW="Default" ENDINCLINATION="653;64;" ID="Arrow_ID_649016741" STARTARROW="Default" STARTINCLINATION="646;0;"/>
|
||||||
|
</node>
|
||||||
|
<node CREATED="1778684203562" ID="ID_1914041075" MODIFIED="1778684232754" TEXT="denn die Eingangs-Queue des LocalPool ist nicht vom Lock abgedeckt"/>
|
||||||
|
<node BACKGROUND_COLOR="#f8f1cb" COLOR="#a50125" CREATED="1778684453026" ID="ID_926570788" MODIFIED="1778684571749" TEXT="der synchrone Request baut nicht wirklich auf dem asynchronen Request auf">
|
||||||
|
<richcontent TYPE="NOTE"><html>
|
||||||
|
<head/>
|
||||||
|
<body>
|
||||||
|
<p>
|
||||||
|
ich dachte, ich kann mich dem Problem entwinden, indem ich beide auf den gleichen Basis-Funktionen aufbaue; der Test zeigt aber, daß das nicht aufgeht
|
||||||
|
</p>
|
||||||
|
</body>
|
||||||
|
</html></richcontent>
|
||||||
|
<icon BUILTIN="clanbomber"/>
|
||||||
|
</node>
|
||||||
|
</node>
|
||||||
|
<node BACKGROUND_COLOR="#c8c0b6" COLOR="#435e98" CREATED="1778694549001" ID="ID_160394565" MODIFIED="1778694768049" TEXT="habe nun verstanden was ich falsch verstanden habe">
|
||||||
|
<richcontent TYPE="NOTE"><html>
|
||||||
|
<head/>
|
||||||
|
<body>
|
||||||
|
<p>
|
||||||
|
Zusammengefaßt: ich brauche einen synchronen Abgleich zur Herstellung der Konsistenz, sofern der zunächt ausgelöste asynchrone Abgleich nicht erfolgreich war. Aber das impliziert <b>nicht</b> einen Aufruf des synchronen API (das ich nun tatsächlich nur für den Test gebaut habe). Vielmehr besteht die <i>elegante Lösung</i> darin, ein »happens-before« herzustellen (in dem wir »jetzt und hier« das globale Processing machen); danach <i>muß</i> der zunächst gesendete asynchrone Request auch tatsächlich behandelt und zugestellt sein, <i>ohne dafür noch extra etwas zu unternehmen.</i> 
|
||||||
|
</p>
|
||||||
|
</body>
|
||||||
|
</html></richcontent>
|
||||||
|
<icon BUILTIN="ksmiletris"/>
|
||||||
|
</node>
|
||||||
|
</node>
|
||||||
|
</node>
|
||||||
|
<node COLOR="#338800" CREATED="1778694437303" ID="ID_389422946" MODIFIED="1778694529808" TEXT="nach dem Umbau funktionierts">
|
||||||
|
<linktarget COLOR="#12d25a" DESTINATION="ID_389422946" ENDARROW="Default" ENDINCLINATION="-296;-885;" ID="Arrow_ID_533922798" SOURCE="ID_1722546045" STARTARROW="None" STARTINCLINATION="158;20;"/>
|
||||||
|
<icon BUILTIN="button_ok"/>
|
||||||
|
</node>
|
||||||
|
</node>
|
||||||
|
<node CREATED="1778695283214" ID="ID_978534045" MODIFIED="1778695295656" TEXT="Vorsicht: Testsuite ist auf 8MiB limitiert">
|
||||||
|
<node CREATED="1778695297087" ID="ID_1119237552" MODIFIED="1778695308343" TEXT="dieser test alloziert »mal eben« 100 MiB"/>
|
||||||
|
<node CREATED="1778695331666" ID="ID_1196538153" MODIFIED="1778695331666" TEXT=""/>
|
||||||
|
</node>
|
||||||
</node>
|
</node>
|
||||||
</node>
|
</node>
|
||||||
<node BACKGROUND_COLOR="#f0d5c5" COLOR="#990033" CREATED="1777330585249" ID="ID_1415692101" MODIFIED="1777330598348" TEXT="Ergebnis / Einsichten">
|
<node BACKGROUND_COLOR="#f0d5c5" COLOR="#990033" CREATED="1777330585249" ID="ID_1415692101" MODIFIED="1777330598348" TEXT="Ergebnis / Einsichten">
|
||||||
|
|
@ -171692,9 +172010,7 @@ std::cout << tmpl.render({"what", "World"}) << s
|
||||||
<icon BUILTIN="messagebox_warning"/>
|
<icon BUILTIN="messagebox_warning"/>
|
||||||
<node COLOR="#435e98" CREATED="1778634707713" HGAP="30" ID="ID_226540112" MODIFIED="1778635048385" TEXT="habe aber inzwischen eine elegante Lösung für dieses Dilemma gefunden" VSHIFT="19">
|
<node COLOR="#435e98" CREATED="1778634707713" HGAP="30" ID="ID_226540112" MODIFIED="1778635048385" TEXT="habe aber inzwischen eine elegante Lösung für dieses Dilemma gefunden" VSHIFT="19">
|
||||||
<richcontent TYPE="NOTE"><html>
|
<richcontent TYPE="NOTE"><html>
|
||||||
<head>
|
<head/>
|
||||||
|
|
||||||
</head>
|
|
||||||
<body>
|
<body>
|
||||||
<p>
|
<p>
|
||||||
...nämlich indem der Job letztlich auf den synchronen Fall zurückgreift, dieser aber durch die gleiche Mechanik implementiert ist, wie der asynchrone Fall, und dabei gleich alle aufgesammelten Requests in den Queues mit verarbeitet. Dadruch wird effektiv ein Batch an Allokator-Aufwand in denjenigen Worker geschoben, der rein zufällig als erster auf eine nicht bediente Allokation stößt. Das Schöne an dieser Code-Struktur ist, daß die Möglichkeit offen bleibt, später zur Performance-Optimierung (oder nur in bestimmten Situationen) dennoch etwas speziell einzurichten / schedulen / auszuführen, ohne daß dazu der Code im EngineBufferManager geändert werden muß
|
...nämlich indem der Job letztlich auf den synchronen Fall zurückgreift, dieser aber durch die gleiche Mechanik implementiert ist, wie der asynchrone Fall, und dabei gleich alle aufgesammelten Requests in den Queues mit verarbeitet. Dadruch wird effektiv ein Batch an Allokator-Aufwand in denjenigen Worker geschoben, der rein zufällig als erster auf eine nicht bediente Allokation stößt. Das Schöne an dieser Code-Struktur ist, daß die Möglichkeit offen bleibt, später zur Performance-Optimierung (oder nur in bestimmten Situationen) dennoch etwas speziell einzurichten / schedulen / auszuführen, ohne daß dazu der Code im EngineBufferManager geändert werden muß
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue