// Example 14 — DebugHub with Shared Resource Token // // Two independent KPN networks compete for one shared inference resource // (simulating a small GPU or single-session ONNX runtime). The DebugHub // serves a single web UI at http://localhost:9090 with: // // [All Networks] — resource utilisation cards + cross-network node table // [detect] — force-directed graph for the detection pipeline // [classify] — force-directed graph for the classification pipeline // // Topology: // // detect pipeline: // [source_detect] ──> [run_detect] ──> [sink_detect] // // classify pipeline: // [source_classify] ──> [run_classify] ──> [sink_classify] // // Both [run_detect] and [run_classify] call gpu.acquire() before touching the // simulated device. The priority-based token awards the next slot to the // waiter that is more likely to make useful progress (higher priority score). // // Build: cmake -DKPN_WEB_DEBUG=ON .. && cmake --build . // Run: ./14_debug_hub // UI: http://localhost:9090 #ifdef KPN_WEB_DEBUG #include #include #include #include #include using namespace kpn; using namespace std::chrono_literals; // ── Simulated inference device ──────────────────────────────────────────────── // // Represents any exclusive, serialised accelerator: GPU session, ONNX runtime, // hardware encoder, etc. Only one caller can hold it at a time. struct GPU { // Detection model: fast, 8 ms per frame. int detect(int frame_id) { std::this_thread::sleep_for(8ms); return frame_id * 2; // synthetic "score" } // Classification model: heavier, 14 ms per frame. int classify(int frame_id) { std::this_thread::sleep_for(14ms); return frame_id % 10; // synthetic "label" } }; // Global pointer so free-function nodes can reach the resource. // In production code, capture by reference inside an ObjectNode functor instead. static SharedResource* g_gpu = nullptr; // ── Detection pipeline ──────────────────────────────────────────────────────── static int source_detect() { static std::atomic id{0}; std::this_thread::sleep_for(25ms); // ~40 fps source rate return id.fetch_add(1, std::memory_order_relaxed); } static int run_detect(int frame_id) { // Higher priority: detection is latency-critical. auto guard = g_gpu->acquire([] { return 0.7f; }); return guard->detect(frame_id); } static std::atomic detect_out{0}; static void sink_detect(int) { detect_out.fetch_add(1, std::memory_order_relaxed); } // ── Classification pipeline ─────────────────────────────────────────────────── static int source_classify() { static std::atomic id{0}; std::this_thread::sleep_for(40ms); // ~25 fps source rate return id.fetch_add(1, std::memory_order_relaxed); } static int run_classify(int frame_id) { // Lower priority: classification is best-effort. auto guard = g_gpu->acquire([] { return 0.3f; }); return guard->classify(frame_id); } static std::atomic classify_out{0}; static void sink_classify(int) { classify_out.fetch_add(1, std::memory_order_relaxed); } // ── main ────────────────────────────────────────────────────────────────────── int main() { SharedResource gpu; g_gpu = &gpu; // ── Detection network ───────────────────────────────────────────────────── auto src_det = make_node(4); auto inf_det = make_node(4); auto snk_det = make_node(4); auto net_detect = make_network( edge(src_det.output<0>(), inf_det.input<0>()), edge(inf_det.output<0>(), snk_det.input<0>()) ); // ── Classification network ──────────────────────────────────────────────── auto src_cls = make_node(4); auto inf_cls = make_node(4); auto snk_cls = make_node(4); auto net_classify = make_network( edge(src_cls.output<0>(), inf_cls.input<0>()), edge(inf_cls.output<0>(), snk_cls.input<0>()) ); // ── Hub — one debug server for both networks + the shared resource ───────── DebugHub hub(9090); hub.register_network("detect", net_detect); hub.register_network("classify", net_classify); hub.register_resource("gpu", &gpu); net_detect.start(); net_classify.start(); hub.start(); std::cout << "Running — open http://localhost:9090\n" << "Tabs: [All Networks] [detect] [classify]\n" << "Press Enter to stop.\n"; std::cin.get(); net_detect.stop(); net_classify.stop(); std::cout << "\nResults:\n" << " detect: " << detect_out.load() << " frames\n" << " classify: " << classify_out.load() << " frames\n"; return 0; } #else // no KPN_WEB_DEBUG #include int main() { std::cerr << "This example requires KPN_WEB_DEBUG.\n" << "Rebuild with: cmake -DKPN_WEB_DEBUG=ON ..\n"; return 1; } #endif