Skip to content

Commit 1123789

Browse files
spherelakinovaktherealyingtong
authored
Multi-input mv-lookup. (#49)
* Add mv_lookup.rs * mv_lookup::prover, mv_lookup::verifier * Replace lookup with mv_lookup * replace halo2 with mv lookup Co-authored-by: ying tong <[email protected]> * cleanups Co-authored-by: ying tong <[email protected]> * ConstraintSystem: setup lookup_tracker Co-authored-by: Andrija <[email protected]> * mv_lookup::hybrid_prover Co-authored-by: Andrija <[email protected]> * WIP * mv_multi_lookup: enable lookup caching Co-authored-by: therealyingtong <[email protected]> * Rename hybrid_lookup -> lookup * Chunk lookups using user-provided minimum degree Co-authored-by: Andrija <[email protected]> * mv_lookup bench Co-authored-by: Andrija <[email protected]> * Introduce counter feature for FFTs and MSMs Co-authored-by: Andrija <[email protected]> * Fix off-by-one errors in chunk_lookup Co-authored-by: Andrija <[email protected]> * bench wip * time evaluate_h * KZG * more efficient batch inversion * extended lookup example * Finalize mv lookup Author: therealyingtong <[email protected]> * Remove main/ * Fix according to the comments * replace scan with parallel grand sum computation * Revert Cargo.lock * mv lookup Argument name * parallel batch invert --------- Co-authored-by: Andrija <[email protected]> Co-authored-by: ying tong <[email protected]> Co-authored-by: therealyingtong <[email protected]>
1 parent 92fe9b3 commit 1123789

File tree

20 files changed

+1645
-286
lines changed

20 files changed

+1645
-286
lines changed

Cargo.lock

Lines changed: 2 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

halo2_proofs/Cargo.toml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,10 @@ harness = false
3939
name = "dev_lookup"
4040
harness = false
4141

42+
[[bench]]
43+
name = "lookups"
44+
harness = false
45+
4246
[[bench]]
4347
name = "fft"
4448
harness = false
@@ -63,12 +67,14 @@ crossbeam = "0.8.0"
6367
# Developer tooling dependencies
6468
plotters = { version = "0.3.0", optional = true }
6569
tabbycat = { version = "0.1", features = ["attributes"], optional = true }
70+
lazy_static = { version = "1", optional = true }
6671
log = "0.4.17"
6772

6873
# timer
6974
ark-std = { version = "0.3.0" }
7075
env_logger = "0.8.0"
7176

77+
7278
[dev-dependencies]
7379
assert_matches = "1.5"
7480
criterion = "0.3"
@@ -90,6 +96,7 @@ gwc = []
9096
parallel_syn = []
9197
phase-check = []
9298
profile = ["ark-std/print-trace"]
99+
counter = ["lazy_static"]
93100
mock-batch-inv = []
94101

95102
[lib]

halo2_proofs/benches/lookups.rs

Lines changed: 239 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,239 @@
1+
#[macro_use]
2+
extern crate criterion;
3+
4+
use halo2_proofs::arithmetic::FieldExt;
5+
use halo2_proofs::circuit::{Layouter, SimpleFloorPlanner, Value};
6+
use halo2_proofs::plonk::*;
7+
use halo2_proofs::poly::kzg::multiopen::VerifierGWC;
8+
use halo2_proofs::poly::{commitment::ParamsProver, Rotation};
9+
use halo2_proofs::transcript::{Blake2bRead, Blake2bWrite, Challenge255};
10+
use halo2curves::bn256::{Bn256, G1Affine};
11+
use halo2curves::pairing::Engine;
12+
use rand_core::OsRng;
13+
14+
use halo2_proofs::{
15+
poly::{
16+
kzg::{
17+
commitment::{KZGCommitmentScheme, ParamsKZG},
18+
multiopen::ProverGWC,
19+
strategy::SingleStrategy,
20+
},
21+
},
22+
transcript::{TranscriptReadBuffer, TranscriptWriterBuffer},
23+
};
24+
25+
use std::marker::PhantomData;
26+
27+
use criterion::{BenchmarkId, Criterion};
28+
29+
fn criterion_benchmark(c: &mut Criterion) {
30+
#[derive(Clone, Default)]
31+
struct MyCircuit<F: FieldExt> {
32+
_marker: PhantomData<F>,
33+
}
34+
35+
#[derive(Clone)]
36+
struct MyConfig {
37+
selector: Selector,
38+
table: TableColumn,
39+
advice: Column<Advice>,
40+
other_advice: Column<Advice>,
41+
}
42+
43+
impl<F: FieldExt> Circuit<F> for MyCircuit<F> {
44+
type Config = MyConfig;
45+
type FloorPlanner = SimpleFloorPlanner;
46+
47+
fn without_witnesses(&self) -> Self {
48+
Self::default()
49+
}
50+
51+
fn configure(meta: &mut ConstraintSystem<F>) -> MyConfig {
52+
let config = MyConfig {
53+
selector: meta.complex_selector(),
54+
table: meta.lookup_table_column(),
55+
advice: meta.advice_column(),
56+
other_advice: meta.advice_column(),
57+
};
58+
59+
let dummy_selector = meta.complex_selector();
60+
61+
meta.create_gate("degree 6 gate", |meta| {
62+
let dummy_selector = meta.query_selector(dummy_selector);
63+
let constraints = vec![dummy_selector.clone(); 4].iter().fold(dummy_selector.clone(), |acc, val| acc * val.clone());
64+
Constraints::with_selector(dummy_selector, Some(constraints))
65+
});
66+
67+
meta.lookup("lookup", |meta| {
68+
let advice = meta.query_advice(config.advice, Rotation::cur());
69+
vec![(advice, config.table)]
70+
});
71+
72+
meta.lookup("lookup", |meta| {
73+
let advice = meta.query_advice(config.advice, Rotation::cur());
74+
vec![(advice, config.table)]
75+
});
76+
77+
meta.lookup("lookup", |meta| {
78+
let advice = meta.query_advice(config.advice, Rotation::cur());
79+
vec![(advice, config.table)]
80+
});
81+
82+
meta.lookup("lookup", |meta| {
83+
let advice = meta.query_advice(config.advice, Rotation::cur());
84+
vec![(advice, config.table)]
85+
});
86+
87+
meta.lookup("lookup", |meta| {
88+
let advice = meta.query_advice(config.advice, Rotation::cur());
89+
vec![(advice, config.table)]
90+
});
91+
92+
/*
93+
- We need degree at least 6 because 6 - 1 = 5 and we need to go to extended domain of 8n
94+
- Our goal is to get to max degree of 9 because now 9 - 1 = 8 and that will fit into domain
95+
96+
- base degree = table_deg + 2
97+
- if we put input_expression_degree = 1
98+
=> degree = base + 1 = 3 + 1 = 4
99+
- we can batch one more with 5 more lookups
100+
*/
101+
102+
config
103+
}
104+
105+
fn synthesize(
106+
&self,
107+
config: MyConfig,
108+
mut layouter: impl Layouter<F>,
109+
) -> Result<(), Error> {
110+
layouter.assign_table(
111+
|| "8-bit table",
112+
|mut table| {
113+
for row in 0u64..(1 << 8) {
114+
table.assign_cell(
115+
|| format!("row {}", row),
116+
config.table,
117+
row as usize,
118+
|| Value::known(F::from(row)),
119+
)?;
120+
}
121+
122+
Ok(())
123+
},
124+
)?;
125+
126+
layouter.assign_region(
127+
|| "assign values",
128+
|mut region| {
129+
for offset in 0u64..(1 << 10) {
130+
config.selector.enable(&mut region, offset as usize)?;
131+
region.assign_advice(
132+
|| format!("offset {}", offset),
133+
config.advice,
134+
offset as usize,
135+
|| Value::known(F::from((offset % 256))),
136+
)?;
137+
}
138+
for offset in 1u64..(1 << 10) {
139+
config.selector.enable(&mut region, offset as usize)?;
140+
region.assign_advice(
141+
|| format!("offset {}", offset),
142+
config.other_advice,
143+
offset as usize - 1,
144+
|| Value::known(F::from((offset % 256))),
145+
)?;
146+
}
147+
Ok(())
148+
},
149+
)
150+
}
151+
}
152+
153+
fn keygen(k: u32) -> (ParamsKZG<Bn256>, ProvingKey<G1Affine>) {
154+
let params: ParamsKZG<Bn256> = ParamsKZG::new(k);
155+
let empty_circuit: MyCircuit<<Bn256 as Engine>::Scalar> = MyCircuit {
156+
_marker: PhantomData,
157+
};
158+
let vk = keygen_vk(&params, &empty_circuit).expect("keygen_vk should not fail");
159+
let pk = keygen_pk(&params, vk, &empty_circuit).expect("keygen_pk should not fail");
160+
(params, pk)
161+
}
162+
163+
fn prover(k: u32, params: &ParamsKZG<Bn256>, pk: &ProvingKey<G1Affine>) -> Vec<u8> {
164+
let rng = OsRng;
165+
166+
let circuit: MyCircuit<<Bn256 as Engine>::Scalar> = MyCircuit {
167+
_marker: PhantomData,
168+
};
169+
170+
let mut transcript = Blake2bWrite::<_, _, Challenge255<G1Affine>>::init(vec![]);
171+
create_proof::<KZGCommitmentScheme<Bn256>, ProverGWC<'_, Bn256>, _, _, _, _>(
172+
params,
173+
pk,
174+
&[circuit],
175+
&[&[]],
176+
rng,
177+
&mut transcript,
178+
)
179+
.expect("proof generation should not fail");
180+
transcript.finalize()
181+
}
182+
183+
fn verifier(params: &ParamsKZG<Bn256>, vk: &VerifyingKey<G1Affine>, proof: &[u8]) {
184+
let strategy = SingleStrategy::new(params);
185+
let mut transcript = Blake2bRead::<_, _, Challenge255<G1Affine>>::init(proof);
186+
assert!(verify_proof::<
187+
KZGCommitmentScheme<Bn256>,
188+
VerifierGWC<'_, Bn256>,
189+
Challenge255<G1Affine>,
190+
Blake2bRead<&[u8], G1Affine, Challenge255<G1Affine>>,
191+
SingleStrategy<'_, Bn256>,
192+
>(params, vk, strategy, &[&[]], &mut transcript)
193+
.is_ok());
194+
}
195+
196+
let k_range = 16..=16;
197+
198+
let mut keygen_group = c.benchmark_group("plonk-keygen");
199+
keygen_group.sample_size(10);
200+
for k in k_range.clone() {
201+
keygen_group.bench_with_input(BenchmarkId::from_parameter(k), &k, |b, &k| {
202+
b.iter(|| keygen(k));
203+
});
204+
}
205+
keygen_group.finish();
206+
207+
let mut prover_group = c.benchmark_group("plonk-prover");
208+
prover_group.sample_size(10);
209+
for k in k_range.clone() {
210+
let (params, pk) = keygen(k);
211+
212+
prover_group.bench_with_input(
213+
BenchmarkId::from_parameter(k),
214+
&(k, &params, &pk),
215+
|b, &(k, params, pk)| {
216+
b.iter(|| prover(k, params, pk));
217+
},
218+
);
219+
}
220+
prover_group.finish();
221+
222+
let mut verifier_group = c.benchmark_group("plonk-verifier");
223+
for k in k_range {
224+
let (params, pk) = keygen(k);
225+
let proof = prover(k, &params, &pk);
226+
227+
verifier_group.bench_with_input(
228+
BenchmarkId::from_parameter(k),
229+
&(&params, pk.get_vk(), &proof[..]),
230+
|b, &(params, vk, proof)| {
231+
b.iter(|| verifier(params, vk, proof));
232+
},
233+
);
234+
}
235+
verifier_group.finish();
236+
}
237+
238+
criterion_group!(benches, criterion_benchmark);
239+
criterion_main!(benches);

halo2_proofs/src/arithmetic.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,17 @@ pub fn small_multiexp<C: CurveAffine>(coeffs: &[C::Scalar], bases: &[C]) -> C::C
132132
///
133133
/// This will use multithreading if beneficial.
134134
pub fn best_multiexp<C: CurveAffine>(coeffs: &[C::Scalar], bases: &[C]) -> C::Curve {
135+
#[cfg(feature = "counter")]
136+
{
137+
use crate::MSM_COUNTER;
138+
*MSM_COUNTER
139+
.lock()
140+
.unwrap()
141+
.entry(coeffs.len())
142+
.and_modify(|cnt| *cnt += 1)
143+
.or_insert(1);
144+
}
145+
135146
assert_eq!(coeffs.len(), bases.len());
136147

137148
let num_threads = multicore::current_num_threads();
@@ -171,6 +182,17 @@ pub fn best_multiexp<C: CurveAffine>(coeffs: &[C::Scalar], bases: &[C]) -> C::Cu
171182
///
172183
/// This will use multithreading if beneficial.
173184
pub fn best_fft<G: Group>(a: &mut [G], omega: G::Scalar, log_n: u32) {
185+
#[cfg(feature = "counter")]
186+
{
187+
use crate::FFT_COUNTER;
188+
*FFT_COUNTER
189+
.lock()
190+
.unwrap()
191+
.entry(a.len())
192+
.and_modify(|cnt| *cnt += 1)
193+
.or_insert(1);
194+
}
195+
174196
let threads = multicore::current_num_threads();
175197
let log_split = log2_floor(threads) as usize;
176198
let n = a.len() as usize;

0 commit comments

Comments
 (0)