Skip to content

Commit a8b9b25

Browse files
committed
feat: Add benchmarks for new features + persistence integration tests
Benchmarks (store.rs, 8 new bench tests): - Batch scoring 10k blocks vs individual scoring - 5-bit and 7-bit dequant fast paths (4096 values) - 5-bit quantize fast path (4096 values) - SVD adaptive rank selection (64x64 matrix) - format_report and format_json throughput - MetricsSeries trend computation (100 snapshots) Persistence tests (10 tests, feature-gated): - FileBlockIO: write/read, multi-tier, delete, overwrite, missing key - FileMetaLog: append/get, upsert, iter, missing key, multi-block 354 tests pass (with --features persistence). https://claude.ai/code/session_01Ksy165BL5nGpVoWaAfTE7t
1 parent 52c8499 commit a8b9b25

2 files changed

Lines changed: 469 additions & 0 deletions

File tree

crates/ruvector-temporal-tensor/src/store.rs

Lines changed: 244 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1934,4 +1934,248 @@ mod tests {
19341934
assert_eq!(*ts, 200);
19351935
assert_eq!(m.total_blocks, 5);
19361936
}
1937+
1938+
// -----------------------------------------------------------------------
1939+
// Benchmarks
1940+
// -----------------------------------------------------------------------
1941+
//
1942+
// Run with: cargo test bench_ -- --nocapture
1943+
// These use std::time::Instant and std::hint::black_box for stable timing.
1944+
1945+
#[test]
1946+
fn bench_batch_scoring_10k() {
1947+
use std::time::Instant;
1948+
use crate::tiering::{
1949+
TierConfig, BlockMeta as TBlockMeta, Tier as TTier,
1950+
compute_scores_batch, compute_score,
1951+
};
1952+
1953+
let cfg = TierConfig::default();
1954+
let metas: Vec<TBlockMeta> = (0..10_000).map(|i| {
1955+
TBlockMeta {
1956+
ema_rate: (i as f32) * 0.0001,
1957+
access_window: 0x5555_5555_5555_5555,
1958+
last_access: 50 + (i as u64 % 100),
1959+
access_count: i as u64,
1960+
current_tier: TTier::Tier1,
1961+
tier_since: 0,
1962+
}
1963+
}).collect();
1964+
1965+
let iters = 1000;
1966+
1967+
// Individual scoring
1968+
let start = Instant::now();
1969+
for _ in 0..iters {
1970+
for m in &metas {
1971+
std::hint::black_box(compute_score(&cfg, 100, m));
1972+
}
1973+
}
1974+
let individual = start.elapsed();
1975+
1976+
// Batch scoring
1977+
let start = Instant::now();
1978+
for _ in 0..iters {
1979+
std::hint::black_box(compute_scores_batch(&cfg, 100, &metas));
1980+
}
1981+
let batch = start.elapsed();
1982+
1983+
eprintln!("Individual scoring 10k x {iters}: {:?} ({:.0} ns/block)",
1984+
individual, individual.as_nanos() as f64 / (iters * 10_000) as f64);
1985+
eprintln!("Batch scoring 10k x {iters}: {:?} ({:.0} ns/block)",
1986+
batch, batch.as_nanos() as f64 / (iters * 10_000) as f64);
1987+
}
1988+
1989+
#[test]
1990+
fn bench_dequant_5bit_4096() {
1991+
use std::time::Instant;
1992+
1993+
let data: Vec<f32> = (0..4096).map(|i| (i as f32 - 2048.0) * 0.01).collect();
1994+
let (packed, scale) = quantize_block(&data, 5);
1995+
let mut out = vec![0.0f32; 4096];
1996+
1997+
let iters = 10_000;
1998+
let start = Instant::now();
1999+
for _ in 0..iters {
2000+
std::hint::black_box(dequantize_block(&packed, scale, 5, 4096, &mut out));
2001+
}
2002+
let elapsed = start.elapsed();
2003+
2004+
let total_bytes = 4096u64 * 4 * iters as u64;
2005+
let gbs = total_bytes as f64 / elapsed.as_secs_f64() / 1e9;
2006+
eprintln!("Dequant 5-bit 4096 x {iters}: {:?} ({:.2} GB/s output throughput)",
2007+
elapsed, gbs);
2008+
}
2009+
2010+
#[test]
2011+
fn bench_dequant_7bit_4096() {
2012+
use std::time::Instant;
2013+
2014+
let data: Vec<f32> = (0..4096).map(|i| (i as f32 - 2048.0) * 0.01).collect();
2015+
let (packed, scale) = quantize_block(&data, 7);
2016+
let mut out = vec![0.0f32; 4096];
2017+
2018+
let iters = 10_000;
2019+
let start = Instant::now();
2020+
for _ in 0..iters {
2021+
std::hint::black_box(dequantize_block(&packed, scale, 7, 4096, &mut out));
2022+
}
2023+
let elapsed = start.elapsed();
2024+
2025+
let total_bytes = 4096u64 * 4 * iters as u64;
2026+
let gbs = total_bytes as f64 / elapsed.as_secs_f64() / 1e9;
2027+
eprintln!("Dequant 7-bit 4096 x {iters}: {:?} ({:.2} GB/s output throughput)",
2028+
elapsed, gbs);
2029+
}
2030+
2031+
#[test]
2032+
fn bench_quant_5bit_4096() {
2033+
use std::time::Instant;
2034+
2035+
let data: Vec<f32> = (0..4096).map(|i| (i as f32 - 2048.0) * 0.01).collect();
2036+
2037+
let iters = 10_000;
2038+
let start = Instant::now();
2039+
for _ in 0..iters {
2040+
std::hint::black_box(quantize_block(&data, 5));
2041+
}
2042+
let elapsed = start.elapsed();
2043+
2044+
let total_bytes = 4096u64 * 4 * iters as u64;
2045+
let gbs = total_bytes as f64 / elapsed.as_secs_f64() / 1e9;
2046+
eprintln!("Quant 5-bit 4096 x {iters}: {:?} ({:.2} GB/s input throughput)",
2047+
elapsed, gbs);
2048+
}
2049+
2050+
#[test]
2051+
fn bench_svd_adaptive_64x64() {
2052+
use std::time::Instant;
2053+
use crate::delta::FactorSet;
2054+
2055+
let (rows, cols) = (64, 64);
2056+
let data: Vec<f32> = (0..rows * cols)
2057+
.map(|i| (i as f32 * 0.37).sin() + (i as f32 * 0.73).cos())
2058+
.collect();
2059+
2060+
let iters = 100;
2061+
let start = Instant::now();
2062+
for _ in 0..iters {
2063+
std::hint::black_box(
2064+
FactorSet::from_data_adaptive(&data, rows, cols, 16, 0.05)
2065+
);
2066+
}
2067+
let elapsed = start.elapsed();
2068+
2069+
eprintln!("SVD adaptive 64x64 (max_rank=16, target=0.05) x {iters}: {:?} ({:.2} ms/iter)",
2070+
elapsed, elapsed.as_secs_f64() * 1000.0 / iters as f64);
2071+
}
2072+
2073+
#[test]
2074+
fn bench_format_report() {
2075+
use std::time::Instant;
2076+
use crate::metrics::StoreMetrics;
2077+
2078+
let m = StoreMetrics {
2079+
total_blocks: 10_000,
2080+
tier0_blocks: 500,
2081+
tier1_blocks: 4000,
2082+
tier2_blocks: 3500,
2083+
tier3_blocks: 2000,
2084+
tier1_bytes: 4_000_000,
2085+
tier2_bytes: 2_500_000,
2086+
tier3_bytes: 750_000,
2087+
total_reads: 1_000_000,
2088+
total_writes: 500_000,
2089+
total_evictions: 5000,
2090+
total_upgrades: 12_000,
2091+
total_downgrades: 8000,
2092+
total_reconstructions: 200,
2093+
total_checksum_failures: 0,
2094+
total_compactions: 150,
2095+
tier_flips_last_minute: 0.023,
2096+
avg_score_tier1: 0.85,
2097+
avg_score_tier2: 0.45,
2098+
avg_score_tier3: 0.12,
2099+
};
2100+
2101+
let iters = 10_000;
2102+
let start = Instant::now();
2103+
for _ in 0..iters {
2104+
std::hint::black_box(m.format_report());
2105+
}
2106+
let elapsed = start.elapsed();
2107+
2108+
eprintln!("format_report x {iters}: {:?} ({:.0} ns/call)",
2109+
elapsed, elapsed.as_nanos() as f64 / iters as f64);
2110+
}
2111+
2112+
#[test]
2113+
fn bench_format_json() {
2114+
use std::time::Instant;
2115+
use crate::metrics::StoreMetrics;
2116+
2117+
let m = StoreMetrics {
2118+
total_blocks: 10_000,
2119+
tier0_blocks: 500,
2120+
tier1_blocks: 4000,
2121+
tier2_blocks: 3500,
2122+
tier3_blocks: 2000,
2123+
tier1_bytes: 4_000_000,
2124+
tier2_bytes: 2_500_000,
2125+
tier3_bytes: 750_000,
2126+
total_reads: 1_000_000,
2127+
total_writes: 500_000,
2128+
total_evictions: 5000,
2129+
total_upgrades: 12_000,
2130+
total_downgrades: 8000,
2131+
total_reconstructions: 200,
2132+
total_checksum_failures: 0,
2133+
total_compactions: 150,
2134+
tier_flips_last_minute: 0.023,
2135+
avg_score_tier1: 0.85,
2136+
avg_score_tier2: 0.45,
2137+
avg_score_tier3: 0.12,
2138+
};
2139+
2140+
let iters = 10_000;
2141+
let start = Instant::now();
2142+
for _ in 0..iters {
2143+
std::hint::black_box(m.format_json());
2144+
}
2145+
let elapsed = start.elapsed();
2146+
2147+
eprintln!("format_json x {iters}: {:?} ({:.0} ns/call)",
2148+
elapsed, elapsed.as_nanos() as f64 / iters as f64);
2149+
}
2150+
2151+
#[test]
2152+
fn bench_metrics_series_trend_100() {
2153+
use std::time::Instant;
2154+
use crate::metrics::{StoreMetrics, MetricsSeries};
2155+
2156+
let mut series = MetricsSeries::new(256);
2157+
for i in 0..100u64 {
2158+
series.record(i, StoreMetrics {
2159+
total_blocks: 1000 + i,
2160+
tier1_blocks: 400 + i % 50,
2161+
tier2_blocks: 350,
2162+
tier3_blocks: 250,
2163+
tier1_bytes: 400_000 + i * 100,
2164+
tier2_bytes: 250_000,
2165+
tier3_bytes: 75_000,
2166+
total_evictions: i * 3,
2167+
..Default::default()
2168+
});
2169+
}
2170+
2171+
let iters = 10_000;
2172+
let start = Instant::now();
2173+
for _ in 0..iters {
2174+
std::hint::black_box(series.trend());
2175+
}
2176+
let elapsed = start.elapsed();
2177+
2178+
eprintln!("MetricsSeries trend (100 snapshots) x {iters}: {:?} ({:.0} ns/call)",
2179+
elapsed, elapsed.as_nanos() as f64 / iters as f64);
2180+
}
19372181
}

0 commit comments

Comments
 (0)