nx_bench_spectral.nx source
↩ module page · 124 lines · 3815 B
1// nx_bench_spectral.nx -- NishiLang twin of bench/xlang/spectral.c (spectral-norm,
2// Computer Language Benchmarks Game). FIRST FLOAT (f64) racing-bench workload,
3// proving the 2026-07-16 f64 codegen on a real 3rd-party task. N=100, published
4// answer 1.274219991 -> line 1 = eigenvalue*1e9 truncated (1274219991), line 2 =
5// us. f64 values ride as i64 bit-patterns in i64 arrays; scalars are `f64`.
6// Structurally identical scalar operations to the C twin (Newton sqrt, same
7// reduction order) so nx/gcc/clang agree bit-for-bit at 9-digit truncation.
8// license_tier: ORIGINAL No hw writes (Rule 26).
9// (nx_syscalls_x86_64.nx import REMOVED 2026-07-31, debt 1785528831: this file already
10// gets the canonical syscall layer via nx_clock.nx -> syscalls.nx, so importing the raw-x86
11// module too put TWO syscall layers in one TU -- every wrapper twice, numbering picked by
12// definition ORDER, silently.)
13import "nx_clock.nx"
14
15const SN_N: i64 = 100
16
17func sp_emit_i64(fd: i64, n: i64) -> i64 {
18 let scratch: *u8 = sys_mmap(32)
19 var v: i64 = n
20 var neg: i64 = 0
21 if v < 0 { neg = 1; v = 0 - v }
22 var k: i64 = 0
23 if v == 0 { scratch[0] = 0x30 as u8; k = 1 }
24 while v > 0 { scratch[k] = (0x30 + (v - (v / 10) * 10)) as u8; v = v / 10; k = k + 1 }
25 let rev: *u8 = sys_mmap(48)
26 var ro: i64 = 0
27 if neg == 1 { rev[0] = 0x2D as u8; ro = 1 }
28 var j: i64 = 0
29 while j < k { rev[ro + j] = scratch[k - 1 - j]; j = j + 1 }
30 rev[ro + k] = 0x0A as u8
31 sys_write(fd, rev, ro + k + 1)
32 return 0
33}
34
35// A(i,j) = 1 / (((i+j)(i+j+1))/2 + i + 1). Denominator is integer; convert then divide.
36func sp_eval_A(i: i64, j: i64) -> f64 {
37 let d: i64 = (((i + j) * (i + j + 1)) >> 1) + i + 1
38 return 1.0 / __f64_from_i64(d)
39}
40
41// Au[i] = sum_j A(i,j) * u[j]. u/Au are *i64 carriers of f64 bits.
42func sp_A_times_u(u: *i64, Au: *i64) -> i64 {
43 var i: i64 = 0
44 while i < SN_N {
45 var s: f64 = 0.0
46 var j: i64 = 0
47 while j < SN_N {
48 let uj: f64 = u[j] as f64
49 s = s + sp_eval_A(i, j) * uj
50 j = j + 1
51 }
52 Au[i] = s as i64
53 i = i + 1
54 }
55 return 0
56}
57
58// Au[i] = sum_j A(j,i) * u[j] (transpose).
59func sp_At_times_u(u: *i64, Au: *i64) -> i64 {
60 var i: i64 = 0
61 while i < SN_N {
62 var s: f64 = 0.0
63 var j: i64 = 0
64 while j < SN_N {
65 let uj: f64 = u[j] as f64
66 s = s + sp_eval_A(j, i) * uj
67 j = j + 1
68 }
69 Au[i] = s as i64
70 i = i + 1
71 }
72 return 0
73}
74
75// AtAu = A^t (A u), via a scratch vector.
76func sp_AtA_times_u(u: *i64, AtAu: *i64, tmp: *i64) -> i64 {
77 sp_A_times_u(u, tmp)
78 sp_At_times_u(tmp, AtAu)
79 return 0
80}
81
82// Newton sqrt on f64 -- matches the C twin exactly (avoids libm rounding drift).
83func sp_sqrt(x: f64) -> f64 {
84 var g: f64 = x
85 var k: i64 = 0
86 while k < 60 { g = 0.5 * (g + x / g); k = k + 1 }
87 return g
88}
89
90func main() -> i64 {
91 let u: *i64 = sys_mmap(SN_N * 8) as *i64
92 let v: *i64 = sys_mmap(SN_N * 8) as *i64
93 let tmp: *i64 = sys_mmap(SN_N * 8) as *i64
94 let start: i64 = nx_clock_monotonic_ns()
95
96 let one: f64 = 1.0
97 var i: i64 = 0
98 while i < SN_N { u[i] = one as i64; i = i + 1 }
99
100 var it: i64 = 0
101 while it < 10 {
102 sp_AtA_times_u(u, v, tmp)
103 sp_AtA_times_u(v, u, tmp)
104 it = it + 1
105 }
106
107 var vBv: f64 = 0.0
108 var vv: f64 = 0.0
109 var q: i64 = 0
110 while q < SN_N {
111 let uq: f64 = u[q] as f64
112 let vq: f64 = v[q] as f64
113 vBv = vBv + uq * vq
114 vv = vv + vq * vq
115 q = q + 1
116 }
117 let eval: f64 = sp_sqrt(vBv / vv)
118
119 let end: i64 = nx_clock_monotonic_ns()
120 let us: i64 = (end - start) / 1000
121 sp_emit_i64(1, __f64_to_i64(eval * 1000000000.0))
122 sp_emit_i64(1, us)
123 return 0
124}