91 lines
3.4 KiB
C++
91 lines
3.4 KiB
C++
// Example 06 — Watchdog & Diagnostics (+ optional web debug UI)
|
|
//
|
|
// A four-stage pipeline with deliberately uneven node speeds:
|
|
//
|
|
// [source] --int--> [fast_filter] --int--> [slow_transform] --int--> [sink]
|
|
//
|
|
// The watchdog fires every second and prints a full diagnostics report:
|
|
// - frames processed, throughput fps, exec time, blocked time per node
|
|
// - channel fill %, peak fill %, pushes, overflows, bandwidth
|
|
// - bottleneck hint (node with highest avg exec time)
|
|
//
|
|
// "slow_transform" sleeps 30 ms per item, making it the obvious bottleneck.
|
|
// Watch the channel upstream of it saturate and the fps converge to ~33.
|
|
//
|
|
// Build with -DKPN_WEB_DEBUG=ON to also get a live D3 graph at localhost:9090.
|
|
|
|
#include <kpn/kpn.hpp>
|
|
#include <chrono>
|
|
#include <cmath>
|
|
#include <iostream>
|
|
#include <thread>
|
|
|
|
// ── Node functions ────────────────────────────────────────────────────────────
|
|
|
|
static int source() {
|
|
// Produces at ~50 fps — faster than slow_transform (33 fps) so the
|
|
// channel between filter and slow gradually fills, but not catastrophically
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(20));
|
|
static int n = 0;
|
|
return ++n;
|
|
}
|
|
|
|
static int fast_filter(int x) {
|
|
// Trivial work: ~0.1 ms
|
|
return (x % 2 == 0) ? x : x + 1;
|
|
}
|
|
|
|
static int slow_transform(int x) {
|
|
// Simulates expensive processing (e.g. a neural net inference step)
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(30));
|
|
return x * x;
|
|
}
|
|
|
|
static void sink(int x) {
|
|
// Print every 10th result to avoid flooding the terminal
|
|
if (x % 100 < 4)
|
|
std::cout << "[sink] " << x << '\n';
|
|
}
|
|
|
|
// ── main ──────────────────────────────────────────────────────────────────────
|
|
|
|
int main() {
|
|
using namespace kpn;
|
|
|
|
// Larger capacity buffers so the fast nodes don't immediately overflow
|
|
auto src = make_node<source> (out<"v">{}, 16);
|
|
auto filt = make_node<fast_filter> (in<"v">{}, out<"v">{}, 16);
|
|
auto slow = make_node<slow_transform> (in<"v">{}, out<"v">{}, 8);
|
|
auto snk = make_node<sink> (in<"v">{}, 8);
|
|
|
|
Network net;
|
|
|
|
// Watchdog fires every 1 second — prints the built-in diagnostics table
|
|
// including the "Bottleneck hint" line
|
|
net.set_watchdog_interval(std::chrono::milliseconds(1000));
|
|
|
|
net.add("source", src)
|
|
.add("filter", filt)
|
|
.add("slow", slow)
|
|
.add("sink", snk)
|
|
.connect("source", src.template output<"v">(), "filter", filt.template input<"v">())
|
|
.connect("filter", filt.template output<"v">(), "slow", slow.template input<"v">())
|
|
.connect("slow", slow.template output<"v">(), "sink", snk.template input<"v">())
|
|
.build();
|
|
|
|
std::cout << "Running for 30 seconds — watch 'slow' become the bottleneck.\n"
|
|
<< "Watchdog diagnostics print every 1 second.\n";
|
|
#ifdef KPN_WEB_DEBUG
|
|
net.set_web_debug_port(9090);
|
|
std::cout << "Web debug UI: http://localhost:9090 (live graph, auto-updates every 500 ms)\n";
|
|
#endif
|
|
std::cout << '\n';
|
|
|
|
net.start();
|
|
std::this_thread::sleep_for(std::chrono::seconds(30));
|
|
net.stop();
|
|
|
|
std::cout << "\n=== Final diagnostics ===\n";
|
|
net.print_diagnostics();
|
|
}
|