code wiki / _hdl_build / nx_f32_hw_matmul_mac4.nx

nx_f32_hw_matmul_mac4.nx source

↩ module page · 97 lines · 4438 B

1// nx_f32_hw_matmul_mac4.nx -- ILP matmul: 4 independent accumulators hide the addss latency. 2// 3// The single-acc inner loop serializes on `acc` (each addss waits ~4 cyc for the previous). Four 4// independent accumulator chains let the CPU pipeline them -> up to ~4x if latency-bound -- and it is 5// PURE .nx (scalar SSE via nx_f32_hw), so NO nx_cc change, NO rebuild, ZERO build risk. The safe win 6// to bank BEFORE the risky packed-SIMD compiler surgery (which adds 4 lanes/instruction on top). 7// 8// Sum order differs from sequential, so f32 results can differ by ROUNDING -- but for small ints 9// (exact) it is bit-identical to nx_f32_matmul/nx_f32_hw_matmul, so the KAT cross-checks exactly. 10// Sovereign: imports nx_f32_hw_matmul (-> nx_f32_hw SSE) + nx_syscalls. license_tier: ORIGINAL 11import "nx_f32_hw_matmul.nx" 12import "nx_syscalls.nx" 13const NX_MAGIC_32768: i64 = 32768 14 15const NX_MAC4_OK: i64 = 0 16const NX_MAC4_ERR: i64 = 1 17 18// C[m,n] = A[m,k] @ B[k,n], hardware f32, 4 accumulator chains. 19func nx_f32_hw_matmul_mac4(a: *i64, b: *i64, c: *i64, m: i64, k: i64, n: i64) -> i64 { 20 if m <= 0 { return NX_MAC4_ERR } 21 if k <= 0 { return NX_MAC4_ERR } 22 if n <= 0 { return NX_MAC4_ERR } 23 var i: i64 = 0 24 while i < m { 25 var j: i64 = 0 26 while j < n { 27 var acc0: i64 = f32_of(0); var acc1: i64 = f32_of(0) 28 var acc2: i64 = f32_of(0); var acc3: i64 = f32_of(0) 29 let arow: i64 = i * k 30 var l: i64 = 0 31 while l + 4 <= k { 32 acc0 = f32_add(acc0, f32_mul(a[arow + l], b[l * n + j])) 33 acc1 = f32_add(acc1, f32_mul(a[arow + l + 1], b[(l + 1) * n + j])) 34 acc2 = f32_add(acc2, f32_mul(a[arow + l + 2], b[(l + 2) * n + j])) 35 acc3 = f32_add(acc3, f32_mul(a[arow + l + 3], b[(l + 3) * n + j])) 36 l = l + 4 37 } 38 while l < k { acc0 = f32_add(acc0, f32_mul(a[arow + l], b[l * n + j])); l = l + 1 } 39 c[i * n + j] = f32_add(f32_add(acc0, acc1), f32_add(acc2, acc3)) 40 j = j + 1 41 } 42 i = i + 1 43 } 44 return NX_MAC4_OK 45} 46 47func mac4_time(which: i64, A: *i64, B: *i64, C: *i64, N: i64) -> i64 { 48 var reps: i64 = 1; var dt: i64 = 0; var go: i64 = 1 49 while go == 1 { 50 let t0: i64 = sys_now_ms() 51 var r: i64 = 0 52 while r < reps { 53 if which == 0 { nx_f32_hw_matmul(A, B, C, N, N, N) } else { nx_f32_hw_matmul_mac4(A, B, C, N, N, N) } 54 r = r + 1 55 } 56 let t1: i64 = sys_now_ms() 57 dt = t1 - t0 58 if dt >= 60 { go = 0 } else { if reps >= NX_MAGIC_32768 { go = 0 } else { reps = reps * 2 } } 59 } 60 if dt <= 0 { dt = 1 } 61 return (2 * N * N * N * reps) / (dt * 1000) 62} 63 64func main() -> i64 { 65 hmm_puts("=== ILP matmul: 4 accumulators (pure .nx, no compiler change) ===\n") 66 // ---- correctness KAT: mac4 == single-acc hw matmul, 8x8 small ints (exact) ---- 67 let N: i64 = 8 68 let A: *i64 = sys_mmap(N * N * 8) as *i64 69 let B: *i64 = sys_mmap(N * N * 8) as *i64 70 let C0: *i64 = sys_mmap(N * N * 8) as *i64 71 let C4: *i64 = sys_mmap(N * N * 8) as *i64 72 var i: i64 = 0 73 while i < N * N { A[i] = f32_of((i % 5) + 1); B[i] = f32_of((i % 7) + 1); i = i + 1 } 74 nx_f32_hw_matmul(A, B, C0, N, N, N) 75 nx_f32_hw_matmul_mac4(A, B, C4, N, N, N) 76 var same: i64 = 1; i = 0 77 while i < N * N { if C0[i] != C4[i] { same = 0; i = N * N } else { i = i + 1 } } 78 if same == 1 { hmm_puts(" KAT mac4==single (8x8 exact): PASS\n") } else { hmm_puts(" KAT mac4==single: FAIL\n"); sys_exit(1); return 1 } 79 80 // ---- perf: single-acc vs 4-acc ---- 81 var nn: i64 = 64 82 while nn <= 128 { 83 let A2: *i64 = sys_mmap(nn * nn * 8) as *i64 84 let B2: *i64 = sys_mmap(nn * nn * 8) as *i64 85 let C2: *i64 = sys_mmap(nn * nn * 8) as *i64 86 var z: i64 = 0 87 while z < nn * nn { A2[z] = f32_of(2); B2[z] = f32_of(2); z = z + 1 } 88 let s: i64 = mac4_time(0, A2, B2, C2, nn) 89 let m4: i64 = mac4_time(1, A2, B2, C2, nn) 90 var sp: i64 = 0; if s > 0 { sp = m4 * 100 / s } 91 hmm_puts(" N="); hmm_putn(nn) 92 hmm_puts(" single="); hmm_putn(s); hmm_puts(" MFLOP/s 4-acc="); hmm_putn(m4); hmm_puts(" MFLOP/s ratio="); hmm_putn(sp); hmm_puts("%\n") 93 nn = nn * 2 94 } 95 hmm_puts(" (ratio>100% => ILP latency-hiding works in this codegen; pure .nx, zero build risk.)\n") 96 sys_exit(0); return 0 97}