From 7d54d992a43d0b68ce70881b3e893d1c22ab9494 Mon Sep 17 00:00:00 2001 From: Srinath Setty Date: Wed, 20 Jul 2022 14:31:24 -0700 Subject: [PATCH] update benches (#94) --- Cargo.toml | 4 +- benches/compressed-snark.rs | 194 ++++++++++++++++++++------------- benches/recursive-snark.rs | 206 ++++++++++++++++++++++-------------- 3 files changed, 251 insertions(+), 153 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index cbf0217..b1867d1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,11 +37,11 @@ flate2 = "1.0" criterion = "0.3.1" [[bench]] -name = "compressed-snark" +name = "recursive-snark" harness = false [[bench]] -name = "recursive-snark" +name = "compressed-snark" harness = false [features] diff --git a/benches/compressed-snark.rs b/benches/compressed-snark.rs index 090a2e9..a94aa4a 100644 --- a/benches/compressed-snark.rs +++ b/benches/compressed-snark.rs @@ -1,8 +1,14 @@ #![allow(non_snake_case)] +use bellperson::{gadgets::num::AllocatedNum, ConstraintSystem, SynthesisError}; +use core::marker::PhantomData; use criterion::*; +use ff::PrimeField; use nova_snark::{ - traits::{circuit::TrivialTestCircuit, Group}, + traits::{ + circuit::{StepCircuit, TrivialTestCircuit}, + Group, + }, CompressedSNARK, PublicParams, RecursiveSNARK, }; use std::time::Duration; @@ -11,94 +17,138 @@ type G1 = pasta_curves::pallas::Point; type G2 = pasta_curves::vesta::Point; type S1 = nova_snark::spartan_with_ipa_pc::RelaxedR1CSSNARK; type S2 = nova_snark::spartan_with_ipa_pc::RelaxedR1CSSNARK; -type C1 = TrivialTestCircuit<::Scalar>; +type C1 = NonTrivialTestCircuit<::Scalar>; type C2 = TrivialTestCircuit<::Scalar>; -fn compressed_snark_benchmark(c: &mut Criterion) { - let num_samples = 10; - bench_compressed_snark(c, num_samples); -} - -fn set_duration() -> Criterion { - Criterion::default().warm_up_time(Duration::from_millis(3000)) -} - criterion_group! { name = compressed_snark; -config = set_duration(); -targets = compressed_snark_benchmark +config = Criterion::default().warm_up_time(Duration::from_millis(3000)); +targets = bench_compressed_snark } criterion_main!(compressed_snark); -fn bench_compressed_snark(c: &mut Criterion, num_samples: usize) { - let mut group = c.benchmark_group("CompressedSNARK"); - group.sample_size(num_samples); +fn bench_compressed_snark(c: &mut Criterion) { + let num_samples = 10; - // Produce public parameters - let pp = PublicParams::::setup( - TrivialTestCircuit::default(), - TrivialTestCircuit::default(), - ); + // we vary the number of constraints in the step circuit + for &log_num_cons_in_step_circuit in [0, 15, 16, 17, 18, 19, 20].iter() { + let num_cons = 1 << log_num_cons_in_step_circuit; - // produce a recursive SNARK - let num_steps = 3; - let mut recursive_snark: Option> = None; + let mut group = c.benchmark_group(format!("RecursiveSNARK-StepCircuitSize-{}", num_cons)); + group.sample_size(num_samples); - for i in 0..num_steps { - let res = RecursiveSNARK::prove_step( - &pp, - recursive_snark, - TrivialTestCircuit::default(), + // Produce public parameters + let pp = PublicParams::::setup( + NonTrivialTestCircuit::new(num_cons), TrivialTestCircuit::default(), - ::Scalar::one(), - ::Scalar::zero(), ); - assert!(res.is_ok()); - let recursive_snark_unwrapped = res.unwrap(); - - // verify the recursive snark at each step of recursion - let res = recursive_snark_unwrapped.verify( - &pp, - i + 1, - ::Scalar::one(), - ::Scalar::zero(), - ); - assert!(res.is_ok()); - // set the running variable for the next iteration - recursive_snark = Some(recursive_snark_unwrapped); - } + // produce a recursive SNARK + let num_steps = 3; + let mut recursive_snark: Option> = None; + + for i in 0..num_steps { + let res = RecursiveSNARK::prove_step( + &pp, + recursive_snark, + NonTrivialTestCircuit::new(num_cons), + TrivialTestCircuit::default(), + ::Scalar::one(), + ::Scalar::one(), + ); + assert!(res.is_ok()); + let recursive_snark_unwrapped = res.unwrap(); + + // verify the recursive snark at each step of recursion + let res = recursive_snark_unwrapped.verify( + &pp, + i + 1, + ::Scalar::one(), + ::Scalar::one(), + ); + assert!(res.is_ok()); - // Bench time to produce a compressed SNARK - let recursive_snark = recursive_snark.unwrap(); - group.bench_function("Prove", |b| { - b.iter(|| { - assert!(CompressedSNARK::<_, _, _, _, S1, S2>::prove( - black_box(&pp), - black_box(&recursive_snark) - ) - .is_ok()); - }) - }); - let res = CompressedSNARK::<_, _, _, _, S1, S2>::prove(&pp, &recursive_snark); - assert!(res.is_ok()); - let compressed_snark = res.unwrap(); - - // Benchmark the verification time - let name = "Verify"; - group.bench_function(name, |b| { - b.iter(|| { - assert!(black_box(&compressed_snark) - .verify( + // set the running variable for the next iteration + recursive_snark = Some(recursive_snark_unwrapped); + } + + // Bench time to produce a compressed SNARK + let recursive_snark = recursive_snark.unwrap(); + group.bench_function("Prove", |b| { + b.iter(|| { + assert!(CompressedSNARK::<_, _, _, _, S1, S2>::prove( black_box(&pp), - black_box(num_steps), - black_box(::Scalar::zero()), - black_box(::Scalar::zero()), + black_box(&recursive_snark) ) .is_ok()); - }) - }); + }) + }); + let res = CompressedSNARK::<_, _, _, _, S1, S2>::prove(&pp, &recursive_snark); + assert!(res.is_ok()); + let compressed_snark = res.unwrap(); - group.finish(); + // Benchmark the verification time + group.bench_function("Verify", |b| { + b.iter(|| { + assert!(black_box(&compressed_snark) + .verify( + black_box(&pp), + black_box(num_steps), + black_box(::Scalar::one()), + black_box(::Scalar::one()), + ) + .is_ok()); + }) + }); + + group.finish(); + } +} + +#[derive(Clone, Debug, Default)] +struct NonTrivialTestCircuit { + num_cons: usize, + _p: PhantomData, +} + +impl NonTrivialTestCircuit +where + F: PrimeField, +{ + pub fn new(num_cons: usize) -> Self { + Self { + num_cons, + _p: Default::default(), + } + } +} +impl StepCircuit for NonTrivialTestCircuit +where + F: PrimeField, +{ + fn synthesize>( + &self, + cs: &mut CS, + z: AllocatedNum, + ) -> Result, SynthesisError> { + // Consider a an equation: `x^2 = y`, where `x` and `y` are respectively the input and output. + let mut x = z; + let mut y = x.clone(); + for i in 0..self.num_cons { + y = x.square(cs.namespace(|| format!("x_sq_{}", i)))?; + x = y.clone(); + } + Ok(y) + } + + fn compute(&self, z: &F) -> F { + let mut x = *z; + let mut y = x; + for _i in 0..self.num_cons { + y = x * x; + x = y; + } + y + } } diff --git a/benches/recursive-snark.rs b/benches/recursive-snark.rs index 31cb86b..5f299f4 100644 --- a/benches/recursive-snark.rs +++ b/benches/recursive-snark.rs @@ -1,106 +1,154 @@ #![allow(non_snake_case)] +use bellperson::{gadgets::num::AllocatedNum, ConstraintSystem, SynthesisError}; +use core::marker::PhantomData; use criterion::*; +use ff::PrimeField; use nova_snark::{ - traits::{circuit::TrivialTestCircuit, Group}, + traits::{ + circuit::{StepCircuit, TrivialTestCircuit}, + Group, + }, PublicParams, RecursiveSNARK, }; use std::time::Duration; type G1 = pasta_curves::pallas::Point; type G2 = pasta_curves::vesta::Point; -type C1 = TrivialTestCircuit<::Scalar>; +type C1 = NonTrivialTestCircuit<::Scalar>; type C2 = TrivialTestCircuit<::Scalar>; -fn recursive_snark_benchmark(c: &mut Criterion) { - let num_samples = 10; - bench_recursive_snark(c, num_samples); -} - -fn set_duration() -> Criterion { - Criterion::default().warm_up_time(Duration::from_millis(3000)) -} - criterion_group! { name = recursive_snark; -config = set_duration(); -targets = recursive_snark_benchmark +config = Criterion::default().warm_up_time(Duration::from_millis(3000)); +targets = bench_recursive_snark } criterion_main!(recursive_snark); -fn bench_recursive_snark(c: &mut Criterion, num_samples: usize) { - let mut group = c.benchmark_group("RecursiveSNARK".to_string()); - group.sample_size(num_samples); - - // Produce public parameters - let pp = PublicParams::::setup( - TrivialTestCircuit::default(), - TrivialTestCircuit::default(), - ); - - // Bench time to produce a recursive SNARK; - // we execute a certain number of warm-up steps since executing - // the first step is cheaper than other steps owing to the presence of - // a lot of zeros in the satisfying assignment - let num_warmup_steps = 10; - let mut recursive_snark: Option> = None; - - for i in 0..num_warmup_steps { - let res = RecursiveSNARK::prove_step( - &pp, - recursive_snark, - TrivialTestCircuit::default(), +fn bench_recursive_snark(c: &mut Criterion) { + // we vary the number of constraints in the step circuit + for &log_num_cons_in_step_circuit in [0, 15, 16, 17, 18, 19, 20].iter() { + let num_cons = 1 << log_num_cons_in_step_circuit; + + let mut group = c.benchmark_group(format!("RecursiveSNARK-StepCircuitSize-{}", num_cons)); + group.sample_size(10); + + // Produce public parameters + let pp = PublicParams::::setup( + NonTrivialTestCircuit::new(num_cons), TrivialTestCircuit::default(), - ::Scalar::one(), - ::Scalar::zero(), ); - assert!(res.is_ok()); - let recursive_snark_unwrapped = res.unwrap(); - - // verify the recursive snark at each step of recursion - let res = recursive_snark_unwrapped.verify( - &pp, - i + 1, - ::Scalar::one(), - ::Scalar::zero(), - ); - assert!(res.is_ok()); - // set the running variable for the next iteration - recursive_snark = Some(recursive_snark_unwrapped); - } + // Bench time to produce a recursive SNARK; + // we execute a certain number of warm-up steps since executing + // the first step is cheaper than other steps owing to the presence of + // a lot of zeros in the satisfying assignment + let num_warmup_steps = 10; + let mut recursive_snark: Option> = None; + + for i in 0..num_warmup_steps { + let res = RecursiveSNARK::prove_step( + &pp, + recursive_snark, + NonTrivialTestCircuit::new(num_cons), + TrivialTestCircuit::default(), + ::Scalar::one(), + ::Scalar::one(), + ); + assert!(res.is_ok()); + let recursive_snark_unwrapped = res.unwrap(); - group.bench_function("Prove", |b| { - b.iter(|| { - // produce a recursive SNARK for a step of the recursion - assert!(RecursiveSNARK::prove_step( - black_box(&pp), - black_box(recursive_snark.clone()), - black_box(TrivialTestCircuit::default()), - black_box(TrivialTestCircuit::default()), - black_box(::Scalar::zero()), - black_box(::Scalar::zero()), - ) - .is_ok()); - }) - }); - - let recursive_snark = recursive_snark.unwrap(); - - // Benchmark the verification time - let name = "Verify"; - group.bench_function(name, |b| { - b.iter(|| { - assert!(black_box(&recursive_snark) - .verify( + // verify the recursive snark at each step of recursion + let res = recursive_snark_unwrapped.verify( + &pp, + i + 1, + ::Scalar::one(), + ::Scalar::one(), + ); + assert!(res.is_ok()); + + // set the running variable for the next iteration + recursive_snark = Some(recursive_snark_unwrapped); + } + + group.bench_function("Prove", |b| { + b.iter(|| { + // produce a recursive SNARK for a step of the recursion + assert!(RecursiveSNARK::prove_step( black_box(&pp), - black_box(num_warmup_steps), - black_box(::Scalar::zero()), - black_box(::Scalar::zero()), + black_box(recursive_snark.clone()), + black_box(NonTrivialTestCircuit::new(num_cons)), + black_box(TrivialTestCircuit::default()), + black_box(::Scalar::one()), + black_box(::Scalar::one()), ) .is_ok()); + }) }); - }); - group.finish(); + + let recursive_snark = recursive_snark.unwrap(); + + // Benchmark the verification time + group.bench_function("Verify", |b| { + b.iter(|| { + assert!(black_box(&recursive_snark) + .verify( + black_box(&pp), + black_box(num_warmup_steps), + black_box(::Scalar::one()), + black_box(::Scalar::one()), + ) + .is_ok()); + }); + }); + group.finish(); + } +} + +#[derive(Clone, Debug, Default)] +struct NonTrivialTestCircuit { + num_cons: usize, + _p: PhantomData, +} + +impl NonTrivialTestCircuit +where + F: PrimeField, +{ + pub fn new(num_cons: usize) -> Self { + Self { + num_cons, + _p: Default::default(), + } + } +} +impl StepCircuit for NonTrivialTestCircuit +where + F: PrimeField, +{ + fn synthesize>( + &self, + cs: &mut CS, + z: AllocatedNum, + ) -> Result, SynthesisError> { + // Consider a an equation: `x^2 = y`, where `x` and `y` are respectively the input and output. + let mut x = z; + let mut y = x.clone(); + for i in 0..self.num_cons { + y = x.square(cs.namespace(|| format!("x_sq_{}", i)))?; + x = y.clone(); + } + Ok(y) + } + + fn compute(&self, z: &F) -> F { + let mut x = *z; + let mut y = x; + for _i in 0..self.num_cons { + y = x * x; + x = y; + } + y + } }