nx_membw.nx source
↩ module page · 65 lines · 2405 B
1// nx_membw.nx -- resolves the open question: is the decode matmul's ~5 GB/s
2// (nx_q8_coldwarm COLD) a HARD memory-bandwidth cap (=> AVX2/prefetch WON'T
3// help, the ~3.5-7x llama.cpp gap is environmental/WSL) or is the SSE kernel
4// LEAVING BANDWIDTH ON THE TABLE (=> a wider kernel WOULD help)? Measures
5// the RAW achievable sequential-read bandwidth of this process: allocate a
6// >>LLC buffer, commit it, then stream-sum it (hardware prefetcher engaged).
7// If raw ~5 GB/s -> bandwidth-capped (matmul is at the floor). If raw >>5
8// GB/s -> the matmul kernel is the limiter (AVX2 pays). expect_exit: 0
9import "nx_syscalls.nx"
10import "nx_tier.nx"
11import "nx_fmt.nx"
12
13const MB: i64 = 1048576
14
15func mb_nl() -> i64 { fmt_puts("\n" as *u8); return 0 }
16
17func main() -> i64 {
18 let bytes: i64 = 400 * MB // >> any LLC
19 let n: i64 = bytes / 8 // i64 words
20 let p: *i64 = sys_mmap(bytes) as *i64
21
22 // commit + seed (also warms TLB; the data is then evicted as we sum a
23 // buffer far larger than cache, so reads stream from DRAM).
24 var i: i64 = 0
25 while i < n { p[i] = i; i = i + 1 }
26
27 // ---- pass 1: single scalar accumulator ----
28 let t0: i64 = sys_now_us()
29 var acc: i64 = 0
30 var j: i64 = 0
31 while j < n { acc = acc + p[j]; j = j + 1 }
32 let us1: i64 = sys_now_us() - t0
33 var u1: i64 = us1
34 if u1 < 1 { u1 = 1 }
35 fmt_puts("SCALAR-SUM us="); fmt_putn(us1)
36 fmt_puts(" MBps="); fmt_putn(bytes / u1); mb_nl()
37
38 // ---- pass 2: 4 independent accumulators (break the load-use chain,
39 // let the prefetcher + multiple in-flight loads saturate the bus) ----
40 let t1: i64 = sys_now_us()
41 var a0: i64 = 0
42 var a1: i64 = 0
43 var a2: i64 = 0
44 var a3: i64 = 0
45 var k: i64 = 0
46 while k + 4 <= n {
47 a0 = a0 + p[k]
48 a1 = a1 + p[k + 1]
49 a2 = a2 + p[k + 2]
50 a3 = a3 + p[k + 3]
51 k = k + 4
52 }
53 let us2: i64 = sys_now_us() - t1
54 var u2: i64 = us2
55 if u2 < 1 { u2 = 1 }
56 fmt_puts("4-ACC-SUM us="); fmt_putn(us2)
57 fmt_puts(" MBps="); fmt_putn(bytes / u2); mb_nl()
58
59 // keep acc/a* live so the loops aren't dead-code-eliminated.
60 let guard: i64 = acc + a0 + a1 + a2 + a3
61 fmt_puts("guard="); fmt_putn(guard & 1); mb_nl()
62 fmt_puts("(matmul COLD was ~5133 MBps; if raw >> that, kernel is the limit)"); mb_nl()
63 fmt_puts("MEMBW DONE"); mb_nl()
64 return 0
65}