Library: investigate drawing random numbers concurrently

''In theory,'' the random number generators are in no way threadsafe,
neither the old `rand()`, nor the mersenne twister of the C++ standard.

However, since all we want is some arbitrarily diffused numbers,
chances are that this issue can be safely ignored; because a random
number computation broken by concurrency will most likely generate --
well, a garbled number or "randomly" corrupted internal state.

Validating this reasoning by an empiric investigation seems advisable though.
This commit is contained in:
Fischlurch 2024-11-16 04:52:58 +01:00
parent 606669aa1b
commit a15006d11a
4 changed files with 136 additions and 6 deletions

View file

@ -15,3 +15,8 @@ END
TEST "Random numbers and Seed" Random_test << END TEST "Random numbers and Seed" Random_test << END
return: 0 return: 0
END END
TEST "Concurrent PRNG access" RandomConcurrent_test << END
return: 0
END

View file

@ -0,0 +1,110 @@
/*
RandomConcurrent(Test) - investigate concurrent random number generation
Copyright (C) Lumiera.org
2024, Hermann Vosseler <Ichthyostega@web.de>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of
the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* *****************************************************/
/** @file random-concurrent-test.cpp
** unit test \ref RandomConcurrent_test
*/
#include "lib/test/run.hpp"
#include "lib/sync-barrier.hpp"
#include "lib/random.hpp"
#include "lib/thread.hpp"
#include "lib/sync.hpp"
#include "lib/util.hpp"
#include "lib/scoped-collection.hpp"
#include "lib/test/microbenchmark.hpp"
#include "lib/test/diagnostic-output.hpp"
#include <deque>
//using util::isLimited;
namespace lib {
namespace test {
namespace {
const uint NUM_THREADS = 8;
const uint NUM_INVOKE = 1'000'000;
}
/******************************************************************//**
* @test demonstrate simple access to random number generation,
* as well as the setup of controlled random number sequences.
* @see random.hpp
*/
class RandomConcurrent_test : public Test
{
virtual void
run (Arg)
{
seedRand();
investigate_concurrentAccess();
}
/** @test examine behaviour of PRNG under concurrency stress */
void
investigate_concurrentAccess()
{
struct Results
: std::deque<double>
, Sync<>
{
void
post (double val)
{
Lock sync(this);
push_back (val);
}
};
Results results;
const uint N = NUM_INVOKE;
auto expect = RAND_MAX / 2;
auto drawRandom = [&]()
{
double avg{0.0};
for (uint i=0; i<N; ++i)
avg += 1.0/N * rani();
auto error = avg/expect - 1;
results.post (error);
};
auto [dur,sum] = threadBenchmark<NUM_THREADS> (drawRandom, 1);
for (auto res : results)
SHOW_EXPR(res);
SHOW_EXPR(sum)
SHOW_EXPR(dur/NUM_INVOKE)
}
};
LAUNCHER (RandomConcurrent_test, "unit common");
}} // namespace lib::test

View file

@ -132,9 +132,9 @@ namespace test {
void void
joinOnceOnly () joinOnceOnly()
{ {
ThreadJoinable theThread{"joining-4" ThreadJoinable theThread{"test join-4"
,[]{ sleep_for (10ms); }}; ,[]{ sleep_for (10ms); }};
theThread.join(); theThread.join();

View file

@ -57622,8 +57622,8 @@
<node CREATED="1731710016247" ID="ID_1116003187" MODIFIED="1731710023256" TEXT="nochmal im Einzelnen analysiert"/> <node CREATED="1731710016247" ID="ID_1116003187" MODIFIED="1731710023256" TEXT="nochmal im Einzelnen analysiert"/>
<node CREATED="1731710023964" ID="ID_778217189" MODIFIED="1731710032544" TEXT="jede denkbare L&#xf6;sung ist nicht-trivial"/> <node CREATED="1731710023964" ID="ID_778217189" MODIFIED="1731710032544" TEXT="jede denkbare L&#xf6;sung ist nicht-trivial"/>
<node COLOR="#435e98" CREATED="1731710053533" ID="ID_722076886" MODIFIED="1731713106542" TEXT="denkbar w&#xe4;re zumindest ein Refactoring"> <node COLOR="#435e98" CREATED="1731710053533" ID="ID_722076886" MODIFIED="1731713106542" TEXT="denkbar w&#xe4;re zumindest ein Refactoring">
<linktarget COLOR="#23b91f" DESTINATION="ID_722076886" ENDARROW="Default" ENDINCLINATION="1530;2127;" ID="Arrow_ID_1785760257" SOURCE="ID_872583288" STARTARROW="None" STARTINCLINATION="-1253;43;"/>
<linktarget COLOR="#7298c6" DESTINATION="ID_722076886" ENDARROW="Default" ENDINCLINATION="-173;13;" ID="Arrow_ID_1252004977" SOURCE="ID_778660910" STARTARROW="None" STARTINCLINATION="411;21;"/> <linktarget COLOR="#7298c6" DESTINATION="ID_722076886" ENDARROW="Default" ENDINCLINATION="-173;13;" ID="Arrow_ID_1252004977" SOURCE="ID_778660910" STARTARROW="None" STARTINCLINATION="411;21;"/>
<linktarget COLOR="#23b91f" DESTINATION="ID_722076886" ENDARROW="Default" ENDINCLINATION="1530;2127;" ID="Arrow_ID_1785760257" SOURCE="ID_872583288" STARTARROW="None" STARTINCLINATION="-1253;43;"/>
<icon BUILTIN="idea"/> <icon BUILTIN="idea"/>
<node COLOR="#338800" CREATED="1731710077230" ID="ID_1009222978" MODIFIED="1731712876022" TEXT="man k&#xf6;nnte die Erkennung dieser Situation in eine Methode auf Layer-1 extrahieren"> <node COLOR="#338800" CREATED="1731710077230" ID="ID_1009222978" MODIFIED="1731712876022" TEXT="man k&#xf6;nnte die Erkennung dieser Situation in eine Methode auf Layer-1 extrahieren">
<icon BUILTIN="button_ok"/> <icon BUILTIN="button_ok"/>
@ -57714,8 +57714,7 @@
Hier wird zun&#228;chst &#187;auf dem Trockenen&#171; demonstriert, wie aus der Kapazit&#228;tsrechnung eine zu erwartende effektive concurrent-run-time abgeleitet wird. Am Ende wird ein tats&#228;chlicher Scheduler-Lauf mit diesem (relativ locker dimensionierten) Schedule gestartet&#160;&#160;&#8212; und <b>hier</b>&#160;&#252;berschreiten wir den <b>Timeout von 5 Sekunden</b>, was bedeutet, da&#223; das Schedule komplett aus dem Ruder gelaufen sein mu&#223; (m&#246;glicherweise wurden sogar Job-Deadlines &#252;berfahren, in welchem Fall das Scheduler unvollst&#228;ndig h&#228;ngen bleibt) Hier wird zun&#228;chst &#187;auf dem Trockenen&#171; demonstriert, wie aus der Kapazit&#228;tsrechnung eine zu erwartende effektive concurrent-run-time abgeleitet wird. Am Ende wird ein tats&#228;chlicher Scheduler-Lauf mit diesem (relativ locker dimensionierten) Schedule gestartet&#160;&#160;&#8212; und <b>hier</b>&#160;&#252;berschreiten wir den <b>Timeout von 5 Sekunden</b>, was bedeutet, da&#223; das Schedule komplett aus dem Ruder gelaufen sein mu&#223; (m&#246;glicherweise wurden sogar Job-Deadlines &#252;berfahren, in welchem Fall das Scheduler unvollst&#228;ndig h&#228;ngen bleibt)
</p> </p>
</body> </body>
</html> </html></richcontent>
</richcontent>
</node> </node>
</node> </node>
</node> </node>
@ -58021,6 +58020,7 @@
</node> </node>
<node BACKGROUND_COLOR="#fdfdcf" COLOR="#ff0000" CREATED="1731455444796" ID="ID_784243083" MODIFIED="1731455723202" TEXT="Concurrent Tests umstellen"> <node BACKGROUND_COLOR="#fdfdcf" COLOR="#ff0000" CREATED="1731455444796" ID="ID_784243083" MODIFIED="1731455723202" TEXT="Concurrent Tests umstellen">
<icon BUILTIN="bell"/> <icon BUILTIN="bell"/>
<node CREATED="1731728363475" ID="ID_1473702684" MODIFIED="1731728367146" TEXT="problematisch">
<node CREATED="1731455475587" ID="ID_1622000187" MODIFIED="1731455475587" TEXT="CallQueue_test"/> <node CREATED="1731455475587" ID="ID_1622000187" MODIFIED="1731455475587" TEXT="CallQueue_test"/>
<node CREATED="1731455716786" ID="ID_943344715" MODIFIED="1731455716786" TEXT="DiagnosticContext_test"/> <node CREATED="1731455716786" ID="ID_943344715" MODIFIED="1731455716786" TEXT="DiagnosticContext_test"/>
<node CREATED="1731458234804" ID="ID_1185469440" MODIFIED="1731458234804" TEXT="SessionCommandFunction_test"/> <node CREATED="1731458234804" ID="ID_1185469440" MODIFIED="1731458234804" TEXT="SessionCommandFunction_test"/>
@ -58047,7 +58047,22 @@
<node CREATED="1731464903922" ID="ID_349618800" MODIFIED="1731464903922" TEXT="ThreadWrapperJoin_test"/> <node CREATED="1731464903922" ID="ID_349618800" MODIFIED="1731464903922" TEXT="ThreadWrapperJoin_test"/>
</node> </node>
</node> </node>
<node CREATED="1731465663005" MODIFIED="1731465663005" TEXT="BusTerm_test"/> <node CREATED="1731465663005" ID="ID_444907234" MODIFIED="1731465663005" TEXT="BusTerm_test"/>
</node>
<node BACKGROUND_COLOR="#f0d5c5" COLOR="#990033" CREATED="1731728379707" ID="ID_38959317" MODIFIED="1731728399492" TEXT="gibt es &#xfc;berhaupt ein Problem hier?">
<icon BUILTIN="help"/>
<node CREATED="1731728402020" ID="ID_216802866" MODIFIED="1731728433241" TEXT="theoretisch ist PRNG nicht thread-safe"/>
<node CREATED="1731728433837" ID="ID_975708385" MODIFIED="1731728441330" TEXT="aber ... was kann schon passieren??"/>
<node CREATED="1731728442361" ID="ID_444376696" MODIFIED="1731728582512" TEXT="schlimmstenfalls eine &quot;zuf&#xe4;llig&quot; kaputte Zahl au&#xdf;erhalb des Definitionsbereichs..."/>
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1731728584572" ID="ID_1568853077" MODIFIED="1731728594906" TEXT="empirisch untersuchen">
<icon BUILTIN="yes"/>
<icon BUILTIN="pencil"/>
<node CREATED="1731728599474" ID="ID_1952720762" MODIFIED="1731728615563" TEXT="mit allen Cores parallel Zufallszahlen ziehen"/>
<node CREATED="1731728616535" ID="ID_59784960" MODIFIED="1731728640632" TEXT="einzelne Zahlen pr&#xfc;fen"/>
<node CREATED="1731728641175" ID="ID_177314578" MODIFIED="1731728668212" TEXT="MIttelwert pr&#xfc;fen (bias)"/>
<node CREATED="1731728672328" ID="ID_132879815" MODIFIED="1731728703703" TEXT="1. Test : 1 Million Aufrufe &#x27f9; sieht gut aus"/>
</node>
</node>
</node> </node>
</node> </node>
</node> </node>