diff --git a/Cargo.toml b/Cargo.toml index 37df7c5..98a8483 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,6 +20,13 @@ publish = false [package.metadata.docs.rs] rustdoc-args = [ "--html-in-header", "katex-header.html" ] +[dev-dependencies] +criterion = "0.3" + +[[bench]] +name = "arithmetic" +harness = false + [dependencies] subtle = "2.2.1" crossbeam-utils = "0.7" diff --git a/benches/arithmetic.rs b/benches/arithmetic.rs new file mode 100644 index 0000000..49d965b --- /dev/null +++ b/benches/arithmetic.rs @@ -0,0 +1,34 @@ +#[macro_use] +extern crate criterion; + +extern crate halo2; +use crate::arithmetic::{small_multiexp, EqAffine, Field, Fp, Fq}; +use crate::poly::commitment::Params; +use crate::transcript::DummyHash; +use halo2::*; + +use criterion::{black_box, Criterion}; + +fn criterion_benchmark(c: &mut Criterion) { + // small multiexp + { + let params: Params = Params::new::>(5); + let g = &mut params.get_g(); + let len = g.len() / 2; + let (g_lo, g_hi) = g.split_at_mut(len); + + let coeff_1 = Fp::random(); + let coeff_2 = Fp::random(); + + c.bench_function("double-and-add", |b| { + b.iter(|| { + for (g_lo, g_hi) in g_lo.iter().zip(g_hi.iter()) { + small_multiexp(&[black_box(coeff_1), black_box(coeff_2)], &[*g_lo, *g_hi]); + } + }) + }); + } +} + +criterion_group!(benches, criterion_benchmark); +criterion_main!(benches); diff --git a/src/arithmetic.rs b/src/arithmetic.rs index 312cfe5..a7193a8 100644 --- a/src/arithmetic.rs +++ b/src/arithmetic.rs @@ -180,6 +180,30 @@ fn multiexp_serial(coeffs: &[C::Scalar], bases: &[C], acc: &mut } } +/// Performs a small multi-exponentiation operation. +/// Uses the double-and-add algorithm with doublings shared across points. +pub fn small_multiexp(coeffs: &[C::Scalar], bases: &[C]) -> C::Projective { + let coeffs: Vec<[u8; 32]> = coeffs.iter().map(|a| a.to_bytes()).collect(); + let mut acc = C::Projective::zero(); + + // for byte idx + for byte_idx in (0..32).rev() { + // for bit idx + for bit_idx in (0..8).rev() { + acc = acc.double(); + // for each coeff + for coeff_idx in 0..coeffs.len() { + let byte = coeffs[coeff_idx][byte_idx]; + if ((byte >> bit_idx) & 1) != 0 { + acc += bases[coeff_idx]; + } + } + } + } + + acc +} + /// Performs a multi-exponentiation operation. /// /// This function will panic if coeffs and bases have a different length. diff --git a/src/poly/commitment.rs b/src/poly/commitment.rs index a1a9f60..2eaa9ef 100644 --- a/src/poly/commitment.rs +++ b/src/poly/commitment.rs @@ -275,6 +275,11 @@ impl Params { other_bases, } } + + /// Getter for g generators + pub fn get_g(&self) -> Vec { + self.g.clone() + } } /// A guard returned by the verifier diff --git a/src/poly/commitment/prover.rs b/src/poly/commitment/prover.rs index 3acddc2..c66fe82 100644 --- a/src/poly/commitment/prover.rs +++ b/src/poly/commitment/prover.rs @@ -1,8 +1,8 @@ use super::super::{Coeff, Polynomial}; use super::{Blind, OpeningProof, Params}; use crate::arithmetic::{ - best_multiexp, compute_inner_product, get_challenge_scalar, parallelize, Challenge, Curve, - CurveAffine, Field, + best_multiexp, compute_inner_product, get_challenge_scalar, parallelize, small_multiexp, + Challenge, Curve, CurveAffine, Field, }; use crate::transcript::Hasher; @@ -226,8 +226,7 @@ fn parallel_generator_collapse( let g_hi = &g_hi[start..]; let mut tmp = Vec::with_capacity(g_lo.len()); for (g_lo, g_hi) in g_lo.iter().zip(g_hi.iter()) { - // TODO: could use multiexp - tmp.push(((*g_lo) * challenge_inv) + &((*g_hi) * challenge)); + tmp.push(small_multiexp(&[challenge_inv, challenge], &[*g_lo, *g_hi])); } C::Projective::batch_to_affine(&tmp, g_lo); });