Skip to content

Commit 094c8ee

Browse files
trim chain
1 parent 19e4685 commit 094c8ee

3 files changed

Lines changed: 141 additions & 17 deletions

File tree

storage/src/qmdb/benches/chained_growth.rs

Lines changed: 64 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,27 +6,40 @@
66
//! Timed: do `batches` more merkleize + apply iterations on top of the pre-built chain, with a
77
//! single random update per batch so each overlay covers a tiny fraction of chunks.
88
9-
use crate::common::{dispatch_arm, make_fixed_value, Digest};
9+
use crate::common::{make_fixed_value, Digest, WRITE_BUFFER_SIZE};
1010
use commonware_cryptography::{Hasher, Sha256};
1111
use commonware_runtime::{
1212
benchmarks::{context, tokio},
13+
buffer::paged::CacheRef,
1314
tokio::{Config, Context},
15+
BufferPooler, ThreadPooler,
1416
};
1517
use commonware_storage::{
16-
merkle::{self, mmb::Family as Mmb},
18+
journal::contiguous::fixed::Config as FConfig,
19+
merkle::{self, journaled, mmb::Family as Mmb},
1720
qmdb::{
1821
any::traits::{DbAny, MerkleizedBatch as _, UnmerkleizedBatch as _},
1922
current::{ordered::fixed::Db as OCFixed, unordered::fixed::Db as UCFixed},
2023
},
2124
translator::EightCap,
2225
};
26+
use commonware_utils::{NZUsize, NZU16, NZU64};
2327
use criterion::{criterion_group, Criterion};
2428
use rand::{rngs::StdRng, RngCore, SeedableRng};
2529
use std::{
2630
hint::black_box,
31+
num::{NonZeroU16, NonZeroU64, NonZeroUsize},
2732
time::{Duration, Instant},
2833
};
2934

35+
// -- Config (mirrors merkleize bench) --
36+
37+
const ITEMS_PER_BLOB: NonZeroU64 = NZU64!(10_000_000);
38+
const THREADS: NonZeroUsize = NZUsize!(8);
39+
const PAGE_SIZE: NonZeroU16 = NZU16!(4096);
40+
const LARGE_PAGE_CACHE_SIZE: NonZeroUsize = NZUsize!(131_072);
41+
const PARTITION: &str = "bench-chained-growth";
42+
3043
const SMALL_CHUNK_SIZE: usize = 32;
3144
const LARGE_CHUNK_SIZE: usize = 256;
3245

@@ -35,6 +48,42 @@ type CurOFix32Mmb = OCFixed<Mmb, Context, Digest, Digest, Sha256, EightCap, SMAL
3548
type CurUFix256Mmb = UCFixed<Mmb, Context, Digest, Digest, Sha256, EightCap, LARGE_CHUNK_SIZE>;
3649
type CurOFix256Mmb = OCFixed<Mmb, Context, Digest, Digest, Sha256, EightCap, LARGE_CHUNK_SIZE>;
3750

51+
fn merkle_cfg(ctx: &(impl BufferPooler + ThreadPooler), pc: CacheRef) -> journaled::Config {
52+
journaled::Config {
53+
journal_partition: format!("journal-{PARTITION}"),
54+
metadata_partition: format!("metadata-{PARTITION}"),
55+
items_per_blob: ITEMS_PER_BLOB,
56+
write_buffer: WRITE_BUFFER_SIZE,
57+
thread_pool: Some(ctx.create_thread_pool(THREADS).unwrap()),
58+
page_cache: pc,
59+
}
60+
}
61+
62+
fn fix_log_cfg(pc: CacheRef) -> FConfig {
63+
FConfig {
64+
partition: format!("log-journal-{PARTITION}"),
65+
items_per_blob: ITEMS_PER_BLOB,
66+
page_cache: pc,
67+
write_buffer: WRITE_BUFFER_SIZE,
68+
}
69+
}
70+
71+
fn pc(ctx: &impl BufferPooler) -> CacheRef {
72+
CacheRef::from_pooler(ctx, PAGE_SIZE, LARGE_PAGE_CACHE_SIZE)
73+
}
74+
75+
fn cur_fix_cfg(
76+
ctx: &(impl BufferPooler + ThreadPooler),
77+
) -> commonware_storage::qmdb::current::FixedConfig<EightCap> {
78+
let pc = pc(ctx);
79+
commonware_storage::qmdb::current::FixedConfig {
80+
merkle_config: merkle_cfg(ctx, pc.clone()),
81+
journal_config: fix_log_cfg(pc),
82+
grafted_metadata_partition: format!("grafted-metadata-{PARTITION}"),
83+
translator: EightCap,
84+
}
85+
}
86+
3887
/// Number of pre-populated keys in the seeded database.
3988
const NUM_KEYS: u64 = 1_000_000;
4089

@@ -78,19 +127,20 @@ const CURRENT_VARIANTS: [CurrentVariant; 4] = [
78127
/// Construct a Current database for `$variant`, bind it as `$db`, and execute `$body`.
79128
macro_rules! with_current_db {
80129
($ctx:expr, $variant:expr, |mut $db:ident| $body:expr) => {{
130+
macro_rules! init_db {
131+
($DbType:ty) => {{
132+
#[allow(unused_mut)]
133+
let mut $db = <$DbType>::init($ctx.clone(), cur_fix_cfg(&$ctx))
134+
.await
135+
.unwrap();
136+
$body
137+
}};
138+
}
81139
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-
}
140+
CurrentVariant::UnorderedFixed32 => init_db!(CurUFix32Mmb),
141+
CurrentVariant::OrderedFixed32 => init_db!(CurOFix32Mmb),
142+
CurrentVariant::UnorderedFixed256 => init_db!(CurUFix256Mmb),
143+
CurrentVariant::OrderedFixed256 => init_db!(CurOFix256Mmb),
94144
}
95145
}};
96146
}

storage/src/qmdb/current/batch.rs

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -767,6 +767,78 @@ pub(crate) struct BitmapBatchLayer<const N: usize> {
767767

768768
impl<const N: usize> BitmapBatch<N> {
769769
const CHUNK_SIZE_BITS: u64 = BitMap::<N>::CHUNK_SIZE_BITS;
770+
771+
/// Walk to the terminal [`SharedBitmap`] at the bottom of the chain.
772+
fn shared(&self) -> &Arc<SharedBitmap<N>> {
773+
let mut current = self;
774+
loop {
775+
match current {
776+
Self::Base(s) => return s,
777+
Self::Layer(layer) => current = &layer.parent,
778+
}
779+
}
780+
}
781+
782+
/// Return a chain equivalent to `self` with any `Layer` whose overlay is now fully committed
783+
/// replaced by a direct reference to the committed bitmap. Since `apply_batch` commits
784+
/// contiguous prefixes, committed `Layer`s are always at the bottom of the chain.
785+
fn trim_committed(&self) -> Self {
786+
// Fast path: if already Base, nothing to do
787+
if let Self::Base(_) = self {
788+
return self.clone();
789+
}
790+
791+
let shared = self.shared();
792+
let committed = shared.read().len();
793+
794+
let mut current = self;
795+
let mut uncommitted = 0;
796+
while let Self::Layer(layer) = current {
797+
if layer.overlay.len <= committed {
798+
break;
799+
}
800+
uncommitted += 1;
801+
current = &layer.parent;
802+
}
803+
804+
// Top layer is already committed
805+
if uncommitted == 0 {
806+
return Self::Base(Arc::clone(shared));
807+
}
808+
809+
// No layer is committed; keep the whole chain
810+
if let Self::Base(_) = current {
811+
return self.clone();
812+
}
813+
814+
// Fast path for exactly 1 uncommitted layer (common in chained growth)
815+
if uncommitted == 1 {
816+
if let Self::Layer(layer) = self {
817+
return Self::Layer(Arc::new(BitmapBatchLayer {
818+
parent: Self::Base(Arc::clone(shared)),
819+
overlay: Arc::clone(&layer.overlay),
820+
}));
821+
}
822+
}
823+
824+
let mut kept = Vec::with_capacity(uncommitted);
825+
current = self;
826+
for _ in 0..uncommitted {
827+
if let Self::Layer(layer) = current {
828+
kept.push(Arc::clone(&layer.overlay));
829+
current = &layer.parent;
830+
}
831+
}
832+
833+
let mut result = Self::Base(Arc::clone(shared));
834+
for overlay in kept.into_iter().rev() {
835+
result = Self::Layer(Arc::new(BitmapBatchLayer {
836+
parent: result,
837+
overlay,
838+
}));
839+
}
840+
result
841+
}
770842
}
771843

772844
impl<const N: usize> BitmapReadable<N> for BitmapBatch<N> {
@@ -861,7 +933,7 @@ where
861933
UnmerkleizedBatch::new(
862934
self.inner.new_batch::<H>(),
863935
Arc::clone(&self.grafted),
864-
self.bitmap.clone(),
936+
self.bitmap.trim_committed(),
865937
)
866938
}
867939

storage/src/qmdb/current/db.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -668,9 +668,11 @@ where
668668
// 6. Apply overlays in place under the write lock.
669669
{
670670
let mut guard = self.status.write();
671+
if let Some(newest) = overlays.first() {
672+
guard.extend_to(newest.len);
673+
}
674+
let pruned = guard.pruned_chunks();
671675
for overlay in overlays.into_iter().rev() {
672-
guard.extend_to(overlay.len);
673-
let pruned = guard.pruned_chunks();
674676
for (&idx, chunk) in &overlay.chunks {
675677
if idx >= pruned {
676678
guard.set_chunk_by_index(idx, chunk);

0 commit comments

Comments
 (0)