code wiki / (root) / nx_perf_bench.nx

nx_perf_bench.nx source

↩ module page · 204 lines · 6105 B

1// nx_perf_bench.nx -- substrate-native performance benchmark primitive. 2// 3// Closes long-pending task #15. Substrate primitive that: 4// 1. Times an operation N times via nx_clock_monotonic_ns 5// 2. Computes min / median / mean / p99 over the runs 6// 3. Compares against a declared baseline (incumbent_label + ns) 7// 4. Returns a sealed verdict: BEATS / TIES / LOSES / NO_BASELINE 8// 9// Used by the TheoremCard's `perf_vs_*_status` fields per 10// S_CLASS_MATH_DOCTRINE axis 3. 11// 12// genealogy_id: knuth_taocp_v1 + benchmarking_discipline 13// lineage_id: performance_measurement + sealed_verdict 14// axioms: NX_AX_ORD_LEAST_UPPER_BOUND (timing values total-ordered) 15 16// nx_safety_envelope: 17// intended_use: AUTO_APPLIED -- primitive-specific tuning queued 18// sil_target: SIL1 19// evidence: [bulk_applied_2026-05-16, see-file-comment-for-detail] 20// verdict: NOT_YET_EVALUATED 21 22import "syscalls.nx" 23import "nx_axioms.nx" 24import "nx_clock.nx" 25 26// ===== sealed verdicts (mirror nx_theorem_card's perf statuses) ======== 27 28const NX_PERF_VERDICT_NO_BASELINE: i64 = 0 29const NX_PERF_VERDICT_WINS: i64 = 1 // ours <= baseline 30const NX_PERF_VERDICT_LOSES: i64 = 2 // ours > baseline by >= 10% 31const NX_PERF_VERDICT_TIES: i64 = 3 // within 10% 32 33// ===== sample buffer + descriptors ==================================== 34 35struct PerfRun { 36 label: *u8, // theorem / op label 37 n_iterations: i64, 38 samples_ns: *i64, // length n_iterations 39 min_ns: i64, 40 max_ns: i64, 41 median_ns: i64, 42 mean_ns: i64, 43 p99_ns: i64, 44 baseline_label: *u8, // incumbent name 45 baseline_ns: i64, // 0 if no baseline 46 verdict: i64, 47} 48 49const NX_PERF_RUN_BYTES: i64 = 88 50 51func nx_perf_run_alloc(label: *u8, n_iter: i64) -> *PerfRun { 52 let raw: *u8 = sys_mmap(NX_PERF_RUN_BYTES) 53 let r: *PerfRun = raw as *PerfRun 54 r.label = label 55 r.n_iterations = n_iter 56 r.samples_ns = (sys_mmap(n_iter * 8)) as *i64 57 r.min_ns = 0 58 r.max_ns = 0 59 r.median_ns = 0 60 r.mean_ns = 0 61 r.p99_ns = 0 62 r.baseline_label = "" as *u8 63 r.baseline_ns = 0 64 r.verdict = NX_PERF_VERDICT_NO_BASELINE 65 return r 66} 67 68// Record one sample. 69func nx_perf_record_sample(r: *PerfRun, idx: i64, ns: i64) -> i64 { 70 if idx < 0 { return -1 } 71 if idx >= r.n_iterations { return -1 } 72 r.samples_ns[idx] = ns 73 return 0 74} 75 76// Insertion sort over the samples (small n_iter -- adequate; queued 77// nx_qsort upgrade for larger N). 78func nx_perf_sort_samples(r: *PerfRun) -> i64 { 79 var i: i64 = 1 80 while i < r.n_iterations { 81 let key: i64 = r.samples_ns[i] 82 var j: i64 = i - 1 83 while j >= 0 { 84 if r.samples_ns[j] > key { 85 r.samples_ns[j + 1] = r.samples_ns[j] 86 j = j - 1 87 } 88 if r.samples_ns[j] <= key { j = -1 } 89 } 90 r.samples_ns[j + 1] = key 91 i = i + 1 92 } 93 return 0 94} 95 96// Compute summary stats over collected samples. 97func nx_perf_summarize(r: *PerfRun) -> i64 { 98 if r.n_iterations <= 0 { return 0 } 99 nx_perf_sort_samples(r) 100 r.min_ns = r.samples_ns[0] 101 r.max_ns = r.samples_ns[r.n_iterations - 1] 102 r.median_ns = r.samples_ns[r.n_iterations / 2] 103 let p99_idx: i64 = (r.n_iterations * 99) / 100 104 var pi: i64 = p99_idx 105 if pi >= r.n_iterations { pi = r.n_iterations - 1 } 106 r.p99_ns = r.samples_ns[pi] 107 var sum: i64 = 0 108 var i: i64 = 0 109 while i < r.n_iterations { 110 sum = sum + r.samples_ns[i] 111 i = i + 1 112 } 113 r.mean_ns = sum / r.n_iterations 114 return 0 115} 116 117// Declare baseline + compute verdict. 118func nx_perf_set_baseline(r: *PerfRun, baseline_label: *u8, baseline_ns: i64) -> i64 { 119 r.baseline_label = baseline_label 120 r.baseline_ns = baseline_ns 121 if baseline_ns <= 0 { 122 r.verdict = NX_PERF_VERDICT_NO_BASELINE 123 return 0 124 } 125 let ours: i64 = r.median_ns 126 // 10% tolerance. 127 let tol: i64 = baseline_ns / 10 128 if ours <= baseline_ns { r.verdict = NX_PERF_VERDICT_WINS } 129 if ours > baseline_ns { 130 if ours - baseline_ns <= tol { r.verdict = NX_PERF_VERDICT_TIES } 131 if ours - baseline_ns > tol { r.verdict = NX_PERF_VERDICT_LOSES } 132 } 133 return 0 134} 135 136// ===== JSON-line emit ================================================= 137 138func pb_putc(fd: i64, c: i64) -> i64 { 139 let buf: *u8 = sys_mmap(1) 140 buf[0] = c & 0xFF 141 sys_write(fd, buf, 1) 142 return 0 143} 144 145func pb_str(fd: i64, s: *u8, n: i64) -> i64 { 146 sys_write(fd, s, n) 147 return 0 148} 149 150func pb_strz(fd: i64, s: *u8) -> i64 { 151 var i: i64 = 0 152 while s[i] != 0 { i = i + 1 } 153 sys_write(fd, s, i) 154 return i 155} 156 157func pb_i64(fd: i64, n: i64) -> i64 { 158 if n < 0 { 159 pb_putc(fd, 45) 160 return pb_i64(fd, -n) 161 } 162 if n == 0 { 163 pb_putc(fd, 48) 164 return 0 165 } 166 let digits: *u8 = sys_mmap(32) 167 var d: i64 = 0 168 var v: i64 = n 169 while v > 0 { 170 digits[d] = (v % 10) + 48 171 v = v / 10 172 d = d + 1 173 } 174 while d > 0 { 175 d = d - 1 176 pb_putc(fd, digits[d]) 177 } 178 return 0 179} 180 181func nx_perf_emit_card(fd: i64, r: *PerfRun) -> i64 { 182 pb_str(fd, "{\"phase\":\"PERF_BENCH\",\"label\":\"", 31) 183 pb_strz(fd, r.label) 184 pb_str(fd, "\",\"n_iter\":", 11) 185 pb_i64(fd, r.n_iterations) 186 pb_str(fd, ",\"min_ns\":", 10) 187 pb_i64(fd, r.min_ns) 188 pb_str(fd, ",\"median_ns\":", 13) 189 pb_i64(fd, r.median_ns) 190 pb_str(fd, ",\"mean_ns\":", 11) 191 pb_i64(fd, r.mean_ns) 192 pb_str(fd, ",\"max_ns\":", 10) 193 pb_i64(fd, r.max_ns) 194 pb_str(fd, ",\"p99_ns\":", 10) 195 pb_i64(fd, r.p99_ns) 196 pb_str(fd, ",\"baseline\":\"", 13) 197 pb_strz(fd, r.baseline_label) 198 pb_str(fd, "\",\"baseline_ns\":", 16) 199 pb_i64(fd, r.baseline_ns) 200 pb_str(fd, ",\"verdict\":", 11) 201 pb_i64(fd, r.verdict) 202 pb_str(fd, "}\n", 2) 203 return 0 204}