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}