From 373be35afc3aeb861c817eb8813e4ce76b65262c Mon Sep 17 00:00:00 2001 From: MauroFab Date: Wed, 18 Mar 2026 09:31:33 -0300 Subject: [PATCH] opt: parallelize FRI fold_evaluations_in_place with rayon The FRI fold loop was sequential over half the evaluation points. Parallelize with rayon par_iter for the first few layers where domain_size is large enough to benefit. --- crypto/stark/src/fri/fri_functions.rs | 42 ++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 8 deletions(-) diff --git a/crypto/stark/src/fri/fri_functions.rs b/crypto/stark/src/fri/fri_functions.rs index acf319c9d..09ea2fc18 100644 --- a/crypto/stark/src/fri/fri_functions.rs +++ b/crypto/stark/src/fri/fri_functions.rs @@ -54,16 +54,42 @@ pub fn fold_evaluations_in_place, E: IsField>( evals: &mut Vec>, zeta: &FieldElement, inv_twiddles: &[FieldElement], -) { +) where + FieldElement: Send + Sync, + FieldElement: Sync, +{ let half = evals.len() / 2; - for j in 0..half { - let lo = &evals[2 * j]; - let hi = &evals[2 * j + 1]; - let sum = lo + hi; - let diff = lo - hi; - evals[j] = &sum + &(&inv_twiddles[j] * &(zeta * &diff)); + + #[cfg(feature = "parallel")] + { + use rayon::prelude::*; + // Parallel fold: split evals into pairs, compute folded value for each. + // Write results into a new Vec to avoid aliasing (evals[j] overlaps evals[2*j]). + let folded: Vec> = (0..half) + .into_par_iter() + .map(|j| { + let lo = &evals[2 * j]; + let hi = &evals[2 * j + 1]; + let sum = lo + hi; + let diff = lo - hi; + &sum + &(&inv_twiddles[j] * &(zeta * &diff)) + }) + .collect(); + evals.truncate(half); + evals[..half].clone_from_slice(&folded); + } + + #[cfg(not(feature = "parallel"))] + { + for j in 0..half { + let lo = &evals[2 * j]; + let hi = &evals[2 * j + 1]; + let sum = lo + hi; + let diff = lo - hi; + evals[j] = &sum + &(&inv_twiddles[j] * &(zeta * &diff)); + } + evals.truncate(half); } - evals.truncate(half); } /// Compute inverse twiddle factors for evaluation-form FRI folding.