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

Commit 8d230b3

Browse files
authored
SubCircuit halo2 stats (#1763)
### 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.
1 parent 2723775 commit 8d230b3

File tree

13 files changed

+782
-97
lines changed

13 files changed

+782
-97
lines changed

gadgets/src/binary_number.rs

Lines changed: 60 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use halo2_proofs::{
99
plonk::{Advice, Column, ConstraintSystem, Error, Expression, Fixed, VirtualCells},
1010
poly::Rotation,
1111
};
12-
use std::{collections::BTreeSet, marker::PhantomData};
12+
use std::{collections::BTreeSet, marker::PhantomData, ops::Deref};
1313
use strum::IntoEnumIterator;
1414

1515
/// Helper trait that implements functionality to represent a generic type as
@@ -34,11 +34,66 @@ where
3434
}
3535
}
3636

37+
/// Columns of the binary number chip. This can be instantiated without the associated constraints
38+
/// of the BinaryNumberChip in order to be used as part of a shared table for unit tests.
39+
#[derive(Clone, Copy, Debug)]
40+
pub struct BinaryNumberBits<const N: usize>(
41+
/// Must be constrained to be binary for correctness.
42+
pub [Column<Advice>; N],
43+
);
44+
45+
impl<const N: usize> Deref for BinaryNumberBits<N> {
46+
type Target = [Column<Advice>; N];
47+
48+
fn deref(&self) -> &Self::Target {
49+
&self.0
50+
}
51+
}
52+
53+
impl<const N: usize> BinaryNumberBits<N> {
54+
/// Construct a new BinaryNumberBits without adding any constraints.
55+
pub fn construct<F: Field>(meta: &mut ConstraintSystem<F>) -> Self {
56+
Self([0; N].map(|_| meta.advice_column()))
57+
}
58+
59+
/// Assign a value to the binary number bits. A generic type that implements
60+
/// the AsBits trait can be provided for assignment.
61+
pub fn assign<F: Field, T: AsBits<N>>(
62+
&self,
63+
region: &mut Region<'_, F>,
64+
offset: usize,
65+
value: &T,
66+
) -> Result<(), Error> {
67+
for (&bit, &column) in value.as_bits().iter().zip(self.iter()) {
68+
region.assign_advice(
69+
|| format!("binary number {:?}", column),
70+
column,
71+
offset,
72+
|| Value::known(F::from(bit as u64)),
73+
)?;
74+
}
75+
Ok(())
76+
}
77+
78+
/// Returns the expression value of the bits at the given rotation.
79+
pub fn value<F: Field>(
80+
&self,
81+
rotation: Rotation,
82+
) -> impl FnOnce(&mut VirtualCells<'_, F>) -> Expression<F> {
83+
let bits = self.0;
84+
move |meta: &mut VirtualCells<'_, F>| {
85+
let bits = bits.map(|bit| meta.query_advice(bit, rotation));
86+
bits.iter()
87+
.fold(0.expr(), |result, bit| bit.clone() + result * 2.expr())
88+
}
89+
}
90+
}
91+
3792
/// Config for the binary number chip.
3893
#[derive(Clone, Copy, Debug)]
3994
pub struct BinaryNumberConfig<T, const N: usize> {
4095
/// Must be constrained to be binary for correctness.
41-
pub bits: [Column<Advice>; N],
96+
pub bits: BinaryNumberBits<N>,
4297
_marker: PhantomData<T>,
4398
}
4499

@@ -51,12 +106,7 @@ where
51106
&self,
52107
rotation: Rotation,
53108
) -> impl FnOnce(&mut VirtualCells<'_, F>) -> Expression<F> {
54-
let bits = self.bits;
55-
move |meta: &mut VirtualCells<'_, F>| {
56-
let bits = bits.map(|bit| meta.query_advice(bit, rotation));
57-
bits.iter()
58-
.fold(0.expr(), |result, bit| bit.clone() + result * 2.expr())
59-
}
109+
self.bits.value(rotation)
60110
}
61111

62112
/// Return the constant that represents a given value. To be compared with the value expression.
@@ -140,10 +190,10 @@ where
140190
/// Configure constraints for the binary number chip.
141191
pub fn configure(
142192
meta: &mut ConstraintSystem<F>,
193+
bits: BinaryNumberBits<N>,
143194
selector: Column<Fixed>,
144195
value: Option<Column<Advice>>,
145196
) -> BinaryNumberConfig<T, N> {
146-
let bits = [0; N].map(|_| meta.advice_column());
147197
bits.map(|bit| {
148198
meta.create_gate("bit column is 0 or 1", |meta| {
149199
let selector = meta.query_fixed(selector, Rotation::cur());
@@ -194,15 +244,7 @@ where
194244
offset: usize,
195245
value: &T,
196246
) -> Result<(), Error> {
197-
for (&bit, &column) in value.as_bits().iter().zip(&self.config.bits) {
198-
region.assign_advice(
199-
|| format!("binary number {:?}", column),
200-
column,
201-
offset,
202-
|| Value::known(F::from(bit as u64)),
203-
)?;
204-
}
205-
Ok(())
247+
self.config.bits.assign(region, offset, value)
206248
}
207249
}
208250

integration-tests/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ serde_json = { version = "1.0.66", features = ["unbounded_depth"] }
1313
serde = { version = "1.0.130", features = ["derive"] }
1414
bus-mapping = { path = "../bus-mapping", features = ["test"] }
1515
eth-types = { path = "../eth-types" }
16-
zkevm-circuits = { path = "../zkevm-circuits", features = ["test-circuits"] }
16+
zkevm-circuits = { path = "../zkevm-circuits", features = ["test-circuits", "mock-challenge"] }
1717
tokio = { version = "1.13", features = ["macros", "rt-multi-thread"] }
1818
url = "2.2.2"
1919
pretty_assertions = "1.0.0"

zkevm-circuits/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,8 @@ test-circuits = []
6363
# Test utilities for testool crate to consume
6464
test-util = ["dep:mock"]
6565
warn-unimplemented = ["eth-types/warn-unimplemented"]
66-
stats = ["warn-unimplemented", "dep:cli-table"]
66+
stats = ["warn-unimplemented", "dep:cli-table", "test-util", "test-circuits", "mock-challenge"]
67+
mock-challenge = []
6768

6869
[[bin]]
6970
name = "stats"

0 commit comments

Comments
 (0)