Files
Oxicloud/examples/bench_round26_diskio.rs
Claude eeb41c9c28 fix(bench): silence clippy neg_cmp_op_on_partial_ord in round25/26 gate helpers
The BEFORE/AFTER rollback gates wrote `if !(after < before)`, which
clippy::neg_cmp_op_on_partial_ord flags on f64 (a negated comparison on a
partially-ordered type). Under CI's `cargo clippy --all-targets -- -D warnings`
this fails. Replace with the equivalent `if after >= before` (fail the gate when
AFTER does not strictly beat BEFORE). No behavioral change to any benchmark.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L8gs91AhmazoxMsDcNk3KT
2026-07-21 01:30:40 +00:00

85 lines
3.4 KiB
Rust

//! Round-26 disk-I/O pack (no Postgres) — async wall on a tmpfs-backed tempdir.
//!
//! [D1] `CachedBlobBackend::initialize` creates ONLY `cache_dir`, never the 256
//! `{00..ff}` shard dirs (the line-122 comment claims otherwise), so each
//! of the three cache-write sites re-runs `tokio::fs::create_dir_all(parent)`
//! on the hot path — a wasted `mkdirat(EEXIST)` + component stat + a
//! blocking-pool dispatch per chunk write on cached-remote deployments.
//! AFTER pre-creates the shard dirs at init (mirroring
//! `LocalBlobBackend::initialize`) and drops the per-write call. Gate:
//! AFTER wall (per write) strictly lower than BEFORE (the redundant
//! create_dir_all).
//!
//! [D2] TESTED AND REVERTED — see benches/ROUND26.md. Moving the moka
//! eviction-listener unlink off the reactor via `spawn_blocking` was
//! refuted by the benchmark: on the local cache dir (fast unlink ~7 µs)
//! the `spawn_blocking` dispatch (~20 µs) costs MORE on the reactor than
//! the inline `std::fs::remove_file` it replaces. The original inline
//! unlink ("a quick unlink on the inserting task's thread") is correct
//! for the fast-local-cache case; kept as-is.
//!
//! Run:
//! RUSTFLAGS="-C target-cpu=x86-64-v3" \
//! cargo run --release --features bench --example bench_round26_diskio
//! Tunables (env): D1_ITERS (20000)
use std::env;
use std::time::Instant;
fn env_or<T: std::str::FromStr>(key: &str, default: T) -> T {
env::var(key)
.ok()
.and_then(|v| v.parse().ok())
.unwrap_or(default)
}
fn gate(tag: &str, metric: &str, before: f64, after: f64) {
if after >= before {
eprintln!("GATE FAIL [{tag}] {metric}: AFTER {after} !< BEFORE {before} — rollback");
std::process::exit(1);
}
}
// ── [D1] redundant create_dir_all on a warm shard vs skip ────────────────────
async fn section_d1() {
let iters: u64 = env_or("D1_ITERS", 20_000);
let dir = tempfile::tempdir().expect("tempdir");
let shard = dir.path().join("ab");
// Shard pre-created once (what AFTER's initialize does).
tokio::fs::create_dir_all(&shard).await.unwrap();
// warm
let _ = tokio::fs::create_dir_all(&shard).await;
// BEFORE: per-write create_dir_all(parent) on the already-existing shard.
let t = Instant::now();
for _ in 0..iters {
let _ = tokio::fs::create_dir_all(&shard).await;
}
let before_ns = t.elapsed().as_nanos() as f64 / iters as f64;
// AFTER: shard guaranteed present at init → the write path skips the call.
let t = Instant::now();
for _ in 0..iters {
std::hint::black_box(&shard);
}
let after_ns = t.elapsed().as_nanos() as f64 / iters as f64;
println!("## [D1] cache-write create_dir_all on a warm shard");
println!("| arm | ns/write |");
println!("| BEFORE | {before_ns:>8.1} |");
println!("| AFTER | {after_ns:>8.1} |");
println!(
"# {:.1}x — redundant create_dir_all removed per cache write\n",
before_ns / after_ns.max(0.001)
);
gate("D1", "ns/write", before_ns, after_ns);
}
#[tokio::main(flavor = "multi_thread", worker_threads = 4)]
async fn main() {
println!("# Round-26 disk-I/O pack\n");
section_d1().await;
println!("All Round-26 disk-I/O sections passed their gate.");
}