From a15006d11a39fc32de41095cfb6febcd1af309e1 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sat, 16 Nov 2024 04:52:58 +0100 Subject: [PATCH] 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. --- tests/16calculation.tests | 5 + tests/library/random-concurrent-test.cpp | 110 +++++++++++++++++++++ tests/library/thread-wrapper-join-test.cpp | 4 +- wiki/thinkPad.ichthyo.mm | 23 ++++- 4 files changed, 136 insertions(+), 6 deletions(-) create mode 100644 tests/library/random-concurrent-test.cpp diff --git a/tests/16calculation.tests b/tests/16calculation.tests index 99defe274..3e3001c53 100644 --- a/tests/16calculation.tests +++ b/tests/16calculation.tests @@ -15,3 +15,8 @@ END TEST "Random numbers and Seed" Random_test << END return: 0 END + + +TEST "Concurrent PRNG access" RandomConcurrent_test << END +return: 0 +END diff --git a/tests/library/random-concurrent-test.cpp b/tests/library/random-concurrent-test.cpp new file mode 100644 index 000000000..2d8447799 --- /dev/null +++ b/tests/library/random-concurrent-test.cpp @@ -0,0 +1,110 @@ +/* + RandomConcurrent(Test) - investigate concurrent random number generation + + Copyright (C) Lumiera.org + 2024, Hermann Vosseler + + 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 +//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 + , 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 (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 diff --git a/tests/library/thread-wrapper-join-test.cpp b/tests/library/thread-wrapper-join-test.cpp index 9269b26ee..4d59a0e04 100644 --- a/tests/library/thread-wrapper-join-test.cpp +++ b/tests/library/thread-wrapper-join-test.cpp @@ -132,9 +132,9 @@ namespace test { void - joinOnceOnly () + joinOnceOnly() { - ThreadJoinable theThread{"joining-4" + ThreadJoinable theThread{"test join-4" ,[]{ sleep_for (10ms); }}; theThread.join(); diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index 1c5fa3b2d..faee094c2 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -57622,8 +57622,8 @@ - + @@ -57714,8 +57714,7 @@ Hier wird zunächst »auf dem Trockenen« demonstriert, wie aus der Kapazitätsrechnung eine zu erwartende effektive concurrent-run-time abgeleitet wird. Am Ende wird ein tatsächlicher Scheduler-Lauf mit diesem (relativ locker dimensionierten) Schedule gestartet  — und hier überschreiten wir den Timeout von 5 Sekunden, was bedeutet, daß das Schedule komplett aus dem Ruder gelaufen sein muß (möglicherweise wurden sogar Job-Deadlines überfahren, in welchem Fall das Scheduler unvollständig hängen bleibt)

- - +
@@ -58021,6 +58020,7 @@
+ @@ -58047,7 +58047,22 @@ - + + + + + + + + + + + + + + + +