use crate::common::{make_fixed_value, Digest, CHUNK_SIZE, WRITE_BUFFER_SIZE};
use commonware_cryptography::{Hasher, Sha256};
use commonware_runtime::{
benchmarks::{context, tokio},
buffer::paged::CacheRef,
tokio::{Config, Context},
BufferPooler, ThreadPooler,
};
use commonware_storage::{
journal::contiguous::{fixed::Config as FConfig, variable::Config as VConfig},
merkle::{self, journaled},
qmdb::any::traits::{DbAny, MerkleizedBatch as _, UnmerkleizedBatch as _},
translator::EightCap,
};
use commonware_utils::{NZUsize, NZU16, NZU64};
use criterion::{criterion_group, Criterion};
use rand::{rngs::StdRng, RngCore, SeedableRng};
use std::{
hint::black_box,
num::{NonZeroU16, NonZeroU64, NonZeroUsize},
time::{Duration, Instant},
};
type AnyUFix = commonware_storage::qmdb::any::unordered::fixed::Db<
commonware_storage::merkle::mmr::Family,
Context,
Digest,
Digest,
Sha256,
EightCap,
>;
type AnyUVar = commonware_storage::qmdb::any::unordered::variable::Db<
commonware_storage::merkle::mmr::Family,
Context,
Digest,
Digest,
Sha256,
EightCap,
>;
type AnyUFixMmb = commonware_storage::qmdb::any::unordered::fixed::Db<
commonware_storage::merkle::mmb::Family,
Context,
Digest,
Digest,
Sha256,
EightCap,
>;
type AnyUVarMmb = commonware_storage::qmdb::any::unordered::variable::Db<
commonware_storage::merkle::mmb::Family,
Context,
Digest,
Digest,
Sha256,
EightCap,
>;
type CurUFix32 = commonware_storage::qmdb::current::unordered::fixed::Db<
commonware_storage::merkle::mmr::Family,
Context,
Digest,
Digest,
Sha256,
EightCap,
CHUNK_SIZE,
>;
type CurUVar32 = commonware_storage::qmdb::current::unordered::variable::Db<
commonware_storage::merkle::mmr::Family,
Context,
Digest,
Digest,
Sha256,
EightCap,
CHUNK_SIZE,
>;
type CurUFix32Mmb = commonware_storage::qmdb::current::unordered::fixed::Db<
commonware_storage::merkle::mmb::Family,
Context,
Digest,
Digest,
Sha256,
EightCap,
CHUNK_SIZE,
>;
type CurUVar32Mmb = commonware_storage::qmdb::current::unordered::variable::Db<
commonware_storage::merkle::mmb::Family,
Context,
Digest,
Digest,
Sha256,
EightCap,
CHUNK_SIZE,
>;
const LARGE_CHUNK_SIZE: usize = 256;
type CurUFix256 = commonware_storage::qmdb::current::unordered::fixed::Db<
commonware_storage::merkle::mmr::Family,
Context,
Digest,
Digest,
Sha256,
EightCap,
LARGE_CHUNK_SIZE,
>;
type CurUVar256 = commonware_storage::qmdb::current::unordered::variable::Db<
commonware_storage::merkle::mmr::Family,
Context,
Digest,
Digest,
Sha256,
EightCap,
LARGE_CHUNK_SIZE,
>;
type CurUFix256Mmb = commonware_storage::qmdb::current::unordered::fixed::Db<
commonware_storage::merkle::mmb::Family,
Context,
Digest,
Digest,
Sha256,
EightCap,
LARGE_CHUNK_SIZE,
>;
type CurUVar256Mmb = commonware_storage::qmdb::current::unordered::variable::Db<
commonware_storage::merkle::mmb::Family,
Context,
Digest,
Digest,
Sha256,
EightCap,
LARGE_CHUNK_SIZE,
>;
type AnyOFix = commonware_storage::qmdb::any::ordered::fixed::Db<
commonware_storage::merkle::mmr::Family,
Context,
Digest,
Digest,
Sha256,
EightCap,
>;
type AnyOVar = commonware_storage::qmdb::any::ordered::variable::Db<
commonware_storage::merkle::mmr::Family,
Context,
Digest,
Digest,
Sha256,
EightCap,
>;
type AnyOFixMmb = commonware_storage::qmdb::any::ordered::fixed::Db<
commonware_storage::merkle::mmb::Family,
Context,
Digest,
Digest,
Sha256,
EightCap,
>;
type AnyOVarMmb = commonware_storage::qmdb::any::ordered::variable::Db<
commonware_storage::merkle::mmb::Family,
Context,
Digest,
Digest,
Sha256,
EightCap,
>;
type CurOFix32 = commonware_storage::qmdb::current::ordered::fixed::Db<
commonware_storage::merkle::mmr::Family,
Context,
Digest,
Digest,
Sha256,
EightCap,
CHUNK_SIZE,
>;
type CurOVar32 = commonware_storage::qmdb::current::ordered::variable::Db<
commonware_storage::merkle::mmr::Family,
Context,
Digest,
Digest,
Sha256,
EightCap,
CHUNK_SIZE,
>;
type CurOFix32Mmb = commonware_storage::qmdb::current::ordered::fixed::Db<
commonware_storage::merkle::mmb::Family,
Context,
Digest,
Digest,
Sha256,
EightCap,
CHUNK_SIZE,
>;
type CurOVar32Mmb = commonware_storage::qmdb::current::ordered::variable::Db<
commonware_storage::merkle::mmb::Family,
Context,
Digest,
Digest,
Sha256,
EightCap,
CHUNK_SIZE,
>;
type CurOFix256 = commonware_storage::qmdb::current::ordered::fixed::Db<
commonware_storage::merkle::mmr::Family,
Context,
Digest,
Digest,
Sha256,
EightCap,
LARGE_CHUNK_SIZE,
>;
type CurOVar256 = commonware_storage::qmdb::current::ordered::variable::Db<
commonware_storage::merkle::mmr::Family,
Context,
Digest,
Digest,
Sha256,
EightCap,
LARGE_CHUNK_SIZE,
>;
type CurOFix256Mmb = commonware_storage::qmdb::current::ordered::fixed::Db<
commonware_storage::merkle::mmb::Family,
Context,
Digest,
Digest,
Sha256,
EightCap,
LARGE_CHUNK_SIZE,
>;
type CurOVar256Mmb = commonware_storage::qmdb::current::ordered::variable::Db<
commonware_storage::merkle::mmb::Family,
Context,
Digest,
Digest,
Sha256,
EightCap,
LARGE_CHUNK_SIZE,
>;
const ITEMS_PER_BLOB: NonZeroU64 = NZU64!(10_000_000);
const THREADS: NonZeroUsize = NZUsize!(8);
const PAGE_SIZE: NonZeroU16 = NZU16!(4096);
const LARGE_PAGE_CACHE_SIZE: NonZeroUsize = NZUsize!(131_072);
const PARTITION: &str = "bench-merkleize";
fn merkle_cfg(ctx: &(impl BufferPooler + ThreadPooler), pc: CacheRef) -> journaled::Config {
journaled::Config {
journal_partition: format!("journal-{PARTITION}"),
metadata_partition: format!("metadata-{PARTITION}"),
items_per_blob: ITEMS_PER_BLOB,
write_buffer: WRITE_BUFFER_SIZE,
thread_pool: Some(ctx.create_thread_pool(THREADS).unwrap()),
page_cache: pc,
}
}
fn fix_log_cfg(pc: CacheRef) -> FConfig {
FConfig {
partition: format!("log-journal-{PARTITION}"),
items_per_blob: ITEMS_PER_BLOB,
page_cache: pc,
write_buffer: WRITE_BUFFER_SIZE,
}
}
fn var_log_cfg(pc: CacheRef) -> VConfig<((), ())> {
VConfig {
partition: format!("log-journal-{PARTITION}"),
items_per_section: ITEMS_PER_BLOB,
compression: None,
codec_config: ((), ()),
page_cache: pc,
write_buffer: WRITE_BUFFER_SIZE,
}
}
fn pc(ctx: &impl BufferPooler) -> CacheRef {
CacheRef::from_pooler(ctx, PAGE_SIZE, LARGE_PAGE_CACHE_SIZE)
}
fn any_fix_cfg(
ctx: &(impl BufferPooler + ThreadPooler),
) -> commonware_storage::qmdb::any::FixedConfig<EightCap> {
let pc = pc(ctx);
commonware_storage::qmdb::any::FixedConfig {
merkle_config: merkle_cfg(ctx, pc.clone()),
journal_config: fix_log_cfg(pc),
translator: EightCap,
}
}
fn any_var_cfg(
ctx: &(impl BufferPooler + ThreadPooler),
) -> commonware_storage::qmdb::any::VariableConfig<EightCap, ((), ())> {
let pc = pc(ctx);
commonware_storage::qmdb::any::VariableConfig {
merkle_config: merkle_cfg(ctx, pc.clone()),
journal_config: var_log_cfg(pc),
translator: EightCap,
}
}
fn cur_fix_cfg(
ctx: &(impl BufferPooler + ThreadPooler),
) -> commonware_storage::qmdb::current::FixedConfig<EightCap> {
let pc = pc(ctx);
commonware_storage::qmdb::current::FixedConfig {
merkle_config: merkle_cfg(ctx, pc.clone()),
journal_config: fix_log_cfg(pc),
grafted_metadata_partition: format!("grafted-metadata-{PARTITION}"),
translator: EightCap,
}
}
fn cur_var_cfg(
ctx: &(impl BufferPooler + ThreadPooler),
) -> commonware_storage::qmdb::current::VariableConfig<EightCap, ((), ())> {
let pc = pc(ctx);
commonware_storage::qmdb::current::VariableConfig {
merkle_config: merkle_cfg(ctx, pc.clone()),
journal_config: var_log_cfg(pc),
grafted_metadata_partition: format!("grafted-metadata-{PARTITION}"),
translator: EightCap,
}
}
async fn seed_db<F: merkle::Family, C: DbAny<F, Key = Digest, Value = Digest>>(
db: &mut C,
num_keys: u64,
) {
let mut rng = StdRng::seed_from_u64(42);
let mut batch = db.new_batch();
for i in 0u64..num_keys {
let k = Sha256::hash(&i.to_be_bytes());
batch = batch.write(k, Some(make_fixed_value(&mut rng)));
}
let merkleized = batch.merkleize(db, None).await.unwrap();
db.apply_batch(merkleized).await.unwrap();
db.commit().await.unwrap();
db.sync().await.unwrap();
}
fn write_random_updates<
B: commonware_storage::qmdb::any::traits::UnmerkleizedBatch<Db, K = Digest, V = Digest>,
Db: ?Sized,
>(
mut batch: B,
num_updates: u64,
num_keys: u64,
rng: &mut StdRng,
) -> B {
for _ in 0..num_updates {
let idx = rng.next_u64() % num_keys;
let k = Sha256::hash(&idx.to_be_bytes());
batch = batch.write(k, Some(make_fixed_value(rng)));
}
batch
}
async fn run_bench<F: merkle::Family, C: DbAny<F, Key = Digest, Value = Digest>>(
mut db: C,
num_keys: u64,
iters: u64,
) -> Duration {
seed_db(&mut db, num_keys).await;
let num_updates = num_keys / 10;
let mut rng = StdRng::seed_from_u64(99);
let mut total = Duration::ZERO;
for _ in 0..iters {
let start = Instant::now();
let batch = write_random_updates(db.new_batch(), num_updates, num_keys, &mut rng);
let merkleized = batch.merkleize(&db, None).await.unwrap();
black_box(merkleized.root());
total += start.elapsed();
}
db.destroy().await.unwrap();
total
}
async fn run_chained_bench<
F: merkle::Family,
C: DbAny<F, Key = Digest, Value = Digest>,
Fn: std::ops::Fn(&C::Merkleized) -> C::Batch,
>(
mut db: C,
num_keys: u64,
iters: u64,
fork_child: Fn,
) -> Duration {
seed_db(&mut db, num_keys).await;
let num_updates = num_keys / 10;
let mut rng = StdRng::seed_from_u64(99);
let mut total = Duration::ZERO;
for _ in 0..iters {
let parent_batch = write_random_updates(db.new_batch(), num_updates, num_keys, &mut rng);
let parent = parent_batch.merkleize(&db, None).await.unwrap();
let start = Instant::now();
let child_batch =
write_random_updates(fork_child(&parent), num_updates, num_keys, &mut rng);
let child = child_batch.merkleize(&db, None).await.unwrap();
black_box(child.root());
total += start.elapsed();
}
db.destroy().await.unwrap();
total
}
macro_rules! variants {
(
$(
$entry:ident {
name: $name:literal,
init: |$ctx:ident| $init:expr,
}
)+
) => {
#[derive(Debug, Clone, Copy)]
enum Variant {
$($entry),+
}
impl Variant {
const fn name(self) -> &'static str {
match self {
$(Self::$entry => $name),+
}
}
}
const VARIANTS: &[Variant] = &[
$(Variant::$entry),+
];
macro_rules! dispatch_variant {
($ctx_expr:expr, $variant_expr:expr, |$db_name:ident| $body:expr) => {
match $variant_expr {
$(
Variant::$entry => {
let $ctx = $ctx_expr;
let $db_name = $init.await.unwrap();
$body
}
)+
}
};
}
};
}
variants! {
AnyFixed {
name: "any::unordered::fixed::mmr",
init: |ctx| AnyUFix::init(ctx.clone(), any_fix_cfg(&ctx)),
}
AnyVariable {
name: "any::unordered::variable::mmr",
init: |ctx| AnyUVar::init(ctx.clone(), any_var_cfg(&ctx)),
}
AnyFixedMmb {
name: "any::unordered::fixed::mmb",
init: |ctx| AnyUFixMmb::init(ctx.clone(), any_fix_cfg(&ctx)),
}
AnyVariableMmb {
name: "any::unordered::variable::mmb",
init: |ctx| AnyUVarMmb::init(ctx.clone(), any_var_cfg(&ctx)),
}
AnyOrderedFixed {
name: "any::ordered::fixed::mmr",
init: |ctx| AnyOFix::init(ctx.clone(), any_fix_cfg(&ctx)),
}
AnyOrderedVariable {
name: "any::ordered::variable::mmr",
init: |ctx| AnyOVar::init(ctx.clone(), any_var_cfg(&ctx)),
}
AnyOrderedFixedMmb {
name: "any::ordered::fixed::mmb",
init: |ctx| AnyOFixMmb::init(ctx.clone(), any_fix_cfg(&ctx)),
}
AnyOrderedVariableMmb {
name: "any::ordered::variable::mmb",
init: |ctx| AnyOVarMmb::init(ctx.clone(), any_var_cfg(&ctx)),
}
CurrentFixed32 {
name: "current::unordered::fixed::mmr chunk=32",
init: |ctx| CurUFix32::init(ctx.clone(), cur_fix_cfg(&ctx)),
}
CurrentVariable32 {
name: "current::unordered::variable::mmr chunk=32",
init: |ctx| CurUVar32::init(ctx.clone(), cur_var_cfg(&ctx)),
}
CurrentFixed32Mmb {
name: "current::unordered::fixed::mmb chunk=32",
init: |ctx| CurUFix32Mmb::init(ctx.clone(), cur_fix_cfg(&ctx)),
}
CurrentVariable32Mmb {
name: "current::unordered::variable::mmb chunk=32",
init: |ctx| CurUVar32Mmb::init(ctx.clone(), cur_var_cfg(&ctx)),
}
CurrentFixed256 {
name: "current::unordered::fixed::mmr chunk=256",
init: |ctx| CurUFix256::init(ctx.clone(), cur_fix_cfg(&ctx)),
}
CurrentVariable256 {
name: "current::unordered::variable::mmr chunk=256",
init: |ctx| CurUVar256::init(ctx.clone(), cur_var_cfg(&ctx)),
}
CurrentFixed256Mmb {
name: "current::unordered::fixed::mmb chunk=256",
init: |ctx| CurUFix256Mmb::init(ctx.clone(), cur_fix_cfg(&ctx)),
}
CurrentVariable256Mmb {
name: "current::unordered::variable::mmb chunk=256",
init: |ctx| CurUVar256Mmb::init(ctx.clone(), cur_var_cfg(&ctx)),
}
CurrentOrderedFixed32 {
name: "current::ordered::fixed::mmr chunk=32",
init: |ctx| CurOFix32::init(ctx.clone(), cur_fix_cfg(&ctx)),
}
CurrentOrderedVariable32 {
name: "current::ordered::variable::mmr chunk=32",
init: |ctx| CurOVar32::init(ctx.clone(), cur_var_cfg(&ctx)),
}
CurrentOrderedFixed32Mmb {
name: "current::ordered::fixed::mmb chunk=32",
init: |ctx| CurOFix32Mmb::init(ctx.clone(), cur_fix_cfg(&ctx)),
}
CurrentOrderedVariable32Mmb {
name: "current::ordered::variable::mmb chunk=32",
init: |ctx| CurOVar32Mmb::init(ctx.clone(), cur_var_cfg(&ctx)),
}
CurrentOrderedFixed256 {
name: "current::ordered::fixed::mmr chunk=256",
init: |ctx| CurOFix256::init(ctx.clone(), cur_fix_cfg(&ctx)),
}
CurrentOrderedVariable256 {
name: "current::ordered::variable::mmr chunk=256",
init: |ctx| CurOVar256::init(ctx.clone(), cur_var_cfg(&ctx)),
}
CurrentOrderedFixed256Mmb {
name: "current::ordered::fixed::mmb chunk=256",
init: |ctx| CurOFix256Mmb::init(ctx.clone(), cur_fix_cfg(&ctx)),
}
CurrentOrderedVariable256Mmb {
name: "current::ordered::variable::mmb chunk=256",
init: |ctx| CurOVar256Mmb::init(ctx.clone(), cur_var_cfg(&ctx)),
}
}
fn bench_merkleize(c: &mut Criterion) {
let runner = tokio::Runner::new(Config::default());
for num_keys in [10_000u64, 100_000, 1_000_000] {
for &variant in VARIANTS {
c.bench_function(
&format!(
"{}/variant={} num_keys={num_keys}",
module_path!(),
variant.name(),
),
|b| {
b.to_async(&runner).iter_custom(|iters| async move {
let ctx = context::get::<Context>();
dispatch_variant!(ctx, variant, |db| {
run_bench(db, num_keys, iters).await
})
});
},
);
}
}
}
fn bench_chained_merkleize(c: &mut Criterion) {
let runner = tokio::Runner::new(Config::default());
for num_keys in [10_000u64, 100_000, 1_000_000] {
for &variant in VARIANTS {
c.bench_function(
&format!(
"{}/chained variant={} num_keys={num_keys}",
module_path!(),
variant.name(),
),
|b| {
b.to_async(&runner).iter_custom(|iters| async move {
let ctx = context::get::<Context>();
dispatch_variant!(ctx, variant, |db| {
run_chained_bench(db, num_keys, iters, |p| p.new_batch()).await
})
});
},
);
}
}
}
criterion_group! {
name = benches;
config = Criterion::default().sample_size(10);
targets = bench_merkleize, bench_chained_merkleize
}