nx_p256_fieldmul_mulx_bench.nx source
↩ module page · 60 lines · 3256 B
1// nx_p256_fieldmul_mulx_bench.nx -- field-mul-level speed: production p256_field_mul (u256_mul_wide
2// 8x32 + Solinas) vs p256_field_mul_mulx_s (fused __mul256_wide + the SAME Solinas, reused scratch).
3// The Solinas reduction is IDENTICAL in both, so the delta isolates the multiply. Dependent-chain
4// harness (DCE/hoist-proof). HONEST caveat printed: the mulx variant reuses scratch (the optimized
5// form); the production field_mul mmaps its product buffer per call -- so this reflects a real
6// drop-in optimized field_mul, and the ratio shows how the 53x primitive win survives Amdahl once
7// the common (unchanged) reduce cost is included. expect_exit: 0 license_tier: ORIGINAL
8import "nx_p256_fieldmul_mulx.nx"
9const N_MAGIC_1000000000: i64 = 1000000000
10
11const N_ITER: i64 = 3000000
12
13func bp(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 }
14func bn(v: i64) -> i64 {
15 let t: *u8 = sys_mmap(28); var m: i64 = v; if m < 0 { sys_write(1, "-" as *u8, 1); m = 0 - m }
16 let b: *u8 = sys_mmap(28); var k: i64 = 0; if m == 0 { t[0] = 48 as u8; k = 1 }
17 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
18 var i: i64 = 0; while i < k { b[i] = t[k-1-i]; i = i + 1 } sys_write(1, b, k); return 0
19}
20func now_ns(ts: *i64) -> i64 { __syscall(SYS_CLOCK_GETTIME, 1, ts as i64, 0, 0, 0, 0); return ts[0] * N_MAGIC_1000000000 + ts[1] }
21
22func main() -> i64 {
23 let a: *i64 = sys_mmap(8 * 8) as *i64
24 let b: *i64 = sys_mmap(8 * 8) as *i64
25 let r: *i64 = sys_mmap(8 * 8) as *i64
26 let ts: *i64 = sys_mmap(32) as *i64
27 // scratch for the mulx variant (reused; no per-call mmap)
28 let a64: *i64 = sys_mmap(4 * 8) as *i64
29 let b64: *i64 = sys_mmap(4 * 8) as *i64
30 let prod: *i64 = sys_mmap(8 * 8) as *i64
31 let c16: *i64 = sys_mmap(16 * 8) as *i64
32
33 var k: i64 = 0
34 while k < 8 { a[k] = (0x1111111100000001 * (k + 1)) & 0xffffffff; b[k] = (0xdeadbeef12345678 * (k + 3)) & 0xffffffff; k = k + 1 }
35
36 bp("=== nx_p256_fieldmul_mulx_bench: production field_mul vs fused-mulx field_mul (same Solinas) ===\n" as *u8)
37
38 // production p256_field_mul
39 var accp: i64 = 0
40 let t0: i64 = now_ns(ts)
41 k = 0
42 while k < N_ITER { a[0] = (a[0] ^ accp) & 0xffffffff; p256_field_mul(r, a, b); accp = accp ^ r[0] ^ r[3] ^ r[7]; k = k + 1 }
43 let prod_ns: i64 = now_ns(ts) - t0
44
45 a[0] = 0x00000001
46 // fused-mulx field_mul (reused scratch)
47 var accm: i64 = 0
48 let t2: i64 = now_ns(ts)
49 k = 0
50 while k < N_ITER { a[0] = (a[0] ^ accm) & 0xffffffff; p256_field_mul_mulx_s(r, a, b, a64, b64, prod, c16); accm = accm ^ r[0] ^ r[3] ^ r[7]; k = k + 1 }
51 let mulx_ns: i64 = now_ns(ts) - t2
52
53 bn(N_ITER); bp(" iters\n" as *u8)
54 bp("production_field_mul = " as *u8); bn(prod_ns); bp(" (" as *u8); bn(prod_ns / N_ITER); bp(" ns/op)\n" as *u8)
55 bp("fused_mulx_field_mul = " as *u8); bn(mulx_ns); bp(" (" as *u8); bn(mulx_ns / N_ITER); bp(" ns/op)\n" as *u8)
56 if mulx_ns > 0 { bp("speedup_x100 = " as *u8); bn(prod_ns * 100 / mulx_ns); bp(" (100 = parity, >100 = fused faster)\n" as *u8)
57 }
58 bp("checksums accp=" as *u8); bn(accp); bp(" accm=" as *u8); bn(accm); bp(" (should be EQUAL = same computation)\n" as *u8)
59 return 0
60}