Skip to content

Commit d164008

Browse files
chained-growth benchmark
1 parent c6a805b commit d164008

2 files changed

Lines changed: 220 additions & 1 deletion

File tree

storage/src/qmdb/benches/bench.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,15 @@
22
33
use criterion::criterion_main;
44

5+
mod chained_growth;
56
mod common;
67
mod generate;
78
mod init;
89
mod merkleize;
910

10-
criterion_main!(generate::benches, init::benches, merkleize::benches);
11+
criterion_main!(
12+
chained_growth::benches,
13+
generate::benches,
14+
init::benches,
15+
merkleize::benches
16+
);
Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
//! Benchmark for chained-growth merkleization against Current QMDB variants.
2+
//!
3+
//! Setup (untimed): seed `NUM_KEYS` keys, then grow a chain of `PREBUILT_CHAIN` batches applying
4+
//! each parent while the child is still alive.
5+
//!
6+
//! Timed: do `batches` more merkleize + apply iterations on top of the pre-built chain, with a
7+
//! single random update per batch so each overlay covers a tiny fraction of chunks.
8+
9+
use crate::common::{dispatch_arm, make_fixed_value, Digest};
10+
use commonware_cryptography::{Hasher, Sha256};
11+
use commonware_runtime::{
12+
benchmarks::{context, tokio},
13+
tokio::{Config, Context},
14+
};
15+
use commonware_storage::{
16+
merkle::{self, mmb::Family as Mmb},
17+
qmdb::{
18+
any::traits::{DbAny, MerkleizedBatch as _, UnmerkleizedBatch as _},
19+
current::{ordered::fixed::Db as OCFixed, unordered::fixed::Db as UCFixed},
20+
},
21+
translator::EightCap,
22+
};
23+
use criterion::{criterion_group, Criterion};
24+
use rand::{rngs::StdRng, RngCore, SeedableRng};
25+
use std::{
26+
hint::black_box,
27+
time::{Duration, Instant},
28+
};
29+
30+
const SMALL_CHUNK_SIZE: usize = 32;
31+
const LARGE_CHUNK_SIZE: usize = 256;
32+
33+
type CurUFix32Mmb = UCFixed<Mmb, Context, Digest, Digest, Sha256, EightCap, SMALL_CHUNK_SIZE>;
34+
type CurOFix32Mmb = OCFixed<Mmb, Context, Digest, Digest, Sha256, EightCap, SMALL_CHUNK_SIZE>;
35+
type CurUFix256Mmb = UCFixed<Mmb, Context, Digest, Digest, Sha256, EightCap, LARGE_CHUNK_SIZE>;
36+
type CurOFix256Mmb = OCFixed<Mmb, Context, Digest, Digest, Sha256, EightCap, LARGE_CHUNK_SIZE>;
37+
38+
/// Number of pre-populated keys in the seeded database.
39+
const NUM_KEYS: u64 = 1_000_000;
40+
41+
/// Random updates per batch. One update means each batch's chunk overlay covers ~1 / num_chunks
42+
/// of the bitmap, forcing chain reads to walk deep before finding a matching layer.
43+
const UPDATES_PER_BATCH: u64 = 1;
44+
45+
/// Number of batches grown during the untimed seed phase, producing a Db::status chain of this
46+
/// depth that subsequent reads must walk through.
47+
const PREBUILT_CHAIN: u64 = 10_000;
48+
49+
/// Number of additional batches to grow during the timed region.
50+
const GROW_COUNTS: [u64; 1] = [100];
51+
52+
#[derive(Debug, Clone, Copy)]
53+
enum CurrentVariant {
54+
UnorderedFixed32,
55+
OrderedFixed32,
56+
UnorderedFixed256,
57+
OrderedFixed256,
58+
}
59+
60+
impl CurrentVariant {
61+
const fn name(self) -> &'static str {
62+
match self {
63+
Self::UnorderedFixed32 => "current::unordered::fixed::mmb chunk=32",
64+
Self::OrderedFixed32 => "current::ordered::fixed::mmb chunk=32",
65+
Self::UnorderedFixed256 => "current::unordered::fixed::mmb chunk=256",
66+
Self::OrderedFixed256 => "current::ordered::fixed::mmb chunk=256",
67+
}
68+
}
69+
}
70+
71+
const CURRENT_VARIANTS: [CurrentVariant; 4] = [
72+
CurrentVariant::UnorderedFixed32,
73+
CurrentVariant::OrderedFixed32,
74+
CurrentVariant::UnorderedFixed256,
75+
CurrentVariant::OrderedFixed256,
76+
];
77+
78+
/// Construct a Current database for `$variant`, bind it as `$db`, and execute `$body`.
79+
macro_rules! with_current_db {
80+
($ctx:expr, $variant:expr, |mut $db:ident| $body:expr) => {{
81+
match $variant {
82+
CurrentVariant::UnorderedFixed32 => {
83+
dispatch_arm!($ctx, $db, $body, CurUFix32Mmb, cur_fix_cfg)
84+
}
85+
CurrentVariant::OrderedFixed32 => {
86+
dispatch_arm!($ctx, $db, $body, CurOFix32Mmb, cur_fix_cfg)
87+
}
88+
CurrentVariant::UnorderedFixed256 => {
89+
dispatch_arm!($ctx, $db, $body, CurUFix256Mmb, cur_fix_cfg)
90+
}
91+
CurrentVariant::OrderedFixed256 => {
92+
dispatch_arm!($ctx, $db, $body, CurOFix256Mmb, cur_fix_cfg)
93+
}
94+
}
95+
}};
96+
}
97+
98+
/// Pre-populate the database with `num_keys` unique keys, commit, and sync so buffered seed data
99+
/// doesn't get attributed to the timed region's `sync_metadata` call.
100+
async fn seed_db<F: merkle::Family, C: DbAny<F, Key = Digest, Value = Digest>>(
101+
db: &mut C,
102+
num_keys: u64,
103+
) {
104+
let mut rng = StdRng::seed_from_u64(42);
105+
let mut batch = db.new_batch();
106+
for i in 0u64..num_keys {
107+
let k = Sha256::hash(&i.to_be_bytes());
108+
batch = batch.write(k, Some(make_fixed_value(&mut rng)));
109+
}
110+
let merkleized = batch.merkleize(db, None).await.unwrap();
111+
db.apply_batch(merkleized).await.unwrap();
112+
db.commit().await.unwrap();
113+
db.sync().await.unwrap();
114+
}
115+
116+
/// Write `num_updates` random key updates into a batch.
117+
fn write_random_updates<
118+
B: commonware_storage::qmdb::any::traits::UnmerkleizedBatch<Db, K = Digest, V = Digest>,
119+
Db: ?Sized,
120+
>(
121+
mut batch: B,
122+
num_updates: u64,
123+
num_keys: u64,
124+
rng: &mut StdRng,
125+
) -> B {
126+
for _ in 0..num_updates {
127+
let idx = rng.next_u64() % num_keys;
128+
let k = Sha256::hash(&idx.to_be_bytes());
129+
batch = batch.write(k, Some(make_fixed_value(rng)));
130+
}
131+
batch
132+
}
133+
134+
/// Run a chained-growth sequence with a pre-built deep chain.
135+
///
136+
/// `fork_child` bridges the generic trait and the concrete `new_batch` method on a merkleized
137+
/// batch.
138+
async fn run_chained_growth<
139+
F: merkle::Family,
140+
C: DbAny<F, Key = Digest, Value = Digest>,
141+
Fork: Fn(&C::Merkleized) -> C::Batch,
142+
>(
143+
mut db: C,
144+
grow: u64,
145+
fork_child: Fork,
146+
) -> Duration {
147+
seed_db(&mut db, NUM_KEYS).await;
148+
let mut rng = StdRng::seed_from_u64(99);
149+
150+
// Pre-build a deep chain (untimed).
151+
let initial = write_random_updates(db.new_batch(), UPDATES_PER_BATCH, NUM_KEYS, &mut rng);
152+
let mut parent = initial.merkleize(&db, None).await.unwrap();
153+
for _ in 0..PREBUILT_CHAIN {
154+
let child_batch =
155+
write_random_updates(fork_child(&parent), UPDATES_PER_BATCH, NUM_KEYS, &mut rng);
156+
let child = child_batch.merkleize(&db, None).await.unwrap();
157+
db.apply_batch(parent).await.unwrap();
158+
parent = child;
159+
}
160+
161+
// Flush buffered data so the timed region doesn't inherit setup fsync cost.
162+
db.commit().await.unwrap();
163+
db.sync().await.unwrap();
164+
165+
// Timed: grow more batches on top of the pre-built chain.
166+
let start = Instant::now();
167+
for _ in 0..grow {
168+
let child_batch =
169+
write_random_updates(fork_child(&parent), UPDATES_PER_BATCH, NUM_KEYS, &mut rng);
170+
let child = child_batch.merkleize(&db, None).await.unwrap();
171+
black_box(child.root());
172+
db.apply_batch(parent).await.unwrap();
173+
parent = child;
174+
}
175+
db.apply_batch(parent).await.unwrap();
176+
let total = start.elapsed();
177+
178+
db.destroy().await.unwrap();
179+
total
180+
}
181+
182+
fn bench_chained_growth(c: &mut Criterion) {
183+
let runner = tokio::Runner::new(Config::default());
184+
for batches in GROW_COUNTS {
185+
for &variant in &CURRENT_VARIANTS {
186+
c.bench_function(
187+
&format!(
188+
"{}/variant={} batches={batches}",
189+
module_path!(),
190+
variant.name()
191+
),
192+
|b| {
193+
b.to_async(&runner).iter_custom(|iters| async move {
194+
let ctx = context::get::<Context>();
195+
let mut total = Duration::ZERO;
196+
for _ in 0..iters {
197+
with_current_db!(ctx.clone(), variant, |mut db| {
198+
total += run_chained_growth(db, batches, |p| p.new_batch()).await;
199+
});
200+
}
201+
total
202+
});
203+
},
204+
);
205+
}
206+
}
207+
}
208+
209+
criterion_group! {
210+
name = benches;
211+
config = Criterion::default().sample_size(10);
212+
targets = bench_chained_growth
213+
}

0 commit comments

Comments
 (0)