From a116b9c6492a661581f62a13183381dc85a502a8 Mon Sep 17 00:00:00 2001 From: vad Date: Tue, 15 Sep 2026 15:01:40 +0200 Subject: [PATCH] perf: Use explicit squaring chain for the x^5 S-box MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `Field::pow` is a generic binary exponentiation, costing 3 squarings + 2 multiplications for x^5. Replacing it with x^2 -> x^4 -> x^4 * x (2 squarings + 1 multiplication) speeds up hashing by up to ~47% for 1-2 inputs, degrading to ~2% at 12 inputs where the MDS dominates. Benchmarked on AMD EPYC 7H12 64-core with the following results. Before: ``` poseidon_bn254_x5_1 time: [22.458 µs 22.501 µs 22.554 µs] poseidon_bn254_x5_2 time: [33.189 µs 33.391 µs 33.602 µs] poseidon_bn254_x5_3 time: [44.983 µs 45.054 µs 45.130 µs] poseidon_bn254_x5_4 time: [63.479 µs 63.533 µs 63.596 µs] poseidon_bn254_x5_5 time: [83.178 µs 83.214 µs 83.247 µs] poseidon_bn254_x5_6 time: [112.51 µs 112.81 µs 113.17 µs] poseidon_bn254_x5_7 time: [142.82 µs 143.19 µs 143.65 µs] poseidon_bn254_x5_8 time: [175.62 µs 176.55 µs 177.72 µs] poseidon_bn254_x5_9 time: [203.31 µs 203.36 µs 203.42 µs] poseidon_bn254_x5_10 time: [275.25 µs 276.37 µs 277.65 µs] poseidon_bn254_x5_11 time: [296.42 µs 296.77 µs 297.26 µs] poseidon_bn254_x5_12 time: [374.93 µs 375.45 µs 376.07 µs] ``` After: ``` poseidon_bn254_x5_1 time: [15.333 µs 15.348 µs 15.367 µs] poseidon_bn254_x5_2 time: [24.189 µs 24.213 µs 24.244 µs] poseidon_bn254_x5_3 time: [35.364 µs 35.392 µs 35.427 µs] poseidon_bn254_x5_4 time: [53.129 µs 53.360 µs 53.639 µs] poseidon_bn254_x5_5 time: [71.739 µs 71.854 µs 71.995 µs] poseidon_bn254_x5_6 time: [98.231 µs 98.314 µs 98.407 µs] poseidon_bn254_x5_7 time: [128.92 µs 129.67 µs 130.74 µs] poseidon_bn254_x5_8 time: [163.55 µs 164.76 µs 166.04 µs] poseidon_bn254_x5_9 time: [190.60 µs 191.32 µs 192.08 µs] poseidon_bn254_x5_10 time: [256.79 µs 257.19 µs 257.65 µs] poseidon_bn254_x5_11 time: [282.91 µs 283.55 µs 284.33 µs] poseidon_bn254_x5_12 time: [367.30 µs 369.78 µs 372.52 µs] ``` | inputs | baseline | sbox opt | speedup | |--------|-----------|-----------|---------| | 1 | 22.50 µs | 15.35 µs | 1.47× | | 2 | 33.39 µs | 24.21 µs | 1.38× | | 3 | 45.05 µs | 35.39 µs | 1.27× | | 4 | 63.53 µs | 53.36 µs | 1.19× | | 5 | 83.21 µs | 71.85 µs | 1.16× | | 6 | 112.81 µs | 98.31 µs | 1.15× | | 7 | 143.19 µs | 129.67 µs | 1.10× | | 8 | 176.55 µs | 164.76 µs | 1.07× | | 9 | 203.36 µs | 191.32 µs | 1.06× | | 10 | 276.37 µs | 257.19 µs | 1.07× | | 11 | 296.77 µs | 283.55 µs | 1.05× | | 12 | 375.45 µs | 369.78 µs | 1.02× | --- light-poseidon/src/lib.rs | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/light-poseidon/src/lib.rs b/light-poseidon/src/lib.rs index 6b51336..d3a9d02 100644 --- a/light-poseidon/src/lib.rs +++ b/light-poseidon/src/lib.rs @@ -341,16 +341,33 @@ impl Poseidon { }); } + /// Raises `a` to the S-box exponent. `Field::pow` is a generic binary + /// exponentiation (3 squarings + 2 multiplications for x^5), so the + /// common exponents get explicit, shorter squaring/multiplication chains. + #[inline(always)] + fn sbox(a: F, alpha: u64) -> F { + match alpha { + 5 => { + let x2 = a.square(); + let x4 = x2.square(); + x4 * a + } + 4 => a.square().square(), + _ => a.pow([alpha]), + } + } + #[inline(always)] fn apply_sbox_full(&mut self) { + let alpha = self.params.alpha; self.state.iter_mut().for_each(|a| { - *a = a.pow([self.params.alpha]); + *a = Self::sbox(*a, alpha); }); } #[inline(always)] fn apply_sbox_partial(&mut self) { - self.state[0] = self.state[0].pow([self.params.alpha]); + self.state[0] = Self::sbox(self.state[0], self.params.alpha); } #[inline(always)]