Skip to content

Commit

Permalink
Benchmarking tool using cost estimator
Browse files Browse the repository at this point in the history
Co-Authored-By: Iñigo Querejeta Azurmendi <[email protected]>
  • Loading branch information
rybla and iquerejeta committed Dec 4, 2023
1 parent 6fc6d7c commit 25238ea
Show file tree
Hide file tree
Showing 5 changed files with 424 additions and 252 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ Cargo.lock
.vscode
**/*.html
.DS_Store
halo2_gadgets/proptest-regressions/utilities/
2 changes: 2 additions & 0 deletions halo2_proofs/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ blake2b_simd = "1" # MSRV 1.66.0
sha3 = "0.9.1"
rand_chacha = "0.3"
maybe-rayon = { version = "0.1.0", default-features = false }
rand = "0.8"
csv = "1.3.0"

# Developer tooling dependencies
plotters = { version = "0.3.0", default-features = false, optional = true }
Expand Down
267 changes: 15 additions & 252 deletions halo2_proofs/examples/cost-model.rs
Original file line number Diff line number Diff line change
@@ -1,53 +1,9 @@
use std::{
cmp, fmt, iter,
num::ParseIntError,
str::FromStr,
time::{Duration, Instant},
};

use ff::Field;
use group::{Curve, Group};
use gumdrop::Options;
use halo2_proofs::arithmetic::best_multiexp;
use halo2curves::pasta::pallas;

struct Estimator {
/// Scalars for estimating multiexp performance.
multiexp_scalars: Vec<pallas::Scalar>,
/// Bases for estimating multiexp performance.
multiexp_bases: Vec<pallas::Affine>,
}

impl fmt::Debug for Estimator {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Estimator")
}
}

impl Estimator {
fn random(k: usize) -> Self {
let max_size = 1 << (k + 1);
let mut rng = rand_core::OsRng;

Estimator {
multiexp_scalars: (0..max_size)
.map(|_| pallas::Scalar::random(&mut rng))
.collect(),
multiexp_bases: (0..max_size)
.map(|_| pallas::Point::random(&mut rng).to_affine())
.collect(),
}
}

fn multiexp(&self, size: usize) -> Duration {
let start = Instant::now();
best_multiexp(&self.multiexp_scalars[..size], &self.multiexp_bases[..size]);
Instant::now().duration_since(start)
}
}
use halo2_proofs::dev::cost_model::*;

/// CLI Options to build a circuit specifiction to measure the cost model of.
#[derive(Debug, Options)]
struct CostOptions {
struct CliCostOptions {
#[options(help = "Print this message.")]
help: bool,

Expand Down Expand Up @@ -85,216 +41,23 @@ struct CostOptions {
k: usize,
}

#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
struct Poly {
rotations: Vec<isize>,
}

impl FromStr for Poly {
type Err = ParseIntError;

fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut rotations: Vec<isize> =
s.split(',').map(|r| r.parse()).collect::<Result<_, _>>()?;
rotations.sort_unstable();
Ok(Poly { rotations })
}
}

#[derive(Debug)]
struct Lookup {
_columns: usize,
input_deg: usize,
table_deg: usize,
}

impl FromStr for Lookup {
type Err = ParseIntError;

fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut parts = s.split(',');
let _columns = parts.next().unwrap().parse()?;
let input_deg = parts.next().unwrap().parse()?;
let table_deg = parts.next().unwrap().parse()?;
Ok(Lookup {
_columns,
input_deg,
table_deg,
})
}
}

impl Lookup {
fn required_degree(&self) -> usize {
2 + cmp::max(1, self.input_deg) + cmp::max(1, self.table_deg)
}

fn queries(&self) -> impl Iterator<Item = Poly> {
// - product commitments at x and x_inv
// - input commitments at x and x_inv
// - table commitments at x
let product = "0,-1".parse().unwrap();
let input = "0,-1".parse().unwrap();
let table = "0".parse().unwrap();

iter::empty()
.chain(Some(product))
.chain(Some(input))
.chain(Some(table))
}
}

#[derive(Debug)]
struct Permutation {
columns: usize,
}

impl FromStr for Permutation {
type Err = ParseIntError;

fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(Permutation {
columns: s.parse()?,
})
}
}

impl Permutation {
fn required_degree(&self) -> usize {
cmp::max(self.columns + 1, 2)
}

fn queries(&self) -> impl Iterator<Item = Poly> {
// - product commitments at x and x_inv
// - polynomial commitments at x
let product = "0,-1".parse().unwrap();
let poly = "0".parse().unwrap();

iter::empty()
.chain(Some(product))
.chain(iter::repeat(poly).take(self.columns))
}
}

#[derive(Debug)]
struct Circuit {
/// Power-of-2 bound on the number of rows in the circuit.
k: usize,
/// Maximum degree of the circuit.
max_deg: usize,
/// Number of advice columns.
advice_columns: usize,
/// Number of lookup arguments.
lookups: usize,
/// Equality constraint permutation arguments.
permutations: Vec<Permutation>,
/// Number of distinct column queries across all gates.
column_queries: usize,
/// Number of distinct sets of points in the multiopening argument.
point_sets: usize,

estimator: Estimator,
}

impl From<CostOptions> for Circuit {
fn from(opts: CostOptions) -> Self {
let max_deg = [1, opts.gate_degree]
.iter()
.cloned()
.chain(opts.lookup.iter().map(|l| l.required_degree()))
.chain(opts.permutation.iter().map(|p| p.required_degree()))
.max()
.unwrap();

let mut queries: Vec<_> = iter::empty()
.chain(opts.advice.iter())
.chain(opts.instance.iter())
.chain(opts.fixed.iter())
.cloned()
.chain(opts.lookup.iter().flat_map(|l| l.queries()))
.chain(opts.permutation.iter().flat_map(|p| p.queries()))
.chain(iter::repeat("0".parse().unwrap()).take(max_deg - 1))
.collect();

let column_queries = queries.len();
queries.sort_unstable();
queries.dedup();
let point_sets = queries.len();

Circuit {
k: opts.k,
max_deg,
advice_columns: opts.advice.len(),
lookups: opts.lookup.len(),
permutations: opts.permutation,
column_queries,
point_sets,
estimator: Estimator::random(opts.k),
impl CliCostOptions {
fn to_cost_options(&self) -> CostOptions {
CostOptions {
advice: self.advice.clone(),
instance: self.instance.clone(),
fixed: self.fixed.clone(),
gate_degree: self.gate_degree,
lookup: self.lookup.clone(),
permutation: self.permutation.clone(),
k: self.k,
}
}
}

impl Circuit {
fn proof_size(&self) -> usize {
let size = |points: usize, scalars: usize| points * 32 + scalars * 32;

// PLONK:
// - 32 bytes (commitment) per advice column
// - 3 * 32 bytes (commitments) + 5 * 32 bytes (evals) per lookup argument
// - 32 bytes (commitment) + 2 * 32 bytes (evals) per permutation argument
// - 32 bytes (eval) per column per permutation argument
let plonk = size(1, 0) * self.advice_columns
+ size(3, 5) * self.lookups
+ self
.permutations
.iter()
.map(|p| size(1, 2 + p.columns))
.sum::<usize>();

// Vanishing argument:
// - (max_deg - 1) * 32 bytes (commitments) + (max_deg - 1) * 32 bytes (h_evals)
// for quotient polynomial
// - 32 bytes (eval) per column query
let vanishing = size(self.max_deg - 1, self.max_deg - 1) + size(0, self.column_queries);

// Multiopening argument:
// - f_commitment (32 bytes)
// - 32 bytes (evals) per set of points in multiopen argument
let multiopen = size(1, self.point_sets);

// Polycommit:
// - s_poly commitment (32 bytes)
// - inner product argument (k rounds * 2 * 32 bytes)
// - a (32 bytes)
// - xi (32 bytes)
let polycomm = size(1 + 2 * self.k, 2);

plonk + vanishing + multiopen + polycomm
}

fn verification_time(&self) -> Duration {
// TODO: Estimate cost of BLAKE2b.

// TODO: This isn't accurate; most of these will have zero scalars.
let g_scalars = 1 << self.k;

// - f_commitment
// - q_commitments
let multiopen = 1 + self.column_queries;

// - \iota
// - Rounds
// - H
// - U
let polycomm = 1 + (2 * self.k) + 1 + 1;

self.estimator.multiexp(g_scalars + multiopen + polycomm)
}
}

fn main() {
let opts = CostOptions::parse_args_default_or_exit();
let c = Circuit::from(opts);
let opts = CliCostOptions::parse_args_default_or_exit();
let c = ModelCircuit::from(opts.to_cost_options());
println!("{:#?}", c);
println!("Proof size: {} bytes", c.proof_size());
println!(
Expand Down
2 changes: 2 additions & 0 deletions halo2_proofs/src/dev.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ pub use failure::{FailureLocation, VerifyFailure};
pub mod cost;
pub use cost::CircuitCost;

pub mod cost_model;

mod gates;
pub use gates::CircuitGates;

Expand Down
Loading

0 comments on commit 25238ea

Please sign in to comment.