nx_q5q8_ab.nx source
↩ module page · 117 lines · 3942 B
1// nx_q5q8_ab.nx -- the memory-bound-era re-test: does reading FEWER BYTES
2// (Q5_0 @ 0.69 B/weight, +__q5_unpack32 SSE unpack) now BEAT Q8_0 @ 1.06
3// B/weight (no unpack), on the COLD forward-representative access pattern
4// under the FUTEX pool? Earlier (yield-storm pool + software elementwise)
5// Q5_0 lost 2x -> repacked to Q8_0. Now the matmul is memory-bound, so
6// fewer bytes SHOULD win if the SSE unpack stays hidden. Same process,
7// same load, COLD (NB distinct buffers each read once). license_tier:
8// ORIGINAL expect_exit: 0
9import "nx_syscalls.nx"
10import "nx_tier.nx"
11import "nx_le.nx"
12import "nx_f32.nx"
13import "nx_f32_cvt.nx"
14import "nx_thread_pool.nx"
15import "nx_f32_lazy_weight.nx"
16import "nx_fmt.nx"
17
18const MK: i64 = 896
19const NG: i64 = 4864
20const Q8B: i64 = 34
21const Q5B: i64 = 22
22const NB: i64 = 40
23
24func qa_nl() -> i64 { fmt_puts("\n" as *u8); return 0 }
25func qa_lcg(s: i64) -> i64 { var v: i64 = s * 1103515245 + 12345; v = v & 2147483647; return v }
26
27func q8_weight(seed: i64) -> *NxF32LazyWeight {
28 let bpr: i64 = (MK / 32) * Q8B
29 let w: *u8 = sys_mmap(NG * bpr)
30 var s: i64 = seed
31 var r: i64 = 0
32 while r < NG {
33 var b: i64 = 0
34 while b < MK / 32 {
35 let off: i64 = r * bpr + b * Q8B
36 w[off + 1] = 0x2C as u8
37 var q: i64 = 0
38 while q < 32 { s = qa_lcg(s); w[off + 2 + q] = ((s % 17) - 8) as u8; q = q + 1 }
39 b = b + 1
40 }
41 r = r + 1
42 }
43 return nx_f32_lazy_weight_new_q8_0(w, 0, NG, MK)
44}
45
46func q5_weight(seed: i64) -> *NxF32LazyWeight {
47 let bpr: i64 = (MK / 32) * Q5B
48 let w: *u8 = sys_mmap(NG * bpr)
49 var s: i64 = seed
50 var r: i64 = 0
51 while r < NG {
52 var b: i64 = 0
53 while b < MK / 32 {
54 let off: i64 = r * bpr + b * Q5B
55 w[off + 0] = 0x00 as u8; w[off + 1] = 0x2C as u8 // d f16
56 s = qa_lcg(s); w[off + 2] = s as u8; w[off + 3] = (s >> 8) as u8 // qh
57 w[off + 4] = (s >> 16) as u8; w[off + 5] = (s >> 24) as u8
58 var q: i64 = 0
59 while q < 16 { s = qa_lcg(s); w[off + 6 + q] = s as u8; q = q + 1 } // qs
60 b = b + 1
61 }
62 r = r + 1
63 }
64 return nx_f32_lazy_weight_new_q5_0(w, 0, NG, MK)
65}
66
67func qa_rep(tag: *u8, us: i64, bpw: i64) -> i64 {
68 var u: i64 = us
69 if u < 1 { u = 1 }
70 let flop: i64 = 2 * MK * NG * NB
71 let bytes: i64 = NG * (MK / 32) * bpw * NB
72 fmt_puts(tag)
73 fmt_puts(" per_call_us="); fmt_putn(us / NB)
74 fmt_puts(" MFLOPs="); fmt_putn(flop / u)
75 fmt_puts(" MBps="); fmt_putn(bytes / u)
76 qa_nl()
77 return 0
78}
79
80func main() -> i64 {
81 let A: *i64 = sys_mmap(MK * 8) as *i64
82 var s: i64 = 4242
83 var i: i64 = 0
84 while i < MK { s = qa_lcg(s); A[i] = nx_i32_to_f32((s % 9) - 4); i = i + 1 }
85 let C: *i64 = sys_mmap(NG * 8) as *i64
86 nx_lw_shared_pool()
87
88 // build NB distinct of each up front.
89 let q8: *i64 = sys_mmap(NB * 8) as *i64
90 let q5: *i64 = sys_mmap(NB * 8) as *i64
91 var b0: i64 = 0
92 while b0 < NB {
93 q8[b0] = q8_weight(700 + b0 * 13) as i64
94 q5[b0] = q5_weight(900 + b0 * 17) as i64
95 b0 = b0 + 1
96 }
97
98 // interleave-free COLD sweeps: Q8 first (each buffer read once), then Q5.
99 let t8: i64 = sys_now_us()
100 var r8: i64 = 0
101 while r8 < NB { nx_f32_lazy_matmul(A, q8[r8] as *NxF32LazyWeight, C, 1, MK, NG); r8 = r8 + 1 }
102 let us8: i64 = sys_now_us() - t8
103 qa_rep("Q8_0 COLD(1.06B/w)" as *u8, us8, Q8B)
104
105 let t5: i64 = sys_now_us()
106 var r5: i64 = 0
107 while r5 < NB { nx_f32_lazy_matmul(A, q5[r5] as *NxF32LazyWeight, C, 1, MK, NG); r5 = r5 + 1 }
108 let us5: i64 = sys_now_us() - t5
109 qa_rep("Q5_0 COLD(0.69B/w)" as *u8, us5, Q5B)
110
111 var u5: i64 = us5
112 if u5 < 1 { u5 = 1 }
113 fmt_puts("Q8_over_Q5_x100="); fmt_putn(us8 * 100 / u5)
114 fmt_puts(" (>100 => Q5_0 fewer-bytes WINS)"); qa_nl()
115 fmt_puts("Q5Q8_AB DONE"); qa_nl()
116 return 0
117}