From a359481886232c9e7fdb52ab7c547d7caf7f1148 Mon Sep 17 00:00:00 2001 From: kilic Date: Mon, 19 Feb 2024 11:41:24 +0300 Subject: [PATCH] bring back old implementation postfix new one as `_independent_points` --- src/msm.rs | 103 +++++++++++++++++++++++++++++++---------------------- 1 file changed, 61 insertions(+), 42 deletions(-) diff --git a/src/msm.rs b/src/msm.rs index 3d6c1b81..25af9711 100644 --- a/src/msm.rs +++ b/src/msm.rs @@ -8,6 +8,8 @@ use rayon::iter::{ IndexedParallelIterator, IntoParallelRefIterator, IntoParallelRefMutIterator, ParallelIterator, }; +const BATCH_SIZE: usize = 64; + fn get_booth_index(window_index: usize, window_size: usize, el: &[u8]) -> i32 { // Booth encoding: // * step by `window` size @@ -222,7 +224,7 @@ impl BucketAffine { struct Schedule { buckets: Vec>, - set: Vec, + set: [SchedulePoint; BATCH_SIZE], ptr: usize, } @@ -244,10 +246,16 @@ impl SchedulePoint { } impl Schedule { - fn new(batch_size: usize, c: usize) -> Self { + fn new(c: usize) -> Self { + let set = (0..BATCH_SIZE) + .map(|_| SchedulePoint::default()) + .collect::>() + .try_into() + .unwrap(); + Self { buckets: vec![BucketAffine::None; 1 << (c - 1)], - set: vec![SchedulePoint::default(); batch_size], + set, ptr: 0, } } @@ -351,9 +359,48 @@ pub fn multiexp_serial(coeffs: &[C::Scalar], bases: &[C], acc: & } } +/// Performs a multi-exponentiation operation. +/// +/// This function will panic if coeffs and bases have a different length. +/// +/// This will use multithreading if beneficial. pub fn best_multiexp(coeffs: &[C::Scalar], bases: &[C]) -> C::Curve { - // TODO: consider adjusting it with emprical data? - let batch_size = 64; + assert_eq!(coeffs.len(), bases.len()); + + let num_threads = rayon::current_num_threads(); + if coeffs.len() > num_threads { + let chunk = coeffs.len() / num_threads; + let num_chunks = coeffs.chunks(chunk).len(); + let mut results = vec![C::Curve::identity(); num_chunks]; + rayon::scope(|scope| { + let chunk = coeffs.len() / num_threads; + + for ((coeffs, bases), acc) in coeffs + .chunks(chunk) + .zip(bases.chunks(chunk)) + .zip(results.iter_mut()) + { + scope.spawn(move |_| { + multiexp_serial(coeffs, bases, acc); + }); + } + }); + results.iter().fold(C::Curve::identity(), |a, b| a + b) + } else { + let mut acc = C::Curve::identity(); + multiexp_serial(coeffs, bases, &mut acc); + acc + } +} +/// +/// This function will panic if coeffs and bases have a different length. +/// +/// This will use multithreading if beneficial. +pub fn best_multiexp_independent_points( + coeffs: &[C::Scalar], + bases: &[C], +) -> C::Curve { + assert_eq!(coeffs.len(), bases.len()); // TODO: consider adjusting it with emprical data? let c = if bases.len() < 4 { @@ -364,6 +411,10 @@ pub fn best_multiexp(coeffs: &[C::Scalar], bases: &[C]) -> C::Cu (f64::from(bases.len() as u32)).ln().ceil() as usize }; + if c < 10 { + return best_multiexp(coeffs, bases); + } + // coeffs to byte representation let coeffs: Vec<_> = coeffs.par_iter().map(|a| a.to_repr()).collect(); // copy bases into `Affine` to skip in on curve check for every access @@ -378,7 +429,7 @@ pub fn best_multiexp(coeffs: &[C::Scalar], bases: &[C]) -> C::Cu let mut j_bucks = vec![Bucket::::None; 1 << (c - 1)]; // schedular for affine addition - let mut sched = Schedule::new(batch_size, c); + let mut sched = Schedule::new(c); for (base_idx, coeff) in coeffs.iter().enumerate() { let buck_idx = get_booth_index(w, c, coeff.as_ref()); @@ -432,36 +483,6 @@ mod test { use pasta_curves::arithmetic::CurveAffine; use rand_core::OsRng; - // keeping older implementation here for benchmarking and testing - pub fn best_multiexp(coeffs: &[C::Scalar], bases: &[C]) -> C::Curve { - assert_eq!(coeffs.len(), bases.len()); - - let num_threads = rayon::current_num_threads(); - if coeffs.len() > num_threads { - let chunk = coeffs.len() / num_threads; - let num_chunks = coeffs.chunks(chunk).len(); - let mut results = vec![C::Curve::identity(); num_chunks]; - rayon::scope(|scope| { - let chunk = coeffs.len() / num_threads; - - for ((coeffs, bases), acc) in coeffs - .chunks(chunk) - .zip(bases.chunks(chunk)) - .zip(results.iter_mut()) - { - scope.spawn(move |_| { - super::multiexp_serial(coeffs, bases, acc); - }); - } - }); - results.iter().fold(C::Curve::identity(), |a, b| a + b) - } else { - let mut acc = C::Curve::identity(); - super::multiexp_serial(coeffs, bases, &mut acc); - acc - } - } - #[test] fn test_booth_encoding() { fn mul(scalar: &Fr, point: &G1Affine, window: usize) -> G1Affine { @@ -524,21 +545,19 @@ mod test { let points = &points[..1 << k]; let scalars = &scalars[..1 << k]; - let t0 = start_timer!(|| format!("older k={}", k)); - let e0 = best_multiexp(scalars, points); + let t0 = start_timer!(|| format!("cyclone k={}", k)); + let e0 = super::best_multiexp_independent_points(scalars, points); end_timer!(t0); - let t1 = start_timer!(|| format!("cyclone k={}", k)); + let t1 = start_timer!(|| format!("older k={}", k)); let e1 = super::best_multiexp(scalars, points); end_timer!(t1); - assert_eq!(e0, e1); } } #[test] fn test_msm_cross() { - run_msm_cross::(16, 22); - // run_msm_cross::(19, 23); + run_msm_cross::(14, 22); } }