code wiki / _hdl_build / nx_f32_intrin_matmul.nx

nx_f32_intrin_matmul.nx source

↩ module page · 103 lines · 4633 B

1// nx_f32_intrin_matmul.nx -- is the matmul bottleneck CALL OVERHEAD (wrapper) vs the inline intrinsic? 2// 3// nx_f32_hw.f32_add/f32_mul are wrapper FUNCTIONS around the __f32_* intrinsics. If nx_cc doesn't 4// inline them, every MAC pays call/ret overhead. This tests the matmul written with __f32_add/ 5// __f32_mul/__f32_from_i64 DIRECTLY (the compiler emits addss/mulss INLINE -- no call) vs the wrapper 6// version, plus a direct+4-accumulator variant. Pure .nx, NO compiler change, ZERO build risk. 7// Sovereign: imports nx_f32_hw_matmul (wrapper baseline) + nx_syscalls. license_tier: ORIGINAL 8import "nx_f32_hw_matmul.nx" 9import "nx_syscalls.nx" 10const K_MAGIC_32768: i64 = 32768 11 12// matmul with the intrinsics emitted INLINE (no wrapper call), single accumulator. 13func intrin_matmul(a: *i64, b: *i64, c: *i64, m: i64, k: i64, n: i64) -> i64 { 14 var i: i64 = 0 15 while i < m { 16 var j: i64 = 0 17 while j < n { 18 var acc: i64 = __f32_from_i64(0) 19 let arow: i64 = i * k 20 var l: i64 = 0 21 while l < k { acc = __f32_add(acc, __f32_mul(a[arow + l], b[l * n + j])); l = l + 1 } 22 c[i * n + j] = acc 23 j = j + 1 24 } 25 i = i + 1 26 } 27 return 0 28} 29 30// inline intrinsics + 4 accumulators (ILP on top of no-call). 31func intrin_matmul_mac4(a: *i64, b: *i64, c: *i64, m: i64, k: i64, n: i64) -> i64 { 32 var i: i64 = 0 33 while i < m { 34 var j: i64 = 0 35 while j < n { 36 var a0: i64 = __f32_from_i64(0); var a1: i64 = __f32_from_i64(0) 37 var a2: i64 = __f32_from_i64(0); var a3: i64 = __f32_from_i64(0) 38 let arow: i64 = i * k 39 var l: i64 = 0 40 while l + 4 <= k { 41 a0 = __f32_add(a0, __f32_mul(a[arow + l], b[l * n + j])) 42 a1 = __f32_add(a1, __f32_mul(a[arow + l + 1], b[(l + 1) * n + j])) 43 a2 = __f32_add(a2, __f32_mul(a[arow + l + 2], b[(l + 2) * n + j])) 44 a3 = __f32_add(a3, __f32_mul(a[arow + l + 3], b[(l + 3) * n + j])) 45 l = l + 4 46 } 47 while l < k { a0 = __f32_add(a0, __f32_mul(a[arow + l], b[l * n + j])); l = l + 1 } 48 c[i * n + j] = __f32_add(__f32_add(a0, a1), __f32_add(a2, a3)) 49 j = j + 1 50 } 51 i = i + 1 52 } 53 return 0 54} 55 56func it_time(which: i64, A: *i64, B: *i64, C: *i64, N: i64) -> i64 { 57 var reps: i64 = 1; var dt: i64 = 0; var go: i64 = 1 58 while go == 1 { 59 let t0: i64 = sys_now_ms() 60 var r: i64 = 0 61 while r < reps { 62 if which == 0 { nx_f32_hw_matmul(A, B, C, N, N, N) } 63 else { if which == 1 { intrin_matmul(A, B, C, N, N, N) } else { intrin_matmul_mac4(A, B, C, N, N, N) } } 64 r = r + 1 65 } 66 let t1: i64 = sys_now_ms() 67 dt = t1 - t0 68 if dt >= 60 { go = 0 } else { if reps >= K_MAGIC_32768 { go = 0 } else { reps = reps * 2 } } 69 } 70 if dt <= 0 { dt = 1 } 71 return (2 * N * N * N * reps) / (dt * 1000) 72} 73 74func main() -> i64 { 75 hmm_puts("=== matmul: wrapper vs INLINE intrinsic vs inline+4acc (pure .nx) ===\n") 76 // KAT: intrin 2x2 == [[19,22],[43,50]] 77 let A: *i64 = sys_mmap(4 * 8) as *i64 78 A[0] = __f32_from_i64(1); A[1] = __f32_from_i64(2); A[2] = __f32_from_i64(3); A[3] = __f32_from_i64(4) 79 let B: *i64 = sys_mmap(4 * 8) as *i64 80 B[0] = __f32_from_i64(5); B[1] = __f32_from_i64(6); B[2] = __f32_from_i64(7); B[3] = __f32_from_i64(8) 81 let C: *i64 = sys_mmap(4 * 8) as *i64 82 intrin_matmul(A, B, C, 2, 2, 2) 83 var ok: i64 = 0 84 if __f32_to_i64(C[0]) == 19 { if __f32_to_i64(C[1]) == 22 { if __f32_to_i64(C[2]) == 43 { if __f32_to_i64(C[3]) == 50 { ok = 1 } } } } 85 if ok == 1 { hmm_puts(" KAT intrin 2x2 = [[19,22],[43,50]]: PASS\n") } else { hmm_puts(" KAT intrin: FAIL\n"); sys_exit(1); return 1 } 86 87 var nn: i64 = 64 88 while nn <= 128 { 89 let A2: *i64 = sys_mmap(nn * nn * 8) as *i64 90 let B2: *i64 = sys_mmap(nn * nn * 8) as *i64 91 let C2: *i64 = sys_mmap(nn * nn * 8) as *i64 92 var z: i64 = 0 93 while z < nn * nn { A2[z] = __f32_from_i64(2); B2[z] = __f32_from_i64(2); z = z + 1 } 94 let w: i64 = it_time(0, A2, B2, C2, nn) 95 let d: i64 = it_time(1, A2, B2, C2, nn) 96 let d4: i64 = it_time(2, A2, B2, C2, nn) 97 hmm_puts(" N="); hmm_putn(nn) 98 hmm_puts(" wrapper="); hmm_putn(w); hmm_puts(" inline="); hmm_putn(d); hmm_puts(" inline+4acc="); hmm_putn(d4); hmm_puts(" MFLOP/s\n") 99 nn = nn * 2 100 } 101 hmm_puts(" (inline >> wrapper => call overhead was the wall; pure .nx, zero build risk.)\n") 102 sys_exit(0); return 0 103}