use rand::{rngs::StdRng, seq::SliceRandom, SeedableRng}; mod models; use models::*; use std::collections::BTreeMap; use criterion::{black_box, criterion_group, criterion_main, BatchSize, BenchmarkId, Criterion}; use sweep_bptree::{BPlusTree, BPlusTreeMap, NodeStoreVec}; const COUNTS: [usize; 3] = [1000, 10000, 10_0000]; const RAND_SEED: u64 = 123; type NodeStoreBench = NodeStoreVec; fn bench_ordered_insert(c: &mut Criterion) { let mut group = c.benchmark_group(format!("ordered_insert/{}", K::name())); for count in COUNTS { group.bench_with_input(BenchmarkId::new("bptree", count), &count, |b, count| { b.iter(|| { let mut tree = BPlusTree::>::new(NodeStoreBench::::new()); for i in 0..*count { let k = K::from_i(i); tree.insert(k, Value::default()); } tree }); }); group.bench_with_input( BenchmarkId::new("bptree_bulk", count), &count, |b, count| { b.iter(|| { let mut kvs = Vec::with_capacity(*count); for i in 0..*count { let k = K::from_i(i); kvs.push((k, Value::default())); } BPlusTree::>::bulk_load(kvs) }); }, ); group.bench_with_input(BenchmarkId::new("btree", count), &count, |b, count| { b.iter(|| { let mut tree = BTreeMap::::new(); for i in 0..*count { let k = K::from_i(i); tree.insert(k, Value::default()); } tree }); }); group.bench_with_input( BenchmarkId::new("btree_from_iter", count), &count, |b, count| { b.iter(|| { let mut kvs = Vec::with_capacity(*count); for i in 0..*count { let k = K::from_i(i); kvs.push((k, Value::default())); } BTreeMap::::from_iter(kvs.into_iter()) }); }, ); } } fn bench_random_insert(c: &mut Criterion) { let mut group = c.benchmark_group(format!("random_insert/{}", K::name())); for count in COUNTS { let mut keys = vec![]; for i in 0..count { let k = K::from_i(i); keys.push(k); } let mut r = StdRng::seed_from_u64(RAND_SEED); keys.shuffle(&mut r); group.bench_function(BenchmarkId::new("bptree", count), |b| { b.iter(|| { let node_store = NodeStoreBench::::new(); let mut tree = BPlusTree::new(node_store); for k in &keys { tree.insert(k.clone(), Value::default()); } tree }); }); group.bench_function(BenchmarkId::new("bptree_sort_load", count), |b| { b.iter_batched( || keys.clone(), |mut keys| { keys.sort(); BPlusTree::>::bulk_load( keys.into_iter() .map(|k| (k, Value::default())) .collect::>(), ) }, BatchSize::NumIterations(1), ) }); group.bench_function(BenchmarkId::new("btree", count), |b| { b.iter(|| { let mut tree = BTreeMap::new(); for k in &keys { tree.insert(k.clone(), Value::default()); } tree }); }); group.bench_function(BenchmarkId::new("bptree_from_iter", count), |b| { b.iter_batched( || keys.clone(), |keys| { BPlusTreeMap::::from_iter( keys.into_iter().map(|k| (k, Value::default())), ) }, BatchSize::NumIterations(1), ) }); group.bench_function(BenchmarkId::new("btree_from_iter", count), |b| { b.iter_batched( || keys.clone(), |keys| { BTreeMap::::from_iter(keys.into_iter().map(|k| (k, Value::default()))) }, BatchSize::NumIterations(1), ); }); } } fn bench_clone(c: &mut Criterion) { let mut group = c.benchmark_group(format!("clone/{}", K::name())); for count in COUNTS { group.bench_with_input(BenchmarkId::new("bptree", count), &count, |b, count| { let tree = create_bptree::(*count); b.iter(|| black_box(tree.clone())); }); group.bench_with_input(BenchmarkId::new("btree", count), &count, |b, count| { let tree = create_btree::(*count); b.iter(|| black_box(tree.clone())); }); } } fn bench_drop(c: &mut Criterion) { let mut group = c.benchmark_group(format!("drop/{}", K::name())); for count in COUNTS { group.bench_with_input(BenchmarkId::new("bptree", count), &count, |b, count| { b.iter_batched( || create_bptree::(*count), |tree| { drop(tree); }, criterion::BatchSize::NumIterations(1), ); }); group.bench_with_input(BenchmarkId::new("btree", count), &count, |b, count| { b.iter_batched( || create_btree::(*count), |tree| { drop(tree); }, criterion::BatchSize::NumIterations(1), ); }); } } fn bench_iter(c: &mut Criterion) { let mut group = c.benchmark_group(format!("iter/{}", K::name())); for count in COUNTS { group.bench_with_input(BenchmarkId::new("bptree", count), &count, |b, count| { let tree = create_bptree::(*count); b.iter(|| { let c = tree.iter().count(); assert_eq!(c, tree.len()); }); }); group.bench_with_input(BenchmarkId::new("btree", count), &count, |b, count| { let tree = create_btree::(*count); b.iter(|| { let c = tree.iter().count(); assert_eq!(c, tree.len()); }); }); } } fn bench_into_iter(c: &mut Criterion) { // note: into_iter is slower than iter, mostly due to heavy drop let mut group = c.benchmark_group(format!("into_iter/{}", K::name())); for count in COUNTS { group.bench_with_input(BenchmarkId::new("bptree", count), &count, |b, count| { b.iter_batched( || create_bptree::(*count), |tree| { let count = tree.len(); let c = tree.into_iter().count(); assert_eq!(c, count); }, criterion::BatchSize::NumIterations(1), ); }); group.bench_with_input(BenchmarkId::new("btree", count), &count, |b, count| { b.iter_batched( || create_btree::(*count), |tree| { let count = tree.len(); let c = tree.into_iter().count(); assert_eq!(c, count); }, criterion::BatchSize::NumIterations(1), ); }); } } fn bench_ordered_remove(c: &mut Criterion) { let mut group = c.benchmark_group(format!("ordered_remove/{}", K::name())); for count in COUNTS { group.bench_with_input(BenchmarkId::new("bptree", count), &count, |b, count| { b.iter_batched( || create_bptree::(*count), |mut tree| { for i in 0..*count { let k = K::from_i(i); assert!(tree.remove(&k).is_some()); } tree }, criterion::BatchSize::NumIterations(1), ); }); group.bench_with_input(BenchmarkId::new("btree", count), &count, |b, count| { b.iter_batched( || create_btree::(*count), |mut tree| { for i in 0..*count { let k = K::from_i(i); assert!(tree.remove(&k).is_some()); } tree }, criterion::BatchSize::NumIterations(1), ); }); } } fn bench_random_remove(c: &mut Criterion) { let mut group = c.benchmark_group(format!("random_remove/{}", K::name())); for count in COUNTS { group.bench_with_input(BenchmarkId::new("bptree", count), &count, |b, count| { b.iter_batched( || { let tree = create_bptree::(*count); let mut keys = tree.iter().map(|(k, _v)| k).cloned().collect::>(); let mut r = StdRng::seed_from_u64(RAND_SEED); keys.shuffle(&mut r); (tree, keys) }, |(mut tree, keys)| { for k in keys.iter() { tree.remove(&k); } tree }, criterion::BatchSize::NumIterations(1), ); }); group.bench_with_input(BenchmarkId::new("btree", count), &count, |b, count| { b.iter_batched( || { let tree = create_btree::(*count); let mut keys = tree.iter().map(|(k, _v)| k).cloned().collect::>(); let mut r = StdRng::seed_from_u64(RAND_SEED); keys.shuffle(&mut r); (tree, keys) }, |(mut tree, keys)| { for k in keys.iter() { tree.remove(&k); } tree }, criterion::BatchSize::NumIterations(1), ); }); } } fn bench_ordered_get(c: &mut Criterion) { let mut group = c.benchmark_group(format!("ordered_get/{}", K::name())); for count in COUNTS { group.bench_with_input(BenchmarkId::new("bptree", count), &count, |b, count| { let tree = create_bptree::(*count); b.iter(|| { for i in 0..*count { let k = K::from_i(i); assert!(tree.get(&k).is_some()); } }); }); group.bench_with_input(BenchmarkId::new("btree", count), &count, |b, count| { let tree = create_btree::(*count); b.iter(|| { for i in 0..*count { let k = K::from_i(i); assert!(tree.get(&k).is_some()); } }); }); } } fn bench_random_get(c: &mut Criterion) { let mut group = c.benchmark_group(format!("random_get/{}", K::name())); for count in COUNTS { group.bench_with_input(BenchmarkId::new("bptree", count), &count, |b, count| { let tree = create_bptree::(*count); let mut r = StdRng::seed_from_u64(RAND_SEED); let mut keys = tree.iter().map(|(k, _v)| k).cloned().collect::>(); keys.shuffle(&mut r); b.iter(|| { for k in keys.iter() { assert!(tree.get(&k).is_some()); } }); }); group.bench_with_input(BenchmarkId::new("btree", count), &count, |b, count| { let tree = create_btree::(*count); let mut r = StdRng::seed_from_u64(RAND_SEED); let mut keys = tree.iter().map(|(k, _v)| k).cloned().collect::>(); keys.shuffle(&mut r); b.iter(|| { for k in keys.iter() { assert!(tree.get(&k).is_some()); } }); }); } } fn bench_cursor(c: &mut Criterion) { let mut group = c.benchmark_group(format!("cursor/{}", K::name())); for count in COUNTS { group.bench_with_input(BenchmarkId::new("bptree", count), &count, |b, count| { let tree = create_bptree::(*count); b.iter(|| { let mut c = tree.cursor_first().unwrap(); let mut count = 1; while let Some(next) = c.next_with_value(&tree) { count += 1; c = next.0; } assert_eq!(count, tree.len()); }); }); } } fn create_bptree(count: usize) -> BPlusTree> { let node_store = NodeStoreBench::new(); let mut tree = BPlusTree::new(node_store); let mut keys = (0..count).collect::>(); let mut r = StdRng::seed_from_u64(RAND_SEED); keys.shuffle(&mut r); for i in keys { let k = K::from_i(i); tree.insert(k, Value::default()); } tree } fn create_btree(count: usize) -> BTreeMap { let mut tree = BTreeMap::default(); let mut keys = (0..count).collect::>(); let mut r = StdRng::seed_from_u64(RAND_SEED); keys.shuffle(&mut r); for i in keys { let k = K::from_i(i); tree.insert(k, Value::default()); } tree } criterion_group!( benches, bench_clone, bench_clone, bench_drop, bench_drop, bench_iter, bench_iter, bench_into_iter, bench_into_iter, bench_ordered_insert, bench_ordered_insert, bench_random_insert, bench_random_insert, bench_ordered_remove, bench_ordered_remove, bench_random_remove, bench_random_remove, bench_ordered_get, bench_ordered_get, bench_random_get, bench_random_get, bench_random_get>, bench_cursor, bench_cursor, ); criterion_main!(benches);