Skip to content

Commit

Permalink
add fibonacchi bench (#690)
Browse files Browse the repository at this point in the history
### Context
Existing benchmark `riscv_add` are just for testing single opcode
performance. This PR aim to have a first e2e bench which is
representative to a workload in real world.

### What has been cover
- add fibonacci bench with sp1 platform hardcode
- refactor e2e flow into sub-function to maximize code sharing

### what's not being covered
Hey @mcalancea, key generation + witness assignment are still
encaptulated in same function and not including in bench latency
meature. Break down key generation and extract witness assignment will
expose massive intermediate variables in between, so probably need
another refactor and design to support #624
  • Loading branch information
hero78119 authored Dec 4, 2024
1 parent 4a1af6c commit b5bbc84
Show file tree
Hide file tree
Showing 7 changed files with 250 additions and 91 deletions.
5 changes: 5 additions & 0 deletions ceno_zkvm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,8 @@ riv64 = []
[[bench]]
harness = false
name = "riscv_add"


[[bench]]
harness = false
name = "fibonacci"
87 changes: 87 additions & 0 deletions ceno_zkvm/benches/fibonacci.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
use std::{
fs,
path::PathBuf,
time::{Duration, Instant},
};

use ceno_emul::{CENO_PLATFORM, Platform, Program, WORD_SIZE};
use ceno_zkvm::{
self,
e2e::{run_e2e_gen_witness, run_e2e_proof},
};
use criterion::*;

use goldilocks::GoldilocksExt2;
use mpcs::BasefoldDefault;

criterion_group! {
name = fibonacci;
config = Criterion::default().warm_up_time(Duration::from_millis(20000));
targets = bench_e2e
}

criterion_main!(fibonacci);

const NUM_SAMPLES: usize = 10;

fn bench_e2e(c: &mut Criterion) {
type Pcs = BasefoldDefault<E>;
let mut file_path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
file_path.push("examples/fibonacci.elf");
let stack_size = 32768;
let heap_size = 2097152;
let elf_bytes = fs::read(&file_path).expect("read elf file");
let program = Program::load_elf(&elf_bytes, u32::MAX).unwrap();

// use sp1 platform
let platform = Platform {
// The stack section is not mentioned in ELF headers, so we repeat the constant STACK_TOP here.
stack_top: 0x0020_0400,
rom: program.base_address
..program.base_address + (program.instructions.len() * WORD_SIZE) as u32,
ram: 0x0010_0000..0xFFFF_0000,
unsafe_ecall_nop: true,
..CENO_PLATFORM
};

for max_steps in [1usize << 20, 1usize << 21, 1usize << 22] {
// expand more input size once runtime is acceptable
let mut group = c.benchmark_group(format!("fibonacci_max_steps_{}", max_steps));
group.sample_size(NUM_SAMPLES);

// Benchmark the proving time
group.bench_function(
BenchmarkId::new(
"prove_fibonacci",
format!("fibonacci_max_steps_{}", max_steps),
),
|b| {
b.iter_with_setup(
|| {
run_e2e_gen_witness::<E, Pcs>(
program.clone(),
platform.clone(),
stack_size,
heap_size,
vec![],
max_steps,
)
},
|(prover, _, zkvm_witness, pi, _, _, _)| {
let timer = Instant::now();
let _ = run_e2e_proof(prover, zkvm_witness, pi);
println!(
"Fibonacci::create_proof, max_steps = {}, time = {}",
max_steps,
timer.elapsed().as_secs_f64()
);
},
);
},
);

group.finish();
}

type E = GoldilocksExt2;
}
11 changes: 4 additions & 7 deletions ceno_zkvm/examples/riscv_opcodes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use ceno_zkvm::{
state::GlobalState,
structs::ProgramParams,
tables::{MemFinalRecord, ProgramTableCircuit},
with_panic_hook,
};
use clap::Parser;

Expand Down Expand Up @@ -323,13 +324,9 @@ fn main() {
zkvm_proof.raw_pi[1] = vec![Goldilocks::ONE];

// capture panic message, if have
let default_hook = panic::take_hook();
panic::set_hook(Box::new(|_info| {
// by default it will print msg to stdout/stderr
// we override it to avoid print msg since we will capture the msg by our own
}));
let result = panic::catch_unwind(|| verifier.verify_proof(zkvm_proof, transcript));
panic::set_hook(default_hook);
let result = with_panic_hook(Box::new(|_info| ()), || {
panic::catch_unwind(|| verifier.verify_proof(zkvm_proof, transcript))
});
match result {
Ok(res) => {
res.expect_err("verify proof should return with error");
Expand Down
85 changes: 76 additions & 9 deletions ceno_zkvm/src/bin/e2e.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
use ceno_emul::{CENO_PLATFORM, IterAddresses, Platform, Program, WORD_SIZE, Word};
use ceno_zkvm::e2e::run_e2e;
use ceno_zkvm::{
e2e::{run_e2e_gen_witness, run_e2e_proof, run_e2e_verify},
with_panic_hook,
};
use clap::{Parser, ValueEnum};
use ff_ext::ff::Field;
use goldilocks::{Goldilocks, GoldilocksExt2};
use itertools::Itertools;
use std::fs;
use mpcs::{Basefold, BasefoldRSParams};
use std::{fs, panic, time::Instant};
use tracing::level_filters::LevelFilter;
use tracing_forest::ForestLayer;
use tracing_subscriber::{
EnvFilter, Registry, filter::filter_fn, fmt, layer::SubscriberExt, util::SubscriberInitExt,
};
use transcript::Transcript;

/// Prove the execution of a fixed RISC-V program.
#[derive(Parser, Debug)]
Expand Down Expand Up @@ -131,14 +138,74 @@ fn main() {
);

let max_steps = args.max_steps.unwrap_or(usize::MAX);
run_e2e(
program,
platform,
args.stack_size,
args.heap_size,
hints,
max_steps,

type E = GoldilocksExt2;
type B = Goldilocks;
type Pcs = Basefold<GoldilocksExt2, BasefoldRSParams>;

let (prover, verifier, zkvm_witness, pi, cycle_num, e2e_start, exit_code) =
run_e2e_gen_witness::<E, Pcs>(
program,
platform,
args.stack_size,
args.heap_size,
hints,
max_steps,
);

let timer = Instant::now();
let mut zkvm_proof = run_e2e_proof(prover, zkvm_witness, pi);
let proving_time = timer.elapsed().as_secs_f64();
let e2e_time = e2e_start.elapsed().as_secs_f64();
let witgen_time = e2e_time - proving_time;
println!(
"Proving finished.\n\
\tProving time = {:.3}s, freq = {:.3}khz\n\
\tWitgen time = {:.3}s, freq = {:.3}khz\n\
\tTotal time = {:.3}s, freq = {:.3}khz\n\
\tthread num: {}",
proving_time,
cycle_num as f64 / proving_time / 1000.0,
witgen_time,
cycle_num as f64 / witgen_time / 1000.0,
e2e_time,
cycle_num as f64 / e2e_time / 1000.0,
rayon::current_num_threads()
);

run_e2e_verify(&verifier, zkvm_proof.clone(), exit_code, max_steps);

// do sanity check
let transcript = Transcript::new(b"riscv");
// change public input maliciously should cause verifier to reject proof
zkvm_proof.raw_pi[0] = vec![B::ONE];
zkvm_proof.raw_pi[1] = vec![B::ONE];

// capture panic message, if have
let result = with_panic_hook(Box::new(|_info| ()), || {
panic::catch_unwind(|| verifier.verify_proof(zkvm_proof, transcript))
});
match result {
Ok(res) => {
res.expect_err("verify proof should return with error");
}
Err(err) => {
let msg: String = if let Some(message) = err.downcast_ref::<&str>() {
message.to_string()
} else if let Some(message) = err.downcast_ref::<String>() {
message.to_string()
} else if let Some(message) = err.downcast_ref::<&String>() {
message.to_string()
} else {
unreachable!()
};

if !msg.starts_with("0th round's prover message is not consistent with the claim") {
println!("unknown panic {msg:?}");
panic::resume_unwind(err);
};
}
};
}

fn memory_from_file(path: &Option<String>) -> Vec<u32> {
Expand Down
Loading

0 comments on commit b5bbc84

Please sign in to comment.