160 lines
5.8 KiB
C++
160 lines
5.8 KiB
C++
// 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 <kpn/kpn.hpp>
|
|
|
|
#include <atomic>
|
|
#include <chrono>
|
|
#include <iostream>
|
|
#include <thread>
|
|
|
|
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<GPU>* g_gpu = nullptr;
|
|
|
|
// ── Detection pipeline ────────────────────────────────────────────────────────
|
|
|
|
static int source_detect() {
|
|
static std::atomic<int> 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<uint64_t> 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<int> 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<uint64_t> classify_out{0};
|
|
static void sink_classify(int) {
|
|
classify_out.fetch_add(1, std::memory_order_relaxed);
|
|
}
|
|
|
|
// ── main ──────────────────────────────────────────────────────────────────────
|
|
|
|
int main() {
|
|
SharedResource<GPU> gpu;
|
|
g_gpu = &gpu;
|
|
|
|
// ── Detection network ─────────────────────────────────────────────────────
|
|
auto src_det = make_node<source_detect, "source_detect">(4);
|
|
auto inf_det = make_node<run_detect, "run_detect" >(4);
|
|
auto snk_det = make_node<sink_detect, "sink_detect" >(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<source_classify, "source_classify">(4);
|
|
auto inf_cls = make_node<run_classify, "run_classify" >(4);
|
|
auto snk_cls = make_node<sink_classify, "sink_classify" >(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 <iostream>
|
|
int main() {
|
|
std::cerr << "This example requires KPN_WEB_DEBUG.\n"
|
|
<< "Rebuild with: cmake -DKPN_WEB_DEBUG=ON ..\n";
|
|
return 1;
|
|
}
|
|
|
|
#endif
|