From 72e1838b36a70c9f2f8df0336b36fa29ce86f74a Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Fri, 17 Jul 2026 23:22:33 +0200 Subject: [PATCH] Prototype/Demo: add IO timing measurement MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - use a helper class with a series of timer variables - these can be addressed like the buffer ring »slots« - commit each individual measurement into an EMA --- FrOSCon26/FrOSCon.26.mm | 65 +++++++++++++++++++++++++------ FrOSCon26/examples/commons.hpp | 42 ++++++++++++++++++++ FrOSCon26/examples/demo-uring.cpp | 15 +++++++ 3 files changed, 110 insertions(+), 12 deletions(-) diff --git a/FrOSCon26/FrOSCon.26.mm b/FrOSCon26/FrOSCon.26.mm index aec8bc3..8b5da0f 100644 --- a/FrOSCon26/FrOSCon.26.mm +++ b/FrOSCon26/FrOSCon.26.mm @@ -2433,6 +2433,49 @@ + + + + + + + + + + + + + +

+ der »alte Wert« wird jeweils mit (N-1)/N multipliziert +

+

+ n meiner Rationalisierung wäre dann α ≔ 1/N  ⟹ 1-α = (N-1)/N +

+

+ Dämpfungsfaktor α +

+

+ EMA(i) = α · value(i) + (1- α) · EMA(i-1) +

+ +
+
+ + +
+ + + + + + + + + + + + @@ -2550,8 +2593,7 @@ Das war ein ganz selten herausragendes Feuerwerk, und ich erinnere mich daß mir einige sehr gut abgeschlossene Sequenzen gelungen sind

- -
+ @@ -2572,8 +2614,7 @@ ...das wäre dann aber eine längere Sequenz, beginnend mit den goldgelben Garben am untern Bildrand, und dann ein Bouquet aus großen Kugeln, die ihre Farbe von Blau über Rot entfalten. Klassisches Feuerwerk

- - +
@@ -2595,8 +2636,7 @@ auch das eine ehr langsame und zeitlose Sequenz; besonders im Zusammenspiel mit den Pulverwolken und Spuren am Ende

- - +
@@ -2613,8 +2653,7 @@ Wegen dem Recht am Bild. Zwar ist die Rechtslage in Deutschland eindeutig (Panoramafreiheit sowie Geschehen der relativen Zeitgeschichte), jedoch könnte es Ärger geben, wenn jemand erkennbar prominent im Bild ist. Bei der bezeichneten Szene wäre das vor 1:15 der Fall, wo mehrere Leute direkt im Vordergrund Gruppen-Selfies machen; das könnte man m.E. unbesorgt einsetzen, wenn man die Gesamt-Szene zeigt, aber für einen kurzen Ausschnitt sollte ich besser vorsichtig sein. Daher wähle ich die nächste Sequenz, in der selbst in der Gruppe mit den Wunderkerzen keine Gesichter prominent erkennbar sind

- - +
@@ -2625,8 +2664,7 @@ Die Kamera bewegt sich flüssig nach links durch eine gut gelaunte Menschenmenge, eine Gruppe mit Wunderkerzen, eine Steinskulptur, dahinter die Flußbrücke randvoll mit einer Menschenmasse und ein irres Feuerwerk darüber, schon halb im Pulverdampf

- - +
@@ -2654,8 +2692,7 @@ bitrate=972000.0kbits/s video:2373047KiB  ≙ 2.3  GiB

- - +
@@ -2789,6 +2826,10 @@ + + + +
diff --git a/FrOSCon26/examples/commons.hpp b/FrOSCon26/examples/commons.hpp index d37c81a..1ebfb7c 100644 --- a/FrOSCon26/examples/commons.hpp +++ b/FrOSCon26/examples/commons.hpp @@ -17,6 +17,7 @@ #include #include #include +#include #include // for struct __kernel_timespec @@ -61,4 +62,45 @@ asKernelTimespec (std::chrono::duration dur) }; } + +/** + * A series of timers for performance measurement, that can be used in interleaved. + * When marking the end of each timed interval, the resulting duration is committed + * into an exponential moving average of all observed timings. + * @note not threadsafe, only to be used from within a single thread. + */ +template +class OverlappingTimers + { + using Scale = std::micro; // timings in µs + using Clock = std::chrono::steady_clock; + using Time = decltype(Clock::now()); + using Dur = std::chrono::duration; + + std::array timer_{Time{}}; + + /** damping factor to compute exponential moving average + EMA(i) = value(i)/N + (N-1)/N · EMA(i-1) */ + static constexpr double ALPHA = 1.0 / ema_period; + double expMA_{1}; + + public: + double expMA() { return expMA_; } + + void + markStart (uint timerID) + { + timer_[timerID % cnt] = Clock::now(); + } + + double + markStop (uint timerID) + { + Dur duration = Clock::now () - timer_[timerID % cnt]; + double runTime = duration.count(); + expMA_ = ALPHA * runTime + (1-ALPHA) * expMA_; + return runTime; + } + }; + #endif /*COMMONS_H*/ diff --git a/FrOSCon26/examples/demo-uring.cpp b/FrOSCon26/examples/demo-uring.cpp index ba1e98a..c7291a8 100644 --- a/FrOSCon26/examples/demo-uring.cpp +++ b/FrOSCon26/examples/demo-uring.cpp @@ -37,9 +37,18 @@ const size_t MAX_LOOP_FRAME = 960; auto calcSrcFrame = [](size_t runningNr){ return START_FRAME + runningNr % MAX_LOOP_FRAME; }; +const uint EMA_PERIOD = 20; //-----------------hard-coded-config-- +namespace { + /** a set of timers, one for each buffer »slot« + * to observe average IO times */ + OverlappingTimers timings; +} + + + /** * Read video frames from a raw video file and pass them to OpenGL display. * Using a »Proactor« thread to coordinate reading via IO_URING. @@ -204,6 +213,7 @@ UringVideoPlayer::addNextReadSubmissions (io_uring& uRing, int inputFD) ); io_uring_sqe_set_data64 (readSubmission, nextRead_); buffState_[nextRead_ % RING_SIZ].store (READ, std::memory_order_relaxed); + timings.markStart (nextRead_); ++nextRead_; } } @@ -219,6 +229,8 @@ UringVideoPlayer::handleCompletedRead (io_uring_cqe& completion) size_t frameNr = completion.user_data; // Note: completion was marked with the frameNr as »user data« auto& state = buffState_[frameNr % RING_SIZ]; // Can thus pick buffer slot based on frame number, cycling through the ring + timings.markStop(frameNr); + if (completion.res != sizeof(Buffer)) state.store(STOP, std::memory_order_relaxed); // Insufficient read, possibly end-of-file => signal stop of playback (to ourselves) else @@ -249,6 +261,9 @@ UringVideoPlayer::maybeDisplayFrame() buffState_[currShow_ % RING_SIZ].store (SHOW, std::memory_order_release); // Notify GUI thread via Glib::Dispatcher dispatchFrame_(); + + if (0 == currShow_ % EMA_PERIOD) + std::cout << "Frame #"<