Squashed 'vendor/ruvector/' content from commit b64c2172
git-subtree-dir: vendor/ruvector git-subtree-split: b64c21726f2bb37286d9ee36a7869fef60cc6900
This commit is contained in:
110
crates/ruvector-mincut/tests/canonical_bench.rs
Normal file
110
crates/ruvector-mincut/tests/canonical_bench.rs
Normal file
@@ -0,0 +1,110 @@
|
||||
//! Performance benchmark for canonical min-cut.
|
||||
//! Run with: cargo test -p ruvector-mincut --features canonical --test canonical_bench --release -- --nocapture
|
||||
|
||||
#[cfg(feature = "canonical")]
|
||||
mod bench {
|
||||
use ruvector_mincut::canonical::CactusGraph;
|
||||
use ruvector_mincut::graph::DynamicGraph;
|
||||
use std::time::Instant;
|
||||
|
||||
/// Benchmark at 30 vertices (typical subgraph partition size).
|
||||
/// The CactusGraph uses Stoer-Wagner (O(n^3)), so performance scales
|
||||
/// cubically. For WASM tiles (<=256 vertices), the ArenaCactus path
|
||||
/// is used instead (measured at ~3µs in the gate-kernel benchmark).
|
||||
#[test]
|
||||
fn bench_canonical_mincut_30v() {
|
||||
let mut graph = DynamicGraph::new();
|
||||
for i in 0..30u64 {
|
||||
graph.add_vertex(i);
|
||||
}
|
||||
// Ring + cross edges (~90 edges)
|
||||
for i in 0..30u64 {
|
||||
let _ = graph.insert_edge(i, (i + 1) % 30, 1.0);
|
||||
}
|
||||
for i in 0..30u64 {
|
||||
let _ = graph.insert_edge(i, (i + 11) % 30, 0.5);
|
||||
let _ = graph.insert_edge(i, (i + 19) % 30, 0.3);
|
||||
}
|
||||
|
||||
// Warm up
|
||||
let _ = CactusGraph::build_from_graph(&graph);
|
||||
|
||||
// Benchmark cactus construction
|
||||
let n_iter = 100;
|
||||
let start = Instant::now();
|
||||
for _ in 0..n_iter {
|
||||
let cactus = CactusGraph::build_from_graph(&graph);
|
||||
std::hint::black_box(&cactus);
|
||||
}
|
||||
let avg_cactus_us = start.elapsed().as_micros() as f64 / n_iter as f64;
|
||||
|
||||
// Benchmark canonical cut extraction
|
||||
let cactus = CactusGraph::build_from_graph(&graph);
|
||||
let start = Instant::now();
|
||||
for _ in 0..n_iter {
|
||||
let result = cactus.canonical_cut();
|
||||
std::hint::black_box(&result);
|
||||
}
|
||||
let avg_cut_us = start.elapsed().as_micros() as f64 / n_iter as f64;
|
||||
|
||||
// Determinism: all 100 produce identical result
|
||||
let reference = cactus.canonical_cut();
|
||||
for _ in 0..100 {
|
||||
let result = cactus.canonical_cut();
|
||||
assert_eq!(result.value, reference.value);
|
||||
assert_eq!(result.canonical_key, reference.canonical_key);
|
||||
}
|
||||
|
||||
let total = avg_cactus_us + avg_cut_us;
|
||||
println!("\n=== Canonical Min-Cut (30v, ~90e) ===");
|
||||
println!(" CactusGraph build: {:.1} µs", avg_cactus_us);
|
||||
println!(" Canonical cut: {:.1} µs", avg_cut_us);
|
||||
println!(
|
||||
" Total: {:.1} µs (target: < 3000 µs native)",
|
||||
total
|
||||
);
|
||||
println!(" Cut value: {}", reference.value);
|
||||
println!(" NOTE: WASM ArenaCactus (64v) = ~3µs (see gate-kernel bench)");
|
||||
|
||||
// Native CactusGraph uses heap-allocated Stoer-Wagner (O(n^3));
|
||||
// the WASM ArenaCactus path (stack-allocated) is 500x faster.
|
||||
assert!(
|
||||
total < 3000.0,
|
||||
"Exceeded 3ms native target: {:.1} µs",
|
||||
total
|
||||
);
|
||||
}
|
||||
|
||||
/// Also benchmark at 100 vertices to track scalability (informational, no assertion).
|
||||
#[test]
|
||||
fn bench_canonical_mincut_100v_info() {
|
||||
let mut graph = DynamicGraph::new();
|
||||
for i in 0..100u64 {
|
||||
graph.add_vertex(i);
|
||||
}
|
||||
for i in 0..100u64 {
|
||||
let _ = graph.insert_edge(i, (i + 1) % 100, 1.0);
|
||||
}
|
||||
for i in 0..100u64 {
|
||||
let _ = graph.insert_edge(i, (i + 37) % 100, 0.5);
|
||||
let _ = graph.insert_edge(i, (i + 73) % 100, 0.3);
|
||||
}
|
||||
|
||||
let _ = CactusGraph::build_from_graph(&graph);
|
||||
let n_iter = 20;
|
||||
let start = Instant::now();
|
||||
for _ in 0..n_iter {
|
||||
let cactus = CactusGraph::build_from_graph(&graph);
|
||||
let _ = cactus.canonical_cut();
|
||||
std::hint::black_box(&cactus);
|
||||
}
|
||||
let avg_total_us = start.elapsed().as_micros() as f64 / n_iter as f64;
|
||||
|
||||
println!("\n=== Canonical Min-Cut Scalability (100v, ~300e) ===");
|
||||
println!(
|
||||
" Total (build+cut): {:.1} µs (informational)",
|
||||
avg_total_us
|
||||
);
|
||||
println!(" Stoer-Wagner is O(n^3), scales cubically with graph size");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user