code wiki / (root) / nx_p256_fieldmul_full_bench.nx

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}