From ec2dfdf4b8efd20d34d44683078bf9e210ab2b52 Mon Sep 17 00:00:00 2001 From: Qyriad Date: Thu, 14 Nov 2024 19:44:26 -0700 Subject: [PATCH] smt: add benchmarks for parallel construction --- Cargo.toml | 5 +++ benches/parallel-subtree.rs | 75 +++++++++++++++++++++++++++++++++++++ src/main.rs | 28 +++++++++++++- 3 files changed, 107 insertions(+), 1 deletion(-) create mode 100644 benches/parallel-subtree.rs diff --git a/Cargo.toml b/Cargo.toml index 347cca8..750748a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,6 +35,11 @@ harness = false name = "merkle" harness = false +[[bench]] +name = "parallel-subtree" +harness = false +required-features = ["concurrent"] + [[bench]] name = "store" harness = false diff --git a/benches/parallel-subtree.rs b/benches/parallel-subtree.rs new file mode 100644 index 0000000..65c3918 --- /dev/null +++ b/benches/parallel-subtree.rs @@ -0,0 +1,75 @@ +use std::{fmt::Debug, hint, mem, time::Duration}; + +use criterion::{criterion_group, criterion_main, BatchSize, BenchmarkId, Criterion}; +use miden_crypto::{hash::rpo::RpoDigest, merkle::Smt, Felt, Word, ONE}; +use rand_utils::prng_array; +use winter_utils::Randomizable; + +// 2^0, 2^4, 2^8, 2^12, 2^16 +const PAIR_COUNTS: [u64; 6] = [1, 16, 256, 4096, 65536, 1_048_576]; + +fn smt_parallel_subtree(c: &mut Criterion) { + let mut seed = [0u8; 32]; + + let mut group = c.benchmark_group("parallel-subtrees"); + + for pair_count in PAIR_COUNTS { + let bench_id = BenchmarkId::from_parameter(pair_count); + group.bench_with_input(bench_id, &pair_count, |b, &pair_count| { + b.iter_batched( + || { + // Setup. + let entries: Vec<(RpoDigest, Word)> = (0..pair_count) + .map(|i| { + let count = pair_count as f64; + let idx = ((i as f64 / count) * (count)) as u64; + let key = RpoDigest::new([ + generate_value(&mut seed), + ONE, + Felt::new(i), + Felt::new(idx), + ]); + let value = generate_word(&mut seed); + (key, value) + }) + .collect(); + + let control = Smt::with_entries(entries.clone()).unwrap(); + (entries, control) + }, + |(entries, control)| { + // Benchmarked function. + let tree = Smt::with_entries_par(hint::black_box(entries)).unwrap(); + assert_eq!(tree.root(), control.root()); + }, + BatchSize::SmallInput, + ); + }); + } +} + +criterion_group! { + name = smt_subtree_group; + config = Criterion::default() + //.measurement_time(Duration::from_secs(960)) + .measurement_time(Duration::from_secs(60)) + .sample_size(10) + .configure_from_args(); + targets = smt_parallel_subtree +} +criterion_main!(smt_subtree_group); + +// HELPER FUNCTIONS +// -------------------------------------------------------------------------------------------- + +fn generate_value(seed: &mut [u8; 32]) -> T { + mem::swap(seed, &mut prng_array(*seed)); + let value: [T; 1] = rand_utils::prng_array(*seed); + value[0] +} + +fn generate_word(seed: &mut [u8; 32]) -> Word { + mem::swap(seed, &mut prng_array(*seed)); + let nums: [u64; 4] = prng_array(*seed); + [Felt::new(nums[0]), Felt::new(nums[1]), Felt::new(nums[2]), Felt::new(nums[3])] +} diff --git a/src/main.rs b/src/main.rs index 776ccc2..018dd50 100644 --- a/src/main.rs +++ b/src/main.rs @@ -33,7 +33,12 @@ pub fn benchmark_smt() { entries.push((key, value)); } - let mut tree = construction(entries, tree_size).unwrap(); + let mut tree = construction(entries.clone(), tree_size).unwrap(); + #[cfg(feature = "concurrent")] + { + let parallel = parallel_construction(entries, tree_size).unwrap(); + assert_eq!(tree, parallel); + } insertion(&mut tree, tree_size).unwrap(); batched_insertion(&mut tree, tree_size).unwrap(); proof_generation(&mut tree, tree_size).unwrap(); @@ -56,6 +61,27 @@ pub fn construction(entries: Vec<(RpoDigest, Word)>, size: u64) -> Result, + size: u64, +) -> Result { + println!("Running a parallel construction benchmark:"); + let now = Instant::now(); + + let tree = Smt::with_entries_par(entries).unwrap(); + + let elapsed = now.elapsed(); + println!( + "Parallel-constructed an SMT with {} key-value pairs in {:.3} seconds", + size, + elapsed.as_secs_f32(), + ); + println!("Number of leaf nodes: {}\n", tree.leaves().count()); + + Ok(tree) +} + /// Runs the insertion benchmark for the [`Smt`]. pub fn insertion(tree: &mut Smt, size: u64) -> Result<(), MerkleError> { println!("Running an insertion benchmark:");