code wiki / (root) / nx_dequant_bench_test.nx

nx_dequant_bench_test.nx source

↩ module page · 193 lines · 7922 B

1// nx_dequant_bench_test.nx -- the throughput-measurement smoke. 2// 3// Builds a synthetic Q4_K buffer of NX_DB_N_BLOCKS super-blocks 4// (NX_DB_N_BLOCKS * 256 values per run, NX_DB_N_BLOCKS * 144 bytes 5// input), runs nx_gguf_dequant_q4_k NX_DB_K_RUNS times, measures 6// wall-clock time via sys_clock_gettime_mono, computes ns/value and 7// MB/s, writes to /tmp/nx_dequant_bench.tsv for the smoke wrapper to 8// cat back to the operator. 9// 10// Pass condition: smoke exits 0 if dequant completes without OOM, 11// timing values are positive, no output value stuck at the INF_SAT 12// clamp (no catastrophic overflow). The MEASURED NUMBERS are 13// reported via the side-channel TSV; the smoke does NOT assert a 14// throughput threshold (that would be aspirational without a known 15// reference for this hardware). 16 17import "nx_syscalls.nx" 18import "nx_tier.nx" 19import "nx_le.nx" 20import "nx_strconv.nx" 21import "nx_gguf.nx" 22import "nx_gguf_load.nx" 23import "nx_dequant_bench.nx" 24 25// Write a labelled (key '\t' value '\n') line to a file descriptor. 26// Returns the byte count written. 27func _emit_kv(fd: i64, key: *u8, key_len: nx_int, value: i64) -> i64 { 28 let line: *u8 = sys_mmap(128) 29 var lo: i64 = 0 30 var ki: nx_int = 0 31 while ki < key_len { 32 line[lo] = key[ki]; lo = lo + 1; ki = ki + 1 33 } 34 line[lo] = 0x09; lo = lo + 1 // tab 35 let dec: *u8 = sys_mmap(32) 36 let n_dec: i64 = nx_strconv_format_i64(value, dec) 37 var k: i64 = 0 38 while k < n_dec { line[lo] = dec[k]; lo = lo + 1; k = k + 1 } 39 line[lo] = 0x0A; lo = lo + 1 // newline 40 return sys_write(fd, line, lo) 41} 42 43func main() -> i64 { 44 // ----- Build NX_DB_N_BLOCKS Q4_K super-blocks of semi-random bytes ----- 45 let buf_bytes: i64 = NX_DB_N_BLOCKS * NX_GL_Q4_K_BPB // 144 bytes each 46 let buf: *u8 = sys_mmap(buf_bytes + 64) 47 48 // Fill with deterministic pseudo-random bytes. Constraints to keep 49 // values from saturating the INF clamp: 50 // - f16 super-scale (bytes 0-1 of each block): bound to a moderate 51 // positive range by setting only the low 8 bits of the f16. 52 // 0x3C00 = 1.0 in f16. We pick patterns 0x3800..0x4000 (0.5..2.0). 53 // - f16 super-min (bytes 2-3): zero (so dmin contribution = 0) 54 // - scales/mins (bytes 4-15): bytes 0xFF masked at the dequant 55 // site (& 0x3F for low 6 bits, top 2 for high half) so any 56 // fill produces in-range scales. 57 // - nibble bytes (bytes 16-143): full pseudo-random ok. 58 var b: i64 = 0 59 var sb: i64 = 0 60 while sb < NX_DB_N_BLOCKS { 61 let base: i64 = sb * NX_GL_Q4_K_BPB 62 // Super-scale d: f16 around 1.0; cycle through 0x3800, 0x3C00, 0x4000 63 let pat: i64 = sb - (sb / 3) * 3 64 var d_raw: i64 = 0x3C00 65 if pat == 0 { d_raw = 0x3800 } 66 if pat == 2 { d_raw = 0x4000 } 67 nx_le_write_u16(buf, base + 0, d_raw) 68 // Super-min: 0 69 nx_le_write_u16(buf, base + 2, 0x0000) 70 // Scale/min bytes 4..15: simple pattern (sb + offset) low byte 71 var i: i64 = 0 72 while i < 12 { 73 buf[base + 4 + i] = (sb * 7 + i * 3) & 0xFF 74 i = i + 1 75 } 76 // Nibble bytes 16..143: pseudo-random (deterministic) 77 var j: i64 = 0 78 while j < 128 { 79 buf[base + 16 + j] = (sb * 11 + j * 5 + 1) & 0xFF 80 j = j + 1 81 } 82 sb = sb + 1 83 b = b + 1 84 } 85 86 // ----- Output buffer ----- 87 let n_values: i64 = NX_DB_N_BLOCKS * NX_GL_Q4_K_VPB // 256 vals per block 88 let out: *i64 = sys_mmap(n_values * 8) as *i64 89 90 // ----- Warm-up: 1 untimed dequant pass ----- 91 nx_gguf_dequant_q4_k(buf, 0, n_values, out) 92 93 // ----- Timed runs ----- 94 let t0: *i64 = sys_mmap(16) as *i64 95 sys_clock_gettime_mono(t0) 96 let t0_sec: i64 = t0[0] 97 let t0_nsec: i64 = t0[1] 98 99 var k: i64 = 0 100 while k < NX_DB_K_RUNS { 101 nx_gguf_dequant_q4_k(buf, 0, n_values, out) 102 k = k + 1 103 } 104 105 let t1: *i64 = sys_mmap(16) as *i64 106 sys_clock_gettime_mono(t1) 107 let t1_sec: i64 = t1[0] 108 let t1_nsec: i64 = t1[1] 109 110 // ----- Elapsed nanoseconds ----- 111 let sec_diff: i64 = t1_sec - t0_sec 112 let nsec_diff: i64 = t1_nsec - t0_nsec 113 let elapsed_ns: i64 = sec_diff * 1000000000 + nsec_diff 114 if elapsed_ns <= 0 { return 50 } 115 116 // ----- Derived metrics ----- 117 let total_values: i64 = n_values * NX_DB_K_RUNS 118 let ns_per_value: i64 = elapsed_ns / total_values 119 let total_input_bytes: i64 = buf_bytes * NX_DB_K_RUNS 120 // MB/s = (bytes / 1e6) / (ns / 1e9) = bytes * 1000 / ns 121 let mb_per_sec: i64 = total_input_bytes * 1000 / elapsed_ns 122 123 // ----- Sanity: no output value stuck at INF_SAT clamp ----- 124 var check: i64 = 0 125 while check < n_values { 126 if out[check] == NX_GL_Q10_INF_SAT { return 60 } 127 if out[check] == (0 - NX_GL_Q10_INF_SAT) { return 61 } 128 check = check + 1 129 } 130 131 // ----- Emit report TSV ----- 132 let path: *u8 = sys_mmap(64) 133 path[0]=0x2F; path[1]=0x74; path[2]=0x6D; path[3]=0x70 134 path[4]=0x2F 135 path[5]=0x6E; path[6]=0x78; path[7]=0x5F 136 path[8]=0x64; path[9]=0x65; path[10]=0x71; path[11]=0x75 137 path[12]=0x61; path[13]=0x6E; path[14]=0x74; path[15]=0x5F 138 path[16]=0x62; path[17]=0x65; path[18]=0x6E; path[19]=0x63 139 path[20]=0x68; path[21]=0x2E; path[22]=0x74; path[23]=0x73 140 path[24]=0x76 // ".tsv" 141 path[25]=0 142 143 let fd: i64 = sys_openat_wr(path, 0x1A4) 144 if fd < 0 { return 70 } 145 146 let k_n_blocks: *u8 = sys_mmap(16) 147 k_n_blocks[0]=0x6E; k_n_blocks[1]=0x5F; k_n_blocks[2]=0x62 148 k_n_blocks[3]=0x6C; k_n_blocks[4]=0x6F; k_n_blocks[5]=0x63 149 k_n_blocks[6]=0x6B; k_n_blocks[7]=0x73 // "n_blocks" 150 _emit_kv(fd, k_n_blocks, 8, NX_DB_N_BLOCKS) 151 152 let k_k_runs: *u8 = sys_mmap(16) 153 k_k_runs[0]=0x6B; k_k_runs[1]=0x5F; k_k_runs[2]=0x72 154 k_k_runs[3]=0x75; k_k_runs[4]=0x6E; k_k_runs[5]=0x73 // "k_runs" 155 _emit_kv(fd, k_k_runs, 6, NX_DB_K_RUNS) 156 157 let k_total_values: *u8 = sys_mmap(16) 158 k_total_values[0]=0x74; k_total_values[1]=0x6F; k_total_values[2]=0x74 159 k_total_values[3]=0x61; k_total_values[4]=0x6C; k_total_values[5]=0x5F 160 k_total_values[6]=0x76; k_total_values[7]=0x61; k_total_values[8]=0x6C 161 k_total_values[9]=0x75; k_total_values[10]=0x65; k_total_values[11]=0x73 // "total_values" 162 _emit_kv(fd, k_total_values, 12, total_values) 163 164 let k_total_bytes: *u8 = sys_mmap(16) 165 k_total_bytes[0]=0x74; k_total_bytes[1]=0x6F; k_total_bytes[2]=0x74 166 k_total_bytes[3]=0x61; k_total_bytes[4]=0x6C; k_total_bytes[5]=0x5F 167 k_total_bytes[6]=0x69; k_total_bytes[7]=0x6E; k_total_bytes[8]=0x5F 168 k_total_bytes[9]=0x62; k_total_bytes[10]=0x79; k_total_bytes[11]=0x74 169 k_total_bytes[12]=0x65; k_total_bytes[13]=0x73 // "total_in_bytes" 170 _emit_kv(fd, k_total_bytes, 14, total_input_bytes) 171 172 let k_elapsed: *u8 = sys_mmap(16) 173 k_elapsed[0]=0x65; k_elapsed[1]=0x6C; k_elapsed[2]=0x61 174 k_elapsed[3]=0x70; k_elapsed[4]=0x73; k_elapsed[5]=0x65 175 k_elapsed[6]=0x64; k_elapsed[7]=0x5F; k_elapsed[8]=0x6E; k_elapsed[9]=0x73 // "elapsed_ns" 176 _emit_kv(fd, k_elapsed, 10, elapsed_ns) 177 178 let k_ns_per_val: *u8 = sys_mmap(16) 179 k_ns_per_val[0]=0x6E; k_ns_per_val[1]=0x73; k_ns_per_val[2]=0x5F 180 k_ns_per_val[3]=0x70; k_ns_per_val[4]=0x65; k_ns_per_val[5]=0x72 181 k_ns_per_val[6]=0x5F; k_ns_per_val[7]=0x76; k_ns_per_val[8]=0x61 182 k_ns_per_val[9]=0x6C // "ns_per_val" 183 _emit_kv(fd, k_ns_per_val, 10, ns_per_value) 184 185 let k_mbps: *u8 = sys_mmap(16) 186 k_mbps[0]=0x6D; k_mbps[1]=0x62; k_mbps[2]=0x5F 187 k_mbps[3]=0x70; k_mbps[4]=0x65; k_mbps[5]=0x72; k_mbps[6]=0x5F 188 k_mbps[7]=0x73 // "mb_per_s" 189 _emit_kv(fd, k_mbps, 8, mb_per_sec) 190 191 sys_close(fd) 192 return 0 193}