code wiki / (root) / nx_bench_companion_tsv.nx

nx_bench_companion_tsv.nx source

↩ module page · 304 lines · 11670 B

1// nx_bench_companion_tsv.nx -- emit NxBenchReport as TSV row to a 2// caller-provided byte buffer. 3// 4// Sibling to [[nx_bench_companion]]. Substrate-honest persistence: 5// the bench primitive captures counters; this primitive serializes 6// them so they survive process exit + cross-session comparison 7// becomes a `sort -t$'\t' -k4 -n` away. 8// 9// Format: tab-separated, label first, then 19 counter columns. 10// Header row emitted by nx_bc_emit_tsv_header(). Data row by 11// nx_bc_emit_tsv_row(). Both return byte-length written (>= 0) 12// or NEGATIVE on buffer overflow. 13// 14// Per CARDINAL [[feedback-no-strawman-perf-comparisons]]: persisting 15// the counters to disk lets future Nishi-vs-llama.cpp comparison 16// rigs read paired rows + emit honest deltas without re-running 17// every measurement. 18 19import "nx_syscalls.nx" 20import "nx_tier.nx" 21import "nx_bench_companion.nx" 22 23// ===== Sealed verdict =========================================== 24 25const NX_BCT_OK: nx_int = 0 26const NX_BCT_OVERFLOW: nx_int = 1 27const NX_BCT_NULL_BUF: nx_int = 2 28const NX_BCT_NULL_REPORT: nx_int = 3 29const NX_BCT_INVALID: nx_int = 4 30const NX_BCT_N: nx_int = 5 31 32func nx_bct_v_is_valid(v: nx_int) -> nx_int { 33 if v < 0 { return 0 } 34 if v >= NX_BCT_N { return 0 } 35 return 1 36} 37 38// ===== Decimal-bytes emitter (i64 -> caller buffer) ============= 39// 40// Writes the decimal ASCII representation of n starting at buf[off]. 41// Returns the new offset (off + bytes_written). Returns -1 on 42// overflow (would write past cap). Negative numbers get a leading 43// '-'; zero gets a single '0'. 44 45func _bct_emit_i64(buf: *u8, off: nx_int, cap: nx_int, n: i64) -> nx_int { 46 if off < 0 { return 0 - 1 } 47 if off >= cap { return 0 - 1 } 48 49 if n == 0 { 50 buf[off] = 0x30 as u8 // '0' 51 return off + 1 52 } 53 54 var v: i64 = n 55 var neg: nx_int = 0 56 if v < 0 { 57 neg = 1 58 v = 0 - v 59 } 60 61 // Reverse-fill into a scratch then copy forward. Worst case for 62 // i64 is 19 digits + 1 sign = 20 bytes; scratch of 24 is safe. 63 let scratch_buf: *u8 = sys_mmap(24) 64 var k: nx_int = 0 65 while v > 0 { 66 let digit: i64 = v - (v / 10) * 10 67 scratch_buf[k] = (0x30 + digit) as u8 68 v = v / 10 69 k = k + 1 70 } 71 72 var out: nx_int = off 73 if neg == 1 { 74 if out >= cap { return 0 - 1 } 75 buf[out] = 0x2D as u8 // '-' 76 out = out + 1 77 } 78 // Copy scratch (which holds digits LSB-first) into buf in reverse. 79 while k > 0 { 80 k = k - 1 81 if out >= cap { return 0 - 1 } 82 buf[out] = scratch_buf[k] 83 out = out + 1 84 } 85 return out 86} 87 88// ===== Byte / string emitter ==================================== 89 90func _bct_emit_byte(buf: *u8, off: nx_int, cap: nx_int, b: nx_int) -> nx_int { 91 if off < 0 { return 0 - 1 } 92 if off >= cap { return 0 - 1 } 93 buf[off] = b as u8 94 return off + 1 95} 96 97func _bct_emit_str(buf: *u8, off: nx_int, cap: nx_int, 98 s: *u8, slen: nx_int) -> nx_int { 99 if off < 0 { return 0 - 1 } 100 if slen < 0 { return 0 - 1 } 101 if (s as i64) == 0 { return 0 - 1 } 102 var out: nx_int = off 103 var i: nx_int = 0 104 while i < slen { 105 if out >= cap { return 0 - 1 } 106 buf[out] = s[i] 107 out = out + 1 108 i = i + 1 109 } 110 return out 111} 112 113// ===== TSV header row ============================================ 114// 115// Static column names matching the 19 NxBenchReport counters + a 116// leading "label" column. Total = 20 columns. 117 118func nx_bc_emit_tsv_header(buf: *u8, cap: nx_int) -> nx_int { 119 if (buf as i64) == 0 { return 0 - NX_BCT_NULL_BUF } 120 if cap < 0 { return 0 - NX_BCT_INVALID } 121 122 // "label\tn_actors\tactors_completed\tactors_failed\tactors_running\t" 123 // "actors_waiting\ttotal_actor_steps\tcumulative_runtime_us\t" 124 // "n_subscriptions\tmux_pending\tarb_grants_full\tarb_grants_partial\t" 125 // "arb_denials\tarb_preemptions\tbroker_grants\tbroker_refusals\t" 126 // "broker_deadline_misses\tepoch_count\tcaptured_at_us\tverdict\n" 127 128 let hdr: *u8 = sys_mmap(384) 129 // We literally write each column name + tab as bytes. Using 130 // explicit byte writes (no .nx string literal escape) keeps the 131 // emitter dependency-free. 132 var k: nx_int = 0 133 // "label" 134 hdr[k+0]=0x6C as u8; hdr[k+1]=0x61 as u8; hdr[k+2]=0x62 as u8 135 hdr[k+3]=0x65 as u8; hdr[k+4]=0x6C as u8; hdr[k+5]=0x09 as u8 136 k = k + 6 137 // "n_actors\t" 138 hdr[k+0]=0x6E as u8; hdr[k+1]=0x5F as u8; hdr[k+2]=0x61 as u8 139 hdr[k+3]=0x63 as u8; hdr[k+4]=0x74 as u8; hdr[k+5]=0x6F as u8 140 hdr[k+6]=0x72 as u8; hdr[k+7]=0x73 as u8; hdr[k+8]=0x09 as u8 141 k = k + 9 142 // "completed\t" 143 hdr[k+0]=0x63 as u8; hdr[k+1]=0x6F as u8; hdr[k+2]=0x6D as u8 144 hdr[k+3]=0x70 as u8; hdr[k+4]=0x6C as u8; hdr[k+5]=0x65 as u8 145 hdr[k+6]=0x74 as u8; hdr[k+7]=0x65 as u8; hdr[k+8]=0x64 as u8 146 hdr[k+9]=0x09 as u8 147 k = k + 10 148 // "failed\t" 149 hdr[k+0]=0x66 as u8; hdr[k+1]=0x61 as u8; hdr[k+2]=0x69 as u8 150 hdr[k+3]=0x6C as u8; hdr[k+4]=0x65 as u8; hdr[k+5]=0x64 as u8 151 hdr[k+6]=0x09 as u8 152 k = k + 7 153 // "running\t" 154 hdr[k+0]=0x72 as u8; hdr[k+1]=0x75 as u8; hdr[k+2]=0x6E as u8 155 hdr[k+3]=0x6E as u8; hdr[k+4]=0x69 as u8; hdr[k+5]=0x6E as u8 156 hdr[k+6]=0x67 as u8; hdr[k+7]=0x09 as u8 157 k = k + 8 158 // "waiting\t" 159 hdr[k+0]=0x77 as u8; hdr[k+1]=0x61 as u8; hdr[k+2]=0x69 as u8 160 hdr[k+3]=0x74 as u8; hdr[k+4]=0x69 as u8; hdr[k+5]=0x6E as u8 161 hdr[k+6]=0x67 as u8; hdr[k+7]=0x09 as u8 162 k = k + 8 163 // "steps\t" 164 hdr[k+0]=0x73 as u8; hdr[k+1]=0x74 as u8; hdr[k+2]=0x65 as u8 165 hdr[k+3]=0x70 as u8; hdr[k+4]=0x73 as u8; hdr[k+5]=0x09 as u8 166 k = k + 6 167 // "runtime_us\t" 168 hdr[k+0]=0x72 as u8; hdr[k+1]=0x75 as u8; hdr[k+2]=0x6E as u8 169 hdr[k+3]=0x74 as u8; hdr[k+4]=0x69 as u8; hdr[k+5]=0x6D as u8 170 hdr[k+6]=0x65 as u8; hdr[k+7]=0x5F as u8; hdr[k+8]=0x75 as u8 171 hdr[k+9]=0x73 as u8; hdr[k+10]=0x09 as u8 172 k = k + 11 173 // "subs\t" 174 hdr[k+0]=0x73 as u8; hdr[k+1]=0x75 as u8; hdr[k+2]=0x62 as u8 175 hdr[k+3]=0x73 as u8; hdr[k+4]=0x09 as u8 176 k = k + 5 177 // "mux_pend\t" 178 hdr[k+0]=0x6D as u8; hdr[k+1]=0x75 as u8; hdr[k+2]=0x78 as u8 179 hdr[k+3]=0x5F as u8; hdr[k+4]=0x70 as u8; hdr[k+5]=0x65 as u8 180 hdr[k+6]=0x6E as u8; hdr[k+7]=0x64 as u8; hdr[k+8]=0x09 as u8 181 k = k + 9 182 // "arb_gf\t" (grants_full) 183 hdr[k+0]=0x61 as u8; hdr[k+1]=0x72 as u8; hdr[k+2]=0x62 as u8 184 hdr[k+3]=0x5F as u8; hdr[k+4]=0x67 as u8; hdr[k+5]=0x66 as u8 185 hdr[k+6]=0x09 as u8 186 k = k + 7 187 // "arb_gp\t" (grants_partial) 188 hdr[k+0]=0x61 as u8; hdr[k+1]=0x72 as u8; hdr[k+2]=0x62 as u8 189 hdr[k+3]=0x5F as u8; hdr[k+4]=0x67 as u8; hdr[k+5]=0x70 as u8 190 hdr[k+6]=0x09 as u8 191 k = k + 7 192 // "arb_d\t" (denials) 193 hdr[k+0]=0x61 as u8; hdr[k+1]=0x72 as u8; hdr[k+2]=0x62 as u8 194 hdr[k+3]=0x5F as u8; hdr[k+4]=0x64 as u8; hdr[k+5]=0x09 as u8 195 k = k + 6 196 // "arb_pr\t" (preemptions) 197 hdr[k+0]=0x61 as u8; hdr[k+1]=0x72 as u8; hdr[k+2]=0x62 as u8 198 hdr[k+3]=0x5F as u8; hdr[k+4]=0x70 as u8; hdr[k+5]=0x72 as u8 199 hdr[k+6]=0x09 as u8 200 k = k + 7 201 // "br_g\t" 202 hdr[k+0]=0x62 as u8; hdr[k+1]=0x72 as u8; hdr[k+2]=0x5F as u8 203 hdr[k+3]=0x67 as u8; hdr[k+4]=0x09 as u8 204 k = k + 5 205 // "br_r\t" 206 hdr[k+0]=0x62 as u8; hdr[k+1]=0x72 as u8; hdr[k+2]=0x5F as u8 207 hdr[k+3]=0x72 as u8; hdr[k+4]=0x09 as u8 208 k = k + 5 209 // "br_dm\t" 210 hdr[k+0]=0x62 as u8; hdr[k+1]=0x72 as u8; hdr[k+2]=0x5F as u8 211 hdr[k+3]=0x64 as u8; hdr[k+4]=0x6D as u8; hdr[k+5]=0x09 as u8 212 k = k + 6 213 // "epoch\t" 214 hdr[k+0]=0x65 as u8; hdr[k+1]=0x70 as u8; hdr[k+2]=0x6F as u8 215 hdr[k+3]=0x63 as u8; hdr[k+4]=0x68 as u8; hdr[k+5]=0x09 as u8 216 k = k + 6 217 // "at_us\t" 218 hdr[k+0]=0x61 as u8; hdr[k+1]=0x74 as u8; hdr[k+2]=0x5F as u8 219 hdr[k+3]=0x75 as u8; hdr[k+4]=0x73 as u8; hdr[k+5]=0x09 as u8 220 k = k + 6 221 // "verdict\n" 222 hdr[k+0]=0x76 as u8; hdr[k+1]=0x65 as u8; hdr[k+2]=0x72 as u8 223 hdr[k+3]=0x64 as u8; hdr[k+4]=0x69 as u8; hdr[k+5]=0x63 as u8 224 hdr[k+6]=0x74 as u8; hdr[k+7]=0x0A as u8 225 k = k + 8 226 227 if k > cap { return 0 - NX_BCT_OVERFLOW } 228 229 // Copy hdr -> buf 230 var i: nx_int = 0 231 while i < k { 232 buf[i] = hdr[i] 233 i = i + 1 234 } 235 return k 236} 237 238// ===== TSV data row ============================================= 239 240func _bct_emit_field(buf: *u8, off: nx_int, cap: nx_int, n: i64, sep: nx_int) -> nx_int { 241 let after_n: nx_int = _bct_emit_i64(buf, off, cap, n) 242 if after_n < 0 { return 0 - 1 } 243 return _bct_emit_byte(buf, after_n, cap, sep) 244} 245 246func nx_bc_emit_tsv_row(buf: *u8, cap: nx_int, 247 r: *NxBenchReport, 248 label: *u8, label_len: nx_int) -> nx_int { 249 if (buf as i64) == 0 { return 0 - NX_BCT_NULL_BUF } 250 if (r as i64) == 0 { return 0 - NX_BCT_NULL_REPORT } 251 if cap < 0 { return 0 - NX_BCT_INVALID } 252 if label_len < 0 { return 0 - NX_BCT_INVALID } 253 254 var off: nx_int = 0 255 if (label as i64) != 0 { 256 off = _bct_emit_str(buf, off, cap, label, label_len) 257 if off < 0 { return 0 - NX_BCT_OVERFLOW } 258 } 259 off = _bct_emit_byte(buf, off, cap, 0x09) // \t 260 if off < 0 { return 0 - NX_BCT_OVERFLOW } 261 262 // 19 fields, all tab-separated except the last which is \n 263 off = _bct_emit_field(buf, off, cap, r.n_actors as i64, 0x09) 264 if off < 0 { return 0 - NX_BCT_OVERFLOW } 265 off = _bct_emit_field(buf, off, cap, r.actors_completed as i64, 0x09) 266 if off < 0 { return 0 - NX_BCT_OVERFLOW } 267 off = _bct_emit_field(buf, off, cap, r.actors_failed as i64, 0x09) 268 if off < 0 { return 0 - NX_BCT_OVERFLOW } 269 off = _bct_emit_field(buf, off, cap, r.actors_running as i64, 0x09) 270 if off < 0 { return 0 - NX_BCT_OVERFLOW } 271 off = _bct_emit_field(buf, off, cap, r.actors_waiting as i64, 0x09) 272 if off < 0 { return 0 - NX_BCT_OVERFLOW } 273 off = _bct_emit_field(buf, off, cap, r.total_actor_steps as i64, 0x09) 274 if off < 0 { return 0 - NX_BCT_OVERFLOW } 275 off = _bct_emit_field(buf, off, cap, r.cumulative_runtime_us as i64, 0x09) 276 if off < 0 { return 0 - NX_BCT_OVERFLOW } 277 off = _bct_emit_field(buf, off, cap, r.n_subscriptions as i64, 0x09) 278 if off < 0 { return 0 - NX_BCT_OVERFLOW } 279 off = _bct_emit_field(buf, off, cap, r.mux_pending as i64, 0x09) 280 if off < 0 { return 0 - NX_BCT_OVERFLOW } 281 off = _bct_emit_field(buf, off, cap, r.arb_grants_full as i64, 0x09) 282 if off < 0 { return 0 - NX_BCT_OVERFLOW } 283 off = _bct_emit_field(buf, off, cap, r.arb_grants_partial as i64, 0x09) 284 if off < 0 { return 0 - NX_BCT_OVERFLOW } 285 off = _bct_emit_field(buf, off, cap, r.arb_denials as i64, 0x09) 286 if off < 0 { return 0 - NX_BCT_OVERFLOW } 287 off = _bct_emit_field(buf, off, cap, r.arb_preemptions as i64, 0x09) 288 if off < 0 { return 0 - NX_BCT_OVERFLOW } 289 off = _bct_emit_field(buf, off, cap, r.broker_grants as i64, 0x09) 290 if off < 0 { return 0 - NX_BCT_OVERFLOW } 291 off = _bct_emit_field(buf, off, cap, r.broker_refusals as i64, 0x09) 292 if off < 0 { return 0 - NX_BCT_OVERFLOW } 293 off = _bct_emit_field(buf, off, cap, r.broker_deadline_misses as i64, 0x09) 294 if off < 0 { return 0 - NX_BCT_OVERFLOW } 295 off = _bct_emit_field(buf, off, cap, r.epoch_count as i64, 0x09) 296 if off < 0 { return 0 - NX_BCT_OVERFLOW } 297 off = _bct_emit_field(buf, off, cap, r.captured_at_us as i64, 0x09) 298 if off < 0 { return 0 - NX_BCT_OVERFLOW } 299 off = _bct_emit_i64(buf, off, cap, r.verdict as i64) 300 if off < 0 { return 0 - NX_BCT_OVERFLOW } 301 off = _bct_emit_byte(buf, off, cap, 0x0A) // \n 302 if off < 0 { return 0 - NX_BCT_OVERFLOW } 303 return off 304}