nx_bench_dist_persist_test.nx source
↩ module page · 197 lines · 7931 B
1// nx_bench_dist_persist_test.nx -- run distribution + persist to disk.
2//
3// Composes the distribution measurement primitive with the disk-write
4// persistence primitive. Produces a REAL TSV file at
5// /tmp/nx_bench_dist.tsv
6// containing one header + N data rows + summary stats.
7//
8// This is the substrate-honest end-to-end workflow the s-class
9// comparison campaign needs: measurement happens, bytes hit disk,
10// external tools (sort / awk / future llama.cpp comparison rigs) read
11// the file and diff against their own.
12//
13// After the smoke runs, the operator can:
14// cat /tmp/nx_bench_dist.tsv
15// sort -k7n /tmp/nx_bench_dist.tsv
16// awk -F'\t' '{print $1, $8}' /tmp/nx_bench_dist.tsv
17//
18// And see real persisted substrate measurements.
19
20import "nx_syscalls.nx"
21import "nx_tier.nx"
22import "nx_clock.nx"
23import "nx_actor.nx"
24import "nx_message.nx"
25import "nx_session.nx"
26import "nx_bench_companion.nx"
27import "nx_bench_companion_timed.nx"
28import "nx_bench_companion_tsv.nx"
29import "nx_bench_stats.nx"
30import "nx_bench_tsv_persist.nx"
31import "nx_gguf_fixture_tiny.nx"
32import "nx_actor_role_llm_v2.nx"
33
34// ===== Emit a decimal i64 into a byte buffer; return new offset =====
35
36func _emit_dec_to_buf(buf: *u8, off: nx_int, cap: nx_int, n: i64) -> nx_int {
37 if off >= cap { return 0 - 1 }
38 if n == 0 { buf[off] = 0x30 as u8; return off + 1 }
39 var v: i64 = n
40 var neg: nx_int = 0
41 if v < 0 { neg = 1; v = 0 - v }
42 let scratch: *u8 = sys_mmap(32)
43 var k: nx_int = 0
44 while v > 0 {
45 scratch[k] = (0x30 + (v - (v / 10) * 10)) as u8
46 v = v / 10
47 k = k + 1
48 }
49 var out: nx_int = off
50 if neg == 1 {
51 if out >= cap { return 0 - 1 }
52 buf[out] = 0x2D as u8
53 out = out + 1
54 }
55 while k > 0 {
56 k = k - 1
57 if out >= cap { return 0 - 1 }
58 buf[out] = scratch[k]
59 out = out + 1
60 }
61 return out
62}
63
64// ===== One scenario run = one wall-clock sample =================
65
66func _drive_one_run(seed: i64, n_iters: nx_int, now: nx_size) -> i64 {
67 let fix: *NxGgufFixtureBundle = nx_gft_build_tiny_llama(seed)
68 if nx_gft_is_built(fix) != 1 { return 0 - 1 }
69 let s: *NxSession = nx_session_new_default(now)
70 if nx_session_is_ready(s) != 1 { return 0 - 1 }
71 let LLM: nx_int = 7001
72 nx_session_spawn_actor(s, LLM, 1, 80, 0, now)
73 let prompt: *u8 = sys_mmap(1); prompt[0] = 0x61
74 let ctx: *NxLlmV2ActorCtx = nx_lv_actor_new(
75 fix.spec, fix.gguf_buf, fix.hdr, fix.bpe, prompt, 1,
76 1024, 4, fix.prng_state, 10000, 724)
77 if (ctx as i64) == 0 { return 0 - 1 }
78 let start: i64 = nx_clock_monotonic_ns()
79 var iter: nx_int = 0
80 var tick: nx_size = now + 100
81 while iter < n_iters {
82 nx_lv_actor_step(ctx, s.scheduler, s.bus, LLM, tick); tick = tick + 50
83 nx_lv_actor_step(ctx, s.scheduler, s.bus, LLM, tick); tick = tick + 50
84 if ctx.runner_result < 0 { return 0 - 1 }
85 nx_lv_actor_step(ctx, s.scheduler, s.bus, LLM, tick); tick = tick + 50
86 if iter < (n_iters - 1) {
87 prompt[0] = nx_gft_vocab_byte(fix, ctx.runner_result) as u8
88 nx_lv_actor_reset_for_next_token(ctx, s.scheduler, LLM, prompt, 1)
89 }
90 iter = iter + 1
91 }
92 let end: i64 = nx_clock_monotonic_ns()
93 return (end - start) / 1000
94}
95
96func main() -> i64 {
97 let now: nx_size = 1000000
98 let N_RUNS: nx_int = 10
99 let N_ITERS: nx_int = 10
100
101 // ===== Path /tmp/nx_bench_dist.tsv (22 bytes) =====
102 let path: *u8 = sys_mmap(32)
103 path[0]=0x2F as u8; path[1]=0x74 as u8; path[2]=0x6D as u8; path[3]=0x70 as u8
104 path[4]=0x2F as u8; path[5]=0x6E as u8; path[6]=0x78 as u8; path[7]=0x5F as u8
105 path[8]=0x62 as u8; path[9]=0x65 as u8; path[10]=0x6E as u8; path[11]=0x63 as u8
106 path[12]=0x68 as u8; path[13]=0x5F as u8; path[14]=0x64 as u8; path[15]=0x69 as u8
107 path[16]=0x73 as u8; path[17]=0x74 as u8; path[18]=0x2E as u8; path[19]=0x74 as u8
108 path[20]=0x73 as u8; path[21]=0x76 as u8
109 let path_len: nx_int = 22
110
111 // ===== Truncate file via O_TRUNC opener =====
112 let pz: *u8 = sys_mmap(path_len + 1)
113 var pi: nx_int = 0
114 while pi < path_len { pz[pi] = path[pi]; pi = pi + 1 }
115 pz[path_len] = 0 as u8
116 let fd_trunc: i64 = sys_openat_wr(pz, 420)
117 if fd_trunc < 0 { return 1 }
118 sys_close(fd_trunc)
119
120 // ===== Write header row: "label\tsample_us\n" =====
121 let hdr: *u8 = sys_mmap(16)
122 hdr[0]=0x6C as u8; hdr[1]=0x61 as u8; hdr[2]=0x62 as u8; hdr[3]=0x65 as u8
123 hdr[4]=0x6C as u8; hdr[5]=0x09 as u8; hdr[6]=0x73 as u8; hdr[7]=0x61 as u8
124 hdr[8]=0x6D as u8; hdr[9]=0x70 as u8; hdr[10]=0x6C as u8; hdr[11]=0x65 as u8
125 hdr[12]=0x5F as u8; hdr[13]=0x75 as u8; hdr[14]=0x73 as u8; hdr[15]=0x0A as u8
126 if nx_bc_persist_row(path, path_len, hdr, 16) != 16 { return 2 }
127
128 // ===== Collect + persist per-run samples =====
129 let samples_buf: *u8 = sys_mmap(N_RUNS * 8)
130 let samples: *i64 = samples_buf as *i64
131
132 // Per-row buffer: "v2_dist_<i>\t<elapsed_us>\n"
133 let row_buf: *u8 = sys_mmap(64)
134
135 var r: nx_int = 0
136 while r < N_RUNS {
137 let elapsed: i64 = _drive_one_run(0xD000 + (r as i64), N_ITERS, now)
138 if elapsed < 0 { return 10 + r }
139 samples[r] = elapsed
140
141 // Build row: "v2_dist_" (8) + digit + "\t" + elapsed_us + "\n"
142 row_buf[0]=0x76 as u8; row_buf[1]=0x32 as u8; row_buf[2]=0x5F as u8
143 row_buf[3]=0x64 as u8; row_buf[4]=0x69 as u8; row_buf[5]=0x73 as u8
144 row_buf[6]=0x74 as u8; row_buf[7]=0x5F as u8
145 var off: nx_int = _emit_dec_to_buf(row_buf, 8, 64, r as i64)
146 if off < 0 { return 100 + r }
147 row_buf[off] = 0x09 as u8
148 off = off + 1
149 off = _emit_dec_to_buf(row_buf, off, 64, elapsed)
150 if off < 0 { return 200 + r }
151 row_buf[off] = 0x0A as u8
152 off = off + 1
153 if nx_bc_persist_row(path, path_len, row_buf, off) != (off as i64) { return 300 + r }
154 r = r + 1
155 }
156
157 // ===== Compute stats + persist summary row =====
158 let stats: *NxBenchStats = nx_bst_new()
159 nx_bench_stats_compute(samples, N_RUNS, stats)
160
161 // Summary row: "STATS\t<n>\t<min>\t<max>\t<mean>\t<p50>\t<p95>\t<stddev>\n"
162 let sbuf: *u8 = sys_mmap(128)
163 sbuf[0]=0x53 as u8; sbuf[1]=0x54 as u8; sbuf[2]=0x41 as u8
164 sbuf[3]=0x54 as u8; sbuf[4]=0x53 as u8; sbuf[5]=0x09 as u8
165 var soff: nx_int = 6
166 soff = _emit_dec_to_buf(sbuf, soff, 128, nx_bst_n(stats) as i64); sbuf[soff] = 0x09 as u8; soff = soff + 1
167 soff = _emit_dec_to_buf(sbuf, soff, 128, nx_bst_min(stats)); sbuf[soff] = 0x09 as u8; soff = soff + 1
168 soff = _emit_dec_to_buf(sbuf, soff, 128, nx_bst_max(stats)); sbuf[soff] = 0x09 as u8; soff = soff + 1
169 soff = _emit_dec_to_buf(sbuf, soff, 128, nx_bst_mean(stats)); sbuf[soff] = 0x09 as u8; soff = soff + 1
170 soff = _emit_dec_to_buf(sbuf, soff, 128, nx_bst_p50(stats)); sbuf[soff] = 0x09 as u8; soff = soff + 1
171 soff = _emit_dec_to_buf(sbuf, soff, 128, nx_bst_p95(stats)); sbuf[soff] = 0x09 as u8; soff = soff + 1
172 soff = _emit_dec_to_buf(sbuf, soff, 128, nx_bst_stddev(stats))
173 sbuf[soff] = 0x0A as u8; soff = soff + 1
174
175 if nx_bc_persist_row(path, path_len, sbuf, soff) != (soff as i64) { return 400 }
176
177 // ===== Read back + verify file is non-empty + ends with \n =====
178 let fd_r: i64 = sys_openat_rd(pz)
179 if fd_r < 0 { return 500 }
180 let rb: *u8 = sys_mmap(1024)
181 let n_read: i64 = sys_read(fd_r, rb, 1024)
182 sys_close(fd_r)
183 if n_read <= 0 { return 501 }
184 if rb[n_read - 1] != (0x0A as u8) { return 502 }
185
186 // First 5 bytes must be "label" (header start)
187 if rb[0] != (0x6C as u8) { return 503 }
188 if rb[1] != (0x61 as u8) { return 504 }
189 if rb[2] != (0x62 as u8) { return 505 }
190
191 // Substrate invariants
192 if nx_bst_n(stats) != N_RUNS { return 600 }
193 if nx_bst_p50(stats) <= 0 { return 601 }
194 if nx_bst_p95(stats) < nx_bst_p50(stats) { return 602 }
195
196 return 0
197}