File size: 5,260 Bytes
26cf661 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 | //! 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);
}
}
|