From 1f3fc875ab26a77a8db15a0d643307b0d7d5e504 Mon Sep 17 00:00:00 2001 From: Sean Bowe Date: Tue, 13 Oct 2020 15:06:21 -0600 Subject: [PATCH 1/8] PLONK benchmarks. --- Cargo.toml | 4 + benches/plonk.rs | 284 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 288 insertions(+) create mode 100644 benches/plonk.rs diff --git a/Cargo.toml b/Cargo.toml index 62f3d27..475291a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,6 +28,10 @@ criterion = "0.3" name = "arithmetic" harness = false +[[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..02f5632 --- /dev/null +++ b/benches/plonk.rs @@ -0,0 +1,284 @@ +#[macro_use] +extern crate criterion; + +extern crate halo2; +use halo2::arithmetic::Field; +use halo2::plonk::*; +use halo2::poly::commitment::Params; +use halo2::transcript::DummyHash; +use halo2::tweedle::{EqAffine, Fp, Fq}; + +use std::marker::PhantomData; + +use criterion::Criterion; + +fn bench_with_k(name: &str, k: u32, c: &mut Criterion) { + /// This represents an advice column at a certain row in the ConstraintSystem + #[derive(Copy, Clone, Debug)] + pub struct Variable(Column, usize); + + // Initialize the polynomial commitment parameters + let params: Params = Params::new::>(k); + + struct PLONKConfig { + a: Column, + b: Column, + c: Column, + + sa: Column, + sb: Column, + sc: Column, + sm: Column, + + perm: usize, + } + + trait StandardCS { + fn raw_multiply(&mut self, f: F) -> Result<(Variable, Variable, Variable), Error> + where + F: FnOnce() -> Result<(FF, FF, FF), Error>; + fn raw_add(&mut self, f: F) -> Result<(Variable, Variable, Variable), Error> + where + F: FnOnce() -> Result<(FF, FF, FF), Error>; + fn copy(&mut self, a: Variable, b: Variable) -> Result<(), Error>; + } + + struct MyCircuit { + a: Option, + k: u32, + } + + struct StandardPLONK<'a, F: Field, CS: Assignment + 'a> { + cs: &'a mut CS, + config: PLONKConfig, + current_gate: usize, + _marker: PhantomData, + } + + impl<'a, FF: Field, CS: Assignment> StandardPLONK<'a, FF, CS> { + fn new(cs: &'a mut CS, config: PLONKConfig) -> Self { + StandardPLONK { + cs, + config, + current_gate: 0, + _marker: PhantomData, + } + } + } + + impl<'a, FF: Field, CS: Assignment> StandardCS for StandardPLONK<'a, FF, CS> { + fn raw_multiply(&mut self, f: F) -> Result<(Variable, Variable, Variable), Error> + where + F: FnOnce() -> Result<(FF, FF, FF), Error>, + { + let index = self.current_gate; + self.current_gate += 1; + let mut value = None; + self.cs.assign_advice(self.config.a, index, || { + value = Some(f()?); + Ok(value.ok_or(Error::SynthesisError)?.0) + })?; + self.cs.assign_advice(self.config.b, index, || { + Ok(value.ok_or(Error::SynthesisError)?.1) + })?; + self.cs.assign_advice(self.config.c, index, || { + Ok(value.ok_or(Error::SynthesisError)?.2) + })?; + + self.cs + .assign_fixed(self.config.sa, index, || Ok(FF::zero()))?; + self.cs + .assign_fixed(self.config.sb, index, || Ok(FF::zero()))?; + self.cs + .assign_fixed(self.config.sc, index, || Ok(FF::one()))?; + self.cs + .assign_fixed(self.config.sm, index, || Ok(FF::one()))?; + Ok(( + Variable(self.config.a, index), + Variable(self.config.b, index), + Variable(self.config.c, index), + )) + } + fn raw_add(&mut self, f: F) -> Result<(Variable, Variable, Variable), Error> + where + F: FnOnce() -> Result<(FF, FF, FF), Error>, + { + let index = self.current_gate; + self.current_gate += 1; + let mut value = None; + self.cs.assign_advice(self.config.a, index, || { + value = Some(f()?); + Ok(value.ok_or(Error::SynthesisError)?.0) + })?; + self.cs.assign_advice(self.config.b, index, || { + Ok(value.ok_or(Error::SynthesisError)?.1) + })?; + self.cs.assign_advice(self.config.c, index, || { + Ok(value.ok_or(Error::SynthesisError)?.2) + })?; + + self.cs + .assign_fixed(self.config.sa, index, || Ok(FF::one()))?; + self.cs + .assign_fixed(self.config.sb, index, || Ok(FF::one()))?; + self.cs + .assign_fixed(self.config.sc, index, || Ok(FF::one()))?; + self.cs + .assign_fixed(self.config.sm, index, || Ok(FF::zero()))?; + Ok(( + Variable(self.config.a, index), + Variable(self.config.b, index), + Variable(self.config.c, index), + )) + } + fn copy(&mut self, left: Variable, right: Variable) -> Result<(), Error> { + let left_column = match left.0 { + x if x == self.config.a => 0, + x if x == self.config.b => 1, + x if x == self.config.c => 2, + _ => unreachable!(), + }; + let right_column = match right.0 { + x if x == self.config.a => 0, + x if x == self.config.b => 1, + x if x == self.config.c => 2, + _ => unreachable!(), + }; + + self.cs + .copy(self.config.perm, left_column, left.1, right_column, right.1) + } + } + + impl Circuit for MyCircuit { + type Config = PLONKConfig; + + fn configure(meta: &mut ConstraintSystem) -> PLONKConfig { + let a = meta.advice_column(); + let b = meta.advice_column(); + let c = meta.advice_column(); + + let perm = meta.permutation(&[a, b, c]); + + let sm = meta.fixed_column(); + let sa = meta.fixed_column(); + let sb = meta.fixed_column(); + let sc = meta.fixed_column(); + + meta.create_gate(|meta| { + let a = meta.query_advice(a, 0); + let b = meta.query_advice(b, 0); + let c = meta.query_advice(c, 0); + + let sa = meta.query_fixed(sa, 0); + let sb = meta.query_fixed(sb, 0); + let sc = meta.query_fixed(sc, 0); + let sm = meta.query_fixed(sm, 0); + + a.clone() * sa + b.clone() * sb + a * b * sm + (c * sc * (-F::one())) + }); + + PLONKConfig { + a, + b, + c, + sa, + sb, + sc, + sm, + perm, + } + } + + fn synthesize( + &self, + cs: &mut impl Assignment, + config: PLONKConfig, + ) -> Result<(), Error> { + let mut cs = StandardPLONK::new(cs, config); + + for _ in 0..(1 << (self.k - 1)) { + let mut a_squared = None; + let (a0, _, c0) = cs.raw_multiply(|| { + a_squared = self.a.map(|a| a.square()); + Ok(( + self.a.ok_or(Error::SynthesisError)?, + self.a.ok_or(Error::SynthesisError)?, + a_squared.ok_or(Error::SynthesisError)?, + )) + })?; + let (a1, b1, _) = cs.raw_add(|| { + let fin = a_squared.and_then(|a2| self.a.map(|a| a + a2)); + Ok(( + self.a.ok_or(Error::SynthesisError)?, + a_squared.ok_or(Error::SynthesisError)?, + fin.ok_or(Error::SynthesisError)?, + )) + })?; + cs.copy(a0, a1)?; + cs.copy(b1, c0)?; + } + + Ok(()) + } + } + + let empty_circuit: MyCircuit = MyCircuit { a: None, k }; + + // Initialize the proving key + let pk = keygen(¶ms, &empty_circuit).expect("keygen should not fail"); + + let prover_name = name.to_string() + "-prover"; + let verifier_name = name.to_string() + "-verifier"; + + c.bench_function(&prover_name, |b| { + b.iter(|| { + let circuit: MyCircuit = MyCircuit { + a: Some(Fp::random()), + k, + }; + + // Create a proof + let proof = + Proof::create::, DummyHash, _>(¶ms, &pk, &circuit, &[]) + .expect("proof generation should not fail"); + + proof + }); + }); + + let circuit: MyCircuit = MyCircuit { + a: Some(Fp::random()), + k, + }; + + // Create a proof + let proof = Proof::create::, DummyHash, _>(¶ms, &pk, &circuit, &[]) + .expect("proof generation should not fail"); + + c.bench_function(&verifier_name, |b| { + b.iter(|| { + let msm = params.empty_msm(); + let guard = proof + .verify::, DummyHash>(¶ms, pk.get_vk(), msm, &[]) + .unwrap(); + let msm = guard.clone().use_challenges(); + assert!(msm.eval()); + }); + }); +} + +fn criterion_benchmark(c: &mut Criterion) { + bench_with_k("plonk-k=8", 8, c); + bench_with_k("plonk-k=9", 9, c); + bench_with_k("plonk-k=10", 10, c); + bench_with_k("plonk-k=11", 11, c); + bench_with_k("plonk-k=12", 12, c); + bench_with_k("plonk-k=13", 13, c); + bench_with_k("plonk-k=14", 14, c); + bench_with_k("plonk-k=15", 15, c); + bench_with_k("plonk-k=16", 16, c); +} + +criterion_group!(benches, criterion_benchmark); +criterion_main!(benches); From fb8f67dfe55f58e7576d05d820311e25dc158dea Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Tue, 10 Nov 2020 23:54:53 +0000 Subject: [PATCH 2/8] Add a simple metrics Recorder for counting things in models --- Cargo.toml | 1 + src/lib.rs | 2 + src/model.rs | 110 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 113 insertions(+) create mode 100644 src/model.rs diff --git a/Cargo.toml b/Cargo.toml index 475291a..9399fa6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,5 +35,6 @@ harness = false [dependencies] subtle = "2.2.1" crossbeam-utils = "0.7" +metrics = "0.13.0-alpha.8" num_cpus = "1.13" rand = "0.7" diff --git a/src/lib.rs b/src/lib.rs index 49441e8..c20d88d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,3 +18,5 @@ pub mod plonk; pub mod poly; pub mod transcript; pub mod tweedle; + +pub mod model; diff --git a/src/model.rs b/src/model.rs new file mode 100644 index 0000000..c979bce --- /dev/null +++ b/src/model.rs @@ -0,0 +1,110 @@ +//! Helpers for modelling halo2 circuit performance. + +use std::cell::RefCell; +use std::collections::HashMap; +use std::fmt; +use std::sync::Arc; + +use metrics::{Key, Recorder, Unit}; + +/// A [`metrics`] recorder for examining halo2 metrics. +/// +/// # Examples +/// +/// ``` +/// use halo2::model::ModelRecorder; +/// +/// fn main() { +/// let recorder = Box::leak(Box::new(ModelRecorder::default())); +/// metrics::set_recorder(recorder).unwrap(); +/// +/// // Create circuit, build and/or verify proof. +/// +/// println!("{}", recorder); +/// } +/// ``` +#[derive(Debug)] +pub struct ModelRecorder { + counters: Arc>>, +} + +impl Default for ModelRecorder { + fn default() -> Self { + ModelRecorder { + counters: Default::default(), + } + } +} + +impl fmt::Display for ModelRecorder { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut counters = self + .counters + .try_borrow() + .unwrap() + .iter() + .map(|(k, v)| (k.clone(), *v)) + .collect::>(); + + counters.sort_by(|(k1, _), (k2, _)| { + let key1 = ( + k1.name(), + k1.labels() + .map(|l| (l.key(), l.value())) + .collect::>(), + ); + let key2 = ( + k2.name(), + k2.labels() + .map(|l| (l.key(), l.value())) + .collect::>(), + ); + key1.cmp(&key2) + }); + + writeln!(f, "Recorded metrics:")?; + for (key, value) in counters.iter() { + writeln!(f, "- {}: {}", key, value)?; + } + Ok(()) + } +} + +impl Recorder for ModelRecorder { + fn register_counter(&self, _key: Key, _unit: Option, _description: Option<&'static str>) { + } + + fn register_gauge(&self, _key: Key, _unit: Option, _description: Option<&'static str>) {} + + fn register_histogram( + &self, + _key: Key, + _unit: Option, + _description: Option<&'static str>, + ) { + } + + fn increment_counter(&self, key: Key, value: u64) { + *self + .counters + .try_borrow_mut() + .unwrap() + .entry(key) + .or_default() += value; + } + + fn update_gauge(&self, _key: Key, _value: f64) { + unimplemented!() + } + + fn record_histogram(&self, _key: Key, _value: u64) { + unimplemented!() + } +} + +impl ModelRecorder { + /// Clear all recorded metrics. + pub fn clear(&self) { + self.counters.try_borrow_mut().unwrap().clear(); + } +} From ba2758696521fd973bca52224946ad97303b40ad Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Tue, 10 Nov 2020 23:55:21 +0000 Subject: [PATCH 3/8] Add an example performance model --- examples/performance_model.rs | 250 ++++++++++++++++++++++++++++++++++ 1 file changed, 250 insertions(+) create mode 100644 examples/performance_model.rs diff --git a/examples/performance_model.rs b/examples/performance_model.rs new file mode 100644 index 0000000..36d5c11 --- /dev/null +++ b/examples/performance_model.rs @@ -0,0 +1,250 @@ +use halo2::{ + arithmetic::Field, + model::ModelRecorder, + plonk::*, + poly::commitment::Params, + transcript::DummyHash, + tweedle::{EqAffine, Fp, Fq}, +}; + +use std::marker::PhantomData; + +/// This represents an advice column at a certain row in the ConstraintSystem +#[derive(Copy, Clone, Debug)] +pub struct Variable(Column, usize); + +struct PLONKConfig { + a: Column, + b: Column, + c: Column, + + sa: Column, + sb: Column, + sc: Column, + sm: Column, + + perm: usize, +} + +trait StandardCS { + fn raw_multiply(&mut self, f: F) -> Result<(Variable, Variable, Variable), Error> + where + F: FnOnce() -> Result<(FF, FF, FF), Error>; + fn raw_add(&mut self, f: F) -> Result<(Variable, Variable, Variable), Error> + where + F: FnOnce() -> Result<(FF, FF, FF), Error>; + fn copy(&mut self, a: Variable, b: Variable) -> Result<(), Error>; +} + +struct MyCircuit { + a: Option, + k: u32, +} + +struct StandardPLONK<'a, F: Field, CS: Assignment + 'a> { + cs: &'a mut CS, + config: PLONKConfig, + current_gate: usize, + _marker: PhantomData, +} + +impl<'a, FF: Field, CS: Assignment> StandardPLONK<'a, FF, CS> { + fn new(cs: &'a mut CS, config: PLONKConfig) -> Self { + StandardPLONK { + cs, + config, + current_gate: 0, + _marker: PhantomData, + } + } +} + +impl<'a, FF: Field, CS: Assignment> StandardCS for StandardPLONK<'a, FF, CS> { + fn raw_multiply(&mut self, f: F) -> Result<(Variable, Variable, Variable), Error> + where + F: FnOnce() -> Result<(FF, FF, FF), Error>, + { + let index = self.current_gate; + self.current_gate += 1; + let mut value = None; + self.cs.assign_advice(self.config.a, index, || { + value = Some(f()?); + Ok(value.ok_or(Error::SynthesisError)?.0) + })?; + self.cs.assign_advice(self.config.b, index, || { + Ok(value.ok_or(Error::SynthesisError)?.1) + })?; + self.cs.assign_advice(self.config.c, index, || { + Ok(value.ok_or(Error::SynthesisError)?.2) + })?; + + self.cs + .assign_fixed(self.config.sa, index, || Ok(FF::zero()))?; + self.cs + .assign_fixed(self.config.sb, index, || Ok(FF::zero()))?; + self.cs + .assign_fixed(self.config.sc, index, || Ok(FF::one()))?; + self.cs + .assign_fixed(self.config.sm, index, || Ok(FF::one()))?; + Ok(( + Variable(self.config.a, index), + Variable(self.config.b, index), + Variable(self.config.c, index), + )) + } + fn raw_add(&mut self, f: F) -> Result<(Variable, Variable, Variable), Error> + where + F: FnOnce() -> Result<(FF, FF, FF), Error>, + { + let index = self.current_gate; + self.current_gate += 1; + let mut value = None; + self.cs.assign_advice(self.config.a, index, || { + value = Some(f()?); + Ok(value.ok_or(Error::SynthesisError)?.0) + })?; + self.cs.assign_advice(self.config.b, index, || { + Ok(value.ok_or(Error::SynthesisError)?.1) + })?; + self.cs.assign_advice(self.config.c, index, || { + Ok(value.ok_or(Error::SynthesisError)?.2) + })?; + + self.cs + .assign_fixed(self.config.sa, index, || Ok(FF::one()))?; + self.cs + .assign_fixed(self.config.sb, index, || Ok(FF::one()))?; + self.cs + .assign_fixed(self.config.sc, index, || Ok(FF::one()))?; + self.cs + .assign_fixed(self.config.sm, index, || Ok(FF::zero()))?; + Ok(( + Variable(self.config.a, index), + Variable(self.config.b, index), + Variable(self.config.c, index), + )) + } + fn copy(&mut self, left: Variable, right: Variable) -> Result<(), Error> { + let left_column = match left.0 { + x if x == self.config.a => 0, + x if x == self.config.b => 1, + x if x == self.config.c => 2, + _ => unreachable!(), + }; + let right_column = match right.0 { + x if x == self.config.a => 0, + x if x == self.config.b => 1, + x if x == self.config.c => 2, + _ => unreachable!(), + }; + + self.cs + .copy(self.config.perm, left_column, left.1, right_column, right.1) + } +} + +impl Circuit for MyCircuit { + type Config = PLONKConfig; + + fn configure(meta: &mut ConstraintSystem) -> PLONKConfig { + let a = meta.advice_column(); + let b = meta.advice_column(); + let c = meta.advice_column(); + + let perm = meta.permutation(&[a, b, c]); + + let sm = meta.fixed_column(); + let sa = meta.fixed_column(); + let sb = meta.fixed_column(); + let sc = meta.fixed_column(); + + meta.create_gate(|meta| { + let a = meta.query_advice(a, 0); + let b = meta.query_advice(b, 0); + let c = meta.query_advice(c, 0); + + let sa = meta.query_fixed(sa, 0); + let sb = meta.query_fixed(sb, 0); + let sc = meta.query_fixed(sc, 0); + let sm = meta.query_fixed(sm, 0); + + a.clone() * sa + b.clone() * sb + a * b * sm + (c * sc * (-F::one())) + }); + + PLONKConfig { + a, + b, + c, + sa, + sb, + sc, + sm, + perm, + } + } + + fn synthesize(&self, cs: &mut impl Assignment, config: PLONKConfig) -> Result<(), Error> { + let mut cs = StandardPLONK::new(cs, config); + + for _ in 0..(1 << (self.k - 1)) { + let mut a_squared = None; + let (a0, _, c0) = cs.raw_multiply(|| { + a_squared = self.a.map(|a| a.square()); + Ok(( + self.a.ok_or(Error::SynthesisError)?, + self.a.ok_or(Error::SynthesisError)?, + a_squared.ok_or(Error::SynthesisError)?, + )) + })?; + let (a1, b1, _) = cs.raw_add(|| { + let fin = a_squared.and_then(|a2| self.a.map(|a| a + a2)); + Ok(( + self.a.ok_or(Error::SynthesisError)?, + a_squared.ok_or(Error::SynthesisError)?, + fin.ok_or(Error::SynthesisError)?, + )) + })?; + cs.copy(a0, a1)?; + cs.copy(b1, c0)?; + } + + Ok(()) + } +} + +fn main() { + let recorder = Box::leak(Box::new(ModelRecorder::default())); + metrics::set_recorder(recorder).unwrap(); + + // TODO: Make dynamic. + let k = 11; + + // Initialize the polynomial commitment parameters + let params: Params = Params::new::>(k); + + let empty_circuit: MyCircuit = MyCircuit { a: None, k }; + + // Initialize the proving key + let pk = keygen(¶ms, &empty_circuit).expect("keygen should not fail"); + + let circuit: MyCircuit = MyCircuit { + a: Some(Fp::random()), + k, + }; + + // Create a proof + let proof = Proof::create::, DummyHash, _>(¶ms, &pk, &circuit, &[]) + .expect("proof generation should not fail"); + + println!("[Prover] {}", recorder); + recorder.clear(); + + let msm = params.empty_msm(); + let guard = proof + .verify::, DummyHash>(¶ms, pk.get_vk(), msm, &[]) + .unwrap(); + let msm = guard.clone().use_challenges(); + assert!(msm.eval()); + + println!("[Verifier] {}", recorder); +} From d4424db8d41aa8c62781ac01c87eb98afff1bbec Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Tue, 10 Nov 2020 23:59:06 +0000 Subject: [PATCH 4/8] Collect some prover metrics --- src/plonk/prover.rs | 1 + src/poly/commitment.rs | 2 ++ src/poly/commitment/prover.rs | 4 ++++ src/poly/domain.rs | 3 +++ 4 files changed, 10 insertions(+) diff --git a/src/plonk/prover.rs b/src/plonk/prover.rs index 020c5b2..0f17481 100644 --- a/src/plonk/prover.rs +++ b/src/plonk/prover.rs @@ -141,6 +141,7 @@ impl Proof { C::Projective::batch_to_affine(&advice_commitments_projective, &mut advice_commitments); let advice_commitments = advice_commitments; drop(advice_commitments_projective); + metrics::counter!("advice_commitments", advice_commitments.len() as u64); for commitment in &advice_commitments { hash_point(&mut transcript, commitment)?; diff --git a/src/poly/commitment.rs b/src/poly/commitment.rs index c135ad0..ea2ad35 100644 --- a/src/poly/commitment.rs +++ b/src/poly/commitment.rs @@ -124,6 +124,7 @@ impl Params { poly: &Polynomial, r: Blind, ) -> C::Projective { + metrics::increment!("multiexp", "size" => format!("{}", poly.len() + 1), "fn" => "commit"); let mut tmp_scalars = Vec::with_capacity(poly.len() + 1); let mut tmp_bases = Vec::with_capacity(poly.len() + 1); @@ -144,6 +145,7 @@ impl Params { poly: &Polynomial, r: Blind, ) -> C::Projective { + metrics::increment!("multiexp", "size" => format!("{}", poly.len() + 1), "fn" => "commit_lagrange"); let mut tmp_scalars = Vec::with_capacity(poly.len() + 1); let mut tmp_bases = Vec::with_capacity(poly.len() + 1); diff --git a/src/poly/commitment/prover.rs b/src/poly/commitment/prover.rs index 1147c1e..6007fed 100644 --- a/src/poly/commitment/prover.rs +++ b/src/poly/commitment/prover.rs @@ -74,12 +74,14 @@ impl Proof { // // TODO: If we modify multiexp to take "extra" bases, we could speed // this piece up a bit by combining the multiexps. + metrics::counter!("multiexp", 2, "val" => "l/r", "size" => format!("{}", half)); let l = best_multiexp(&a[0..half], &g[half..]); let r = best_multiexp(&a[half..], &g[0..half]); let value_l = compute_inner_product(&a[0..half], &b[half..]); let value_r = compute_inner_product(&a[half..], &b[0..half]); let mut l_randomness = C::Scalar::random(); let r_randomness = C::Scalar::random(); + metrics::counter!("multiexp", 2, "val" => "l/r", "size" => "2"); let l = l + &best_multiexp(&[value_l, l_randomness], &[u, params.h]); let r = r + &best_multiexp(&[value_r, r_randomness], &[u, params.h]); let mut l = l.to_affine(); @@ -170,6 +172,7 @@ impl Proof { let d = C::Scalar::random(); let s = C::Scalar::random(); + metrics::increment!("multiexp", "val" => "delta", "size" => "3"); let delta = best_multiexp(&[d, d * &b, s], &[g, u, params.h]).to_affine(); let (delta_x, delta_y) = delta.get_xy().unwrap(); @@ -202,6 +205,7 @@ fn parallel_generator_collapse( ) { let len = g.len() / 2; let (mut g_lo, g_hi) = g.split_at_mut(len); + metrics::counter!("multiexp", len as u64, "size" => "2", "fn" => "parallel_generator_collapse"); parallelize(&mut g_lo, |g_lo, start| { let g_hi = &g_hi[start..]; diff --git a/src/poly/domain.rs b/src/poly/domain.rs index 6cc82f5..d336818 100644 --- a/src/poly/domain.rs +++ b/src/poly/domain.rs @@ -203,6 +203,7 @@ impl EvaluationDomain { assert_eq!(a.values.len(), 1 << self.k); // Perform inverse FFT to obtain the polynomial in coefficient form + metrics::increment!("ifft", "size" => format!("{}", a.len()), "fn" => "lagrange_to_coeff"); Self::ifft(&mut a.values, self.omega_inv, self.k, self.ifft_divisor); Polynomial { @@ -237,6 +238,7 @@ impl EvaluationDomain { Self::distribute_powers(&mut a.values, g); } a.values.resize(self.extended_len(), G::group_zero()); + metrics::increment!("fft", "size" => format!("{}", self.extended_len()), "fn" => "coeff_to_extended"); best_fft(&mut a.values, self.extended_omega, self.extended_k); Polynomial { @@ -255,6 +257,7 @@ impl EvaluationDomain { assert_eq!(a.values.len(), self.extended_len()); // Inverse FFT + metrics::increment!("ifft", "size" => format!("{}", a.len()), "fn" => "extended_to_coeff"); Self::ifft( &mut a.values, self.extended_omega_inv, From 236b3a6692f4ddea965a3244d51b2122fc0b5270 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Wed, 11 Nov 2020 15:16:52 +0000 Subject: [PATCH 5/8] Collect some verifier metrics --- src/poly/commitment/msm.rs | 1 + src/poly/commitment/verifier.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/src/poly/commitment/msm.rs b/src/poly/commitment/msm.rs index 42e6b87..08c0914 100644 --- a/src/poly/commitment/msm.rs +++ b/src/poly/commitment/msm.rs @@ -106,6 +106,7 @@ impl<'a, C: CurveAffine> MSM<'a, C> { assert_eq!(scalars.len(), len); + metrics::increment!("multiexp", "size" => format!("{}", len), "fn" => "MSM::eval"); bool::from(best_multiexp(&scalars, &bases).is_zero()) } } diff --git a/src/poly/commitment/verifier.rs b/src/poly/commitment/verifier.rs index 649c2c3..97e7d85 100644 --- a/src/poly/commitment/verifier.rs +++ b/src/poly/commitment/verifier.rs @@ -55,6 +55,7 @@ impl<'a, C: CurveAffine> Guard<'a, C> { pub fn compute_g(&self) -> C { let s = compute_s(&self.challenges_sq, self.allinv); + metrics::increment!("multiexp", "size" => format!("{}", s.len()), "fn" => "compute_g"); let mut tmp = best_multiexp(&s, &self.msm.params.g); tmp += self.msm.params.h; tmp.to_affine() From 37c4927dac8fd51208842a2ba845cbc84bd621cb Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Thu, 12 Nov 2020 15:27:46 +0000 Subject: [PATCH 6/8] model: Measure keygen and prover separately --- examples/performance_model.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/examples/performance_model.rs b/examples/performance_model.rs index 36d5c11..4b900fc 100644 --- a/examples/performance_model.rs +++ b/examples/performance_model.rs @@ -227,6 +227,9 @@ fn main() { // Initialize the proving key let pk = keygen(¶ms, &empty_circuit).expect("keygen should not fail"); + println!("[Keygen] {}", recorder); + recorder.clear(); + let circuit: MyCircuit = MyCircuit { a: Some(Fp::random()), k, From 3eb6712c6c3b794dc0ff9ff3e6c69d65d789c592 Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Mon, 23 Nov 2020 23:35:50 +0800 Subject: [PATCH 7/8] Add aux information to metrics --- benches/plonk.rs | 7 ++--- examples/performance_model.rs | 51 ++++++++++++++++++++++++++++++----- src/plonk/prover.rs | 1 + 3 files changed, 48 insertions(+), 11 deletions(-) diff --git a/benches/plonk.rs b/benches/plonk.rs index 02f5632..b92e398 100644 --- a/benches/plonk.rs +++ b/benches/plonk.rs @@ -239,11 +239,8 @@ fn bench_with_k(name: &str, k: u32, c: &mut Criterion) { }; // Create a proof - let proof = - Proof::create::, DummyHash, _>(¶ms, &pk, &circuit, &[]) - .expect("proof generation should not fail"); - - proof + Proof::create::, DummyHash, _>(¶ms, &pk, &circuit, &[]) + .expect("proof generation should not fail") }); }); diff --git a/examples/performance_model.rs b/examples/performance_model.rs index 4b900fc..9da022e 100644 --- a/examples/performance_model.rs +++ b/examples/performance_model.rs @@ -1,8 +1,8 @@ use halo2::{ - arithmetic::Field, + arithmetic::{Curve, Field}, model::ModelRecorder, plonk::*, - poly::commitment::Params, + poly::commitment::{Blind, Params}, transcript::DummyHash, tweedle::{EqAffine, Fp, Fq}, }; @@ -22,6 +22,7 @@ struct PLONKConfig { sb: Column, sc: Column, sm: Column, + sp: Column, perm: usize, } @@ -34,6 +35,9 @@ trait StandardCS { where F: FnOnce() -> Result<(FF, FF, FF), Error>; fn copy(&mut self, a: Variable, b: Variable) -> Result<(), Error>; + fn public_input(&mut self, f: F) -> Result + where + F: FnOnce() -> Result; } struct MyCircuit { @@ -141,6 +145,18 @@ impl<'a, FF: Field, CS: Assignment> StandardCS for StandardPLONK<'a, FF, self.cs .copy(self.config.perm, left_column, left.1, right_column, right.1) } + fn public_input(&mut self, f: F) -> Result + where + F: FnOnce() -> Result, + { + let index = self.current_gate; + self.current_gate += 1; + self.cs.assign_advice(self.config.a, index, || f())?; + self.cs + .assign_fixed(self.config.sp, index, || Ok(FF::one()))?; + + Ok(Variable(self.config.a, index)) + } } impl Circuit for MyCircuit { @@ -150,6 +166,7 @@ impl Circuit for MyCircuit { let a = meta.advice_column(); let b = meta.advice_column(); let c = meta.advice_column(); + let p = meta.aux_column(); let perm = meta.permutation(&[a, b, c]); @@ -157,6 +174,7 @@ impl Circuit for MyCircuit { let sa = meta.fixed_column(); let sb = meta.fixed_column(); let sc = meta.fixed_column(); + let sp = meta.fixed_column(); meta.create_gate(|meta| { let a = meta.query_advice(a, 0); @@ -171,6 +189,14 @@ impl Circuit for MyCircuit { a.clone() * sa + b.clone() * sb + a * b * sm + (c * sc * (-F::one())) }); + meta.create_gate(|meta| { + let a = meta.query_advice(a, 0); + let p = meta.query_aux(p, 0); + let sp = meta.query_fixed(sp, 0); + + sp * (a + p * (-F::one())) + }); + PLONKConfig { a, b, @@ -179,6 +205,7 @@ impl Circuit for MyCircuit { sb, sc, sm, + sp, perm, } } @@ -186,7 +213,9 @@ impl Circuit for MyCircuit { fn synthesize(&self, cs: &mut impl Assignment, config: PLONKConfig) -> Result<(), Error> { let mut cs = StandardPLONK::new(cs, config); - for _ in 0..(1 << (self.k - 1)) { + let _ = cs.public_input(|| Ok(F::one() + F::one()))?; + + for _ in 0..((1 << (self.k - 1)) - 1) { let mut a_squared = None; let (a0, _, c0) = cs.raw_multiply(|| { a_squared = self.a.map(|a| a.square()); @@ -230,21 +259,31 @@ fn main() { println!("[Keygen] {}", recorder); recorder.clear(); + let mut pubinputs = pk.get_vk().get_domain().empty_lagrange(); + pubinputs[0] = Fp::one(); + pubinputs[0] += Fp::one(); + let pubinput = params + .commit_lagrange(&pubinputs, Blind::default()) + .to_affine(); + recorder.clear(); + let circuit: MyCircuit = MyCircuit { a: Some(Fp::random()), k, }; // Create a proof - let proof = Proof::create::, DummyHash, _>(¶ms, &pk, &circuit, &[]) - .expect("proof generation should not fail"); + let proof = + Proof::create::, DummyHash, _>(¶ms, &pk, &circuit, &[pubinputs]) + .expect("proof generation should not fail"); println!("[Prover] {}", recorder); recorder.clear(); + let pubinput_slice = &[pubinput]; let msm = params.empty_msm(); let guard = proof - .verify::, DummyHash>(¶ms, pk.get_vk(), msm, &[]) + .verify::, DummyHash>(¶ms, pk.get_vk(), msm, pubinput_slice) .unwrap(); let msm = guard.clone().use_challenges(); assert!(msm.eval()); diff --git a/src/plonk/prover.rs b/src/plonk/prover.rs index 0f17481..24e6856 100644 --- a/src/plonk/prover.rs +++ b/src/plonk/prover.rs @@ -103,6 +103,7 @@ impl Proof { C::Projective::batch_to_affine(&aux_commitments_projective, &mut aux_commitments); let aux_commitments = aux_commitments; drop(aux_commitments_projective); + metrics::counter!("aux_commitments", aux_commitments.len() as u64); for commitment in &aux_commitments { hash_point(&mut transcript, commitment)?; From 9a4f27056cbcfb21c84e97494394dba16bee2f2d Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Tue, 24 Nov 2020 17:56:33 +0000 Subject: [PATCH 8/8] Fix clippy lint in metrics model doctest --- src/model.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/model.rs b/src/model.rs index c979bce..1bbdc8c 100644 --- a/src/model.rs +++ b/src/model.rs @@ -14,14 +14,15 @@ use metrics::{Key, Recorder, Unit}; /// ``` /// use halo2::model::ModelRecorder; /// -/// fn main() { -/// let recorder = Box::leak(Box::new(ModelRecorder::default())); -/// metrics::set_recorder(recorder).unwrap(); +/// let recorder = Box::leak(Box::new(ModelRecorder::default())); +/// metrics::set_recorder(recorder).unwrap(); /// -/// // Create circuit, build and/or verify proof. +/// // Create circuit, build and/or verify proof. /// -/// println!("{}", recorder); -/// } +/// println!("{}", recorder); +/// recorder.clear(); +/// +/// // Perform another operation to collect separate metrics. /// ``` #[derive(Debug)] pub struct ModelRecorder {