diff --git a/Cargo.lock b/Cargo.lock index e5fc6dd4e..375b9203d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -329,7 +329,9 @@ dependencies = [ "ark-std", "base64", "bincode", + "ceno-examples", "ceno_emul", + "ceno_host", "cfg-if", "clap", "criterion", diff --git a/ceno_zkvm/Cargo.toml b/ceno_zkvm/Cargo.toml index 97a7c9364..14a5c15ad 100644 --- a/ceno_zkvm/Cargo.toml +++ b/ceno_zkvm/Cargo.toml @@ -19,7 +19,9 @@ serde.workspace = true serde_json.workspace = true base64 = "0.22" +ceno-examples = { path = "../examples-builder" } ceno_emul = { path = "../ceno_emul" } +ceno_host = { path = "../ceno_host" } ff_ext = { path = "../ff_ext" } mpcs = { path = "../mpcs" } multilinear_extensions = { version = "0", path = "../multilinear_extensions" } @@ -51,6 +53,7 @@ criterion.workspace = true pprof2.workspace = true proptest.workspace = true + [build-dependencies] glob = "0.3" @@ -71,3 +74,11 @@ name = "fibonacci" [[bench]] harness = false name = "fibonacci_witness" + +[[bench]] +harness = false +name = "quadratic_sorting" + +[[bench]] +harness = false +name = "is_prime" diff --git a/ceno_zkvm/benches/is_prime.rs b/ceno_zkvm/benches/is_prime.rs new file mode 100644 index 000000000..b24f74311 --- /dev/null +++ b/ceno_zkvm/benches/is_prime.rs @@ -0,0 +1,73 @@ +use std::time::Duration; + +use ceno_emul::{Platform, Program}; +use ceno_host::CenoStdin; +use ceno_zkvm::{ + self, + e2e::{Checkpoint, Preset, run_e2e_with_checkpoint, setup_platform}, +}; +use criterion::*; + +use goldilocks::GoldilocksExt2; +use mpcs::BasefoldDefault; + +criterion_group! { + name = is_prime; + config = Criterion::default().warm_up_time(Duration::from_millis(5000)); + targets = is_prime_1 +} + +criterion_main!(is_prime); + +const NUM_SAMPLES: usize = 10; +type Pcs = BasefoldDefault; +type E = GoldilocksExt2; + +// Relevant init data for fibonacci run +fn setup() -> (Program, Platform) { + let stack_size = 32 << 10; + let heap_size = 2 << 20; + let pub_io_size = 16; + let program = Program::load_elf(ceno_examples::is_prime, u32::MAX).unwrap(); + let platform = setup_platform(Preset::Ceno, &program, stack_size, heap_size, pub_io_size); + (program, platform) +} + +fn is_prime_1(c: &mut Criterion) { + let (program, platform) = setup(); + + for n in [100u32, 10000u32] { + let max_steps = usize::MAX; + let mut hints = CenoStdin::default(); + hints.write(&n).unwrap(); + let hints: Vec = (&hints).into(); + + let mut group = c.benchmark_group("is_prime".to_string()); + group.sample_size(NUM_SAMPLES); + + // Benchmark the proving time + group.bench_function(BenchmarkId::new("is_prime", format!("n = {}", n)), |b| { + b.iter_custom(|iters| { + let mut time = Duration::new(0, 0); + + for _ in 0..iters { + let (_, prove) = run_e2e_with_checkpoint::( + program.clone(), + platform.clone(), + hints.clone(), + max_steps, + Checkpoint::PrepE2EProving, + ); + let instant = std::time::Instant::now(); + prove(); + time += instant.elapsed(); + } + time + }); + }); + + group.finish(); + } + + type E = GoldilocksExt2; +} diff --git a/ceno_zkvm/benches/quadratic_sorting.rs b/ceno_zkvm/benches/quadratic_sorting.rs new file mode 100644 index 000000000..7f742f4cc --- /dev/null +++ b/ceno_zkvm/benches/quadratic_sorting.rs @@ -0,0 +1,80 @@ +use std::time::Duration; + +use ceno_emul::{Platform, Program}; +use ceno_host::CenoStdin; +use ceno_zkvm::{ + self, + e2e::{Checkpoint, Preset, run_e2e_with_checkpoint, setup_platform}, +}; +use criterion::*; + +use goldilocks::GoldilocksExt2; +use mpcs::BasefoldDefault; + +criterion_group! { + name = quadratic_sorting; + config = Criterion::default().warm_up_time(Duration::from_millis(5000)); + targets = quadratic_sorting_1 +} + +criterion_main!(quadratic_sorting); + +const NUM_SAMPLES: usize = 10; +type Pcs = BasefoldDefault; +type E = GoldilocksExt2; + +// Relevant init data for fibonacci run +fn setup() -> (Program, Platform) { + let stack_size = 32 << 10; + let heap_size = 2 << 20; + let pub_io_size = 16; + + let program = Program::load_elf(ceno_examples::quadratic_sorting, u32::MAX).unwrap(); + let platform = setup_platform(Preset::Ceno, &program, stack_size, heap_size, pub_io_size); + (program, platform) +} + +fn quadratic_sorting_1(c: &mut Criterion) { + let (program, platform) = setup(); + use rand::{Rng, SeedableRng}; + let mut rng = rand::rngs::StdRng::seed_from_u64(42); + + for n in [100, 500] { + let max_steps = usize::MAX; + let mut hints = CenoStdin::default(); + hints + .write(&(0..n).map(|_| rng.gen::()).collect::>()) + .unwrap(); + let hints: Vec = (&hints).into(); + + let mut group = c.benchmark_group("quadratic_sorting".to_string()); + group.sample_size(NUM_SAMPLES); + + // Benchmark the proving time + group.bench_function( + BenchmarkId::new("quadratic_sorting", format!("n = {}", n)), + |b| { + b.iter_custom(|iters| { + let mut time = Duration::new(0, 0); + for _ in 0..iters { + let (_, prove) = run_e2e_with_checkpoint::( + program.clone(), + platform.clone(), + hints.clone(), + max_steps, + Checkpoint::PrepE2EProving, + ); + let instant = std::time::Instant::now(); + prove(); + time += instant.elapsed(); + } + time + }); + }, + ); + + group.finish(); + } + + type E = GoldilocksExt2; +} diff --git a/ceno_zkvm/src/e2e.rs b/ceno_zkvm/src/e2e.rs index 31cf9d55c..87f0bd318 100644 --- a/ceno_zkvm/src/e2e.rs +++ b/ceno_zkvm/src/e2e.rs @@ -14,18 +14,14 @@ use crate::{ tables::{MemFinalRecord, MemInitRecord, ProgramTableCircuit, ProgramTableConfig}, }; use ceno_emul::{ - ByteAddr, CENO_PLATFORM, EmuContext, InsnKind, IterAddresses, Platform, Program, StepRecord, - Tracer, VMState, WORD_SIZE, WordAddr, + CENO_PLATFORM, EmuContext, InsnKind, IterAddresses, Platform, Program, StepRecord, Tracer, + VMState, WORD_SIZE, WordAddr, }; use clap::ValueEnum; use ff_ext::ExtensionField; -use itertools::{Itertools, MinMaxResult, chain}; +use itertools::{Itertools, chain}; use mpcs::PolynomialCommitmentScheme; -use std::{ - collections::{BTreeSet, HashMap, HashSet}, - iter::zip, - sync::Arc, -}; +use std::{collections::BTreeSet, iter::zip, sync::Arc}; use transcript::BasicTranscript as Transcript; pub struct FullMemState { @@ -129,7 +125,6 @@ fn emulate_program( } }) .collect_vec(); - debug_memory_ranges(&vm, &mem_final); // Find the final public IO cycles. let io_final = io_init @@ -548,50 +543,3 @@ pub fn run_e2e_verify>( None => tracing::error!("Unfinished execution. max_steps={:?}.", max_steps), } } - -fn debug_memory_ranges(vm: &VMState, mem_final: &[MemFinalRecord]) { - let accessed_addrs = vm - .tracer() - .final_accesses() - .iter() - .filter(|(_, &cycle)| (cycle != 0)) - .map(|(&addr, _)| addr.baddr()) - .filter(|addr| vm.platform().can_read(addr.0)) - .collect_vec(); - - let handled_addrs = mem_final - .iter() - .filter(|rec| rec.cycle != 0) - .map(|rec| ByteAddr(rec.addr)) - .collect::>(); - - tracing::debug!( - "Memory range (accessed): {:?}", - format_segments(vm.platform(), accessed_addrs.iter().copied()) - ); - tracing::debug!( - "Memory range (handled): {:?}", - format_segments(vm.platform(), handled_addrs.iter().copied()) - ); - - for addr in &accessed_addrs { - assert!(handled_addrs.contains(addr), "unhandled addr: {:?}", addr); - } -} - -fn format_segments( - platform: &Platform, - addrs: impl Iterator, -) -> HashMap> { - addrs - .into_grouping_map_by(|addr| format_segment(platform, addr.0)) - .minmax() -} - -fn format_segment(platform: &Platform, addr: u32) -> String { - format!( - "{}{}", - if platform.can_read(addr) { "R" } else { "-" }, - if platform.can_write(addr) { "W" } else { "-" }, - ) -} diff --git a/examples/examples/is_prime.rs b/examples/examples/is_prime.rs new file mode 100644 index 000000000..8e6455010 --- /dev/null +++ b/examples/examples/is_prime.rs @@ -0,0 +1,30 @@ +extern crate ceno_rt; +use rkyv::Archived; + +fn is_prime(n: u32) -> bool { + if n < 2 { + return false; + } + let mut i = 2; + while i * i <= n { + if n % i == 0 { + return false; + } + i += 1; + } + + true +} + +fn main() { + let n: &Archived = ceno_rt::read(); + let mut cnt_primes = 0; + + for i in 0..=n.into() { + cnt_primes += is_prime(i) as u32; + } + + if cnt_primes > 1000 * 1000 { + panic!(); + } +} diff --git a/examples/examples/quadratic_sorting.rs b/examples/examples/quadratic_sorting.rs index 1267f6a61..0c4cd5fb3 100644 --- a/examples/examples/quadratic_sorting.rs +++ b/examples/examples/quadratic_sorting.rs @@ -1,6 +1,4 @@ extern crate ceno_rt; -use ceno_rt::println; -use core::fmt::Write; use rkyv::vec::ArchivedVec; fn sort(slice: &mut [T]) { @@ -18,6 +16,4 @@ fn main() { let input: &ArchivedVec = ceno_rt::read(); let mut scratch = input.to_vec(); sort(&mut scratch); - // Print any output you feel like, eg the first element of the sorted vector: - println!("{}", scratch[0]); }