code wiki / (root) / nx_compute_opt2.nx

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}