code wiki / _hdl_build / nx_build_bench.nx
nx_build_bench.nx source
↩ module page · 111 lines · 5099 B
1// nx_build_bench.nx -- MEASURE the build/compile pipeline speed, per phase, natively.
2//
3// Times the two sovereign build phases the WOMB runs, with the clock read IN-ORGAN (sys_now_ms)
4// around each fork+exec (sovereign, not a host stopwatch):
5// PHASE 1 compile : _offc/nx_cc_sovereign.elf <module.nx> (stdout -> /tmp/<m>.s)
6// PHASE 2 assemble: _offc/nxasm_x86_main.elf <m>.s <m>.elf
7// Reports compile_ms / assemble_ms / .s+.elf bytes / throughput across a small, medium, and large
8// organ, and names the bottleneck -- so build-speed optimization is evidence-based, not "30 is ok".
9// Sovereign: imports only nx_syscalls. license_tier: ORIGINAL
10import "nx_syscalls.nx"
11import "nx_itoa_lib.nx" // shared MSB-first emitter (zero-alloc)
12const BB_MAGIC_33554432: i64 = 33554432
13const BB_MAGIC_1024: i64 = 1024
14
15const BB_CC: *u8 = "_offc/nx_cc_sovereign.elf"
16const BB_ASM: *u8 = "_offc/nxasm_x86_main.elf"
17
18func bb_puts(s: *u8) -> i64 { var n: i64 = 0; while s[n] != 0 as u8 { n = n + 1 } sys_write(1, s, n); return 0 }
19// MIGRATED to the shared emitter (debt 1785563586). The old body mmapped a scratch buffer
20// per call and never freed it. At PAGE granularity that is 4096B leaked PER CALL -- the
21// defect that took 28.5GB of a 36GB host in nx_ts_lumadiff (2MB input, ~3.66M calls).
22// nxi_* is MSB-first, allocates NOTHING, and emits identical bytes including the sign.
23func bb_putn(v: i64) -> i64 { nxi_out(v); return 0 }
24func bb_cat(dst: *u8, off: i64, s: *u8) -> i64 { var i: i64 = 0; while s[i] != 0 as u8 { dst[off + i] = s[i]; i = i + 1 } return off + i }
25
26// fork+exec elf with up to 2 args; out_fd>=0 -> child stdout redirected there; stderr muted.
27func bb_run(elf: *u8, a1: *u8, a2: *u8, out_fd: i64) -> i64 {
28 let pid: i64 = sys_fork()
29 if pid < 0 { return 125 }
30 if pid == 0 {
31 let argv: *i64 = sys_mmap(32) as *i64
32 argv[0] = elf as i64
33 var i: i64 = 1
34 if (a1 as i64) != 0 { argv[i] = a1 as i64; i = i + 1 }
35 if (a2 as i64) != 0 { argv[i] = a2 as i64; i = i + 1 }
36 argv[i] = 0
37 let envp: *i64 = sys_mmap(16) as *i64
38 envp[0] = "PATH=/usr/bin:/bin" as *u8 as i64; envp[1] = 0
39 if out_fd >= 0 { sys_dup3(out_fd, 1, 0) }
40 let dn: i64 = sys_openat_wr("/dev/null" as *u8, 0x1a4)
41 if dn >= 0 { sys_dup3(dn, 2, 0) }
42 sys_execve(elf, argv, envp)
43 sys_exit(127)
44 }
45 let st: *i64 = sys_mmap(16) as *i64
46 let w: i64 = sys_wait4(pid, st, 0)
47 if w < 0 { return 125 }
48 let sig: i64 = st[0] & 0x7f
49 if sig != 0 { return 128 + sig }
50 return (st[0] >> 8) & 0xff
51}
52
53// byte size of a file via a bounded read (cost outside the timed window).
54func bb_filesize(path: *u8) -> i64 {
55 let fd: i64 = sys_openat_rd(path)
56 if fd < 0 { return 0 }
57 let cap: i64 = BB_MAGIC_33554432
58 let buf: *u8 = sys_mmap(cap + 16)
59 var n: i64 = 0; var go: i64 = 1
60 while go == 1 {
61 let r: i64 = sys_read(fd, ((buf as i64) + n) as *u8, cap - n)
62 if r <= 0 { go = 0 } else { n = n + r }
63 if n >= cap { go = 0 }
64 }
65 sys_close(fd)
66 return n
67}
68
69func bench(name: *u8) -> i64 {
70 let src: *u8 = sys_mmap(256); var o: i64 = 0
71 o = bb_cat(src, o, "runtime/_hdl_build/" as *u8); o = bb_cat(src, o, name); o = bb_cat(src, o, ".nx\x00" as *u8); src[o] = 0 as u8
72 let sp: *u8 = sys_mmap(256); o = 0
73 o = bb_cat(sp, o, "/tmp/bb_" as *u8); o = bb_cat(sp, o, name); o = bb_cat(sp, o, ".s\x00" as *u8); sp[o] = 0 as u8
74 let ep: *u8 = sys_mmap(256); o = 0
75 o = bb_cat(ep, o, "/tmp/bb_" as *u8); o = bb_cat(ep, o, name); o = bb_cat(ep, o, ".elf\x00" as *u8); ep[o] = 0 as u8
76
77 // PHASE 1: compile (stdout -> .s)
78 let sfd: i64 = sys_openat_wr(sp, 0x1a4)
79 let t0: i64 = sys_now_ms()
80 let rc_c: i64 = bb_run(BB_CC, src, 0 as *u8, sfd)
81 let t1: i64 = sys_now_ms()
82 sys_close(sfd)
83 let cms: i64 = t1 - t0
84 let sbytes: i64 = bb_filesize(sp)
85
86 // PHASE 2: assemble (.s -> .elf)
87 let t2: i64 = sys_now_ms()
88 let rc_a: i64 = bb_run(BB_ASM, sp, ep, 0 - 1)
89 let t3: i64 = sys_now_ms()
90 let ams: i64 = t3 - t2
91 let ebytes: i64 = bb_filesize(ep)
92
93 var cms2: i64 = cms; if cms2 <= 0 { cms2 = 1 }
94 bb_puts(" "); bb_puts(name)
95 bb_puts(" compile="); bb_putn(cms); bb_puts("ms (.s="); bb_putn(sbytes / BB_MAGIC_1024); bb_puts("KB, "); bb_putn(sbytes / cms2); bb_puts(" B/ms)")
96 bb_puts(" assemble="); bb_putn(ams); bb_puts("ms (.elf="); bb_putn(ebytes / BB_MAGIC_1024); bb_puts("KB)")
97 bb_puts(" total="); bb_putn(cms + ams); bb_puts("ms")
98 if rc_c != 0 { bb_puts(" [compile rc="); bb_putn(rc_c); bb_puts("]") }
99 if rc_a != 0 { bb_puts(" [asm rc="); bb_putn(rc_a); bb_puts("]") }
100 bb_puts("\n")
101 return cms + ams
102}
103
104func main() -> i64 {
105 bb_puts("=== sovereign build/compile speed (per phase, in-organ clock) ===\n")
106 bench("nx_genesis_trace" as *u8)
107 bench("nx_f32_hw_matmul" as *u8)
108 bench("nx_fw_capstone_gate" as *u8)
109 bb_puts(" (compile = nx_cc_sovereign, assemble = nxasm_x86_main; the larger phase is the build bottleneck to optimize.)\n")
110 sys_exit(0); return 0
111}