From f2fc068db0ef0c2429812c3a0154b82c8334dfc5 Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Tue, 15 Sep 2020 18:49:12 +0800 Subject: [PATCH 1/6] Implement small_multiexp() in arithmetic.rs --- src/arithmetic.rs | 33 +++++++++++++++++++++++++++++++++ src/poly/commitment/prover.rs | 18 +++++++----------- 2 files changed, 40 insertions(+), 11 deletions(-) diff --git a/src/arithmetic.rs b/src/arithmetic.rs index 312cfe5..afb186c 100644 --- a/src/arithmetic.rs +++ b/src/arithmetic.rs @@ -180,6 +180,39 @@ 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 { + // Gets the bit at position `i`. Bits are numbered from 0 (least significant) to 7 (most significant). + fn get_bit_at(byte: u8, i: usize) -> bool { + if i < 8 { + ((byte >> i) & 1u8) != 0 + } else { + false + } + } + + 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() { + if get_bit_at(coeffs[coeff_idx][byte_idx], bit_idx) { + acc = acc + &bases[coeff_idx].to_projective(); + } + } + } + } + + acc +} + /// Performs a multi-exponentiation operation. /// /// This function will panic if coeffs and bases have a different length. diff --git a/src/poly/commitment/prover.rs b/src/poly/commitment/prover.rs index 3acddc2..0349b28 100644 --- a/src/poly/commitment/prover.rs +++ b/src/poly/commitment/prover.rs @@ -1,7 +1,7 @@ use super::super::{Coeff, Polynomial}; use super::{Blind, OpeningProof, Params}; use crate::arithmetic::{ - best_multiexp, compute_inner_product, get_challenge_scalar, parallelize, Challenge, Curve, + best_multiexp, compute_inner_product, get_challenge_scalar, small_multiexp, Challenge, Curve, CurveAffine, Field, }; use crate::transcript::Hasher; @@ -220,15 +220,11 @@ fn parallel_generator_collapse( challenge_inv: C::Scalar, ) { let len = g.len() / 2; - let (mut g_lo, g_hi) = g.split_at_mut(len); + let (g_lo, g_hi) = g.split_at_mut(len); - parallelize(&mut g_lo, |g_lo, start| { - 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)); - } - C::Projective::batch_to_affine(&tmp, g_lo); - }); + let mut tmp = Vec::with_capacity(g_lo.len()); + for (g_lo, g_hi) in g_lo.iter().zip(g_hi.iter()) { + tmp.push(small_multiexp(&[challenge_inv, challenge], &[*g_lo, *g_hi])); + } + C::Projective::batch_to_affine(&tmp, g_lo); } From d70c8cc0d739aaf3a4853f4604a6bce4a739df35 Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Wed, 16 Sep 2020 00:44:56 +0800 Subject: [PATCH 2/6] Add benchmarks for shared double-and-add --- Cargo.toml | 7 +++++++ benches/plonk.rs | 42 ++++++++++++++++++++++++++++++++++++++++++ src/poly/commitment.rs | 5 +++++ 3 files changed, 54 insertions(+) create mode 100644 benches/plonk.rs diff --git a/Cargo.toml b/Cargo.toml index 37df7c5..7f2eea5 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 = "plonk" +harness = false + [dependencies] subtle = "2.2.1" crossbeam-utils = "0.7" diff --git a/benches/plonk.rs b/benches/plonk.rs new file mode 100644 index 0000000..56228e0 --- /dev/null +++ b/benches/plonk.rs @@ -0,0 +1,42 @@ +#[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("shared 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]); + } + }) + }); + + c.bench_function("double-and-add", |b| { + b.iter(|| { + for (g_lo, g_hi) in g_lo.iter().zip(g_hi.iter()) { + let _ = ((*g_lo) * coeff_1) + &((*g_hi) * coeff_2); + } + }) + }); + } +} + +criterion_group!(benches, criterion_benchmark); +criterion_main!(benches); diff --git a/src/poly/commitment.rs b/src/poly/commitment.rs index eb24af9..b3fa7e0 100644 --- a/src/poly/commitment.rs +++ b/src/poly/commitment.rs @@ -261,6 +261,11 @@ impl Params { other_bases, } } + + /// Getter for g generators + pub fn get_g(&self) -> Vec { + self.g.clone() + } } /// A guard returned by the verifier From 58708c2c20cf577962aa456e7c498fe10c894f37 Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Wed, 16 Sep 2020 01:07:45 +0800 Subject: [PATCH 3/6] Remove old 'double-and-add' benchmark --- benches/plonk.rs | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/benches/plonk.rs b/benches/plonk.rs index 56228e0..49d965b 100644 --- a/benches/plonk.rs +++ b/benches/plonk.rs @@ -20,18 +20,10 @@ fn criterion_benchmark(c: &mut Criterion) { let coeff_1 = Fp::random(); let coeff_2 = Fp::random(); - c.bench_function("shared 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]); - } - }) - }); - c.bench_function("double-and-add", |b| { b.iter(|| { for (g_lo, g_hi) in g_lo.iter().zip(g_hi.iter()) { - let _ = ((*g_lo) * coeff_1) + &((*g_hi) * coeff_2); + small_multiexp(&[black_box(coeff_1), black_box(coeff_2)], &[*g_lo, *g_hi]); } }) }); From 7710b73bff5f7aad36578cc8f430c09bd3750480 Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Wed, 16 Sep 2020 08:55:39 +0800 Subject: [PATCH 4/6] Rename 'plonk' benchmark to 'arithmetic' --- Cargo.toml | 2 +- benches/{plonk.rs => arithmetic.rs} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename benches/{plonk.rs => arithmetic.rs} (100%) diff --git a/Cargo.toml b/Cargo.toml index 7f2eea5..98a8483 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,7 +24,7 @@ rustdoc-args = [ "--html-in-header", "katex-header.html" ] criterion = "0.3" [[bench]] -name = "plonk" +name = "arithmetic" harness = false [dependencies] diff --git a/benches/plonk.rs b/benches/arithmetic.rs similarity index 100% rename from benches/plonk.rs rename to benches/arithmetic.rs From a63602df2a11de94ee9ae70fb535b40516301a8a Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Wed, 16 Sep 2020 08:56:45 +0800 Subject: [PATCH 5/6] Inline bit-shifting --- src/arithmetic.rs | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/src/arithmetic.rs b/src/arithmetic.rs index afb186c..750c9fb 100644 --- a/src/arithmetic.rs +++ b/src/arithmetic.rs @@ -184,15 +184,6 @@ fn multiexp_serial(coeffs: &[C::Scalar], bases: &[C], acc: &mut /// Uses the double-and-add algorithm with doublings shared across points. pub fn small_multiexp(coeffs: &[C::Scalar], bases: &[C]) -> C::Projective { - // Gets the bit at position `i`. Bits are numbered from 0 (least significant) to 7 (most significant). - fn get_bit_at(byte: u8, i: usize) -> bool { - if i < 8 { - ((byte >> i) & 1u8) != 0 - } else { - false - } - } - let coeffs: Vec<[u8; 32]> = coeffs.iter().map(|a| a.to_bytes()).collect(); let mut acc = C::Projective::zero(); @@ -203,7 +194,8 @@ pub fn small_multiexp(coeffs: &[C::Scalar], bases: &[C]) -> C::P acc = acc.double(); // for each coeff for coeff_idx in 0..coeffs.len() { - if get_bit_at(coeffs[coeff_idx][byte_idx], bit_idx) { + let byte = coeffs[coeff_idx][byte_idx]; + if (byte >> bit_idx & 1) != 0 { acc = acc + &bases[coeff_idx].to_projective(); } } From ced73c2bf74471e80b779853917a47f01ca73b96 Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Wed, 16 Sep 2020 09:02:58 +0800 Subject: [PATCH 6/6] Minor fixes --- src/arithmetic.rs | 5 ++--- src/poly/commitment/prover.rs | 19 +++++++++++-------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/src/arithmetic.rs b/src/arithmetic.rs index 750c9fb..a7193a8 100644 --- a/src/arithmetic.rs +++ b/src/arithmetic.rs @@ -182,7 +182,6 @@ 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(); @@ -195,8 +194,8 @@ pub fn small_multiexp(coeffs: &[C::Scalar], bases: &[C]) -> C::P // for each coeff for coeff_idx in 0..coeffs.len() { let byte = coeffs[coeff_idx][byte_idx]; - if (byte >> bit_idx & 1) != 0 { - acc = acc + &bases[coeff_idx].to_projective(); + if ((byte >> bit_idx) & 1) != 0 { + acc += bases[coeff_idx]; } } } diff --git a/src/poly/commitment/prover.rs b/src/poly/commitment/prover.rs index 0349b28..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, small_multiexp, Challenge, Curve, - CurveAffine, Field, + best_multiexp, compute_inner_product, get_challenge_scalar, parallelize, small_multiexp, + Challenge, Curve, CurveAffine, Field, }; use crate::transcript::Hasher; @@ -220,11 +220,14 @@ fn parallel_generator_collapse( challenge_inv: C::Scalar, ) { let len = g.len() / 2; - let (g_lo, g_hi) = g.split_at_mut(len); + let (mut g_lo, g_hi) = g.split_at_mut(len); - let mut tmp = Vec::with_capacity(g_lo.len()); - for (g_lo, g_hi) in g_lo.iter().zip(g_hi.iter()) { - tmp.push(small_multiexp(&[challenge_inv, challenge], &[*g_lo, *g_hi])); - } - C::Projective::batch_to_affine(&tmp, g_lo); + parallelize(&mut g_lo, |g_lo, start| { + 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()) { + tmp.push(small_multiexp(&[challenge_inv, challenge], &[*g_lo, *g_hi])); + } + C::Projective::batch_to_affine(&tmp, g_lo); + }); }