code wiki / _hdl_build / nx_osbench_micro.nx
nx_osbench_micro.nx source
↩ module page · 495 lines · 24724 B
1// nx_osbench_micro.nx -- OSBENCH: NishiOS native-contract vs Linux host primitives, measured in the
2// SAME process on the SAME silicon through the nx_pabi seam (backends PABI_NISHIOS / PABI_LINUX).
3// Rung F853 (lane osbench). Operator altitude ruling 2026-07-20: native-contract-on-same-metal --
4// NOT emulated-guest-vs-native-host, so these rows are honestly MEASURED, not DIFFERENT-ALTITUDE.
5//
6// ROLE: this organ MEASURES and asserts NOTHING. It emits tab rows to knowledge/status/osbench_measured.dat;
7// nx_osbench_judge (F855) computes every verdict from the numbers. No verdict is decided here.
8//
9// LIAR-KILL / anti-stale-binary: every run records the measured artifact's own byte-length and a
10// wrapping polynomial digest (artifact identity). This is a CHANGE DETECTOR, not a cryptographic
11// hash -- named honestly as fnv-class 'adig', not sha256. It exists because debts seq207/124/236/242
12// are one family: you cannot otherwise prove the binary you measured is the binary you built, and a
13// speed verdict on byte-identical binaries measures nothing (the asm-diff-guard lesson).
14//
15// FAIRNESS: both backends CREATE-NEW every iteration (names carry the round index), so neither side
16// gets a cheaper overwrite path. The NishiOS block-VFS is additive-only (vb_create REFUSES overwrite)
17// and gets a FRESH 256KB image per round; VB_BS*VB_NB = 1024 blocks bounds N.
18//
19// DECLARED ENVELOPE (printed in output -- silent capping is forbidden):
20// N=200 ops/round, ROUNDS=3, payload 64B, single-threaded, single-node, warm cache.
21// COVERS: create+write, read, resolve. DOES NOT COVER: context-switch, IPC, thread-spawn,
22// syscall-trap cost -- those need NishiOS running AS an OS and are gated on the bare-metal node
23// (rung F856). They are emitted as UNMEASURED rows, never as zeros.
24// license_tier: ORIGINAL No hw writes (Rule 26). expect_exit: 0
25import "nx_pabi.nx"
26import "nx_itoa_lib.nx" // shared MSB-first emitter (zero-alloc)
27const OB_MAGIC_4096: i64 = 4096
28const OB_MAGIC_100000: i64 = 100000
29const OB_MAGIC_1000000: i64 = 1000000
30
31const OB_N: i64 = 200
32const OB_ROUNDS: i64 = 15
33const OB_WARM: i64 = 20
34const OB_PAY: i64 = 64
35const OB_MODE: i64 = 420
36const OB_NSEC: i64 = 1000000000
37const OB_HMUL: i64 = 1000003
38const OB_ELFCAP: i64 = 1048576
39const OB_UNSTABLE_PERMIL: i64 = 100
40
41func ob_len(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
42func ob_p(s: *u8) -> i64 { let n: i64 = ob_len(s); sys_write(1, s, n); return 0 }
43func ob_w(fd: i64, s: *u8) -> i64 { let n: i64 = ob_len(s); sys_write(fd, s, n); return 0 }
44
45// MIGRATED to the shared emitter (debt 1785563586). The old body mmapped a scratch buffer
46// per call and never freed it. At PAGE granularity that is 4096B leaked PER CALL -- the
47// defect that took 28.5GB of a 36GB host in nx_ts_lumadiff (2MB input, ~3.66M calls).
48// nxi_* is MSB-first, allocates NOTHING, and emits identical bytes including the sign.
49func ob_wn(fd: i64, v: i64) -> i64 { nxi_fd(fd, v); return 0 }
50func ob_pn(v: i64) -> i64 { return ob_wn(1, v) }
51
52// monotonic nanoseconds. ts must be a 16-byte {sec,nsec} scratch.
53func ob_ns(ts: *i64) -> i64 {
54 sys_clock_gettime_mono(ts)
55 let s: i64 = ts[0]
56 let n: i64 = ts[1]
57 return s * OB_NSEC + n
58}
59
60// append decimal k to buf at offset o; returns new offset.
61func ob_catn(buf: *u8, o: i64, v: i64) -> i64 {
62 let t: *u8 = sys_mmap(32)
63 var m: i64 = v
64 var k: i64 = 0
65 if m == 0 { t[0] = 48 as u8; k = 1 }
66 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
67 var i: i64 = 0
68 var oo: i64 = o
69 while i < k { buf[oo] = t[k-1-i]; oo = oo + 1; i = i + 1 }
70 return oo
71}
72func ob_cat(buf: *u8, o: i64, s: *u8) -> i64 {
73 var i: i64 = 0
74 var oo: i64 = o
75 while s[i] != (0 as u8) { buf[oo] = s[i]; oo = oo + 1; i = i + 1 }
76 return oo
77}
78
79// NISHIOS name: slash-free (pabi_write takes basename, pabi_read does vb_resolve -- keep symmetric).
80func ob_nname(buf: *u8, k: i64) -> i64 {
81 var o: i64 = ob_cat(buf, 0, "f" as *u8)
82 o = ob_catn(buf, o, k)
83 buf[o] = 0 as u8
84 return o
85}
86// LINUX name, DISK tier: a throwaway under /tmp (real on-disk filesystem, pays for durability).
87func ob_lname(buf: *u8, k: i64) -> i64 {
88 var o: i64 = ob_cat(buf, 0, "/tmp/obm_f" as *u8)
89 o = ob_catn(buf, o, k)
90 buf[o] = 0 as u8
91 return o
92}
93// LINUX name, MEM tier: /dev/shm is a real tmpfs = an IN-MEMORY filesystem. This is the tier that is
94// altitude-symmetric with the NishiOS mmap'd block-VFS, so a mem-vs-mem comparison is legitimate
95// (eats debt seq285: the defect was comparing an in-memory store against an on-disk one).
96func ob_lnamem(buf: *u8, k: i64) -> i64 {
97 var o: i64 = ob_cat(buf, 0, "/dev/shm/obm_f" as *u8)
98 o = ob_catn(buf, o, k)
99 buf[o] = 0 as u8
100 return o
101}
102
103// ---- artifact identity (anti-stale-binary tooth) --------------------------------------------
104// bounded read (NEVER sys_read_file here: it mmaps 4GB per call and never frees).
105func ob_artifact(path: *u8, out: *i64) -> i64 {
106 out[0] = 0
107 out[1] = 0
108 let fd: i64 = sys_openat_rd(path)
109 if fd < 0 { return 0 - 1 }
110 let buf: *u8 = sys_mmap(OB_ELFCAP)
111 var tot: i64 = 0
112 var go: i64 = 1
113 while go == 1 {
114 let rem: i64 = OB_ELFCAP - tot
115 if rem <= 0 { go = 0 }
116 if go == 1 {
117 let q: *u8 = buf + tot
118 let r: i64 = sys_read(fd, q, rem)
119 if r <= 0 { go = 0 }
120 if r > 0 { tot = tot + r }
121 }
122 }
123 sys_close(fd)
124 var h: i64 = 0
125 var i: i64 = 0
126 while i < tot { h = h * OB_HMUL + (buf[i] as i64); i = i + 1 }
127 if h < 0 { h = 0 - h }
128 out[0] = tot
129 out[1] = h
130 return tot
131}
132
133// ---- per-axis timing ------------------------------------------------------------------------
134// axis: 0=write 1=read 2=resolve. Returns ns-per-op for ONE round. Fresh VFS image per round.
135func ob_round(be: i64, axis: i64, round: i64) -> i64 {
136 let ts: *i64 = sys_mmap(32) as *i64
137 let nm: *u8 = sys_mmap(96) as *u8
138 let pay: *u8 = sys_mmap(OB_PAY) as *u8
139 let rd: *u8 = sys_mmap(OB_MAGIC_4096) as *u8
140 var z: i64 = 0
141 while z < OB_PAY { pay[z] = 120 as u8; z = z + 1 }
142 // subject id -> real pabi backend. Subject 2 (tmpfs) is still the LINUX backend; only the
143 // target path differs, which is exactly what makes it the mem-tier peer of the NishiOS VFS.
144 var rbe: i64 = PABI_LINUX
145 if be == 1 { rbe = PABI_NISHIOS }
146 var vimg: *u8 = pay
147 if be == 1 { vimg = pabi_nishios_new() }
148 let base: i64 = round * OB_MAGIC_100000
149
150 // For read/resolve the subject must already exist: pre-create OUTSIDE the timed window.
151 if axis > 0 {
152 var pc: i64 = 0
153 while pc < OB_N {
154 let k: i64 = base + pc
155 if be == 0 { ob_lname(nm, k) }
156 if be == 1 { ob_nname(nm, k) }
157 if be == 2 { ob_lnamem(nm, k) }
158 pabi_write(rbe, vimg, nm, pay, OB_PAY)
159 pc = pc + 1
160 }
161 }
162 // warmup (untimed)
163 var wi: i64 = 0
164 while wi < OB_WARM {
165 let wk: i64 = base + (wi % OB_N)
166 if be == 0 { ob_lname(nm, wk) }
167 if be == 1 { ob_nname(nm, wk) }
168 if be == 2 { ob_lnamem(nm, wk) }
169 if axis == 1 { pabi_read(rbe, vimg, nm, rd, OB_MAGIC_4096) }
170 if axis == 2 { pabi_exists(rbe, vimg, nm) }
171 wi = wi + 1
172 }
173
174 let t0: i64 = ob_ns(ts)
175 var i: i64 = 0
176 while i < OB_N {
177 let k: i64 = base + i
178 if be == 0 { ob_lname(nm, k) }
179 if be == 1 { ob_nname(nm, k) }
180 if be == 2 { ob_lnamem(nm, k) }
181 if axis == 0 { pabi_write(rbe, vimg, nm, pay, OB_PAY) }
182 if axis == 1 { pabi_read(rbe, vimg, nm, rd, OB_MAGIC_4096) }
183 if axis == 2 { pabi_exists(rbe, vimg, nm) }
184 i = i + 1
185 }
186 let t1: i64 = ob_ns(ts)
187 // ★ CLEANUP, OUTSIDE the timed window (eats debt seq287). Without this the Linux target dir grows
188 // by N files EVERY round of EVERY run, so create/lookup cost climbs monotonically, while the
189 // NishiOS side gets a FRESH image per round. That is a SYSTEMATIC BIAS THAT FAVOURS US and it
190 // GROWS: measured drift linux-tmpfs create_write 5509 -> 168968 ns/op over three runs, with 1400
191 // files left behind in /dev/shm and 1723 in /tmp. Both sides must start each round EMPTY.
192 if be != 1 {
193 var u: i64 = 0
194 while u < OB_N {
195 let uk: i64 = base + u
196 if be == 0 { ob_lname(nm, uk) }
197 if be == 2 { ob_lnamem(nm, uk) }
198 sys_unlinkat(nm)
199 u = u + 1
200 }
201 }
202 let d: i64 = t1 - t0
203 if d < 0 { return 0 }
204 return d / OB_N
205}
206
207// median of n values (insertion sort on a copy; n <= OB_ROUNDS so O(n^2) is free here).
208// MEDIAN not MIN: min is the optimistic tail and rewards a lucky round; median is the robust
209// central estimator and is what survives a noisy host.
210// DRY: one sort implementation (ob_rank) so there is exactly ONE place to be correct.
211// Forward function calls are fine in NishiLang; only forward DATA refs break.
212func ob_med(arr: *i64, n: i64) -> i64 { return ob_rank(arr, n, n/2) }
213// sort a copy and return the value at rank r (0-based). Shared by the dispersion measures.
214func ob_rank(arr: *i64, n: i64, r: i64) -> i64 {
215 let c: *i64 = sys_mmap(n * 8 + 8) as *i64
216 var i: i64 = 0
217 while i < n { c[i] = arr[i]; i = i + 1 }
218 var a: i64 = 1
219 while a < n {
220 let key: i64 = c[a]
221 var b: i64 = a - 1
222 // ⚠NishiLang has NO break. The obvious `b = 0 - 1` pseudo-break DOES exit this loop but it
223 // DESTROYS b, which the insertion below depends on -- the array ends up shuffled, not sorted,
224 // so medians and quartiles are silently wrong (it surfaced as a NEGATIVE IQR of -67 that then
225 // passed the stability gate as a false GREEN). Use a FLAG loop, which preserves b.
226 var placed: i64 = 0
227 while placed == 0 {
228 if b < 0 { placed = 1 } else {
229 let cb: i64 = c[b]
230 if cb > key { c[b+1] = cb; b = b - 1 } else { placed = 1 }
231 }
232 }
233 c[b+1] = key
234 a = a + 1
235 }
236 return c[r]
237}
238// ★ ROBUST dispersion: interquartile range over median, in permille.
239// The earlier metric was (max-min)/median = the RANGE, which is the MOST outlier-sensitive
240// dispersion measure that exists. On a host with occasional scheduler stalls the range is
241// dominated entirely by the single worst round, so it can never certify stability even when the
242// central mass is rock solid -- that was a flaw in the MEASURING STICK, not in the measurement.
243// IQR is the standard robust choice. This is NOT lowering the bar: ob_rngp below still computes
244// the range and it is still PRINTED on every row, so the worst case is disclosed, never hidden.
245func ob_cvp(arr: *i64, n: i64) -> i64 {
246 let m: i64 = ob_med(arr, n)
247 if m <= 0 { return 0 }
248 let q1: i64 = ob_rank(arr, n, n/4)
249 let q3: i64 = ob_rank(arr, n, (n*3)/4)
250 return ((q3 - q1) * 1000) / m
251}
252// full-range dispersion in permille -- retained and always reported alongside IQR so the tail is visible.
253func ob_rngp(arr: *i64, n: i64) -> i64 {
254 var lo: i64 = arr[0]
255 var hi: i64 = arr[0]
256 var i: i64 = 0
257 while i < n {
258 let v: i64 = arr[i]
259 if v < lo { lo = v }
260 if v > hi { hi = v }
261 i = i + 1
262 }
263 let m: i64 = ob_med(arr, n)
264 if m <= 0 { return 0 }
265 return ((hi - lo) * 1000) / m
266}
267
268// ★ PAIRED INTERLEAVED MEASUREMENT (methodology fix, seq286). The earlier shape ran all ROUNDS of
269// subject A, THEN all ROUNDS of subject B -- so A and B were sampled at different times under
270// different host load, which is a SYSTEMATIC BIAS, not just noise. Here every round measures ALL
271// THREE subjects back-to-back, so host noise is COMMON-MODE across them. That lets us report a
272// PER-ROUND RATIO whose median is stable even when the absolute numbers are not: if the host
273// stalls 3x, both sides stall 3x and the ratio is unmoved. This is the only honest way to measure
274// on a host we have already proven noisy (WSL2), and it is the standard paired-comparison design.
275// out[] = [med0,cv0, med1,cv1, med2,cv2, ratio_permil_med, ratio_permil_cv]
276func ob_axis_all(axis: i64, out: *i64) -> i64 {
277 let s0: *i64 = sys_mmap(OB_ROUNDS * 8 + 8) as *i64
278 let s1: *i64 = sys_mmap(OB_ROUNDS * 8 + 8) as *i64
279 let s2: *i64 = sys_mmap(OB_ROUNDS * 8 + 8) as *i64
280 let rr: *i64 = sys_mmap(OB_ROUNDS * 8 + 8) as *i64
281 var r: i64 = 0
282 while r < OB_ROUNDS {
283 // interleaved within the round -- do NOT hoist these into per-subject loops.
284 let v0: i64 = ob_round(0, axis, r)
285 let v1: i64 = ob_round(1, axis, r)
286 let v2: i64 = ob_round(2, axis, r)
287 s0[r] = v0
288 s1[r] = v1
289 s2[r] = v2
290 // mem-tier paired ratio: nishios vs linux-tmpfs, computed WITHIN the round.
291 var pr: i64 = 0
292 if v2 > 0 { pr = (v1 * 1000) / v2 }
293 rr[r] = pr
294 r = r + 1
295 }
296 out[0] = ob_med(s0, OB_ROUNDS)
297 out[1] = ob_cvp(s0, OB_ROUNDS)
298 out[2] = ob_med(s1, OB_ROUNDS)
299 out[3] = ob_cvp(s1, OB_ROUNDS)
300 out[4] = ob_med(s2, OB_ROUNDS)
301 out[5] = ob_cvp(s2, OB_ROUNDS)
302 out[6] = ob_med(rr, OB_ROUNDS)
303 out[7] = ob_cvp(rr, OB_ROUNDS)
304 out[8] = ob_rngp(rr, OB_ROUNDS)
305 return 0
306}
307
308func ob_axname(axis: i64) -> *u8 {
309 if axis == 0 { return "create_write_64B" as *u8 }
310 if axis == 1 { return "read_64B" as *u8 }
311 return "resolve_path" as *u8
312}
313// SUBJECT ids: 0=linux on-disk FS, 1=NishiOS block-VFS (mmap), 2=linux tmpfs (/dev/shm).
314func ob_bename(be: i64) -> *u8 {
315 if be == 0 { return "linux-host-fs-disk" as *u8 }
316 if be == 1 { return "nishios-block-vfs" as *u8 }
317 return "linux-host-fs-tmpfs" as *u8
318}
319// ALTITUDE TIER. The judge may ONLY compare rows sharing a tier. This is the structural fix for
320// seq285: the row is always a real measurement; it is the COMPARISON that carries the altitude.
321func ob_tier(be: i64) -> *u8 {
322 if be == 0 { return "disk" as *u8 }
323 return "mem" as *u8
324}
325
326// ★ INSTRUMENT SELF-CHECK. Everything in this lane had liar-killers on the VERDICT and NONE on the
327// MEASUREMENT: if ob_ns() were wrong, every number downstream is garbage and no tooth would notice.
328// sys_sleep_ms is an INDEPENDENT time source (the kernel timer), so it can validate our clock read,
329// the sec*1e9+nsec arithmetic, the subtraction and the division -- end to end.
330// The load-bearing invariant is PHYSICS, not a threshold: a measured sleep can NEVER be shorter than
331// the sleep that was requested. If it is, the arithmetic is broken and nothing else may be believed.
332func ob_selfcheck() -> i64 {
333 ob_p("=== nx_osbench_micro INSTRUMENT SELF-CHECK (validate the clock before trusting any number) ===\n" as *u8)
334 let ts: *i64 = sys_mmap(32) as *i64
335 var pass: i64 = 0
336 var total: i64 = 0
337
338 // T1 monotonicity: successive reads never go backwards.
339 let a0: i64 = ob_ns(ts)
340 let a1: i64 = ob_ns(ts)
341 total = total + 1
342 if a1 >= a0 { pass = pass + 1; ob_p(" PASS T1 clock is monotonic\n" as *u8) } else { ob_p(" FAIL T1 clock went BACKWARDS\n" as *u8) }
343
344 // T2 physics floor: a 20ms sleep cannot measure as less than 20ms.
345 let req: i64 = 20
346 let reqns: i64 = req * OB_MAGIC_1000000
347 let b0: i64 = ob_ns(ts)
348 sys_sleep_ms(req)
349 let b1: i64 = ob_ns(ts)
350 let meas: i64 = b1 - b0
351 total = total + 1
352 if meas >= reqns { pass = pass + 1; ob_p(" PASS T2 20ms sleep measured >= 20ms (" as *u8); ob_pn(meas); ob_p(" ns)\n" as *u8) } else { ob_p(" FAIL T2 sleep measured SHORTER than requested -- clock arithmetic is broken (" as *u8); ob_pn(meas); ob_p(" ns)\n" as *u8) }
353
354 // T3 unit sanity: catches an ms/ns/sec conversion error, which T2 alone would not.
355 total = total + 1
356 if meas < reqns * 5 { pass = pass + 1; ob_p(" PASS T3 sleep within 5x of request (no unit-scale error)\n" as *u8) } else { ob_p(" FAIL T3 sleep wildly over request -- unit-scale error suspected\n" as *u8) }
357
358 // T4 empty interval is non-negative and small: catches a sign/overflow defect in the subtraction.
359 let c0: i64 = ob_ns(ts)
360 let c1: i64 = ob_ns(ts)
361 let d: i64 = c1 - c0
362 total = total + 1
363 if d >= 0 { if d < OB_MAGIC_1000000 { pass = pass + 1; ob_p(" PASS T4 empty interval is non-negative and sub-ms\n" as *u8) } else { ob_p(" FAIL T4 empty interval implausibly large\n" as *u8) } } else { ob_p(" FAIL T4 empty interval NEGATIVE\n" as *u8) }
364
365 ob_p("---- instrument self-check "); ob_pn(pass); ob_p(" / "); ob_pn(total); ob_p(" ----\n" as *u8)
366 if pass == total { ob_p("NX-OSBENCH-MICRO-SELFCHECK verdict=GREEN (the instrument may be trusted)\n" as *u8); return 0 }
367 ob_p("NX-OSBENCH-MICRO-SELFCHECK verdict=RED (DO NOT trust any number this harness produced)\n" as *u8)
368 return 1
369}
370
371func main(argc: i64, argv: *i64) -> i64 {
372 if argc > 1 {
373 let a1s: *u8 = argv[1] as *u8
374 var isc: i64 = 1
375 var ci: i64 = 0
376 let lit: *u8 = "selfcheck" as *u8
377 while lit[ci] != (0 as u8) { if a1s[ci] != lit[ci] { isc = 0; ci = ci + 1 } else { ci = ci + 1 } }
378 if a1s[ci] != (0 as u8) { isc = 0 }
379 if isc == 1 { let rc: i64 = ob_selfcheck(); sys_exit(rc); return rc }
380 }
381 ob_p("nx_osbench_micro -- NishiOS native-contract vs Linux host, SAME process, SAME silicon (nx_pabi seam)\n" as *u8)
382 ob_p(" MEASURES ONLY. Every verdict is computed downstream by nx_osbench_judge (F855).\n" as *u8)
383
384 // ANTI-STALE-BINARY TOOTH. It must actually FIND the artifact or it is inert -- which it was:
385 // it looked only for a CWD-relative name while the sovereign build stages /tmp/<name>.sov.elf,
386 // so it silently reported bytes=0 adig=0 every run. A guarantee that cannot fail is not a
387 // guarantee. Try the staged path first, then the promoted one, and SAY SO if neither resolves.
388 let aid: *i64 = sys_mmap(32) as *i64
389 var abytes: i64 = ob_artifact("/tmp/nx_osbench_micro.sov.elf" as *u8, aid)
390 if abytes <= 0 { abytes = ob_artifact("nx_osbench_micro.elf" as *u8, aid) }
391 if abytes <= 0 {
392 ob_p(" ARTIFACT IDENTITY UNRESOLVED -- the anti-stale-binary tooth is INERT this run.\n" as *u8)
393 ob_p(" Treat any delta against a previous run as UNATTRIBUTABLE: we cannot prove the binary\n" as *u8)
394 ob_p(" measured is the binary built (debt family seq207/124/236/242).\n" as *u8)
395 } else {
396 ob_p(" artifact bytes="); ob_pn(aid[0]); ob_p(" adig="); ob_pn(aid[1]); ob_p(" (change-detector, not sha256)\n" as *u8)
397 }
398
399 let fd: i64 = sys_openat_append("knowledge/status/osbench_measured.dat" as *u8, OB_MODE)
400 if fd < 0 { ob_p("OSBENCH-MICRO FAIL cannot open dat\n" as *u8); sys_exit(1); return 1 }
401
402 ob_w(fd, "# osbench_micro run artifact_bytes=" as *u8); ob_wn(fd, aid[0])
403 ob_w(fd, " adig=" as *u8); ob_wn(fd, aid[1])
404 ob_w(fd, " N=" as *u8); ob_wn(fd, OB_N)
405 ob_w(fd, " rounds=" as *u8); ob_wn(fd, OB_ROUNDS)
406 ob_w(fd, " payload=" as *u8); ob_wn(fd, OB_PAY)
407 ob_w(fd, "\n" as *u8)
408
409 // TIER RULE consumed by nx_osbench_judge: a comparison is legitimate ONLY between rows sharing
410 // a tier. mem-vs-mem (tmpfs vs block-VFS) is apples-to-apples; anything crossing tiers is a
411 // durability-asymmetric comparison and must be refused. This is the seq285 structural fix.
412 ob_w(fd, "TIERRULE\tcompare-only-within-tier\tmem=in-memory-store\tdisk=durable-on-disk\tseq285\n" as *u8)
413
414 ob_p("\n AXIS SUBJECT TIER ns/op(med) cv-permil stability\n" as *u8)
415 let res: *i64 = sys_mmap(128) as *i64
416 var axis: i64 = 0
417 while axis < 3 {
418 ob_axis_all(axis, res)
419 var be: i64 = 0
420 while be < 3 {
421 let best: i64 = res[be*2]
422 let cv: i64 = res[be*2+1]
423 // stdout, human
424 ob_p(" "); ob_p(ob_axname(axis))
425 ob_p(" "); ob_p(ob_bename(be))
426 ob_p(" "); ob_p(ob_tier(be))
427 ob_p(" "); ob_pn(best)
428 ob_p(" "); ob_pn(cv)
429 if cv > OB_UNSTABLE_PERMIL { ob_p(" UNSTABLE\n" as *u8) } else { ob_p(" stable\n" as *u8) }
430 // machine row: class axis backend ns_per_op cv_permil reason
431 // CLASS = DIFFERENT-ALTITUDE, not MEASURED (debt seq285, found by running this organ and
432 // reading the output): the NishiOS side is an in-memory mmap'd block-VFS image (zero
433 // syscalls, no page cache, no fsync, no disk) while the Linux side does real openat+
434 // write+close against a real filesystem paying for durability. The apparent 18x
435 // create_write win is a measurement artifact, and the judge MUST refuse to score it.
436 // This flips to MEASURED only when durability is symmetric on both sides.
437 ob_w(fd, "MEASURED\t" as *u8); ob_w(fd, ob_axname(axis))
438 ob_w(fd, "\t" as *u8); ob_w(fd, ob_bename(be))
439 ob_w(fd, "\t" as *u8); ob_w(fd, ob_tier(be))
440 ob_w(fd, "\t" as *u8); ob_wn(fd, best)
441 ob_w(fd, "\t" as *u8); ob_wn(fd, cv)
442 ob_w(fd, "\n" as *u8)
443 be = be + 1
444 }
445 // ★ the row that actually survives a noisy host: the WITHIN-ROUND paired ratio, mem tier only.
446 // permille of nishios-block-vfs over linux-host-fs-tmpfs. <1000 = NishiOS faster, >1000 = slower.
447 let rmed: i64 = res[6]
448 let rcv: i64 = res[7]
449 let rrng: i64 = res[8]
450 ob_p(" -> PAIRED mem-tier ratio nishios/linux-tmpfs = " as *u8); ob_pn(rmed)
451 ob_p(" permil (1000=parity) IQR=" as *u8); ob_pn(rcv)
452 ob_p(" range=" as *u8); ob_pn(rrng)
453 if rcv > OB_UNSTABLE_PERMIL { ob_p(" UNSTABLE\n" as *u8) } else { ob_p(" STABLE <- usable\n" as *u8) }
454 ob_w(fd, "PAIRED\t" as *u8); ob_w(fd, ob_axname(axis))
455 ob_w(fd, "\tnishios-over-linux-tmpfs\tmem\t" as *u8); ob_wn(fd, rmed)
456 ob_w(fd, "\t" as *u8); ob_wn(fd, rcv)
457 ob_w(fd, "\tpermille-1000-is-parity-within-round-paired\n" as *u8)
458 axis = axis + 1
459 }
460
461 // HONEST UNMEASURED rows -- the mission-profile axes this increment does NOT cover.
462 // Emitted explicitly so the judge reports them as gaps, never as zeros or omissions.
463 ob_w(fd, "UNMEASURED\tcontext_switch\tboth\t-1\t-1\tneeds NishiOS running AS an OS - rung F856 bare-metal node\n" as *u8)
464 ob_w(fd, "UNMEASURED\tipc_roundtrip\tboth\t-1\t-1\tneeds NishiOS running AS an OS - rung F856 bare-metal node\n" as *u8)
465 ob_w(fd, "UNMEASURED\tthread_spawn\tboth\t-1\t-1\tneeds NishiOS running AS an OS - rung F856 bare-metal node\n" as *u8)
466 ob_w(fd, "UNMEASURED\tsyscall_trap\tboth\t-1\t-1\tneeds NishiOS running AS an OS - rung F856 bare-metal node\n" as *u8)
467 ob_w(fd, "UNMEASURED\tenergy_joules\tboth\t-1\t-1\tno intel-rapl under WSL2 - needs native-Linux target - rung F856\n" as *u8)
468 sys_close(fd)
469
470 ob_p("\n DECLARED ENVELOPE: N="); ob_pn(OB_N)
471 ob_p(" ops/round, rounds="); ob_pn(OB_ROUNDS)
472 ob_p(", payload="); ob_pn(OB_PAY)
473 ob_p("B, single-threaded, single-node, warm cache.\n" as *u8)
474 ob_p(" COVERS create_write/read/resolve. NOT COVERED (emitted UNMEASURED, gated on F856 bare-metal):\n" as *u8)
475 ob_p(" context_switch, ipc_roundtrip, thread_spawn, syscall_trap, energy_joules.\n" as *u8)
476 ob_p(" NishiOS side = block-VFS additive-only store; Linux side = host FS. Both CREATE-NEW every op.\n" as *u8)
477 ob_p("\n *** TIER RULE (eats debt seq285) *** A comparison is legitimate ONLY within a tier.\n" as *u8)
478 ob_p(" mem = in-memory store -> linux-host-fs-tmpfs (/dev/shm) vs nishios-block-vfs (mmap).\n" as *u8)
479 ob_p(" THIS pair is apples-to-apples: neither side pays for durability.\n" as *u8)
480 ob_p(" disk = durable on-disk -> linux-host-fs-disk (/tmp, real openat+write+close).\n" as *u8)
481 ob_p(" Comparing it to the mem tier is durability-asymmetric and the judge REFUSES it.\n" as *u8)
482 ob_p(" The earlier 18x create_write 'win' was exactly that cross-tier artifact, not an OS result.\n" as *u8)
483 ob_p(" A mem-tier row is a valid MEASURED row ONLY when its cv is under the threshold; on a noisy\n" as *u8)
484 ob_p(" host every row is UNSTABLE and NOTHING here may be published as a claim.\n" as *u8)
485
486 let lg: i64 = sys_openat_append("knowledge/status/osbench_micro.log" as *u8, OB_MODE)
487 if lg >= 0 {
488 ob_w(lg, "NX-OSBENCH-MICRO verdict=GREEN measured 3 axes x 2 backends; 5 axes UNMEASURED (F856); adig=" as *u8)
489 ob_wn(lg, aid[1])
490 ob_w(lg, "\n" as *u8)
491 sys_close(lg)
492 }
493 ob_p("\nNX-OSBENCH-MICRO verdict=GREEN (measurement emitted; grading belongs to nx_osbench_judge)\n" as *u8)
494 return 0
495}