Library: actually verify parallelism
Now the ThreadWrapper_test offers both - a really simple usage example - a comprehensive test to verify that actually the thread-function is invoked the expected number of times and that this invocations must have been parallelised
This commit is contained in:
parent
1d30d47b9a
commit
d37a3abd6c
2 changed files with 96 additions and 70 deletions
|
|
@ -26,10 +26,10 @@
|
|||
|
||||
|
||||
#include "lib/test/run.hpp"
|
||||
#include "lib/test/test-helper.hpp"///////////TODO
|
||||
#include "lib/thread.hpp"
|
||||
#include "lib/iter-explorer.hpp"
|
||||
#include "lib/scoped-collection.hpp"
|
||||
#include "lib/test/microbenchmark.hpp"
|
||||
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
|
|
@ -43,47 +43,21 @@ using std::chrono::microseconds;
|
|||
|
||||
|
||||
namespace lib {
|
||||
namespace test {
|
||||
namespace test{
|
||||
|
||||
namespace { // private test classes and data...
|
||||
namespace { // test parameters
|
||||
|
||||
const uint NUM_THREADS = 200;
|
||||
|
||||
|
||||
struct TestThread
|
||||
: Thread
|
||||
{
|
||||
using Thread::Thread;
|
||||
|
||||
uint local{0};
|
||||
|
||||
void
|
||||
doIt (uint a, uint b) ///< the actual operation running in a separate thread
|
||||
{
|
||||
uint sum = a + b;
|
||||
sleep_for (microseconds{sum});
|
||||
local = sum;
|
||||
}
|
||||
};
|
||||
|
||||
} // (End) test classes and data....
|
||||
const uint REPETITIONS = 10;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**********************************************************************//**
|
||||
* @test use the Lumiera Vault to create some new threads, utilising the
|
||||
* lumiera::Thread wrapper for binding to an arbitrary operation
|
||||
* and passing the appropriate context.
|
||||
*
|
||||
* @see vault::Thread
|
||||
* @see threads.h
|
||||
/*******************************************************************//**
|
||||
* @test use the lib::Thread wrapper for simplified definition of the
|
||||
* thread-function, argument binding and starting of threads.
|
||||
* @see thread.hpp
|
||||
* @see ThreadWrapperJoin_test
|
||||
* @see SyncLocking_test
|
||||
*/
|
||||
class ThreadWrapper_test : public Test
|
||||
{
|
||||
|
|
@ -95,54 +69,78 @@ namespace lib {
|
|||
verifyConcurrentExecution();
|
||||
}
|
||||
|
||||
/**
|
||||
* @test demonstrate simple usage of the thread-wrapper
|
||||
*/
|
||||
|
||||
/** @test demonstrate simple usage of the thread-wrapper a λ-binding */
|
||||
void
|
||||
demonstrateSimpleUsage()
|
||||
{
|
||||
lib::ScopedCollection<Thread> threads{NUM_THREADS};
|
||||
atomic_uint i{0};
|
||||
Thread thread("counter", [&]{ ++i; }); // bind a λ and launch thread
|
||||
while (thread) yield(); // ensure thread has finished and detached
|
||||
|
||||
atomic_uint invocations{0};
|
||||
for (uint i=0; i<NUM_THREADS; ++i)
|
||||
threads.emplace<Thread> ("counter"
|
||||
,[&]{ ++invocations; });
|
||||
|
||||
while (explore(threads).has_any())
|
||||
yield();
|
||||
|
||||
CHECK (invocations == NUM_THREADS);
|
||||
CHECK (i == 1); // verify the effect has taken place
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @test verify the thread function is actually performed concurrently
|
||||
* - use a derived Thread object, also holding a local data field
|
||||
* - the thread function sleeps, and then stores the sum of two numbers
|
||||
* - demonstrate that each instance can have a different argument binding
|
||||
* - verify each thread function has actually been invoked once per thread,
|
||||
* by comparing a local sum with values collected from the Thread objects,
|
||||
* - moreover measure the overall time required for launching the threads
|
||||
* and then waiting for all threads to have terminated and detached;
|
||||
* this time must be _shorter_ than all the _average_ sleep times
|
||||
* compounded (as if the function was invoked sequentially).
|
||||
*/
|
||||
void
|
||||
verifyConcurrentExecution()
|
||||
{
|
||||
struct TestThread
|
||||
: Thread
|
||||
{
|
||||
using Thread::Thread;
|
||||
|
||||
uint local{0};
|
||||
|
||||
void
|
||||
doIt (uint a, uint b) ///< the actual operation running in a separate thread
|
||||
{
|
||||
uint sum = a + b;
|
||||
sleep_for (microseconds{sum}); // Note: explicit random delay before local store
|
||||
local = sum;
|
||||
}
|
||||
};
|
||||
|
||||
// prepare Storage for these objects (not created yet)
|
||||
lib::ScopedCollection<TestThread> threads{NUM_THREADS};
|
||||
|
||||
size_t globalSum = 0;
|
||||
for (uint i=1; i<=NUM_THREADS; ++i)
|
||||
{
|
||||
uint x = rand() % 1000;
|
||||
globalSum += (i + x);
|
||||
threads.emplace<TestThread> (&TestThread::doIt, uint{i}, uint{x});
|
||||
}
|
||||
|
||||
while (explore(threads).has_any())
|
||||
yield();
|
||||
|
||||
size_t checkSum = 0;
|
||||
for (auto& t : threads)
|
||||
{
|
||||
CHECK (not t);
|
||||
CHECK (0 < t.local);
|
||||
checkSum += t.local;
|
||||
}
|
||||
CHECK (checkSum == globalSum);
|
||||
size_t globalSum = 0;
|
||||
auto launchThreads = [&]
|
||||
{
|
||||
for (uint i=1; i<=NUM_THREADS; ++i)
|
||||
{
|
||||
uint x = rand() % 1000;
|
||||
globalSum += (i + x);
|
||||
threads.emplace<TestThread> (&TestThread::doIt, uint{i}, uint{x});
|
||||
} // Note: bind to member function, copying arguments
|
||||
|
||||
while (explore(threads).has_any())
|
||||
yield(); // wait for all threads to have detached
|
||||
|
||||
for (auto& t : threads)
|
||||
{
|
||||
CHECK (0 < t.local);
|
||||
checkSum += t.local;
|
||||
}
|
||||
};
|
||||
|
||||
double runTime = benchmarkTime (launchThreads, REPETITIONS);
|
||||
|
||||
CHECK (checkSum == globalSum); // sum of precomputed random numbers matches sum from threads
|
||||
CHECK (runTime < NUM_THREADS * 1000/2); // random sleep time should be > 500ms on average
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -79951,8 +79951,8 @@ Date:   Thu Apr 20 18:53:17 2023 +0200<br/>
|
|||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1695394205547" ID="ID_1719816317" MODIFIED="1695394259544" TEXT="Tests umstellen und modernisieren">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1695394270093" ID="ID_585437655" MODIFIED="1696002274251" TEXT="ThreadWrapper_test">
|
||||
<icon BUILTIN="pencil"/>
|
||||
<node COLOR="#338800" CREATED="1695394270093" ID="ID_585437655" MODIFIED="1696007768225" TEXT="ThreadWrapper_test">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
<node COLOR="#338800" CREATED="1695911786871" ID="ID_1371513124" MODIFIED="1695911794232" TEXT="läuft nach Umstellung">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
</node>
|
||||
|
|
@ -79971,6 +79971,9 @@ Date:   Thu Apr 20 18:53:17 2023 +0200<br/>
|
|||
</html></richcontent>
|
||||
<arrowlink COLOR="#fedec1" DESTINATION="ID_1532807851" ENDARROW="Default" ENDINCLINATION="416;-16;" ID="Arrow_ID_1646166026" STARTARROW="None" STARTINCLINATION="479;36;"/>
|
||||
</node>
|
||||
<node COLOR="#338800" CREATED="1696007772427" ID="ID_1815976624" MODIFIED="1696007791627" TEXT="einfachste Verwendung zeigen">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
</node>
|
||||
<node COLOR="#338800" CREATED="1695394519627" ID="ID_776125132" MODIFIED="1696002223375" TEXT="testen: individuelle Argumentübergabe">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
</node>
|
||||
|
|
@ -79988,6 +79991,31 @@ Date:   Thu Apr 20 18:53:17 2023 +0200<br/>
|
|||
</richcontent>
|
||||
<icon BUILTIN="button_ok"/>
|
||||
</node>
|
||||
<node COLOR="#338800" CREATED="1696007745882" ID="ID_523093343" MODIFIED="1696007928650" TEXT="Zeitmessung ⟹ Beweis der Parallelisierung">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
Da ich jetzt einen einfachsten Fall habe, kann der eigentliche Test doch wieder etwas komplexer sein...
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
in jeden Thread ist eine zufällige Verzögerung eingebaut (1µs < 1ms)
|
||||
</li>
|
||||
<li>
|
||||
mache ein Benchmark über das Starten-warten-Prüfen
|
||||
</li>
|
||||
<li>
|
||||
die <i>durchschnittliche</i> Zeit muß kleiner sein, als die sequentielle Ausführung aller durchschinittlichen Wartezeiten
|
||||
</li>
|
||||
</ul>
|
||||
</body>
|
||||
</html>
|
||||
</richcontent>
|
||||
<icon BUILTIN="button_ok"/>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1695394665822" ID="ID_881973960" MODIFIED="1695394668399" TEXT="ThreadWrapperJoin_test">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
|
|
|
|||
Loading…
Reference in a new issue