use donadb_x::DonaDbX;
use std::sync::Arc;
use std::time::Instant;
fn rand_key(n: u64) -> [u8; 32] {
let mut k = [0u8; 32];
k[0..8].copy_from_slice(&n.to_le_bytes());
k[8..16].copy_from_slice(
&n.wrapping_mul(6364136223846793005)
.wrapping_add(1442695040888963407)
.to_le_bytes(),
);
k
}
fn rand_val(seed: u64, size: usize) -> Vec<u8> {
let mut v = vec![0u8; size];
for i in 0..size {
v[i] = (seed.wrapping_add(i as u64)
.wrapping_mul(2654435761)
>> 24) as u8;
}
v
}
fn sep(title: &str) {
println!("\n╔══════════════════════════════════════════════════════════════╗");
println!("║ {:<60}║", title);
println!("╚══════════════════════════════════════════════════════════════╝");
}
fn row(label: &str, ops: u64, ms: u128) {
let ops_sec = if ms == 0 { ops as u128 * 1000 } else { ops as u128 * 1000 / ms };
let lat_us = if ops == 0 { 0 } else { ms * 1000 / ops as u128 };
println!(" {:<38} {:>10} ops/s {:>7} µs/op ({} ms)",
label, ops_sec, lat_us, ms);
}
fn main() {
let dir = tempfile::tempdir().expect("tempdir");
let cpus: usize = num_cpus::get();
let max_threads: usize = std::env::args()
.zip(std::env::args().skip(1))
.find_map(|(flag, val)| {
if flag == "--threads" { val.parse().ok() } else { None }
})
.or_else(|| std::env::var("BENCH_THREADS").ok().and_then(|v| v.parse().ok()))
.unwrap_or(cpus)
.max(1);
println!("\n┌──────────────────────────────────────────────────────────────┐");
println!("│ donadb-X — REAL BENCHMARK (zero stubs, zero mocking) │");
println!("│ mmap I/O · lock-free append · SIMD state root │");
println!("└──────────────────────────────────────────────────────────────┘");
println!(" Platform : {}", std::env::consts::OS);
println!(" Arch : {}", std::env::consts::ARCH);
let simd = {
#[cfg(target_arch = "x86_64")]
{
if is_x86_feature_detected!("avx512f") { "AVX-512" }
else if is_x86_feature_detected!("avx2") { "AVX2" }
else { "scalar" }
}
#[cfg(not(target_arch = "x86_64"))]
{ "NEON / scalar" }
};
println!(" SIMD : {simd}");
println!(" Threads : {max_threads} (logical CPUs: {cpus} | override: --threads N or BENCH_THREADS=N)");
sep("1. Pure Write Throughput — fetch_add + memcpy only (no fold)");
for &n in &[10_000u64, 100_000, 500_000, 1_000_000] {
let p = dir.path().join(format!("w{n}.log"));
let db = DonaDbX::open(&p, donadb_x::Config { buffer_size: 256 << 20, ..Default::default() }).unwrap();
let val = rand_val(1, 64);
let warmup = 1_000u64.min(n / 10);
for i in 0..warmup { db.put(rand_key(i), &val).unwrap(); }
db.commit(0).unwrap().wait();
let t = Instant::now();
for i in warmup..warmup + n {
db.put(rand_key(i), &val).unwrap();
}
let write_ms = t.elapsed().as_millis();
let fold_t = Instant::now();
db.commit(0).unwrap().wait();
let fold_ms = fold_t.elapsed().as_millis();
let ops_s = if write_ms == 0 { n as u128 * 1000 } else { n as u128 * 1000 / write_ms };
println!(" {:>9} writes write={:>4}ms ({:>10} ops/s) fold={:>4}ms",
n, write_ms, ops_s, fold_ms);
}
sep("2. Random Read Throughput (hot in-memory index)");
{
let p = dir.path().join("read.log");
let db = DonaDbX::open(&p, donadb_x::Config { buffer_size: 512 << 20, ..Default::default() }).unwrap();
let n = 100_000u64;
let val = rand_val(2, 64);
for i in 0..n { db.put(rand_key(i), &val).unwrap(); }
db.commit(0).unwrap().wait();
let reads = 200_000u64;
let t = Instant::now();
let mut sink = 0usize;
for i in 0..reads {
let k = rand_key((i.wrapping_mul(7919)) % n);
if let Ok(v) = db.get(&k) { sink += v.len(); }
}
let _ = sink;
row(&format!("{reads} random reads over {n} keys"), reads, t.elapsed().as_millis());
}
sep("3. Ingestion Throughput — Flat-line Stability (10 × 50 000 ops)");
{
let p = dir.path().join("ingest.log");
let db = DonaDbX::open(&p, donadb_x::Config { buffer_size: 256 << 20, ..Default::default() }).unwrap();
let val = rand_val(3, 128);
println!(" Window │ write ops/s │ write ms │ fold ms");
println!(" ────────┼───────────────┼──────────┼────────");
for w in 0..10u64 {
let base = w * 50_000;
let t = Instant::now();
for i in 0..50_000u64 {
db.put(rand_key(base + i), &val).unwrap();
}
let write_ms = t.elapsed().as_millis().max(1);
let fold_t = Instant::now();
db.commit(0).unwrap().wait();
let fold_ms = fold_t.elapsed().as_millis();
let ops_s = 50_000u128 * 1000 / write_ms;
println!(" {:>6} │ {:>12} │ {:>4} │ {:>4}", w + 1, ops_s, write_ms, fold_ms);
}
}
sep("4. State Root Latency Under Concurrent Write Load");
{
let p = dir.path().join("concurrent.log");
let db = Arc::new(DonaDbX::open(&p, donadb_x::Config { buffer_size: 512 << 20, ..Default::default() }).unwrap());
let val = rand_val(4, 64);
for i in 0..10_000u64 {
db.put(rand_key(i), &val).unwrap();
}
let db_w = Arc::clone(&db);
let writer = std::thread::spawn(move || {
let val = rand_val(5, 64);
for i in 0..200_000u64 {
let _ = db_w.put(rand_key(10_000 + i), &val);
}
});
let iters = 20u64;
let mut timings = Vec::with_capacity(iters as usize);
for _ in 0..iters {
let t = Instant::now();
let _ = db.state_root();
timings.push(t.elapsed().as_micros());
}
writer.join().unwrap();
let min = timings.iter().min().unwrap();
let max = timings.iter().max().unwrap();
let avg = timings.iter().sum::<u128>() / iters as u128;
println!(" {iters} state_root calls while background writer running:");
println!(" min={min} µs avg={avg} µs max={max} µs");
println!(" (flat avg = SIMD math isolated from I/O. spike = OS page pressure)");
}
sep("5. MVCC Chain Walk Depth (100 / 1 000 / 10 000 versions)");
{
let p = dir.path().join("mvcc_depth.log");
let db = DonaDbX::open(&p, donadb_x::Config { buffer_size: 64 << 20, ..Default::default() }).unwrap();
let key = rand_key(999_999);
for &depth in &[100u64, 1_000, 10_000] {
for v in 0..depth {
let val = rand_val(v, 32);
db.put(key, &val).unwrap();
}
db.commit(0).unwrap().wait();
let oldest_height = 0u64;
let iters = if depth <= 1_000 { 10_000u64 } else { 1_000 };
let t = Instant::now();
for _ in 0..iters {
let _ = db.get_at(&key, oldest_height);
}
let ms = t.elapsed().as_millis().max(1);
let lat_ns = ms * 1_000_000 / iters as u128;
println!(" Chain depth {:>6} → full walk ×{:>6} = {:>5} ms ({} ns/walk)",
depth, iters, ms, lat_ns);
}
}
sep("6. Crash Recovery — Log Replay Speed");
{
let val = rand_val(6, 64);
for &n in &[10_000u64, 100_000, 500_000] {
let p = dir.path().join(format!("recover{n}.log"));
{
let db = DonaDbX::open(&p, donadb_x::Config { buffer_size: 128 << 20, ..Default::default() }).unwrap();
for i in 0..n { db.put(rand_key(i), &val).unwrap(); }
db.commit(0).unwrap().wait();
}
let t = Instant::now();
let db2 = DonaDbX::open(&p, donadb_x::Config { buffer_size: 128 << 20, ..Default::default() }).unwrap();
let ms = t.elapsed().as_millis();
println!(" Reopen + replay {:>7} records = {:>5} ms ({} keys indexed)",
n, ms, db2.len());
}
}
sep("7. Mixed Workload — 80% Write / 20% Read");
{
let p = dir.path().join("mixed.log");
let db = DonaDbX::open(&p, donadb_x::Config { buffer_size: 512 << 20, ..Default::default() }).unwrap();
let n = 200_000u64;
let val = rand_val(7, 128);
let pre = 100_000u64;
for i in 0..pre { db.put(rand_key(i), &val).unwrap(); }
db.commit(0).unwrap().wait();
let (mut w, mut r) = (0u64, 0u64);
let t = Instant::now();
for i in 0..n {
if i % 5 == 0 {
let _ = db.get(&rand_key(i % pre));
r += 1;
} else {
db.put(rand_key(pre + i), &val).unwrap();
w += 1;
}
}
let ms = t.elapsed().as_millis().max(1);
db.commit(1).unwrap().wait();
println!(" {w} writes + {r} reads in {ms} ms → {} total ops/s",
n as u128 * 1000 / ms);
}
sep("8. Concurrent Write Throughput — sharded, all cores, pure ingestion");
{
println!(" Logical CPUs: {cpus} | Write shards: {max_threads} | override: --threads N");
println!(" Threads │ ops/s │ write ms │ fold ms │ total ops");
println!(" ────────┼──────────────┼──────────┼─────────┼────────────");
let mut steps: Vec<usize> = vec![1, 2, 4]
.into_iter()
.filter(|&t| t < max_threads)
.collect();
steps.push(max_threads);
for threads in steps {
let p = dir.path().join(format!("conc_shard_{threads}.log"));
let db = Arc::new(DonaDbX::open(&p, donadb_x::Config {
buffer_size: 512 << 20, ..Default::default()
}).unwrap());
let warmup = 5_000u64;
for shard in 0..threads {
let w = db.writer(shard);
for i in 0..warmup { w.put(rand_key(shard as u64 * 1_000_000 + i), &rand_val(0, 64)).unwrap(); }
}
db.commit(0).unwrap().wait();
let ops_per_thread = 500_000u64;
let total_ops = ops_per_thread * threads as u64;
let val = rand_val(8, 64);
let t = Instant::now();
let handles: Vec<_> = (0..threads).map(|tid| {
let db2 = Arc::clone(&db);
let v2 = val.clone();
std::thread::spawn(move || {
let writer = db2.writer(tid);
let base = warmup + tid as u64 * ops_per_thread;
for i in 0..ops_per_thread {
writer.put(rand_key(base + i), &v2).unwrap();
}
})
}).collect();
for h in handles { h.join().unwrap(); }
let write_ms = t.elapsed().as_millis().max(1);
let fold_t = Instant::now();
db.commit(0).unwrap().wait();
let fold_ms = fold_t.elapsed().as_millis();
let ops_s = total_ops as u128 * 1000 / write_ms;
println!(" {:>7} │ {:>11} │ {:>4} │ {:>4} │ {}",
threads, ops_s, write_ms, fold_ms, total_ops);
}
}
println!("\n ✓ All benchmarks complete.");
println!(" ✓ Zero stubs. Zero mocking. Real wall-clock. Real memory. Real disk.\n");
}