diff --git a/src/lib/sync-barrier.hpp b/src/lib/sync-barrier.hpp index 6ba40fb3b..3229db5f0 100644 --- a/src/lib/sync-barrier.hpp +++ b/src/lib/sync-barrier.hpp @@ -40,6 +40,7 @@ ** - Sync 4 threads : 700ns ** - increasing with number of threads, which implies we are measuring the time ** it takes all threads to catch-up on average... + ** - the classical Mutex+Condition-Var solution is slower by orders of magnitude! ** - these values are on par with typical thread scheduling leeway, ** so this implementation seems adequate for the time being (2023). */ diff --git a/tests/library/sync-barrier-performance-test.cpp b/tests/library/sync-barrier-performance-test.cpp index 2e7d89061..538f87039 100644 --- a/tests/library/sync-barrier-performance-test.cpp +++ b/tests/library/sync-barrier-performance-test.cpp @@ -30,6 +30,7 @@ #include "lib/sync-barrier.hpp" #include "lib/test/microbenchmark.hpp" #include "lib/format-cout.hpp" +#include "lib/sync.hpp" using test::Test; using std::array; @@ -52,6 +53,35 @@ namespace test { FakeBarrier(uint=0) { /* be happy */ } void sync() { /* indulge */ } }; + + + /** + * A Monitor based reference implementation, + * using Mutex + Condition Variable for sleeping wait. + */ + class MonitorSync + : public Sync + { + int latch_; + + bool allPassed() { return latch_ <= 0; } + + public: + MonitorSync (uint nFold =2) + : latch_{int(nFold)} + { } + + void + sync() + { + Lock sync(this); + --latch_; + sync.wait(*this, &MonitorSync::allPassed); + sync.notifyAll(); + } + + private: + }; }//(End)Test setup @@ -101,10 +131,16 @@ namespace test { * - SyncBarrier (48 Thr) : 30µs * - SyncBarrier (64 Thr) : 50µs * - SyncBarrier (80 Thr) : 80µs + * - MonitorWait (2 Thr) : 7µs + * - MonitorWait (4 Thr) : 12µs + * - MonitorWait (8 Thr) : 27µs + * - MonitorWait (16 Thr) : 75µs * @note what we are measuring here is actually the *time to catch up* * for all threads involved, implying we are observing the _operational_ * delay introduced by synchronisation, and not an overhead of the - * implementation technique. + * implementation technique as such. However — the classical implementation + * based on Mutex + ConditionVar, which enters a thread sleep state on wait, + * is slower by orders of magnitude. */ virtual void run (Arg) @@ -121,9 +157,15 @@ namespace test { double time_yieldWait_2 = performanceTest(); // double time_emptySetup = performanceTest(); + // + double time_sleepWait_16 = performanceTest(); + double time_sleepWait_8 = performanceTest(); + double time_sleepWait_4 = performanceTest(); + double time_sleepWait_2 = performanceTest(); cout<<"\n___Microbenchmark_______" <<"\nemptySetup : "< - + @@ -79106,6 +79106,19 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
+ + + + + + +

+ ...trotzdem war ich überrascht, um wie viel langsamer sie ist; das kann ich mir eigentlich nur dadurch erklären, daß die Threads in einen Schlafzustand versetzt werden, ggfs auch bereits schon beim Versuch, die exclusive Zone zu betreten. Möglicherweise dauert es auch grundsätzlich länger, bis ein schlafender Thread überhaupt wieder aufgeweckt wird. Die Progression scheint allerdings linear in der Zahl der Threads zu sein, während die Atomic-yield-Implementierung etwas überproportional langsamer wird. Das ist jetzt aber mehr Intuition, denn jenseits von 8 Threads gibt es ja zunehmend Stau im OS-Scheduler +

+ +
+ +
@@ -79216,14 +79229,14 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + - - + + @@ -79243,7 +79256,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + @@ -79428,8 +79441,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
es ist ja ein einziger Zufallszahlengenerator, und es wäre eine schlechte Idee, wenn die Stdlib das nicht gegen concurrency schützen würde

- - +
@@ -79500,6 +79512,10 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
+ + + + @@ -79551,8 +79567,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
wir messen, wie lange ein Thread im Durchschnitt baucht, bis er sich via SyncBarrier mit den anderen Partner-Threads synchronisiert hat. Dieser Wert ist nicht deterministisch, da die zeitliche Lage der Threads zueinander nicht deterministisch ist. Wir können aber auch nicht anders messen, da der Thread typischerweise in der sync()-Funktion blockt.

- - +
@@ -79564,8 +79579,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
⟹ wir beobachten die Barriere bei ihrer bestimmungsgemäßen Arbeit

- -
+
@@ -79578,8 +79592,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
⟹ wir bekommen so nicht den Implementierungs-Overhead  zu fassen

- - +