nx_p256_fieldmul_full_bench.nx source
↩ module page · 84 lines · 4623 B
1// nx_p256_fieldmul_full_bench.nx -- the FULLY-optimized P-256 field multiply: fused __mul256_wide
2// (MULX/ADCX/ADOX) multiply + deferred-carry fast Solinas reduce, vs the production p256_field_mul.
3// Both components are individually proven bit-exact; this verifies the COMPOSITION (correctness
4// pre-check) then times it. Dependent-chain harness (DCE/hoist-proof). Uses __mul256_wide ->
5// build via nx_cand_build_run (candidate compiler; zero live risk). expect_exit: 0 license_tier: ORIGINAL
6import "nx_p256_solinas_fast.nx" // _p256_solinas_reduce_fast + (base) p256_field_mul + constants
7const N_MAGIC_1000000000: i64 = 1000000000
8const N_MAGIC_6364136223846793005: i64 = 6364136223846793005
9const N_MAGIC_1442695040888963407: i64 = 1442695040888963407
10const N_MAGIC_2000: i64 = 2000
11
12const N_ITER: i64 = 3000000
13
14func bp(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 }
15func bn(v: i64) -> i64 {
16 let t: *u8 = sys_mmap(28); var m: i64 = v; if m < 0 { sys_write(1, "-" as *u8, 1); m = 0 - m }
17 let b: *u8 = sys_mmap(28); var k: i64 = 0; if m == 0 { t[0] = 48 as u8; k = 1 }
18 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
19 var i: i64 = 0; while i < k { b[i] = t[k-1-i]; i = i + 1 } sys_write(1, b, k); return 0
20}
21func 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] }
22func lcg(st: *i64) -> i64 { let x: i64 = st[0] * N_MAGIC_6364136223846793005 + N_MAGIC_1442695040888963407; st[0] = x; return x }
23
24// FULLY-optimized field-mul (scratch passed in; no per-call mmap).
25func p256_field_mul_full(out8: *i64, a: *i64, b: *i64, a64: *i64, b64: *i64, prod: *i64, c16: *i64) -> i64 {
26 var k: i64 = 0
27 while k < 4 { a64[k] = (a[2*k] & 0xffffffff) | ((a[2*k+1] & 0xffffffff) << 32); b64[k] = (b[2*k] & 0xffffffff) | ((b[2*k+1] & 0xffffffff) << 32); k = k + 1 }
28 __mul256_wide(prod, a64, b64)
29 k = 0
30 while k < 8 { c16[2*k] = prod[k] & 0xffffffff; c16[2*k+1] = (prod[k] >> 32) & 0xffffffff; k = k + 1 }
31 _p256_solinas_reduce_fast(out8, c16)
32 return 0
33}
34
35func main() -> i64 {
36 let a: *i64 = sys_mmap(8 * 8) as *i64
37 let b: *i64 = sys_mmap(8 * 8) as *i64
38 let r: *i64 = sys_mmap(8 * 8) as *i64
39 let r2: *i64 = sys_mmap(8 * 8) as *i64
40 let ts: *i64 = sys_mmap(32) as *i64
41 let a64: *i64 = sys_mmap(4 * 8) as *i64
42 let b64: *i64 = sys_mmap(4 * 8) as *i64
43 let prod: *i64 = sys_mmap(8 * 8) as *i64
44 let c16: *i64 = sys_mmap(16 * 8) as *i64
45
46 // ---- correctness pre-check: full == production over 2000 random field elements ----
47 let st: *i64 = sys_mmap(8) as *i64
48 st[0] = 0xb7e151628aed2a6b
49 var bad: i64 = 0
50 var n: i64 = 0
51 while n < N_MAGIC_2000 {
52 var k: i64 = 0; while k < 8 { a[k] = lcg(st) & 0xffffffff; b[k] = lcg(st) & 0xffffffff; k = k + 1 }
53 p256_field_mul(r, a, b)
54 p256_field_mul_full(r2, a, b, a64, b64, prod, c16)
55 k = 0; while k < 8 { if (r[k] & 0xffffffff) != (r2[k] & 0xffffffff) { bad = bad + 1 } k = k + 1 }
56 n = n + 1
57 }
58 bp("=== nx_p256_fieldmul_full_bench: production field_mul vs (mulx + fast-reduce) field_mul ===\n" as *u8)
59 if bad != 0 { bp("CORRECTNESS FAIL bad_limbs=" as *u8); bn(bad); bp("\n" as *u8); sys_exit(1); return 1 }
60 bp("correctness: full == production over 2000 vectors (GREEN)\n" as *u8)
61
62 var k: i64 = 0; while k < 8 { a[k] = (0x1111111100000001 * (k + 1)) & 0xffffffff; b[k] = (0xdeadbeef12345678 * (k + 3)) & 0xffffffff; k = k + 1 }
63 let a0i: i64 = a[0]
64
65 var accp: i64 = 0
66 let t0: i64 = now_ns(ts)
67 k = 0
68 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 }
69 let prod_ns: i64 = now_ns(ts) - t0
70
71 a[0] = a0i
72 var accf: i64 = 0
73 let t2: i64 = now_ns(ts)
74 k = 0
75 while k < N_ITER { a[0] = (a[0] ^ accf) & 0xffffffff; p256_field_mul_full(r, a, b, a64, b64, prod, c16); accf = accf ^ r[0] ^ r[3] ^ r[7]; k = k + 1 }
76 let full_ns: i64 = now_ns(ts) - t2
77
78 bn(N_ITER); bp(" iters\n" as *u8)
79 bp("production_field_mul = " as *u8); bn(prod_ns); bp(" (" as *u8); bn(prod_ns / N_ITER); bp(" ns/op)\n" as *u8)
80 bp("full_opt_field_mul = " as *u8); bn(full_ns); bp(" (" as *u8); bn(full_ns / N_ITER); bp(" ns/op)\n" as *u8)
81 if full_ns > 0 { bp("speedup_x100 = " as *u8); bn(prod_ns * 100 / full_ns); bp(" (100 = parity, >100 = full faster)\n" as *u8) }
82 bp("checksums accp=" as *u8); bn(accp); bp(" accf=" as *u8); bn(accf); bp(" (EQUAL = same computation)\n" as *u8)
83 return 0
84}