code wiki / (root) / nx_q5q8_ab.nx

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}