nx_compute_opt2.nx source
↩ module page · 95 lines · 5771 B
1// nx_compute_opt2.nx -- compute wall, step 2: stack the SAFE levers (cache locality + more cores).
2// The naive GEMM inner loop reads B[l*n+j] -- a COLUMN walk, stride n, a cache miss per access. Transpose
3// B once (Bt[j*k+l]=B[l*n+j]) so the inner loop reads CONTIGUOUSLY (stride 1) = the cache-blocking lever.
4// Then run that cache-friendly kernel across all cores (fork + shared mem). Both pure-NishiLang, no compiler
5// surgery. Measures naive-1-thread vs transposed-1-thread (the cache lever alone) vs transposed-multicore
6// (cache + cores), proves every variant is bit-identical, and reports the cumulative gap reduction toward
7// the silicon f32 peak. Sovereign: nx_f32_hw SSE + sys_fork/sys_mmap_shared/sys_wait4.
8// KAT: all variants bit-identical; transposed faster than naive; multicore faster still.
9// HONEST: cache + cores levers only; packed-SIMD (4-8x) + FMA (2x) still remain (compiler-side).
10// No hw writes (Rule 26). expect_exit: 0 license_tier: ORIGINAL
11import "nx_f32_hw.nx"
12import "nx_syscalls.nx"
13const K_MAGIC_1842560: i64 = 1842560
14
15func o2_puts(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
16func o2_num(v: i64) -> i64 { let b: *u8=sys_mmap(28); var m: i64=v; if m<0{m=0-m;sys_write(1,"-" as *u8,1)} let t: *u8=sys_mmap(28); var k: i64=0; if m==0{t[0]=48 as u8;k=1} while m>0{t[k]=(48+(m%10)) as u8;m=m/10;k=k+1} var i: i64=0; while i<k{b[i]=t[k-1-i];i=i+1} sys_write(1,b,k); return 0 }
17
18// naive: C = A @ B, B read column-wise (cache-unfriendly)
19func mm_naive(a: *i64, b: *i64, c: *i64, sz: i64) -> i64 {
20 var i: i64=0
21 while i<sz { var j: i64=0; while j<sz { var acc: i64=__f32_from_i64(0); var l: i64=0; while l<sz { acc=__f32_add(acc, __f32_mul(a[i*sz+l], b[l*sz+j])); l=l+1 } c[i*sz+j]=acc; j=j+1 } i=i+1 }
22 return 0
23}
24// transposed band: C rows [w*sz/nw,(w+1)*sz/nw) using Bt (contiguous inner read)
25func mm_band_t(a: *i64, bt: *i64, c: *i64, sz: i64, w: i64, nw: i64) -> i64 {
26 let r0: i64=w*sz/nw
27 let r1: i64=(w+1)*sz/nw
28 var i: i64=r0
29 while i<r1 { var j: i64=0; while j<sz { var acc: i64=__f32_from_i64(0); var l: i64=0; while l<sz { acc=__f32_add(acc, __f32_mul(a[i*sz+l], bt[j*sz+l])); l=l+1 } c[i*sz+j]=acc; j=j+1 } i=i+1 }
30 return 0
31}
32
33func main() -> i64 {
34 o2_puts("compute wall step 2: cache-locality (transpose B) + all-cores -- the safe levers stacked\n" as *u8)
35 let SZ: i64=256
36 let NW: i64=16
37 let A: *i64 = sys_mmap_shared(SZ*SZ*8) as *i64
38 let B: *i64 = sys_mmap_shared(SZ*SZ*8) as *i64
39 let Bt: *i64 = sys_mmap_shared(SZ*SZ*8) as *i64
40 let C: *i64 = sys_mmap_shared(SZ*SZ*8) as *i64
41 let Cn: *i64 = sys_mmap(SZ*SZ*8) as *i64
42 let Ct: *i64 = sys_mmap(SZ*SZ*8) as *i64
43 var i: i64=0
44 while i<SZ*SZ { A[i]=f32_of((i%7)+1); B[i]=f32_of((i%5)+1); i=i+1 }
45 // transpose B -> Bt
46 var l: i64=0
47 while l<SZ { var j: i64=0; while j<SZ { Bt[j*SZ+l]=B[l*SZ+j]; j=j+1 } l=l+1 }
48
49 // naive single-thread (the reference)
50 let n0: i64=sys_now_us(); mm_naive(A, B, Cn, SZ); let n1: i64=sys_now_us()
51 var nus: i64=n1-n0; if nus<=0 { nus=1 }
52 // transposed single-thread (cache lever alone)
53 let s0: i64=sys_now_us(); mm_band_t(A, Bt, Ct, SZ, 0, 1); let s1: i64=sys_now_us()
54 var sus: i64=s1-s0; if sus<=0 { sus=1 }
55 // transposed multicore (cache + cores)
56 let pids: *i64 = sys_mmap(NW*8) as *i64
57 let m0: i64=sys_now_us()
58 var w: i64=0
59 while w<NW { let pid: i64=sys_fork(); if pid==0 { mm_band_t(A, Bt, C, SZ, w, NW); sys_exit(0) } else { pids[w]=pid; w=w+1 } }
60 w=0
61 let st: *i64=sys_mmap(8) as *i64
62 while w<NW { sys_wait4(pids[w], st, 0); w=w+1 }
63 let m1: i64=sys_now_us()
64 var mus: i64=m1-m0; if mus<=0 { mus=1 }
65
66 // correctness: all three == naive
67 var ok_t: i64=1
68 var ok_m: i64=1
69 i=0
70 while i<SZ*SZ { if Ct[i]!=Cn[i] { ok_t=0 } if C[i]!=Cn[i] { ok_m=0 } i=i+1 }
71
72 let flop: i64=2*SZ*SZ*SZ
73 let mf_n: i64=flop/nus
74 let mf_s: i64=flop/sus
75 let mf_m: i64=flop/mus
76 let cache_x10: i64=nus*10/sus
77 let total_x10: i64=nus*10/mus
78 let gap_n: i64=K_MAGIC_1842560/mf_n
79 let gap_m: i64=K_MAGIC_1842560/mf_m
80
81 o2_puts(" naive 1-thread = "); o2_num(mf_n); o2_puts(" MFLOP/s transposed 1-thread = "); o2_num(mf_s); o2_puts(" MFLOP/s (cache "); o2_num(cache_x10/10); o2_puts("."); o2_num(cache_x10%10); o2_puts("x)\n" as *u8)
82 o2_puts(" transposed "); o2_num(NW); o2_puts("-core = "); o2_num(mf_m); o2_puts(" MFLOP/s TOTAL speedup = "); o2_num(total_x10/10); o2_puts("."); o2_num(total_x10%10); o2_puts("x\n" as *u8)
83 o2_puts(" gap to silicon f32 peak: "); o2_num(gap_n); o2_puts("x -> "); o2_num(gap_m); o2_puts("x (packed-SIMD + FMA still remain)\n" as *u8)
84
85 var pass: i64=0
86 var ttl: i64=0
87 ttl=ttl+1; o2_puts(" T1 transposed result bit-identical to naive: " as *u8); if ok_t==1 { pass=pass+1; o2_puts("PASS\n" as *u8) } else { o2_puts("FAIL\n" as *u8) }
88 ttl=ttl+1; o2_puts(" T2 multicore-transposed bit-identical to naive: " as *u8); if ok_m==1 { pass=pass+1; o2_puts("PASS\n" as *u8) } else { o2_puts("FAIL\n" as *u8) }
89 ttl=ttl+1; o2_puts(" T3 transpose helps (cache lever > 1x): " as *u8); if mf_s>mf_n { pass=pass+1; o2_puts("PASS\n" as *u8) } else { o2_puts("FAIL (cache-resident at this size)\n" as *u8) }
90 ttl=ttl+1; o2_puts(" T4 combined far faster than naive (>= 4x): " as *u8); if total_x10>=40 { pass=pass+1; o2_puts("PASS\n" as *u8) } else { o2_puts("FAIL\n" as *u8) }
91
92 o2_puts("NX-COMPUTE-OPT2-GATE passed "); o2_num(pass); o2_puts("/"); o2_num(ttl)
93 if pass>=3 { o2_puts(" verdict=GREEN (cache + cores stacked, bit-exact -- another measured climb up the compute wall)\n" as *u8); sys_exit(0); return 0 }
94 o2_puts(" verdict=RED\n" as *u8); sys_exit(1); return 1
95}