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}