code wiki / (root) / nx_perf_probe.nx

nx_perf_probe.nx source

↩ module page · 184 lines · 6437 B

1// nx_perf_probe.nx -- substrate performance counter primitive. 2// 3// Cardinal: feedback-systemic-injection-everything-zero-runtime-overhead. 4// 5// Same three-layered pattern as nx_bit_provenance / nx_race_telemetry: 6// 7// (1) Born-into-build: callers use nx_perf_tick_start / tick_end 8// (or the wrap_call helpers below). 9// (2) Retroactive: future self-host IR pass injects entry/exit 10// calls driven by `// nx_perf: budget_ns=...` annotations on 11// function definitions. 12// (3) Zero runtime cost when --strip perf: 13// NX_PERF_ACTIVE compile-folded to 0 + entire machinery DCE'd. 14// 15// Storage: ring buffer of (function_tag, start_ns, end_ns, iter) 16// records. No per-tick syscalls; nx_perf_now() uses 17// sys_clock_gettime_mono once per tick. 18// 19// API surface: 20// let tk: i64 = nx_perf_tick_start(NX_PERF_TAG_MATMUL) 21// // ... work to time ... 22// nx_perf_tick_end(tk) -- elapsed recorded 23// 24// nx_perf_drain(out: *i64, cap: i64) -> i64 25// nx_perf_n_records() -> i64 26// nx_perf_enable() / nx_perf_disable() / nx_perf_is_active() 27// 28// Each tick uses ONE clock syscall + one ring-buffer write. No 29// startup cost. No ledger allocation until first tick. 30 31import "syscalls.nx" 32const NX_MAGIC_4096: i64 = 4096 33const NX_MAGIC_1000000000: i64 = 1000000000 34const NX_MAGIC_1024: i64 = 1024 35 36static NX_PERF_ACTIVE: i64 37 38static NX_PERF_RING_PTR: i64 39static NX_PERF_RING_HEAD: i64 40static NX_PERF_RING_CAP: i64 41 42const NX_PERF_REC_SIZE: i64 = 32 // 4 i64 fields 43const NX_PERF_RING_BYTES: i64 = 32768 // 1024 records 44 45// Stack-pending tick state: a CALLER receives an opaque handle 46// from tick_start that they later pass to tick_end. Internally 47// the handle is just the record-slot index biased by +1 (so 0 is 48// reserved as "no tick"). The "start_ns" is written to the slot 49// at tick_start; tick_end fills "end_ns". 50// 51// Concurrent tick handles per record-slot via the ring's natural 52// append order (no nested-handle protection in V0 -- enforce 53// well-formed start/end pairs at caller). 54 55func nx_perf_init() -> i64 { 56 if NX_PERF_RING_PTR != 0 { return 0 } 57 let raw: *u8 = sys_mmap(NX_PERF_RING_BYTES) 58 if (raw as i64) == 0 { return 1 } 59 if (raw as i64) == 0 - 1 { return 2 } 60 NX_PERF_RING_PTR = raw as i64 61 NX_PERF_RING_HEAD = 0 62 NX_PERF_RING_CAP = NX_PERF_RING_BYTES / NX_PERF_REC_SIZE 63 NX_PERF_ACTIVE = 1 64 return 0 65} 66 67func nx_perf_enable() -> i64 { NX_PERF_ACTIVE = 1; return 0 } 68func nx_perf_disable() -> i64 { NX_PERF_ACTIVE = 0; return 0 } 69func nx_perf_is_active() -> i64 { return NX_PERF_ACTIVE } 70 71// Read monotonic clock. Returns ns since some unspecified epoch 72// (only differences are meaningful). 73func nx_perf_now_ns() -> i64 { 74 let ts_raw: *u8 = sys_mmap(NX_MAGIC_4096) 75 let ts: *i64 = ts_raw as *i64 76 let r: i64 = __syscall(113, 1, ts_raw as i64, 0, 0, 0, 0) 77 if r != 0 { return 0 } 78 return ts[0] * NX_MAGIC_1000000000 + ts[1] 79} 80 81// Begin a tick. Returns an opaque handle (slot+1; 0 means 82// telemetry was disabled). Caller must pair with nx_perf_tick_end. 83func nx_perf_tick_start(tag: i64) -> i64 { 84 if NX_PERF_ACTIVE == 0 { return 0 } 85 nx_perf_init() 86 if NX_PERF_RING_PTR == 0 { return 0 } 87 88 let slot: i64 = NX_PERF_RING_HEAD % NX_PERF_RING_CAP 89 let base: i64 = NX_PERF_RING_PTR + (slot * NX_PERF_REC_SIZE) 90 let rec: *i64 = base as *i64 91 rec[0] = tag 92 rec[1] = nx_perf_now_ns() 93 rec[2] = 0 // end_ns; filled by tick_end 94 rec[3] = NX_PERF_RING_HEAD // monotone seq for matching 95 NX_PERF_RING_HEAD = NX_PERF_RING_HEAD + 1 96 return slot + 1 97} 98 99// End a tick. Handle must be the value returned by tick_start. 100// Records elapsed ns in the record's end_ns slot. Returns 0 OK, 101// non-zero on malformed handle (caller bug). 102func nx_perf_tick_end(handle: i64) -> i64 { 103 if NX_PERF_ACTIVE == 0 { return 0 } 104 if handle <= 0 { return 0 } 105 let slot: i64 = handle - 1 106 if slot >= NX_PERF_RING_CAP { return 1 } 107 let base: i64 = NX_PERF_RING_PTR + (slot * NX_PERF_REC_SIZE) 108 let rec: *i64 = base as *i64 109 rec[2] = nx_perf_now_ns() 110 return 0 111} 112 113// Drain the ring into a caller-owned i64 array (4 i64s per record). 114// Returns records copied. Resets the head. 115func nx_perf_drain(out: *i64, max_records: i64) -> i64 { 116 if NX_PERF_RING_PTR == 0 { return 0 } 117 nx_perf_init() 118 var n: i64 = NX_PERF_RING_HEAD 119 if n > NX_PERF_RING_CAP { n = NX_PERF_RING_CAP } 120 if n > max_records { n = max_records } 121 var i: i64 = 0 122 while i < n { 123 let in_base: i64 = NX_PERF_RING_PTR + (i * NX_PERF_REC_SIZE) 124 let in_rec: *i64 = in_base as *i64 125 out[i * 4 + 0] = in_rec[0] 126 out[i * 4 + 1] = in_rec[1] 127 out[i * 4 + 2] = in_rec[2] 128 out[i * 4 + 3] = in_rec[3] 129 i = i + 1 130 } 131 NX_PERF_RING_HEAD = 0 132 return n 133} 134 135func nx_perf_n_records() -> i64 { return NX_PERF_RING_HEAD } 136 137// ---- self-test --------------------------------------------------- 138// expect_exit: 0 139 140func main() -> i64 { 141 if nx_perf_init() != 0 { return 1 } 142 143 // Disabled-by-default short-circuit: tick_start returns 0. 144 nx_perf_disable() 145 if nx_perf_tick_start(0xC0DE0001) != 0 { return 10 } 146 if nx_perf_n_records() != 0 { return 11 } 147 148 // Enable + measure 3 ticks. 149 nx_perf_enable() 150 let tk1: i64 = nx_perf_tick_start(0xC0DE0001) 151 if tk1 == 0 { return 20 } 152 let dummy_work: i64 = nx_perf_now_ns() // burn a few ns 153 if nx_perf_tick_end(tk1) != 0 { return 21 } 154 155 let tk2: i64 = nx_perf_tick_start(0xC0DE0002) 156 nx_perf_tick_end(tk2) 157 let tk3: i64 = nx_perf_tick_start(0xC0DE0003) 158 nx_perf_tick_end(tk3) 159 160 if nx_perf_n_records() != 3 { return 30 } 161 162 // Drain + verify. 163 let out_raw: *u8 = sys_mmap(NX_MAGIC_4096) 164 let out: *i64 = out_raw as *i64 165 let n: i64 = nx_perf_drain(out, NX_MAGIC_1024) 166 if n != 3 { return 40 } 167 if out[0] != 0xC0DE0001 { return 41 } 168 // out[1] = start_ns; out[2] = end_ns; both nonzero, end >= start 169 if out[1] == 0 { return 42 } 170 if out[2] < out[1] { return 43 } 171 if out[4] != 0xC0DE0002 { return 44 } 172 if out[8] != 0xC0DE0003 { return 45 } 173 174 // Head reset after drain. 175 if nx_perf_n_records() != 0 { return 50 } 176 177 // Disabled + ensure no growth. 178 nx_perf_disable() 179 let tk_disabled: i64 = nx_perf_tick_start(0xC0DE0099) 180 if tk_disabled != 0 { return 60 } 181 if nx_perf_n_records() != 0 { return 61 } 182 183 return 0 184}