code wiki / (root) / nx_bench_companion.nx

nx_bench_companion.nx source

↩ module page · 212 lines · 8374 B

1// nx_bench_companion.nx -- substrate-honest measurement primitive for 2// parallel-companion scenarios. 3// 4// Captures EVERY measurable axis from a running [[nx_session]] into 5// one structured report. Designed for substrate-internal counters 6// (deterministic + paired) so qemu-riscv64 produces meaningful 7// comparison numbers BEFORE the physical-hardware run lands. 8// 9// Per CARDINAL [[feedback-no-strawman-perf-comparisons]]: this 10// primitive emits ONLY measured counters from real substrate calls. 11// No throughput ratio is claimed without paired measurement of the 12// SAME workload on the SAME substrate (e.g. nx_actor_role_llm v1 vs 13// v2; or future Nishi vs llama.cpp on identical Llama-7B Q4_K). 14// 15// V1 ships the counter-tabulation primitive + the report struct. 16// V2 will add the run-N-iterations loop that drives a session 17// through multiple cycles and accumulates the deltas. V3 will add 18// TSV emission via nx_runtime fact stream for cross-session 19// comparison. 20 21import "nx_syscalls.nx" 22import "nx_tier.nx" 23import "nx_actor.nx" 24import "nx_message.nx" 25import "nx_stream_multiplexer.nx" 26import "nx_resource_arbiter.nx" 27import "nx_interrupt_broker.nx" 28import "nx_session.nx" 29 30// ===== Sealed enum: NxBenchVerdict ================================= 31 32const NX_BC_V_OK: nx_int = 0 33const NX_BC_V_NULL_SESSION: nx_int = 1 34const NX_BC_V_NULL_REPORT: nx_int = 2 35const NX_BC_V_SESSION_NOT_READY: nx_int = 3 36const NX_BC_V_INVALID: nx_int = 4 37const NX_BC_V_N: nx_int = 5 38 39func nx_bc_v_is_valid(v: nx_int) -> nx_int { 40 if v < 0 { return 0 } 41 if v >= NX_BC_V_N { return 0 } 42 return 1 43} 44 45// ===== Struct: NxBenchReport ======================================= 46// 47// Every field carries a SUBSTRATE-MEASURED counter, not an estimate. 48// Counters that combine multiple sub-substrates (e.g. "total messages 49// across all mailboxes") are aggregated by walking the relevant 50// primitive's accessor funcs. 51 52struct NxBenchReport { 53 n_actors: nx_int, 54 actors_completed: nx_int, 55 actors_failed: nx_int, 56 actors_running: nx_int, 57 actors_waiting: nx_int, 58 total_actor_steps: nx_int, 59 cumulative_runtime_us: nx_size, 60 n_subscriptions: nx_int, 61 mux_pending: nx_int, 62 arb_grants_full: nx_int, 63 arb_grants_partial: nx_int, 64 arb_denials: nx_int, 65 arb_preemptions: nx_int, 66 broker_grants: nx_int, 67 broker_refusals: nx_int, 68 broker_deadline_misses: nx_int, 69 epoch_count: nx_int, 70 captured_at_us: nx_size, 71 verdict: nx_int, 72} 73 74const NX_BC_REPORT_BYTES: nx_int = 152 // 19 fields * 8 75 76// ===== Constructor ================================================= 77 78func nx_bc_report_new() -> *NxBenchReport { 79 let raw: *u8 = sys_mmap(NX_BC_REPORT_BYTES) 80 let r: *NxBenchReport = raw as *NxBenchReport 81 r.n_actors = 0 82 r.actors_completed = 0 83 r.actors_failed = 0 84 r.actors_running = 0 85 r.actors_waiting = 0 86 r.total_actor_steps = 0 87 r.cumulative_runtime_us = 0 88 r.n_subscriptions = 0 89 r.mux_pending = 0 90 r.arb_grants_full = 0 91 r.arb_grants_partial = 0 92 r.arb_denials = 0 93 r.arb_preemptions = 0 94 r.broker_grants = 0 95 r.broker_refusals = 0 96 r.broker_deadline_misses = 0 97 r.epoch_count = 0 98 r.captured_at_us = 0 99 r.verdict = NX_BC_V_OK 100 return r 101} 102 103// ===== Capture: walk session counters into report ================= 104// 105// Single-shot: takes an already-driven session + a report, fills the 106// report with every counter the substrate exposes. Caller decides 107// when to capture (after one iteration / N iterations / at scene end). 108 109func nx_bc_capture(s: *NxSession, 110 r: *NxBenchReport, 111 n_subscription_kinds_to_count: nx_int, 112 now_us: nx_size) -> nx_int { 113 if (s as i64) == 0 { return NX_BC_V_NULL_SESSION } 114 if (r as i64) == 0 { return NX_BC_V_NULL_REPORT } 115 if nx_session_is_ready(s) != 1 { return NX_BC_V_SESSION_NOT_READY } 116 if n_subscription_kinds_to_count < 0 { return NX_BC_V_INVALID } 117 118 // ===== Scheduler counters ===== 119 r.n_actors = nx_ac_actor_count(s.scheduler) 120 r.actors_completed = nx_ac_count_by_state(s.scheduler, NX_AC_STATE_COMPLETED) 121 r.actors_failed = nx_ac_count_by_state(s.scheduler, NX_AC_STATE_FAILED) 122 r.actors_running = nx_ac_count_by_state(s.scheduler, NX_AC_STATE_RUNNING) 123 r.actors_waiting = nx_ac_count_by_state(s.scheduler, NX_AC_STATE_WAITING) 124 r.total_actor_steps = s.scheduler.steps_executed_total 125 r.cumulative_runtime_us = nx_ac_total_runtime_us(s.scheduler) 126 r.epoch_count = s.scheduler.epoch_count 127 128 // ===== Message bus counters ===== 129 // Aggregate subscription count across the first N message kinds 130 var subs: nx_int = 0 131 var k: nx_int = 0 132 while k < n_subscription_kinds_to_count { 133 subs = subs + nx_ms_subscription_count(s.bus, k) 134 k = k + 1 135 } 136 r.n_subscriptions = subs 137 138 // ===== Multiplexer counters ===== 139 r.mux_pending = nx_sm_pending_count(s.multiplexer) 140 141 // ===== Arbiter counters ===== 142 r.arb_grants_full = nx_ra_grants_full(s.arbiter) 143 r.arb_grants_partial = nx_ra_grants_partial(s.arbiter) 144 r.arb_denials = nx_ra_denials(s.arbiter) 145 r.arb_preemptions = nx_ra_preemptions(s.arbiter) 146 147 // ===== Interrupt broker counters ===== 148 r.broker_grants = nx_ib_grants(s.broker) 149 r.broker_refusals = nx_ib_refusals(s.broker) 150 r.broker_deadline_misses = nx_ib_deadline_misses(s.broker) 151 152 r.captured_at_us = now_us 153 r.verdict = NX_BC_V_OK 154 return NX_BC_V_OK 155} 156 157// ===== Paired-comparison helper =================================== 158// 159// Given two reports captured under different conditions (e.g. v1 actor 160// vs v2 actor on same fixture), compute the substrate-internal delta 161// for honest comparison. Writes deltas into the OUTPUT report; original 162// reports unchanged. 163// 164// Per [[feedback-no-strawman-perf-comparisons]]: a delta is meaningful 165// ONLY if both inputs were captured on the SAME workload. This 166// primitive enforces nothing about that contract -- caller is 167// responsible for paired discipline. 168 169func nx_bc_diff(a: *NxBenchReport, 170 b: *NxBenchReport, 171 out: *NxBenchReport) -> nx_int { 172 if (a as i64) == 0 { return NX_BC_V_NULL_REPORT } 173 if (b as i64) == 0 { return NX_BC_V_NULL_REPORT } 174 if (out as i64) == 0 { return NX_BC_V_NULL_REPORT } 175 out.n_actors = b.n_actors - a.n_actors 176 out.actors_completed = b.actors_completed - a.actors_completed 177 out.actors_failed = b.actors_failed - a.actors_failed 178 out.actors_running = b.actors_running - a.actors_running 179 out.actors_waiting = b.actors_waiting - a.actors_waiting 180 out.total_actor_steps = b.total_actor_steps - a.total_actor_steps 181 out.cumulative_runtime_us = b.cumulative_runtime_us - a.cumulative_runtime_us 182 out.n_subscriptions = b.n_subscriptions - a.n_subscriptions 183 out.mux_pending = b.mux_pending - a.mux_pending 184 out.arb_grants_full = b.arb_grants_full - a.arb_grants_full 185 out.arb_grants_partial = b.arb_grants_partial - a.arb_grants_partial 186 out.arb_denials = b.arb_denials - a.arb_denials 187 out.arb_preemptions = b.arb_preemptions - a.arb_preemptions 188 out.broker_grants = b.broker_grants - a.broker_grants 189 out.broker_refusals = b.broker_refusals - a.broker_refusals 190 out.broker_deadline_misses = b.broker_deadline_misses - a.broker_deadline_misses 191 out.epoch_count = b.epoch_count - a.epoch_count 192 out.captured_at_us = b.captured_at_us - a.captured_at_us 193 out.verdict = NX_BC_V_OK 194 return NX_BC_V_OK 195} 196 197// ===== Accessors ================================================== 198 199func nx_bc_report_verdict(r: *NxBenchReport) -> nx_int { 200 if (r as i64) == 0 { return NX_BC_V_NULL_REPORT } 201 return r.verdict 202} 203 204func nx_bc_report_actors_completed(r: *NxBenchReport) -> nx_int { 205 if (r as i64) == 0 { return 0 } 206 return r.actors_completed 207} 208 209func nx_bc_report_total_steps(r: *NxBenchReport) -> nx_int { 210 if (r as i64) == 0 { return 0 } 211 return r.total_actor_steps 212}