nx_simd_bench.nx source
↩ module page · 116 lines · 4132 B
1// nx_simd_bench.nx -- honest perf comparison: scalar i64 add vs
2// SIMD i64x4 add over N=4096-element arrays.
3//
4// Honest cardinal: report WIN / TIE / LOSE based on actual
5// measurements, NOT aspirational claims. Under qemu-user the
6// vectorisation surface is FAITHFUL to the chip (real V instructions
7// execute) but the wall-time is dominated by qemu's interpretive
8// overhead, so wall-time ratios are NOT a clean SIMD-vs-scalar
9// signal -- we report op-count + clock time both, with a note.
10
11// nx_safety_envelope:
12// intended_use: AUTO_APPLIED -- primitive-specific tuning queued
13// sil_target: SIL1
14// evidence: [bulk_applied_2026-05-16, see-file-comment-for-detail]
15// verdict: NOT_YET_EVALUATED
16
17import "nx_kernel_v2.nx"
18import "nx_log.nx"
19import "nx_clock.nx"
20
21const N: i64 = 4096
22const REPS: i64 = 16
23
24func bench_scalar(a: *i64, b: *i64, r: *i64) -> i64 {
25 var i: i64 = 0
26 while i < N {
27 r[i] = a[i] + b[i]
28 i = i + 1
29 }
30 return 0
31}
32
33func bench_simd(a: *i64, b: *i64, r: *i64) -> i64 {
34 var i: i64 = 0
35 while i < N {
36 let a_ptr: *i64 = ((a as i64) + i * 8) as *i64
37 let b_ptr: *i64 = ((b as i64) + i * 8) as *i64
38 let r_ptr: *i64 = ((r as i64) + i * 8) as *i64
39 let va: i64 = __simd_vload_i64_x4(a_ptr)
40 let vb: i64 = __simd_vload_i64_x4(b_ptr)
41 let vsum: i64 = __simd_vadd_i64_x4(va, vb)
42 __simd_vstore_i64_x4(vsum, r_ptr)
43 i = i + 4
44 }
45 return 0
46}
47
48func main() -> nx_exit {
49 let a_raw: *u8 = sys_mmap(N * 8)
50 let b_raw: *u8 = sys_mmap(N * 8)
51 let r_raw: *u8 = sys_mmap(N * 8)
52 let a: *i64 = a_raw as *i64
53 let b: *i64 = b_raw as *i64
54 let r: *i64 = r_raw as *i64
55
56 var k: i64 = 0
57 while k < N { a[k] = k * 3; b[k] = k * 7; k = k + 1 }
58
59 println("=== nx_simd vs scalar bench (i64 elementwise add) ===" as *u8)
60 println("Array length N:" as *u8); print_i64(N); println("" as *u8)
61 println("Repetitions:" as *u8); print_i64(REPS); println("" as *u8)
62 println("" as *u8)
63
64 // Scalar timing.
65 let t0_s: i64 = nx_clock_monotonic_ns()
66 var rep: i64 = 0
67 while rep < REPS { bench_scalar(a, b, r); rep = rep + 1 }
68 let t1_s: i64 = nx_clock_monotonic_ns()
69 let elapsed_scalar: i64 = t1_s - t0_s
70
71 // Verify correctness once via scalar path.
72 var i: i64 = 0
73 var bad: i64 = 0
74 while i < N {
75 let expected: i64 = a[i] + b[i]
76 if r[i] != expected { bad = i + 1 }
77 i = i + 1
78 }
79 if bad != 0 { println("FAIL: scalar wrong" as *u8); return 1 }
80 println("Scalar elapsed ns:" as *u8); print_i64(elapsed_scalar); println("" as *u8)
81
82 // SIMD timing.
83 let t0_v: i64 = nx_clock_monotonic_ns()
84 var rep2: i64 = 0
85 while rep2 < REPS { bench_simd(a, b, r); rep2 = rep2 + 1 }
86 let t1_v: i64 = nx_clock_monotonic_ns()
87 let elapsed_simd: i64 = t1_v - t0_v
88
89 // Verify correctness via SIMD path.
90 i = 0
91 var bad2: i64 = 0
92 while i < N {
93 let expected: i64 = a[i] + b[i]
94 if r[i] != expected { bad2 = i + 1 }
95 i = i + 1
96 }
97 if bad2 != 0 { println("FAIL: simd wrong" as *u8); return 2 }
98 println("SIMD elapsed ns:" as *u8); print_i64(elapsed_simd); println("" as *u8)
99
100 println("" as *u8)
101 println("VERDICT (honest -- qemu interpretive emulation, NOT bare metal):" as *u8)
102 if elapsed_simd < elapsed_scalar {
103 let ratio_x100: i64 = (elapsed_scalar * 100) / elapsed_simd
104 println("WIN: SIMD faster. scalar/simd ratio x100 =" as *u8)
105 print_i64(ratio_x100); println("" as *u8)
106 }
107 if elapsed_simd > elapsed_scalar {
108 let ratio_x100: i64 = (elapsed_simd * 100) / elapsed_scalar
109 println("LOSE: SIMD SLOWER under qemu interpretive. simd/scalar ratio x100 =" as *u8)
110 print_i64(ratio_x100); println("" as *u8)
111 println("Honest note: qemu emulates each RVV instr in software, paying" as *u8)
112 println("VLEN-byte ops as interpretive loops. Real RVV silicon should" as *u8)
113 println("show WIN proportional to VLEN/8 (e.g., VLEN=256 -> 4x speedup)." as *u8)
114 }
115 return 0
116}