Condensate / rust_core /src /bench.rs
Executor-Tyrant-Framework's picture
Rust core: AccessGraph + RustPredictor with PyO3 bindings
26cf661
//! Benchmark: Rust graph build + predict vs equivalent Python workload.
//!
//! Run with: cargo test --release bench_ -- --nocapture
#[cfg(test)]
mod bench {
use crate::graph::AccessGraph;
use crate::predictor::RustPredictor;
use std::time::Instant;
/// Generate a realistic workload: N layers, some hot, some cold,
/// with causal chains. Same pattern as test_predictor.py.
fn generate_inference_workload(
num_layers: u32,
num_hot: u32,
iterations: u32,
) -> Vec<(u64, String, u64)> {
let mut events = Vec::new();
let mut ts: u64 = 0;
for _ in 0..iterations {
// Hot layers accessed every iteration
for i in 0..num_hot {
events.push((ts, format!("layer_{}", i), 65536));
ts += 100_000; // 0.1ms between accesses
}
// Cold layers: 3% chance
for i in num_hot..num_layers {
if (ts / 1000 + i as u64) % 33 == 0 {
events.push((ts, format!("layer_{}", i), 65536));
ts += 100_000;
}
}
ts += 2_000_000; // 2ms between iterations
}
events
}
#[test]
fn bench_graph_build_small() {
// 16 layers, 4 hot, 100 iterations — ~similar to Python test
let events = generate_inference_workload(16, 4, 100);
println!("\nSmall workload: {} events", events.len());
let start = Instant::now();
let mut graph = AccessGraph::new(5_000_000, 0.7);
graph.build(events.clone());
let elapsed = start.elapsed();
println!(" Graph build: {:?}", elapsed);
println!(" Nodes: {}, Edges: {}, Clusters: {}",
graph.node_count(), graph.edge_count(), graph.cluster_count());
// Predict
let mut predictor = RustPredictor::new();
predictor.learn(&graph);
let start = Instant::now();
let result = predictor.score(events);
let elapsed = start.elapsed();
println!(" Score: {:?}", elapsed);
println!(" Accuracy: {}%, Predictions: {}, Hits: {}",
result.accuracy, result.predictions_made, result.hits);
}
#[test]
fn bench_graph_build_medium() {
// 64 layers, 8 hot, 100 iterations
let events = generate_inference_workload(64, 8, 100);
println!("\nMedium workload: {} events", events.len());
let start = Instant::now();
let mut graph = AccessGraph::new(5_000_000, 0.7);
graph.build(events.clone());
let elapsed = start.elapsed();
println!(" Graph build: {:?}", elapsed);
println!(" Nodes: {}, Edges: {}, Clusters: {}",
graph.node_count(), graph.edge_count(), graph.cluster_count());
let mut predictor = RustPredictor::new();
predictor.learn(&graph);
let start = Instant::now();
let result = predictor.score(events);
let elapsed = start.elapsed();
println!(" Score: {:?}", elapsed);
println!(" Accuracy: {}%, Predictions: {}, Hits: {}",
result.accuracy, result.predictions_made, result.hits);
}
#[test]
fn bench_graph_build_large() {
// 256 layers, 16 hot, 50 iterations — stress test
let events = generate_inference_workload(256, 16, 50);
println!("\nLarge workload: {} events", events.len());
let start = Instant::now();
let mut graph = AccessGraph::new(5_000_000, 0.7);
graph.build(events.clone());
let elapsed = start.elapsed();
println!(" Graph build: {:?}", elapsed);
println!(" Nodes: {}, Edges: {}, Clusters: {}",
graph.node_count(), graph.edge_count(), graph.cluster_count());
let mut predictor = RustPredictor::new();
predictor.learn(&graph);
let start = Instant::now();
let result = predictor.score(events);
let elapsed = start.elapsed();
println!(" Score: {:?}", elapsed);
println!(" Accuracy: {}%, Predictions: {}, Hits: {}",
result.accuracy, result.predictions_made, result.hits);
}
#[test]
fn bench_predict_latency() {
// Measure single-prediction latency — this is the hot path
let events = generate_inference_workload(64, 8, 100);
let mut graph = AccessGraph::new(5_000_000, 0.7);
graph.build(events);
let mut predictor = RustPredictor::new();
predictor.learn(&graph);
// Warm up
for _ in 0..100 {
let _ = predictor.predict("layer_0", 10);
}
// Measure
let iterations = 100_000;
let start = Instant::now();
for _ in 0..iterations {
let _ = predictor.predict("layer_0", 10);
}
let elapsed = start.elapsed();
let per_predict_ns = elapsed.as_nanos() / iterations as u128;
println!("\nSingle predict() latency:");
println!(" {} iterations in {:?}", iterations, elapsed);
println!(" *** {per_predict_ns} ns per prediction ***");
println!(" ({:.1} million predictions/sec)",
1_000_000_000.0 / per_predict_ns as f64);
}
}