code wiki / (root) / nx_p256_fieldmul_mulx_bench.nx

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}