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}