Skip to content
This repository was archived by the owner on Apr 18, 2025. It is now read-only.

Commit a62b586

Browse files
committed
parallelize bytecode circuit assignment
1 parent 7e821a9 commit a62b586

File tree

3 files changed

+145
-35
lines changed

3 files changed

+145
-35
lines changed

circuit-benchmarks/src/bytecode_circuit.rs

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,17 @@ mod tests {
4242
const BENCHMARK_ID: &str = "Bytecode Circuit";
4343

4444
// Contract code size exceeds 24576 bytes may not be deployable on Mainnet.
45-
const MAX_BYTECODE_LEN: usize = 24576;
45+
const MAX_BYTECODE_LEN: usize = 4096;
4646

4747
let num_rows = 1 << degree;
4848
const NUM_BLINDING_ROWS: usize = 7 - 1;
4949
let max_bytecode_row_num = num_rows - NUM_BLINDING_ROWS;
5050
let bytecode_len = std::cmp::min(MAX_BYTECODE_LEN, max_bytecode_row_num);
5151
let bytecodes_num: usize = max_bytecode_row_num / bytecode_len;
52+
println!(
53+
"Bytecode length: {}, Bytecodes number: {}",
54+
bytecode_len, bytecodes_num
55+
);
5256

5357
// Create the circuit
5458
let bytecode_circuit = TestBytecodeCircuit::<Fr>::new(
@@ -69,10 +73,32 @@ mod tests {
6973
let verifier_params: ParamsVerifierKZG<Bn256> = general_params.verifier_params().clone();
7074
end_timer!(start1);
7175

76+
std::env::set_var("ASSIGNMENT_TYPE", "default");
77+
let assign_var = std::env::var("ASSIGNMENT_TYPE").ok().unwrap_or_default(); //.into();
78+
println!("assign_var: {}", assign_var);
79+
80+
let assignment_type = std::env::var("ASSIGNMENT_TYPE").ok().unwrap_or_default();
81+
let is_parallel_assignment = match assignment_type.as_str() {
82+
"default" => false,
83+
"parallel" => true,
84+
&_ => todo!(),
85+
};
86+
println!("is_parallel_assignment: {}", is_parallel_assignment);
87+
7288
// Initialize the proving key
7389
let vk = keygen_vk(&general_params, &bytecode_circuit).expect("keygen_vk should not fail");
7490
let pk =
7591
keygen_pk(&general_params, vk, &bytecode_circuit).expect("keygen_pk should not fail");
92+
std::env::set_var("ASSIGNMENT_TYPE", "parallel");
93+
let assign_var = std::env::var("ASSIGNMENT_TYPE"); //.ok().unwrap_or_default().into();
94+
println!("assign_var: {:?}", assign_var);
95+
let assignment_type = std::env::var("ASSIGNMENT_TYPE").ok().unwrap_or_default();
96+
let is_parallel_assignment = match assignment_type.as_str() {
97+
"default" => false,
98+
"parallel" => true,
99+
&_ => todo!(),
100+
};
101+
println!("is_parallel_assignment: {}", is_parallel_assignment);
76102
// Create a proof
77103
let mut transcript = Blake2bWrite::<_, G1Affine, Challenge255<_>>::init(vec![]);
78104

@@ -121,6 +147,7 @@ mod tests {
121147
)
122148
.expect("failed to verify bench circuit");
123149
end_timer!(start3);
150+
std::env::set_var("ASSIGNMENT_TYPE", "default");
124151
}
125152

126153
/// fill bytecodes_num * bytecode_len bytes to the witness table

zkevm-circuits/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ serde = { version = "1.0.130", features = ["derive"] }
5858
serde_json = "1.0.78"
5959

6060
[features]
61-
default = ["test", "test-circuits", "enable-sign-verify"]
61+
default = ["test", "test-circuits", "enable-sign-verify", "scroll", "halo2_proofs/parallel_syn"]
6262
test = ["ethers-signers", "mock", "bus-mapping/test"]
6363
# even if "scroll" feature is enabled, unittests use l1 geth to generate execution traces,
6464
# which cannot pass some constraints related to poseidon hash since codehash there is keccak hash.

zkevm-circuits/src/bytecode_circuit/circuit/to_poseidon_hash.rs

Lines changed: 116 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use halo2_proofs::{
1818
use itertools::Itertools;
1919
use log::trace;
2020
use mpt_zktrie::hash::HASHABLE_DOMAIN_SPEC;
21-
use std::vec;
21+
use std::{time::Instant, vec};
2222

2323
use super::{
2424
super::bytecode_unroller::{BytecodeRow, UnrolledBytecode},
@@ -433,44 +433,125 @@ impl<F: Field, const BYTES_IN_FIELD: usize> ToHashBlockCircuitConfig<F, BYTES_IN
433433

434434
let empty_hash = Value::known(POSEIDON_CODE_HASH_ZERO.to_word().to_scalar().unwrap());
435435

436-
layouter.assign_region(
437-
|| "assign bytecode with poseidon hash extension",
438-
|mut region| {
439-
let mut offset = 0;
440-
let mut row_input = F::zero();
441-
for bytecode in witness.iter() {
442-
let bytecode_offset_begin = offset;
443-
base_conf.assign_bytecode(
444-
&mut region,
445-
bytecode,
446-
challenges,
447-
&push_data_left_is_zero_chip,
448-
&index_length_diff_is_zero_chip,
449-
empty_hash,
450-
&mut offset,
451-
last_row_offset,
452-
fail_fast,
453-
)?;
436+
println!("assign bytecode with poseidon hash extension");
454437

455-
for (idx, row) in bytecode.rows.iter().enumerate() {
456-
// if the base_conf's assignment not fail fast,
457-
// we also avoid the failure of "NotEnoughRowsAvailable"
458-
// in prover creation (so bytecode_incomplete test could pass)
459-
let offset = bytecode_offset_begin + idx;
460-
if offset <= last_row_offset {
461-
row_input = self.assign_extended_row(
438+
let assignment_type = std::env::var("ASSIGNMENT_TYPE").ok().unwrap_or_default();
439+
let is_parallel_assignment = match assignment_type.as_str() {
440+
"default" => false,
441+
"parallel" => true,
442+
&_ => todo!(),
443+
};
444+
println!("is_parallel_assignment: {}", is_parallel_assignment);
445+
446+
let mut offset = 0;
447+
if is_parallel_assignment {
448+
let part1_timer = Instant::now();
449+
let offsets = layouter.assign_regions(
450+
|| "assign(regions) bytecode with poseidon hash extension(part1)",
451+
witness
452+
.iter()
453+
.map(|bytecode| {
454+
let push_data_left_is_zero_chip =
455+
IsZeroChip::construct(base_conf.push_data_left_is_zero.clone());
456+
let index_length_diff_is_zero_chip =
457+
IsZeroChip::construct(base_conf.index_length_diff_is_zero.clone());
458+
move |region: Region<'_, F>| {
459+
// |mut region| {
460+
let mut offset = 0;
461+
let mut row_input = F::zero();
462+
let mut region = region;
463+
let bytecode_offset_begin = offset;
464+
base_conf.assign_bytecode(
462465
&mut region,
463-
offset,
464-
row,
465-
row_input,
466-
bytecode.bytes.len(),
466+
bytecode,
467+
challenges,
468+
&push_data_left_is_zero_chip,
469+
&index_length_diff_is_zero_chip,
470+
empty_hash,
471+
&mut offset,
472+
last_row_offset,
473+
fail_fast,
467474
)?;
475+
476+
for (idx, row) in bytecode.rows.iter().enumerate() {
477+
// if the base_conf's assignment not fail fast,
478+
// we also avoid the failure of "NotEnoughRowsAvailable"
479+
// in prover creation (so bytecode_incomplete test could pass)
480+
let offset = bytecode_offset_begin + idx;
481+
if offset <= last_row_offset {
482+
row_input = self.assign_extended_row(
483+
&mut region,
484+
offset,
485+
row,
486+
row_input,
487+
bytecode.bytes.len(),
488+
)?;
489+
}
490+
}
491+
// println!("offset: {}", offset);
492+
Ok(offset)
468493
}
494+
})
495+
.collect_vec(),
496+
)?;
497+
println!("part1_timer: {:?}", part1_timer.elapsed());
498+
println!("offsets: {:?}", offsets);
499+
500+
offset = offsets.into_iter().fold(0, |acc, x| acc + x);
501+
println!("offset: {}", offset);
502+
} else {
503+
offset = layouter.assign_region(
504+
|| "assign bytecode with poseidon hash extension(part1)",
505+
|mut region| {
506+
let mut offset = 0;
507+
let mut row_input = F::zero();
508+
println!("witness len: {}", witness.len());
509+
let part1_timer = Instant::now();
510+
for bytecode in witness.iter() {
511+
let bytecode_offset_begin = offset;
512+
base_conf.assign_bytecode(
513+
&mut region,
514+
bytecode,
515+
challenges,
516+
&push_data_left_is_zero_chip,
517+
&index_length_diff_is_zero_chip,
518+
empty_hash,
519+
&mut offset,
520+
last_row_offset,
521+
fail_fast,
522+
)?;
523+
524+
for (idx, row) in bytecode.rows.iter().enumerate() {
525+
// if the base_conf's assignment not fail fast,
526+
// we also avoid the failure of "NotEnoughRowsAvailable"
527+
// in prover creation (so bytecode_incomplete test could pass)
528+
let offset = bytecode_offset_begin + idx;
529+
if offset <= last_row_offset {
530+
row_input = self.assign_extended_row(
531+
&mut region,
532+
offset,
533+
row,
534+
row_input,
535+
bytecode.bytes.len(),
536+
)?;
537+
}
538+
}
539+
540+
println!(" offset: {}", offset);
469541
}
470-
}
542+
println!("part1_timer: {:?}", part1_timer.elapsed());
543+
Ok(offset)
544+
},
545+
)?;
546+
}
471547

548+
layouter.assign_region(
549+
|| "assign bytecode with poseidon hash extension(part2)",
550+
|mut region| {
551+
let part2_timer = Instant::now();
472552
// Padding
473-
for idx in offset..=last_row_offset {
553+
// for idx in offset..=last_row_offset {
554+
for idx in 0..=(last_row_offset - offset) {
474555
base_conf.set_padding_row(
475556
&mut region,
476557
&push_data_left_is_zero_chip,
@@ -481,9 +562,11 @@ impl<F: Field, const BYTES_IN_FIELD: usize> ToHashBlockCircuitConfig<F, BYTES_IN
481562
)?;
482563
self.set_header_row(&mut region, 0, idx)?;
483564
}
565+
println!("part2_timer: {:?}", part2_timer.elapsed());
484566

567+
let part3_timer = Instant::now();
485568
base_conf.assign_overwrite(&mut region, overwrite, challenges)?;
486-
569+
println!("part3_timer: {:?}", part3_timer.elapsed());
487570
Ok(())
488571
},
489572
)

0 commit comments

Comments
 (0)