#![allow(dead_code)]
use std::hint::black_box;
use fluxbench::prelude::*;
use fluxbench::{TrackingAllocator, bench};
use pagedb::vfs::memory::MemVfs;
use pagedb::vfs::tokio_backend::TokioVfs;
use pagedb::{CipherId, Db, OpenOptions, RealmId, RetainPolicy};
#[global_allocator]
static GLOBAL: TrackingAllocator = TrackingAllocator;
const PAGE: usize = 4096;
const ROWS: usize = 10_000;
const KEY_SIZE: usize = 24;
const VALUE_SIZE: usize = 150;
thread_local! {
static RT: tokio::runtime::Runtime = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.unwrap();
}
fn with_rt<R>(f: impl FnOnce(&tokio::runtime::Runtime) -> R) -> R {
RT.with(|rt| f(rt))
}
fn bench_opts() -> OpenOptions {
OpenOptions::default()
.with_commit_history_retain(RetainPolicy::Disabled)
.with_cipher(CipherId::Aes256Gcm)
.with_anchor_budget(10_000_000)
}
async fn open_mem() -> Db<MemVfs> {
Db::open(
MemVfs::new(),
[7u8; 32],
PAGE,
RealmId::new([3u8; 16]),
bench_opts(),
)
.await
.unwrap()
}
fn scrambled_key(i: usize) -> [u8; KEY_SIZE] {
let mut key = [0u8; KEY_SIZE];
let mixed = (i as u64).wrapping_mul(0x9E37_79B9_7F4A_7C15);
key[..8].copy_from_slice(&mixed.to_be_bytes());
key[8..16].copy_from_slice(&(i as u64).to_le_bytes());
key
}
fn monotonic_key(i: usize) -> [u8; KEY_SIZE] {
let mut key = [0u8; KEY_SIZE];
key[..8].copy_from_slice(&(i as u64).to_be_bytes());
key
}
fn value() -> Vec<u8> {
vec![0xA5u8; VALUE_SIZE]
}
#[bench(group = "btree/write-path", samples = 10)]
fn bulk_put_no_rows(b: &mut Bencher) {
b.iter_with_setup(
|| with_rt(|rt| rt.block_on(open_mem())),
|db| {
with_rt(|rt| {
rt.block_on(async {
let w = db.begin_write().await.unwrap();
w.abort().await;
})
})
},
);
}
#[bench(group = "btree/write-path", samples = 10)]
fn bulk_put_uncommitted(b: &mut Bencher) {
let v = value();
b.iter_with_setup(
|| with_rt(|rt| rt.block_on(open_mem())),
|db| {
with_rt(|rt| {
rt.block_on(async {
let mut w = db.begin_write().await.unwrap();
for i in 0..ROWS {
w.put(&scrambled_key(i), &v).await.unwrap();
}
w.abort().await;
})
})
},
);
}
#[bench(group = "btree/write-path", samples = 10)]
fn bulk_put(b: &mut Bencher) {
let v = value();
b.iter_with_setup(
|| with_rt(|rt| rt.block_on(open_mem())),
|db| {
with_rt(|rt| {
rt.block_on(async {
let mut w = db.begin_write().await.unwrap();
for i in 0..ROWS {
w.put(&scrambled_key(i), &v).await.unwrap();
}
w.commit().await.unwrap();
})
})
},
);
}
#[bench(group = "btree/write-path", samples = 10)]
fn bulk_put_append(b: &mut Bencher) {
let v = value();
b.iter_with_setup(
|| with_rt(|rt| rt.block_on(open_mem())),
|db| {
with_rt(|rt| {
rt.block_on(async {
let mut w = db.begin_write().await.unwrap();
for i in 0..ROWS {
w.put_append(&monotonic_key(i), &v).await.unwrap();
}
w.commit().await.unwrap();
})
})
},
);
}
#[bench(group = "btree/write-path/scaling", args = [2500, 5000, 10000, 20000], samples = 5)]
fn scaling_uncommitted(b: &mut Bencher, rows: u32) {
let v = value();
let rows = rows as usize;
b.iter_with_setup(
|| with_rt(|rt| rt.block_on(open_mem())),
|db| {
with_rt(|rt| {
rt.block_on(async {
let mut w = db.begin_write().await.unwrap();
for i in 0..rows {
w.put(&scrambled_key(i), &v).await.unwrap();
}
w.abort().await;
})
})
},
);
}
#[bench(group = "btree/write-path/scaling", args = [10000, 25000, 50000, 100000], samples = 3)]
fn scaling_file_committed(b: &mut Bencher, rows: u32) {
let v = value();
let rows = rows as usize;
b.iter_with_setup(
|| {
let dir = tempfile::TempDir::new().unwrap();
let db = with_rt(|rt| {
rt.block_on(async {
Db::open(
TokioVfs::new(dir.path()),
[7u8; 32],
PAGE,
RealmId::new([3u8; 16]),
bench_opts(),
)
.await
.unwrap()
})
});
(db, dir)
},
|(db, _keep)| {
with_rt(|rt| {
rt.block_on(async {
let mut w = db.begin_write().await.unwrap();
for i in 0..rows {
w.put(&scrambled_key(i), &v).await.unwrap();
}
w.commit().await.unwrap();
})
})
},
);
}
#[bench(group = "btree/write-path", samples = 10)]
fn bulk_put_file(b: &mut Bencher) {
let v = value();
b.iter_with_setup(
|| {
let dir = tempfile::TempDir::new().unwrap();
let db = with_rt(|rt| {
rt.block_on(async {
Db::open(
TokioVfs::new(dir.path()),
[7u8; 32],
PAGE,
RealmId::new([3u8; 16]),
bench_opts(),
)
.await
.unwrap()
})
});
(db, dir)
},
|(db, _keep)| {
with_rt(|rt| {
rt.block_on(async {
let mut w = db.begin_write().await.unwrap();
for i in 0..ROWS {
w.put(&scrambled_key(i), &v).await.unwrap();
}
w.commit().await.unwrap();
})
})
},
);
}
#[bench(group = "btree/write-path", samples = 50)]
fn put_one_hot(b: &mut Bencher) {
let v = value();
let db = with_rt(|rt| {
rt.block_on(async {
let db = open_mem().await;
let mut w = db.begin_write().await.unwrap();
for i in 0..ROWS {
w.put(&scrambled_key(i), &v).await.unwrap();
}
w.commit().await.unwrap();
db
})
});
let mut i = ROWS;
b.iter(|| {
let k = scrambled_key(i);
i += 1;
with_rt(|rt| {
rt.block_on(async {
let mut w = db.begin_write().await.unwrap();
w.put(&k, &v).await.unwrap();
black_box(w.commit().await.unwrap())
})
})
});
}
fn main() {
if let Err(e) = fluxbench::run() {
eprintln!("Error: {e}");
std::process::exit(1);
}
}