Skip to content
This repository has been archived by the owner on Jul 5, 2024. It is now read-only.

Commit

Permalink
SubCircuit halo2 stats (#1763)
Browse files Browse the repository at this point in the history
### Description

Add a new command to the stats binary that reports halo2 circuit stats
for each circuit, and the supercircuit.

### Issue Link

Resolve
#1645

### Type of change

- [x] New feature (non-breaking change which adds functionality)

### Contents

- Added new module in `stats` that records halo2 circuit stats from the
configured `ConstraintSystem`, based on Scroll's code from
https://github.com/scroll-tech/zkevm-circuits/blob/7d9bc181953cfc6e7baf82ff0ce651281fd70a8a/zkevm-circuits/src/util.rs#L294
- The collection of stats works in two phases, first it records
per-circuit stats and then total stats (to get super circuit stats). The
per-circuit stats works by configuring all the shared tables into the
same `ConstraintSystem` (and obtaining stats via deltas), and then
configuring each sub-circuit starting from the shared tables
`ConstraintSystems` each time.
- In order to separate the shared column configuration from the
sub-circuit and constraints configuration, I had to extract the column
definition of the BinaryNumberChip into BinaryNumberBits so that the
CopyTable can be constructed without any constraint.
- Added peak memory estimation based on @han0110's analysis

### Results

These results are for `k = 26`. At the current worst-case estimation of
At 0.0139 gas/row this gives us 2^26 * 0.0139 = ~900k gas. For 30M gas
we would need 33 chunks like that.


|circuit|constraints|rots|min/max(rots)|fix_cols|sels|advs|perms|lookups|degree|mem_gb|
|:----|:----|:----|:----|:----|:----|:----|:----|:----|:----|:----|
|tx_table|0|0|0/0|1|0|4|0|0|3|58|
|wd_table|0|0|0/0|0|0|5|0|0|3|56|
|rw_table|0|0|0/0|0|0|14|0|0|3|110|
|mpt_table|0|0|0/0|0|0|12|0|0|3|98|
|bytecode_table|0|0|0/0|0|0|6|0|0|3|62|
|block_table|0|0|0/0|2|0|2|0|0|3|54|
|copy_table|0|0|0/0|1|0|12|0|0|3|106|
|exp_table|0|0|0/0|1|0|5|0|0|3|64|
|keccak_table|0|0|0/0|0|0|5|0|0|3|56|
|sig_table|0|0|0/0|1|0|9|0|0|3|88|
|u8_table|0|0|0/0|1|0|0|0|0|3|34|
|u10_table|0|0|0/0|1|0|0|0|0|3|34|
|u16_table|0|0|0/0|1|0|0|0|0|3|34|
|keccak|2523|105|-89/207|18|0|198|0|123|4|3000|
|pi|21|2|0/1|3|7|10|15|3|9|754|
|tx|2|2|0/1|13|4|6|9|6|6|780|
|bytecode|23|2|0/1|5|0|6|0|2|8|342|
|copy|34|3|0/2|1|1|14|0|12|9|466|
|state|203|3|-1/1|3|0|49|0|36|10|2224|
|exp|44|11|0/10|0|1|10|0|0|5|142|
|evm|42318|21|0/20|5|3|131|3|53|7|2976|
|super|45168|106|-89/207|57|16|498|25|235|10|21736|


PD: Yes, the estimation is 21TB of memory for a super circuit proof :(
Nevertheless there are various things we can do to improve this.
  • Loading branch information
ed255 authored Feb 22, 2024
1 parent 2723775 commit 8d230b3
Show file tree
Hide file tree
Showing 13 changed files with 782 additions and 97 deletions.
78 changes: 60 additions & 18 deletions gadgets/src/binary_number.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use halo2_proofs::{
plonk::{Advice, Column, ConstraintSystem, Error, Expression, Fixed, VirtualCells},
poly::Rotation,
};
use std::{collections::BTreeSet, marker::PhantomData};
use std::{collections::BTreeSet, marker::PhantomData, ops::Deref};
use strum::IntoEnumIterator;

/// Helper trait that implements functionality to represent a generic type as
Expand All @@ -34,11 +34,66 @@ where
}
}

/// Columns of the binary number chip. This can be instantiated without the associated constraints
/// of the BinaryNumberChip in order to be used as part of a shared table for unit tests.
#[derive(Clone, Copy, Debug)]
pub struct BinaryNumberBits<const N: usize>(
/// Must be constrained to be binary for correctness.
pub [Column<Advice>; N],
);

impl<const N: usize> Deref for BinaryNumberBits<N> {
type Target = [Column<Advice>; N];

fn deref(&self) -> &Self::Target {
&self.0
}
}

impl<const N: usize> BinaryNumberBits<N> {
/// Construct a new BinaryNumberBits without adding any constraints.
pub fn construct<F: Field>(meta: &mut ConstraintSystem<F>) -> Self {
Self([0; N].map(|_| meta.advice_column()))
}

/// Assign a value to the binary number bits. A generic type that implements
/// the AsBits trait can be provided for assignment.
pub fn assign<F: Field, T: AsBits<N>>(
&self,
region: &mut Region<'_, F>,
offset: usize,
value: &T,
) -> Result<(), Error> {
for (&bit, &column) in value.as_bits().iter().zip(self.iter()) {
region.assign_advice(
|| format!("binary number {:?}", column),
column,
offset,
|| Value::known(F::from(bit as u64)),
)?;
}
Ok(())
}

/// Returns the expression value of the bits at the given rotation.
pub fn value<F: Field>(
&self,
rotation: Rotation,
) -> impl FnOnce(&mut VirtualCells<'_, F>) -> Expression<F> {
let bits = self.0;
move |meta: &mut VirtualCells<'_, F>| {
let bits = bits.map(|bit| meta.query_advice(bit, rotation));
bits.iter()
.fold(0.expr(), |result, bit| bit.clone() + result * 2.expr())
}
}
}

/// Config for the binary number chip.
#[derive(Clone, Copy, Debug)]
pub struct BinaryNumberConfig<T, const N: usize> {
/// Must be constrained to be binary for correctness.
pub bits: [Column<Advice>; N],
pub bits: BinaryNumberBits<N>,
_marker: PhantomData<T>,
}

Expand All @@ -51,12 +106,7 @@ where
&self,
rotation: Rotation,
) -> impl FnOnce(&mut VirtualCells<'_, F>) -> Expression<F> {
let bits = self.bits;
move |meta: &mut VirtualCells<'_, F>| {
let bits = bits.map(|bit| meta.query_advice(bit, rotation));
bits.iter()
.fold(0.expr(), |result, bit| bit.clone() + result * 2.expr())
}
self.bits.value(rotation)
}

/// Return the constant that represents a given value. To be compared with the value expression.
Expand Down Expand Up @@ -140,10 +190,10 @@ where
/// Configure constraints for the binary number chip.
pub fn configure(
meta: &mut ConstraintSystem<F>,
bits: BinaryNumberBits<N>,
selector: Column<Fixed>,
value: Option<Column<Advice>>,
) -> BinaryNumberConfig<T, N> {
let bits = [0; N].map(|_| meta.advice_column());
bits.map(|bit| {
meta.create_gate("bit column is 0 or 1", |meta| {
let selector = meta.query_fixed(selector, Rotation::cur());
Expand Down Expand Up @@ -194,15 +244,7 @@ where
offset: usize,
value: &T,
) -> Result<(), Error> {
for (&bit, &column) in value.as_bits().iter().zip(&self.config.bits) {
region.assign_advice(
|| format!("binary number {:?}", column),
column,
offset,
|| Value::known(F::from(bit as u64)),
)?;
}
Ok(())
self.config.bits.assign(region, offset, value)
}
}

Expand Down
2 changes: 1 addition & 1 deletion integration-tests/Cargo.toml