Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

dev::cost: Estimate prover work for a given circuit #753

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
157 changes: 157 additions & 0 deletions halo2_proofs/src/dev/cost.rs
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,97 @@ impl<G: PrimeGroup, ConcreteCircuit: Circuit<G::Scalar>> CircuitCost<G, Concrete
_marker: PhantomData::default(),
}
}

/// Returns the marginal prover work per instance of this circuit.
pub fn marginal_prover_work(&self) -> MarginalProverWork {
// Instance columns
// - 1 `lagrange_to_coeff` for each instance column
// - 1 `coeff_to_extended` for each instance column
// - 1 `commit_lagrange` for each instance column
let instance = ProverWorkContribution::new(
self.num_instance_columns,
self.num_instance_columns,
self.num_instance_columns,
);
// Instance columns
// - 1 `lagrange_to_coeff` for each advice column
// - 1 `coeff_to_extended` for each advice column
// - 1 `commit_lagrange` for each advice column
let advice = ProverWorkContribution::new(
self.num_advice_columns,
self.num_advice_columns,
self.num_advice_columns,
);
// Lookup arguments
// - for the permuted input expression:
// - 1 `lagrange_to_coeff`
// - 1 `coeff_to_extended`
// - 1 `commit_lagrange`
// - for the permuted table expression:
// - 1 `lagrange_to_coeff`
// - 1 `coeff_to_extended`
// - 1 `commit_lagrange`
// - for the lookup product:
// - 1 `lagrange_to_coeff`
// - 1 `coeff_to_extended`
// - 1 `commit_lagrange`
let lookups =
ProverWorkContribution::new(self.lookups * 3, self.lookups * 3, self.lookups * 3);
// For each chunk:
// - 1 `lagrange_to_coeff`
// - 1 `coeff_to_extended`
// - 1 `commit_lagrange`
let equality = ProverWorkContribution::new(
self.permutation_chunks(),
self.permutation_chunks(),
self.permutation_chunks(),
);

MarginalProverWork {
instance,
advice,
lookups,
equality,
}
}

/// Returns the prover work for the given number of instances of this circuit.
pub fn prover_work(&self, instances: usize) -> ProverWork {
let mut extended_k = self.k;
let n = 1u64 << self.k;
while (1 << extended_k) < (n * (self.max_deg as u64 - 1)) {
extended_k += 1;
}

let marginal = self.marginal_prover_work();

// Vanishing argument
// - 1 `extended_to_coeff` on `h_poly`
// - 1 commitment to random_poly
// - (max_deg - 1) commitments (1 for each `h(X)` piece)
let vanishing = ProverWorkContribution::new(0, 1, 1 + (self.max_deg - 1));

// Multiopen argument
// - 1 commitment to q_prime
let multiopen = ProverWorkContribution::new(0, 0, 1);

// IPA polynomial commitment
// - 1 commitment to s_poly
// - the number of scalar multiplications resulting from all rounds of IPA
// is equivalent to about 2 * (2 * 2^k) (excluding some constants)
let polycomm = ProverWorkContribution::new(0, 0, 4);

ProverWork {
extended_k,
instance: marginal.instance * instances,
advice: marginal.advice * instances,
lookups: marginal.lookups * instances,
equality: marginal.equality * instances,
vanishing,
multiopen,
polycomm,
}
}
}

/// (commitments, evaluations)
Expand Down Expand Up @@ -510,6 +601,72 @@ impl<G: PrimeGroup> From<ProofSize<G>> for usize {
}
}

#[derive(Debug)]
struct ProverWorkContribution {
// Number of FFTs in the 2^k domain
ffts: usize,
// Number of FFTs in the 2^extended_k domain
extended_ffts: usize,
// Number of multi-scalar multiplications of length 2^k
msms: usize,
Comment on lines +606 to +611
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should also account for the cost of the transcript operations, which can be sizeable.

I don't know if the above is sufficiently granular to get a useable estimate of prover costs. OTOH, it may be sufficiently high-level that people can reliably just plug in their own estimates for the cost of each of these things, and get a reasonable result.

Your PR also reminded me that I started implementing verifier time cost estimation almost a year ago, but haven't had time to finish it. I was trying to base my estimates on the number of additions and multiplications, but deriving equations for them was rather laborious. I've opened #754 with my draft changes for comparison.

We should test both approaches against several different circuits to check how far off their estimates are.

}

impl ProverWorkContribution {
fn new(ffts: usize, extended_ffts: usize, msms: usize) -> Self {
ProverWorkContribution {
ffts,
extended_ffts,
msms,
}
}
}

impl Add for ProverWorkContribution {
type Output = Self;

fn add(self, rhs: Self) -> Self::Output {
Self {
ffts: self.ffts + rhs.ffts,
extended_ffts: self.extended_ffts + rhs.extended_ffts,
msms: self.msms + rhs.msms,
}
}
}

impl Mul<usize> for ProverWorkContribution {
type Output = Self;

fn mul(self, instances: usize) -> Self::Output {
Self {
ffts: self.ffts * instances,
extended_ffts: self.extended_ffts * instances,
msms: self.msms * instances,
}
}
}

/// The marginal prover work to generate a Halo 2 proof, broken down into its contributing factors.
#[derive(Debug)]
pub struct MarginalProverWork {
instance: ProverWorkContribution,
advice: ProverWorkContribution,
lookups: ProverWorkContribution,
equality: ProverWorkContribution,
}

/// The prover work to generate a Halo 2 proof, broken down into its contributing factors.
#[derive(Debug)]
pub struct ProverWork {
extended_k: u32,
instance: ProverWorkContribution,
advice: ProverWorkContribution,
lookups: ProverWorkContribution,
equality: ProverWorkContribution,
vanishing: ProverWorkContribution,
multiopen: ProverWorkContribution,
polycomm: ProverWorkContribution,
}

#[cfg(test)]
mod tests {
use pasta_curves::{Eq, Fp};
Expand Down