Merge commit 'd803bfe2b1fe7f5e219e50ac20d6801a0a58ac75' as 'vendor/ruvector'

This commit is contained in:
ruv
2026-02-28 14:39:40 -05:00
7854 changed files with 3522914 additions and 0 deletions

View File

@@ -0,0 +1,707 @@
//! Critical path latency benchmarks for ruQu Coherence Gate.
//!
//! Primary performance target: **sub-4μs gate decision latency (p99)**
//!
//! Latency Budget (Target: <4μs p99):
//! ```text
//! Syndrome Arrival → 0 ns
//! Ring buffer append → +50 ns
//! Graph update → +200 ns (amortized O(n^{o(1)}))
//! Worker Tick → +500 ns (local cut eval)
//! Report generation → +100 ns
//! TileZero Merge → +500 ns (parallel from 255 tiles)
//! Global cut → +300 ns
//! Three-filter eval → +100 ns
//! Token signing → +500 ns (Ed25519)
//! Receipt append → +100 ns
//! ─────────────────────────────────
//! Total → ~2,350 ns
//! ```
//!
//! Run with: `cargo bench -p ruqu --bench latency_bench`
use criterion::{
black_box, criterion_group, criterion_main, measurement::WallTime, BenchmarkGroup, BenchmarkId,
Criterion, SamplingMode,
};
use ruqu::filters::{
EvidenceAccumulator as FilterEvidenceAccumulator, EvidenceFilter, FilterConfig, FilterPipeline,
ShiftFilter, StructuralFilter, SystemState,
};
use ruqu::tile::{
GateDecision, GateThresholds, LocalCutState, PatchGraph, SyndromeDelta, TileReport, TileZero,
WorkerTile,
};
// ============================================================================
// HELPER FUNCTIONS
// ============================================================================
/// Create a pre-populated worker tile for benchmarking
fn create_benchmark_worker_tile(tile_id: u8, num_vertices: u16, num_edges: u16) -> WorkerTile {
let mut tile = WorkerTile::new(tile_id);
// Add vertices and edges to the patch graph
for i in 0..num_vertices.min(255) {
tile.patch_graph.ensure_vertex(i);
}
// Add edges in a mesh pattern
let mut edges_added = 0u16;
'outer: for i in 0..num_vertices.saturating_sub(1) {
for j in (i + 1)..num_vertices.min(i + 4) {
if edges_added >= num_edges {
break 'outer;
}
if tile.patch_graph.add_edge(i, j, 1000).is_some() {
edges_added += 1;
}
}
}
tile.patch_graph.recompute_components();
tile
}
/// Create a pre-populated filter pipeline for benchmarking
fn create_benchmark_filter_pipeline() -> FilterPipeline {
let config = FilterConfig::default();
let mut pipeline = FilterPipeline::new(config);
// Add graph structure
for i in 0..50u64 {
let _ = pipeline.structural_mut().insert_edge(i, i + 1, 1.0);
}
pipeline.structural_mut().build();
// Warm up shift filter with observations
for region in 0..10 {
for _ in 0..50 {
pipeline.shift_mut().update(region, 0.5);
}
}
// Warm up evidence filter
for _ in 0..20 {
pipeline.evidence_mut().update(1.5);
}
pipeline
}
/// Create benchmark tile reports
fn create_benchmark_tile_reports(count: usize) -> Vec<TileReport> {
(1..=count)
.map(|i| {
let mut report = TileReport::new(i as u8);
report.local_cut = 10.0 + (i as f64 * 0.1);
report.shift_score = 0.1 + (i as f64 * 0.01);
report.e_value = 100.0 + (i as f64);
report.num_vertices = 100;
report.num_edges = 200;
report.num_components = 1;
report
})
.collect()
}
// ============================================================================
// GATE DECISION LATENCY (Critical Path)
// ============================================================================
/// Benchmark the full decision cycle - the critical <4μs path
fn bench_gate_decision(c: &mut Criterion) {
let mut group = c.benchmark_group("gate_decision");
group.sampling_mode(SamplingMode::Flat);
group.sample_size(1000);
// Full decision cycle: worker tick + tilezero merge
group.bench_function("full_cycle", |b| {
let mut tile = create_benchmark_worker_tile(1, 64, 128);
let thresholds = GateThresholds::default();
let mut tilezero = TileZero::new(thresholds);
b.iter(|| {
// 1. Worker tick - process syndrome delta
let delta = SyndromeDelta::new(0, 1, 100);
let report = tile.tick(&delta);
// 2. TileZero merge reports (simulating all 255 tiles with the same report)
let reports = vec![report; 10]; // Reduced for single-threaded benchmark
let decision = tilezero.merge_reports(reports);
black_box(decision)
});
});
// Worker tick only
group.bench_function("worker_tick_only", |b| {
let mut tile = create_benchmark_worker_tile(1, 64, 128);
let delta = SyndromeDelta::new(0, 1, 100);
b.iter(|| {
let report = tile.tick(black_box(&delta));
black_box(report)
});
});
// TileZero merge only
group.bench_function("tilezero_merge_only", |b| {
let thresholds = GateThresholds::default();
let mut tilezero = TileZero::new(thresholds);
let reports = create_benchmark_tile_reports(255);
b.iter(|| {
let decision = tilezero.merge_reports(black_box(reports.clone()));
black_box(decision)
});
});
// TileZero merge with varying tile counts
for tile_count in [10, 50, 100, 255].iter() {
group.bench_with_input(
BenchmarkId::new("tilezero_merge_tiles", tile_count),
tile_count,
|b, &count| {
let thresholds = GateThresholds::default();
let mut tilezero = TileZero::new(thresholds);
let reports = create_benchmark_tile_reports(count);
b.iter(|| {
let decision = tilezero.merge_reports(black_box(reports.clone()));
black_box(decision)
});
},
);
}
group.finish();
}
// ============================================================================
// INDIVIDUAL FILTER EVALUATION LATENCY
// ============================================================================
/// Benchmark structural (min-cut) filter evaluation
fn bench_structural_filter(c: &mut Criterion) {
let mut group = c.benchmark_group("structural_filter");
group.sampling_mode(SamplingMode::Flat);
group.sample_size(1000);
// Basic evaluation with small graph
group.bench_function("evaluate_small", |b| {
let mut filter = StructuralFilter::new(2.0);
for i in 0..20u64 {
let _ = filter.insert_edge(i, i + 1, 1.0);
}
filter.build();
let state = SystemState::new(20);
b.iter(|| {
let result = filter.evaluate(black_box(&state));
black_box(result)
});
});
// Evaluation with medium graph
group.bench_function("evaluate_medium", |b| {
let mut filter = StructuralFilter::new(2.0);
for i in 0..100u64 {
let _ = filter.insert_edge(i, (i + 1) % 100, 1.0);
let _ = filter.insert_edge(i, (i + 50) % 100, 0.5);
}
filter.build();
let state = SystemState::new(100);
b.iter(|| {
let result = filter.evaluate(black_box(&state));
black_box(result)
});
});
// Edge insertion (hot path during updates)
group.bench_function("insert_edge", |b| {
b.iter_batched(
|| (StructuralFilter::new(2.0), 0u64),
|(mut filter, mut edge_id)| {
for _ in 0..100 {
let u = edge_id % 256;
let v = (edge_id + 1) % 256;
let _ = filter.insert_edge(u, v, 1.0);
edge_id += 2;
}
black_box(edge_id)
},
criterion::BatchSize::SmallInput,
);
});
// Edge deletion
group.bench_function("delete_edge", |b| {
b.iter_batched(
|| {
let mut filter = StructuralFilter::new(2.0);
for i in 0..100u64 {
let _ = filter.insert_edge(i, i + 1, 1.0);
}
filter
},
|mut filter| {
let result = filter.delete_edge(50, 51);
black_box(result)
},
criterion::BatchSize::SmallInput,
);
});
group.finish();
}
/// Benchmark shift (drift detection) filter evaluation
fn bench_shift_filter(c: &mut Criterion) {
let mut group = c.benchmark_group("shift_filter");
group.sampling_mode(SamplingMode::Flat);
group.sample_size(1000);
// Evaluate with warm filter
group.bench_function("evaluate_warm", |b| {
let mut filter = ShiftFilter::new(0.5, 100);
// Warm up with observations
for region in 0..64 {
for _ in 0..100 {
filter.update(region, 0.5 + (region as f64 * 0.001));
}
}
let state = SystemState::new(100);
b.iter(|| {
let result = filter.evaluate(black_box(&state));
black_box(result)
});
});
// Evaluate with cold filter
group.bench_function("evaluate_cold", |b| {
let filter = ShiftFilter::new(0.5, 100);
let state = SystemState::new(100);
b.iter(|| {
let result = filter.evaluate(black_box(&state));
black_box(result)
});
});
// Single update operation
group.bench_function("update_single", |b| {
let mut filter = ShiftFilter::new(0.5, 100);
let mut i = 0usize;
b.iter(|| {
filter.update(black_box(i % 64), black_box(0.5));
i += 1;
});
});
// Batch update (64 regions)
group.bench_function("update_batch_64", |b| {
let mut filter = ShiftFilter::new(0.5, 100);
b.iter(|| {
for region in 0..64 {
filter.update(black_box(region), black_box(0.5));
}
});
});
group.finish();
}
/// Benchmark evidence (e-value) filter evaluation
fn bench_evidence_filter(c: &mut Criterion) {
let mut group = c.benchmark_group("evidence_filter");
group.sampling_mode(SamplingMode::Flat);
group.sample_size(1000);
// Evaluate with accumulated evidence
group.bench_function("evaluate_accumulated", |b| {
let mut filter = EvidenceFilter::new(20.0, 0.05);
for _ in 0..100 {
filter.update(1.5);
}
let state = SystemState::new(100);
b.iter(|| {
let result = filter.evaluate(black_box(&state));
black_box(result)
});
});
// Single evidence update
group.bench_function("update_single", |b| {
let mut filter = EvidenceFilter::new(20.0, 0.05);
b.iter(|| {
filter.update(black_box(1.5));
});
});
// Evidence accumulator operations
group.bench_function("accumulator_observe", |b| {
let mut accumulator = FilterEvidenceAccumulator::new();
b.iter(|| {
accumulator.update(black_box(1.5));
});
});
group.bench_function("accumulator_e_value", |b| {
let mut accumulator = FilterEvidenceAccumulator::new();
for _ in 0..100 {
accumulator.update(1.5);
}
b.iter(|| {
let e = accumulator.e_value();
black_box(e)
});
});
group.finish();
}
// ============================================================================
// TILE PROCESSING LATENCY
// ============================================================================
/// Benchmark worker tile tick processing
fn bench_worker_tile_tick(c: &mut Criterion) {
let mut group = c.benchmark_group("worker_tile_tick");
group.sampling_mode(SamplingMode::Flat);
group.sample_size(1000);
// Tick with syndrome delta
group.bench_function("tick_syndrome", |b| {
let mut tile = create_benchmark_worker_tile(1, 64, 128);
let delta = SyndromeDelta::new(0, 1, 100);
b.iter(|| {
let report = tile.tick(black_box(&delta));
black_box(report)
});
});
// Tick with edge addition
group.bench_function("tick_edge_add", |b| {
let mut tile = create_benchmark_worker_tile(1, 64, 128);
let delta = SyndromeDelta::edge_add(10, 20, 1000);
b.iter(|| {
let report = tile.tick(black_box(&delta));
black_box(report)
});
});
// Tick with edge removal
group.bench_function("tick_edge_remove", |b| {
b.iter_batched(
|| {
let mut tile = create_benchmark_worker_tile(1, 64, 128);
// Add edge before removing
let _ = tile.patch_graph.add_edge(5, 6, 1000);
(tile, SyndromeDelta::edge_remove(5, 6))
},
|(mut tile, delta)| {
let report = tile.tick(&delta);
black_box(report)
},
criterion::BatchSize::SmallInput,
);
});
// Varying graph sizes
for (vertices, edges) in [(32, 64), (64, 128), (128, 256), (200, 400)].iter() {
group.bench_with_input(
BenchmarkId::new("tick_graph_size", format!("v{}e{}", vertices, edges)),
&(*vertices, *edges),
|b, &(v, e)| {
let mut tile = create_benchmark_worker_tile(1, v, e);
let delta = SyndromeDelta::new(0, 1, 100);
b.iter(|| {
let report = tile.tick(black_box(&delta));
black_box(report)
});
},
);
}
group.finish();
}
/// Benchmark TileZero merge operations
fn bench_tilezero_merge(c: &mut Criterion) {
let mut group = c.benchmark_group("tilezero_merge");
group.sampling_mode(SamplingMode::Flat);
group.sample_size(1000);
// Merge leading to PERMIT
group.bench_function("merge_permit", |b| {
let thresholds = GateThresholds::default();
let mut tilezero = TileZero::new(thresholds);
let reports: Vec<TileReport> = (1..=100)
.map(|i| {
let mut report = TileReport::new(i as u8);
report.local_cut = 10.0;
report.shift_score = 0.1;
report.e_value = 200.0;
report
})
.collect();
b.iter(|| {
let decision = tilezero.merge_reports(black_box(reports.clone()));
debug_assert_eq!(decision, GateDecision::Permit);
black_box(decision)
});
});
// Merge leading to DENY (structural)
group.bench_function("merge_deny_structural", |b| {
let thresholds = GateThresholds::default();
let mut tilezero = TileZero::new(thresholds);
let reports: Vec<TileReport> = (1..=100)
.map(|i| {
let mut report = TileReport::new(i as u8);
report.local_cut = 1.0; // Below threshold
report.shift_score = 0.1;
report.e_value = 200.0;
report
})
.collect();
b.iter(|| {
let decision = tilezero.merge_reports(black_box(reports.clone()));
debug_assert_eq!(decision, GateDecision::Deny);
black_box(decision)
});
});
// Merge leading to DEFER (shift)
group.bench_function("merge_defer_shift", |b| {
let thresholds = GateThresholds::default();
let mut tilezero = TileZero::new(thresholds);
let reports: Vec<TileReport> = (1..=100)
.map(|i| {
let mut report = TileReport::new(i as u8);
report.local_cut = 10.0;
report.shift_score = 0.8; // Above threshold
report.e_value = 200.0;
report
})
.collect();
b.iter(|| {
let decision = tilezero.merge_reports(black_box(reports.clone()));
debug_assert_eq!(decision, GateDecision::Defer);
black_box(decision)
});
});
// Permit token issuance
group.bench_function("issue_permit", |b| {
let thresholds = GateThresholds::default();
let tilezero = TileZero::new(thresholds);
let decision = GateDecision::Permit;
b.iter(|| {
let token = tilezero.issue_permit(black_box(&decision));
black_box(token)
});
});
group.finish();
}
// ============================================================================
// PATCH GRAPH LATENCY
// ============================================================================
/// Benchmark patch graph operations (critical for structural filter)
fn bench_patch_graph_operations(c: &mut Criterion) {
let mut group = c.benchmark_group("patch_graph");
group.sampling_mode(SamplingMode::Flat);
group.sample_size(1000);
// Edge addition
group.bench_function("add_edge", |b| {
b.iter_batched(
PatchGraph::new,
|mut graph| {
for edge_count in 0..100u16 {
let v1 = (edge_count * 2) % 256;
let v2 = (edge_count * 2 + 1) % 256;
let _ = graph.add_edge(v1, v2, 1000);
}
black_box(graph.num_edges)
},
criterion::BatchSize::SmallInput,
);
});
// Edge removal
group.bench_function("remove_edge", |b| {
b.iter_batched(
|| {
let mut graph = PatchGraph::new();
for i in 0..100u16 {
let _ = graph.add_edge(i, i + 1, 1000);
}
graph
},
|mut graph| {
let removed = graph.remove_edge(50, 51);
black_box(removed)
},
criterion::BatchSize::SmallInput,
);
});
// Local cut estimation
group.bench_function("estimate_local_cut", |b| {
let mut graph = PatchGraph::new();
for i in 0..100u16 {
let _ = graph.add_edge(i, (i + 1) % 100, 1000);
let _ = graph.add_edge(i, (i + 50) % 100, 500);
}
graph.recompute_components();
b.iter(|| {
let cut = graph.estimate_local_cut();
black_box(cut)
});
});
// Component recomputation
group.bench_function("recompute_components", |b| {
let mut graph = PatchGraph::new();
for i in 0..100u16 {
let _ = graph.add_edge(i, (i + 1) % 100, 1000);
}
b.iter(|| {
graph.status |= PatchGraph::STATUS_DIRTY;
let count = graph.recompute_components();
black_box(count)
});
});
// Boundary candidate identification
group.bench_function("identify_boundary_candidates", |b| {
let mut graph = PatchGraph::new();
for i in 0..100u16 {
let _ = graph.add_edge(i, (i + 1) % 100, 1000);
}
graph.recompute_components();
let mut candidates = [0u16; 64];
b.iter(|| {
let count = graph.identify_boundary_candidates(&mut candidates);
black_box(count)
});
});
group.finish();
}
// ============================================================================
// LOCAL CUT STATE LATENCY
// ============================================================================
/// Benchmark local cut state operations
fn bench_local_cut_state(c: &mut Criterion) {
let mut group = c.benchmark_group("local_cut_state");
group.sampling_mode(SamplingMode::Flat);
group.sample_size(1000);
// Update from graph
group.bench_function("update_from_graph", |b| {
let mut graph = PatchGraph::new();
for i in 0..100u16 {
let _ = graph.add_edge(i, (i + 1) % 100, 1000);
}
graph.recompute_components();
let mut cut_state = LocalCutState::new();
b.iter(|| {
cut_state.update_from_graph(&graph);
black_box(cut_state.cut_value)
});
});
group.finish();
}
// ============================================================================
// FILTER PIPELINE LATENCY
// ============================================================================
/// Benchmark full filter pipeline evaluation
fn bench_filter_pipeline(c: &mut Criterion) {
let mut group = c.benchmark_group("filter_pipeline");
group.sampling_mode(SamplingMode::Flat);
group.sample_size(1000);
// Full evaluation
group.bench_function("evaluate_full", |b| {
let pipeline = create_benchmark_filter_pipeline();
let state = SystemState::new(100);
b.iter(|| {
let result = pipeline.evaluate(black_box(&state));
black_box(result)
});
});
// Cold start evaluation
group.bench_function("evaluate_cold", |b| {
b.iter_batched(
|| {
let config = FilterConfig::default();
let pipeline = FilterPipeline::new(config);
let state = SystemState::new(100);
(pipeline, state)
},
|(pipeline, state)| {
let result = pipeline.evaluate(&state);
black_box(result)
},
criterion::BatchSize::SmallInput,
);
});
group.finish();
}
// ============================================================================
// CRITERION GROUPS
// ============================================================================
criterion_group!(
latency_benches,
bench_gate_decision,
bench_structural_filter,
bench_shift_filter,
bench_evidence_filter,
bench_worker_tile_tick,
bench_tilezero_merge,
bench_patch_graph_operations,
bench_local_cut_state,
bench_filter_pipeline,
);
criterion_main!(latency_benches);

View File

@@ -0,0 +1,619 @@
//! Memory efficiency benchmarks for ruQu Coherence Gate.
//!
//! Memory Targets:
//! - Per-tile memory usage: **<64KB**
//! - Allocation counts per cycle: **0 (steady state)**
//! - Cache line efficiency: **>80%**
//!
//! Run with: `cargo bench -p ruqu --bench memory_bench`
use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion, Throughput};
use std::alloc::{GlobalAlloc, Layout, System};
use std::sync::atomic::{AtomicUsize, Ordering};
use ruqu::filters::{FilterConfig, FilterPipeline, ShiftFilter, StructuralFilter};
use ruqu::syndrome::{DetectorBitmap, SyndromeBuffer, SyndromeRound};
use ruqu::tile::{
EvidenceAccumulator, GateThresholds, LocalCutState, PatchGraph, ReceiptLog, SyndromBuffer,
SyndromeDelta, TileReport, TileZero, WorkerTile,
};
// ============================================================================
// ALLOCATION TRACKING ALLOCATOR
// ============================================================================
/// Global allocation counter for tracking allocations
static ALLOC_COUNT: AtomicUsize = AtomicUsize::new(0);
static DEALLOC_COUNT: AtomicUsize = AtomicUsize::new(0);
static BYTES_ALLOCATED: AtomicUsize = AtomicUsize::new(0);
static BYTES_DEALLOCATED: AtomicUsize = AtomicUsize::new(0);
/// Reset allocation counters
fn reset_allocation_counters() {
ALLOC_COUNT.store(0, Ordering::SeqCst);
DEALLOC_COUNT.store(0, Ordering::SeqCst);
BYTES_ALLOCATED.store(0, Ordering::SeqCst);
BYTES_DEALLOCATED.store(0, Ordering::SeqCst);
}
/// Get allocation statistics
fn get_allocation_stats() -> (usize, usize, usize, usize) {
(
ALLOC_COUNT.load(Ordering::SeqCst),
DEALLOC_COUNT.load(Ordering::SeqCst),
BYTES_ALLOCATED.load(Ordering::SeqCst),
BYTES_DEALLOCATED.load(Ordering::SeqCst),
)
}
// ============================================================================
// SIZE VERIFICATION BENCHMARKS
// ============================================================================
/// Benchmark and verify structure sizes
fn bench_structure_sizes(c: &mut Criterion) {
let mut group = c.benchmark_group("structure_sizes");
// Report sizes (this is informational, not a timed benchmark)
println!("\n=== Structure Sizes ===");
println!(
"WorkerTile: {} bytes",
std::mem::size_of::<WorkerTile>()
);
println!(
"PatchGraph: {} bytes",
std::mem::size_of::<PatchGraph>()
);
println!(
"SyndromBuffer: {} bytes",
std::mem::size_of::<SyndromBuffer>()
);
println!(
"EvidenceAccumulator: {} bytes",
std::mem::size_of::<EvidenceAccumulator>()
);
println!(
"LocalCutState: {} bytes",
std::mem::size_of::<LocalCutState>()
);
println!(
"TileReport: {} bytes",
std::mem::size_of::<TileReport>()
);
println!(
"DetectorBitmap: {} bytes",
std::mem::size_of::<DetectorBitmap>()
);
println!(
"SyndromeRound: {} bytes",
std::mem::size_of::<SyndromeRound>()
);
println!(
"SyndromeDelta: {} bytes",
std::mem::size_of::<SyndromeDelta>()
);
println!();
// Verify 64KB budget
let total_tile_size = std::mem::size_of::<WorkerTile>();
let budget = 65536; // 64KB
println!(
"WorkerTile size: {} bytes ({:.1}% of 64KB budget)",
total_tile_size,
(total_tile_size as f64 / budget as f64) * 100.0
);
// Benchmark size computation (ensures compiler doesn't optimize away)
group.bench_function("size_of_worker_tile", |b| {
b.iter(|| black_box(std::mem::size_of::<WorkerTile>()));
});
group.bench_function("size_of_patch_graph", |b| {
b.iter(|| black_box(std::mem::size_of::<PatchGraph>()));
});
group.bench_function("size_of_tile_report", |b| {
b.iter(|| black_box(std::mem::size_of::<TileReport>()));
});
group.finish();
}
// ============================================================================
// PER-TILE MEMORY USAGE
// ============================================================================
/// Benchmark per-tile memory usage
fn bench_per_tile_memory(c: &mut Criterion) {
let mut group = c.benchmark_group("per_tile_memory");
// WorkerTile memory footprint
let worker_tile_size = std::mem::size_of::<WorkerTile>();
assert!(
worker_tile_size <= 131072, // 128KB max (some padding allowed)
"WorkerTile exceeds memory budget: {} bytes",
worker_tile_size
);
// Benchmark WorkerTile creation (measures stack allocation)
group.bench_function("create_worker_tile", |b| {
b.iter(|| {
let tile = WorkerTile::new(1);
black_box(&tile);
// Note: WorkerTile is large, measure creation overhead
});
});
// Benchmark WorkerTile reset (should be allocation-free)
group.bench_function("reset_worker_tile", |b| {
let mut tile = WorkerTile::new(1);
// Populate with some data
for i in 0..50u16 {
let _ = tile.patch_graph.add_edge(i, i + 1, 1000);
}
b.iter(|| {
tile.reset();
black_box(&tile);
});
});
// Benchmark PatchGraph memory efficiency
group.bench_function("patch_graph_memory", |b| {
b.iter(|| {
let graph = PatchGraph::new();
black_box(&graph);
black_box(std::mem::size_of_val(&graph));
});
});
// Benchmark SyndromBuffer memory efficiency
group.bench_function("syndrom_buffer_memory", |b| {
b.iter(|| {
let buffer = SyndromBuffer::new();
black_box(&buffer);
black_box(std::mem::size_of_val(&buffer));
});
});
group.finish();
}
// ============================================================================
// ALLOCATION-FREE OPERATIONS
// ============================================================================
/// Benchmark operations that should be allocation-free in steady state
fn bench_allocation_free_ops(c: &mut Criterion) {
let mut group = c.benchmark_group("allocation_free");
// Worker tile tick should be allocation-free
group.bench_function("worker_tick_no_alloc", |b| {
let mut tile = WorkerTile::new(1);
// Pre-populate
for i in 0..50u16 {
let _ = tile.patch_graph.add_edge(i, i + 1, 1000);
}
tile.patch_graph.recompute_components();
let delta = SyndromeDelta::new(0, 1, 100);
b.iter(|| {
let report = tile.tick(&delta);
black_box(report);
});
});
// PatchGraph operations should be allocation-free
group.bench_function("patch_graph_ops_no_alloc", |b| {
let mut graph = PatchGraph::new();
for i in 0..100u16 {
let _ = graph.add_edge(i, (i + 1) % 100, 1000);
}
graph.recompute_components();
b.iter(|| {
// These operations should not allocate
let cut = graph.estimate_local_cut();
let mut candidates = [0u16; 64];
let count = graph.identify_boundary_candidates(&mut candidates);
black_box((cut, count));
});
});
// DetectorBitmap operations should be allocation-free
group.bench_function("bitmap_ops_no_alloc", |b| {
let mut a = DetectorBitmap::new(1024);
let mut bb = DetectorBitmap::new(1024);
for i in (0..512).step_by(2) {
a.set(i, true);
}
for i in (256..768).step_by(2) {
bb.set(i, true);
}
b.iter(|| {
let result = a.xor(&bb);
let count = result.popcount();
black_box(count);
});
});
// TileReport copy should be allocation-free
group.bench_function("tile_report_copy_no_alloc", |b| {
let mut report = TileReport::new(1);
report.local_cut = 10.0;
report.shift_score = 0.1;
report.e_value = 200.0;
b.iter(|| {
let copy = report;
black_box(copy);
});
});
// Evidence accumulator operations should be allocation-free
group.bench_function("evidence_update_no_alloc", |b| {
let mut evidence = EvidenceAccumulator::new();
b.iter(|| {
evidence.observe(1000);
let e = evidence.e_value();
black_box(e);
});
});
// LocalCutState update should be allocation-free
group.bench_function("local_cut_update_no_alloc", |b| {
let mut graph = PatchGraph::new();
for i in 0..100u16 {
let _ = graph.add_edge(i, (i + 1) % 100, 1000);
}
graph.recompute_components();
let mut cut_state = LocalCutState::new();
b.iter(|| {
cut_state.update_from_graph(&graph);
black_box(&cut_state);
});
});
group.finish();
}
// ============================================================================
// CACHE LINE EFFICIENCY
// ============================================================================
/// Benchmark cache line efficiency
fn bench_cache_efficiency(c: &mut Criterion) {
let mut group = c.benchmark_group("cache_efficiency");
const CACHE_LINE_SIZE: usize = 64;
// Verify cache-line alignment
println!("\n=== Cache Line Alignment ===");
println!(
"TileReport alignment: {} bytes (cache line: {})",
std::mem::align_of::<TileReport>(),
CACHE_LINE_SIZE
);
println!(
"PatchGraph alignment: {} bytes",
std::mem::align_of::<PatchGraph>()
);
println!(
"SyndromBuffer alignment: {} bytes",
std::mem::align_of::<SyndromBuffer>()
);
println!(
"DetectorBitmap alignment: {} bytes",
std::mem::align_of::<DetectorBitmap>()
);
println!();
// Sequential access pattern (cache-friendly)
group.bench_function("sequential_access", |b| {
let mut graph = PatchGraph::new();
for i in 0..200u16 {
graph.ensure_vertex(i);
}
b.iter(|| {
let mut sum = 0u32;
for i in 0..200 {
if graph.vertices[i].is_active() {
sum += graph.vertices[i].degree as u32;
}
}
black_box(sum);
});
});
// Strided access pattern (potential cache misses)
group.bench_function("strided_access", |b| {
let mut graph = PatchGraph::new();
for i in 0..200u16 {
graph.ensure_vertex(i);
}
b.iter(|| {
let mut sum = 0u32;
// Access every 8th element (stride across multiple cache lines)
for i in (0..200).step_by(8) {
if graph.vertices[i].is_active() {
sum += graph.vertices[i].degree as u32;
}
}
black_box(sum);
});
});
// TileReport array access (should be cache-line aligned)
group.bench_function("tile_report_array_access", |b| {
let reports: Vec<TileReport> = (1..=255)
.map(|i| {
let mut r = TileReport::new(i);
r.local_cut = i as f64;
r
})
.collect();
b.iter(|| {
let mut sum = 0.0f64;
for report in &reports {
sum += report.local_cut;
}
black_box(sum);
});
});
// DetectorBitmap word access (should be aligned)
group.bench_function("bitmap_word_access", |b| {
let mut bitmap = DetectorBitmap::new(1024);
for i in (0..1024).step_by(3) {
bitmap.set(i, true);
}
b.iter(|| {
let raw = bitmap.raw_bits();
let mut sum = 0u64;
for word in raw {
sum = sum.wrapping_add(*word);
}
black_box(sum);
});
});
group.finish();
}
// ============================================================================
// MEMORY POOL SIMULATION
// ============================================================================
/// Benchmark simulated memory pool operations
fn bench_memory_pool(c: &mut Criterion) {
let mut group = c.benchmark_group("memory_pool");
// Pre-allocated tile pool
group.bench_function("tile_pool_reuse", |b| {
// Simulate a pool of worker tiles
let mut tile_pool: Vec<WorkerTile> = (1..=10).map(|i| WorkerTile::new(i)).collect();
let delta = SyndromeDelta::new(0, 1, 100);
b.iter(|| {
// Use tiles from pool without allocation
for tile in &mut tile_pool {
let report = tile.tick(&delta);
black_box(&report);
}
});
});
// Pre-allocated report buffer
group.bench_function("report_buffer_reuse", |b| {
// Simulate a reusable report buffer
let mut report_buffer: [TileReport; 255] = [TileReport::default(); 255];
b.iter(|| {
// Fill buffer without allocation
for i in 0..255 {
report_buffer[i].tile_id = i as u8;
report_buffer[i].local_cut = 10.0;
report_buffer[i].shift_score = 0.1;
report_buffer[i].e_value = 200.0;
}
black_box(&report_buffer);
});
});
// Pre-allocated syndrome round buffer
group.bench_function("syndrome_round_reuse", |b| {
let mut buffer = SyndromeBuffer::new(1024);
let mut round_id = 0u64;
// Pre-fill
for i in 0..1024 {
let round = SyndromeRound::new(i, i, i * 1000, DetectorBitmap::new(64), 0);
buffer.push(round);
}
b.iter(|| {
// Push rounds (reusing buffer space)
for _ in 0..100 {
let round = SyndromeRound::new(
round_id,
round_id,
round_id * 1000,
DetectorBitmap::new(64),
0,
);
buffer.push(round);
round_id += 1;
}
black_box(&buffer);
});
});
group.finish();
}
// ============================================================================
// HEAP ALLOCATION BENCHMARKS
// ============================================================================
/// Benchmark operations that require heap allocation
fn bench_heap_allocations(c: &mut Criterion) {
let mut group = c.benchmark_group("heap_allocations");
// Filter pipeline (requires heap for collections)
group.bench_function("filter_pipeline_create", |b| {
b.iter(|| {
let config = FilterConfig::default();
let pipeline = FilterPipeline::new(config);
black_box(pipeline);
});
});
// TileZero creation (requires heap)
group.bench_function("tilezero_create", |b| {
b.iter(|| {
let thresholds = GateThresholds::default();
let tilezero = TileZero::new(thresholds);
black_box(tilezero);
});
});
// ReceiptLog append (heap allocation)
group.bench_function("receipt_log_grow", |b| {
b.iter_batched(
ReceiptLog::new,
|mut log| {
for i in 0..100 {
log.append(ruqu::tile::GateDecision::Permit, i, i * 1000, [0u8; 32]);
}
black_box(&log);
},
criterion::BatchSize::SmallInput,
);
});
// SyndromeBuffer create (heap allocation)
group.bench_function("syndrome_buffer_create", |b| {
b.iter(|| {
let buffer = SyndromeBuffer::new(1024);
black_box(buffer);
});
});
// Large buffer sizes
for size in [1024, 4096, 16384, 65536].iter() {
group.bench_with_input(
BenchmarkId::new("syndrome_buffer_create", size),
size,
|b, &sz| {
b.iter(|| {
let buffer = SyndromeBuffer::new(sz);
black_box(buffer);
});
},
);
}
group.finish();
}
// ============================================================================
// MEMORY BANDWIDTH BENCHMARKS
// ============================================================================
/// Benchmark memory bandwidth operations
fn bench_memory_bandwidth(c: &mut Criterion) {
let mut group = c.benchmark_group("memory_bandwidth");
// Large data copy (TileReport array)
group.throughput(Throughput::Bytes(
255 * std::mem::size_of::<TileReport>() as u64,
));
group.bench_function("copy_255_reports", |b| {
let source: Vec<TileReport> = (1..=255).map(|i| TileReport::new(i)).collect();
b.iter(|| {
let copy: Vec<TileReport> = source.clone();
black_box(copy);
});
});
// DetectorBitmap copy
group.throughput(Throughput::Bytes(
std::mem::size_of::<DetectorBitmap>() as u64
));
group.bench_function("copy_bitmap", |b| {
let mut bitmap = DetectorBitmap::new(1024);
for i in 0..512 {
bitmap.set(i, true);
}
b.iter(|| {
let copy = bitmap;
black_box(copy);
});
});
// Batch bitmap copy
group.throughput(Throughput::Bytes(
100 * std::mem::size_of::<DetectorBitmap>() as u64,
));
group.bench_function("copy_100_bitmaps", |b| {
let bitmaps: Vec<DetectorBitmap> = (0..100)
.map(|i| {
let mut bm = DetectorBitmap::new(1024);
bm.set(i * 10, true);
bm
})
.collect();
b.iter(|| {
let copy: Vec<DetectorBitmap> = bitmaps.clone();
black_box(copy);
});
});
// SyndromeRound copy
group.throughput(Throughput::Bytes(
std::mem::size_of::<SyndromeRound>() as u64
));
group.bench_function("copy_syndrome_round", |b| {
let mut detectors = DetectorBitmap::new(256);
for i in 0..25 {
detectors.set(i * 10, true);
}
let round = SyndromeRound::new(12345, 100, 1000000, detectors, 0);
b.iter(|| {
let copy = round.clone();
black_box(copy);
});
});
group.finish();
}
// ============================================================================
// CRITERION GROUPS
// ============================================================================
criterion_group!(
memory_benches,
bench_structure_sizes,
bench_per_tile_memory,
bench_allocation_free_ops,
bench_cache_efficiency,
bench_memory_pool,
bench_heap_allocations,
bench_memory_bandwidth,
);
criterion_main!(memory_benches);

View File

@@ -0,0 +1,164 @@
//! Benchmarks for the real SubpolynomialMinCut integration
//!
//! Tests the El-Hayek/Henzinger/Li O(n^{o(1)}) algorithm performance.
use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion, Throughput};
use ruqu::mincut::DynamicMinCutEngine;
/// Benchmark min-cut engine creation
fn bench_engine_creation(c: &mut Criterion) {
c.bench_function("mincut_engine_creation", |b| {
b.iter(|| black_box(DynamicMinCutEngine::new()));
});
}
/// Benchmark edge insertion
fn bench_edge_insertion(c: &mut Criterion) {
let mut group = c.benchmark_group("mincut_edge_insertion");
for size in [10, 50, 100, 500] {
group.throughput(Throughput::Elements(size as u64));
group.bench_with_input(BenchmarkId::from_parameter(size), &size, |b, &size| {
b.iter_batched(
|| DynamicMinCutEngine::new(),
|mut engine| {
for i in 0..size {
engine.insert_edge(i as u32, (i + 1) as u32, 1.0);
}
black_box(engine)
},
criterion::BatchSize::SmallInput,
);
});
}
group.finish();
}
/// Benchmark min-cut query after building a graph
fn bench_mincut_query(c: &mut Criterion) {
let mut group = c.benchmark_group("mincut_query");
for size in [10, 50, 100, 200] {
group.bench_with_input(BenchmarkId::from_parameter(size), &size, |b, &size| {
// Build a random-ish graph
let mut engine = DynamicMinCutEngine::new();
for i in 0..size {
engine.insert_edge(i as u32, ((i + 1) % size) as u32, 1.0);
if i > 0 {
engine.insert_edge(i as u32, ((i + size / 2) % size) as u32, 0.5);
}
}
b.iter(|| black_box(engine.min_cut_value()));
});
}
group.finish();
}
/// Benchmark dynamic updates (insert + query)
fn bench_dynamic_updates(c: &mut Criterion) {
let mut group = c.benchmark_group("mincut_dynamic_updates");
for size in [50, 100] {
group.bench_with_input(BenchmarkId::from_parameter(size), &size, |b, &size| {
// Build initial graph
let mut engine = DynamicMinCutEngine::new();
for i in 0..size {
engine.insert_edge(i as u32, ((i + 1) % size) as u32, 1.0);
}
// Query once to prime
let _ = engine.min_cut_value();
let mut counter = 0u32;
b.iter(|| {
// Insert edge
engine.insert_edge(counter % size as u32, (counter + 10) % size as u32, 1.5);
// Query
let cut = engine.min_cut_value();
// Delete edge
engine.delete_edge(counter % size as u32, (counter + 10) % size as u32);
counter = counter.wrapping_add(1);
black_box(cut)
});
});
}
group.finish();
}
/// Benchmark grid graph (surface code-like)
fn bench_surface_code_graph(c: &mut Criterion) {
let mut group = c.benchmark_group("mincut_surface_code");
for distance in [5, 7, 9] {
let num_qubits = 2 * distance * distance - 2 * distance + 1;
group.bench_with_input(
BenchmarkId::new("distance", distance),
&distance,
|b, &d| {
b.iter_batched(
|| {
// Build a grid graph approximating surface code
let mut engine = DynamicMinCutEngine::new();
for row in 0..d {
for col in 0..d {
let v = (row * d + col) as u32;
// Horizontal edges
if col + 1 < d {
engine.insert_edge(v, v + 1, 1.0);
}
// Vertical edges
if row + 1 < d {
engine.insert_edge(v, v + d as u32, 1.0);
}
}
}
engine
},
|mut engine| {
// Simulate syndrome updates
for i in 0..10 {
let v = (i % (d * d)) as u32;
engine.insert_edge(v, v + 1, 0.8);
let _ = engine.min_cut_value();
}
black_box(engine)
},
criterion::BatchSize::SmallInput,
);
},
);
}
group.finish();
}
/// Benchmark full min-cut result with certificate
fn bench_mincut_certified(c: &mut Criterion) {
let mut group = c.benchmark_group("mincut_certified");
for size in [50, 100] {
group.bench_with_input(BenchmarkId::from_parameter(size), &size, |b, &size| {
let mut engine = DynamicMinCutEngine::new();
for i in 0..size {
engine.insert_edge(i as u32, ((i + 1) % size) as u32, 1.0);
}
b.iter(|| {
let result = engine.min_cut();
black_box((result.value, result.is_exact, result.witness_hash))
});
});
}
group.finish();
}
criterion_group!(
benches,
bench_engine_creation,
bench_edge_insertion,
bench_mincut_query,
bench_dynamic_updates,
bench_surface_code_graph,
bench_mincut_certified,
);
criterion_main!(benches);

View File

@@ -0,0 +1,586 @@
//! Scaling benchmarks for ruQu Coherence Gate.
//!
//! Measures how performance scales with:
//! - Code distance (5, 9, 13, 17, 21)
//! - Qubit count (50, 100, 500, 1000)
//! - Tile count (10, 50, 100, 255)
//! - Graph density
//!
//! Run with: `cargo bench -p ruqu --bench scaling_bench`
use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion, Throughput};
use std::hint::black_box as hint_black_box;
use ruqu::filters::{FilterConfig, FilterPipeline, SystemState};
use ruqu::syndrome::{DetectorBitmap, SyndromeBuffer, SyndromeRound};
use ruqu::tile::{GateThresholds, PatchGraph, SyndromeDelta, TileReport, TileZero, WorkerTile};
// ============================================================================
// HELPER FUNCTIONS
// ============================================================================
/// Calculate approximate detector count for a surface code distance
fn detectors_for_distance(distance: usize) -> usize {
// For surface code, detector count is roughly d^2
distance * distance
}
/// Calculate approximate qubit count for a surface code distance
fn qubits_for_distance(distance: usize) -> usize {
// For surface code, data qubits = 2*d^2 - 2*d + 1
2 * distance * distance - 2 * distance + 1
}
/// Create a worker tile sized for a given qubit count
fn create_scaled_worker_tile(tile_id: u8, qubit_count: usize) -> WorkerTile {
let mut tile = WorkerTile::new(tile_id);
let vertices = (qubit_count / 4).min(255) as u16; // Tile handles a fraction of qubits
let edges_per_vertex = 4; // Surface code connectivity
for i in 0..vertices {
tile.patch_graph.ensure_vertex(i);
}
let mut edges_added = 0u16;
let max_edges = (vertices as usize * edges_per_vertex / 2).min(1000) as u16;
'outer: for i in 0..vertices.saturating_sub(1) {
// Lattice-like connectivity
let neighbors = [i + 1, i.wrapping_add(vertices / 10)];
for &neighbor in &neighbors {
if neighbor < vertices && neighbor != i && edges_added < max_edges {
if tile.patch_graph.add_edge(i, neighbor, 1000).is_some() {
edges_added += 1;
}
}
if edges_added >= max_edges {
break 'outer;
}
}
}
tile.patch_graph.recompute_components();
tile
}
/// Create a filter pipeline sized for a given qubit count
fn create_scaled_filter_pipeline(qubit_count: usize) -> FilterPipeline {
let config = FilterConfig::default();
let mut pipeline = FilterPipeline::new(config);
let vertices = qubit_count.min(500) as u64;
// Add graph structure proportional to qubit count
for i in 0..vertices.saturating_sub(1) {
let _ = pipeline.structural_mut().insert_edge(i, i + 1, 1.0);
if i % 10 == 0 && i + 10 < vertices {
let _ = pipeline.structural_mut().insert_edge(i, i + 10, 0.5);
}
}
pipeline.structural_mut().build();
// Warm up shift filter
let num_regions = (qubit_count / 16).min(64);
for region in 0..num_regions {
for _ in 0..50 {
pipeline.shift_mut().update(region, 0.5);
}
}
// Warm up evidence filter
for _ in 0..20 {
pipeline.evidence_mut().update(1.5);
}
pipeline
}
// ============================================================================
// LATENCY VS CODE DISTANCE
// ============================================================================
/// Benchmark latency scaling with code distance
fn bench_latency_vs_distance(c: &mut Criterion) {
let mut group = c.benchmark_group("latency_vs_distance");
group.sample_size(100);
let distances = [5, 9, 13, 17, 21];
for distance in distances.iter() {
let qubit_count = qubits_for_distance(*distance);
let detector_count = detectors_for_distance(*distance);
// Worker tile tick latency
group.bench_with_input(
BenchmarkId::new("worker_tick", format!("d{}", distance)),
&qubit_count,
|b, &qubits| {
let mut tile = create_scaled_worker_tile(1, qubits);
let delta = SyndromeDelta::new(0, 1, 100);
b.iter(|| {
let report = tile.tick(black_box(&delta));
black_box(report)
});
},
);
// Filter pipeline evaluation latency
group.bench_with_input(
BenchmarkId::new("filter_pipeline", format!("d{}", distance)),
&qubit_count,
|b, &qubits| {
let pipeline = create_scaled_filter_pipeline(qubits);
let state = SystemState::new(qubits);
b.iter(|| {
let result = pipeline.evaluate(black_box(&state));
black_box(result)
});
},
);
// Full decision cycle latency
group.bench_with_input(
BenchmarkId::new("full_decision", format!("d{}", distance)),
&qubit_count,
|b, &qubits| {
let mut tile = create_scaled_worker_tile(1, qubits);
let thresholds = GateThresholds::default();
let mut tilezero = TileZero::new(thresholds);
b.iter(|| {
let delta = SyndromeDelta::new(0, 1, 100);
let report = tile.tick(&delta);
let reports = vec![report; 10];
let decision = tilezero.merge_reports(reports);
black_box(decision)
});
},
);
// Syndrome buffer push latency
group.bench_with_input(
BenchmarkId::new("syndrome_push", format!("d{}", distance)),
&detector_count,
|b, &detectors| {
let mut buffer = SyndromeBuffer::new(1024);
let mut round_id = 0u64;
b.iter(|| {
let mut bitmap = DetectorBitmap::new(detectors.min(1024));
for i in 0..detectors.min(1024) / 10 {
bitmap.set(i * 10, true);
}
let round = SyndromeRound::new(round_id, round_id, round_id * 1000, bitmap, 0);
buffer.push(round);
round_id += 1;
black_box(buffer.len())
});
},
);
}
group.finish();
}
// ============================================================================
// LATENCY VS QUBIT COUNT
// ============================================================================
/// Benchmark latency scaling with qubit count
fn bench_latency_vs_qubit_count(c: &mut Criterion) {
let mut group = c.benchmark_group("latency_vs_qubits");
group.sample_size(100);
let qubit_counts = [50, 100, 500, 1000];
for qubit_count in qubit_counts.iter() {
// Worker tile tick latency
group.bench_with_input(
BenchmarkId::new("worker_tick", format!("q{}", qubit_count)),
qubit_count,
|b, &qubits| {
let mut tile = create_scaled_worker_tile(1, qubits);
let delta = SyndromeDelta::new(0, 1, 100);
b.iter(|| {
let report = tile.tick(black_box(&delta));
black_box(report)
});
},
);
// Filter pipeline evaluation
group.bench_with_input(
BenchmarkId::new("filter_pipeline", format!("q{}", qubit_count)),
qubit_count,
|b, &qubits| {
let pipeline = create_scaled_filter_pipeline(qubits);
let state = SystemState::new(qubits);
b.iter(|| {
let result = pipeline.evaluate(black_box(&state));
black_box(result)
});
},
);
// Patch graph operations
group.bench_with_input(
BenchmarkId::new("patch_graph_estimate_cut", format!("q{}", qubit_count)),
qubit_count,
|b, &qubits| {
let tile = create_scaled_worker_tile(1, qubits);
b.iter(|| {
let cut = tile.patch_graph.estimate_local_cut();
black_box(cut)
});
},
);
// Component recomputation
group.bench_with_input(
BenchmarkId::new("recompute_components", format!("q{}", qubit_count)),
qubit_count,
|b, &qubits| {
let mut tile = create_scaled_worker_tile(1, qubits);
b.iter(|| {
tile.patch_graph.status |= PatchGraph::STATUS_DIRTY;
let count = tile.patch_graph.recompute_components();
black_box(count)
});
},
);
}
group.finish();
}
// ============================================================================
// LATENCY VS TILE COUNT
// ============================================================================
/// Benchmark latency scaling with tile count (TileZero merge)
fn bench_latency_vs_tile_count(c: &mut Criterion) {
let mut group = c.benchmark_group("latency_vs_tiles");
group.sample_size(100);
let tile_counts = [10, 50, 100, 150, 200, 255];
for tile_count in tile_counts.iter() {
// TileZero merge latency
group.bench_with_input(
BenchmarkId::new("tilezero_merge", format!("t{}", tile_count)),
tile_count,
|b, &count| {
let thresholds = GateThresholds::default();
let mut tilezero = TileZero::new(thresholds);
let reports: Vec<TileReport> = (1..=count)
.map(|i| {
let mut report = TileReport::new(i as u8);
report.local_cut = 10.0 + (i as f64 * 0.1);
report.shift_score = 0.1;
report.e_value = 200.0;
report.num_vertices = 100;
report.num_edges = 200;
report
})
.collect();
b.iter(|| {
let decision = tilezero.merge_reports(black_box(reports.clone()));
black_box(decision)
});
},
);
// Full decision cycle with scaled tiles
group.bench_with_input(
BenchmarkId::new("full_decision", format!("t{}", tile_count)),
tile_count,
|b, &count| {
let mut tile = create_scaled_worker_tile(1, 100);
let thresholds = GateThresholds::default();
let mut tilezero = TileZero::new(thresholds);
b.iter(|| {
let delta = SyndromeDelta::new(0, 1, 100);
let report = tile.tick(&delta);
let reports = vec![report; count];
let decision = tilezero.merge_reports(reports);
black_box(decision)
});
},
);
}
group.finish();
}
// ============================================================================
// THROUGHPUT VS SYSTEM SIZE
// ============================================================================
/// Benchmark throughput scaling with system size
fn bench_throughput_vs_size(c: &mut Criterion) {
let mut group = c.benchmark_group("throughput_vs_size");
let qubit_counts = [50, 100, 500, 1000];
for qubit_count in qubit_counts.iter() {
// Syndrome ingestion throughput
group.throughput(Throughput::Elements(1000));
group.bench_with_input(
BenchmarkId::new("syndrome_ingestion", format!("q{}", qubit_count)),
qubit_count,
|b, &qubits| {
let mut buffer = SyndromeBuffer::new(4096);
let detector_count = (qubits / 2).min(1024);
let mut round_id = 0u64;
b.iter(|| {
for _ in 0..1000 {
let mut bitmap = DetectorBitmap::new(detector_count);
for i in 0..detector_count / 10 {
bitmap.set(i * 10, true);
}
let round =
SyndromeRound::new(round_id, round_id, round_id * 1000, bitmap, 0);
buffer.push(round);
round_id += 1;
}
black_box(buffer.len())
});
},
);
// Decision throughput
group.throughput(Throughput::Elements(100));
group.bench_with_input(
BenchmarkId::new("decision_throughput", format!("q{}", qubit_count)),
qubit_count,
|b, &qubits| {
let mut tile = create_scaled_worker_tile(1, qubits);
let thresholds = GateThresholds::default();
let mut tilezero = TileZero::new(thresholds);
b.iter(|| {
for i in 0..100 {
let delta = SyndromeDelta::new(0, 1, (i % 256) as u16);
let report = tile.tick(&delta);
let reports = vec![report; 10];
let decision = tilezero.merge_reports(reports);
hint_black_box(decision);
}
});
},
);
}
group.finish();
}
// ============================================================================
// GRAPH DENSITY SCALING
// ============================================================================
/// Benchmark latency scaling with graph density
fn bench_latency_vs_density(c: &mut Criterion) {
let mut group = c.benchmark_group("latency_vs_density");
group.sample_size(100);
let base_vertices = 100u16;
let densities = [
("sparse", base_vertices / 2), // 0.5 edges per vertex
("linear", base_vertices), // 1 edge per vertex
("lattice", base_vertices * 2), // 2 edges per vertex
("dense", base_vertices * 4), // 4 edges per vertex
("very_dense", base_vertices * 8), // 8 edges per vertex
];
for (name, edge_count) in densities.iter() {
// Worker tile tick
group.bench_with_input(
BenchmarkId::new("worker_tick", *name),
edge_count,
|b, &edges| {
let mut tile = WorkerTile::new(1);
for i in 0..base_vertices {
tile.patch_graph.ensure_vertex(i);
}
let mut added = 0u16;
'outer: for i in 0..base_vertices {
for j in (i + 1)..base_vertices.min(i + 10) {
if added >= edges {
break 'outer;
}
if tile.patch_graph.add_edge(i, j, 1000).is_some() {
added += 1;
}
}
}
tile.patch_graph.recompute_components();
let delta = SyndromeDelta::new(0, 1, 100);
b.iter(|| {
let report = tile.tick(black_box(&delta));
black_box(report)
});
},
);
// Local cut estimation
group.bench_with_input(
BenchmarkId::new("estimate_local_cut", *name),
edge_count,
|b, &edges| {
let mut graph = PatchGraph::new();
for i in 0..base_vertices {
graph.ensure_vertex(i);
}
let mut added = 0u16;
'outer: for i in 0..base_vertices {
for j in (i + 1)..base_vertices.min(i + 10) {
if added >= edges {
break 'outer;
}
if graph.add_edge(i, j, 1000).is_some() {
added += 1;
}
}
}
graph.recompute_components();
b.iter(|| {
let cut = graph.estimate_local_cut();
black_box(cut)
});
},
);
// Component recomputation
group.bench_with_input(
BenchmarkId::new("recompute_components", *name),
edge_count,
|b, &edges| {
let mut graph = PatchGraph::new();
for i in 0..base_vertices {
graph.ensure_vertex(i);
}
let mut added = 0u16;
'outer: for i in 0..base_vertices {
for j in (i + 1)..base_vertices.min(i + 10) {
if added >= edges {
break 'outer;
}
if graph.add_edge(i, j, 1000).is_some() {
added += 1;
}
}
}
b.iter(|| {
graph.status |= PatchGraph::STATUS_DIRTY;
let count = graph.recompute_components();
black_box(count)
});
},
);
}
group.finish();
}
// ============================================================================
// MEMORY PRESSURE SCALING
// ============================================================================
/// Benchmark under memory pressure (large buffers)
fn bench_memory_pressure(c: &mut Criterion) {
let mut group = c.benchmark_group("memory_pressure");
group.sample_size(50);
let buffer_sizes = [1024, 4096, 16384, 65536];
for buffer_size in buffer_sizes.iter() {
// Syndrome buffer under pressure
group.throughput(Throughput::Elements(1000));
group.bench_with_input(
BenchmarkId::new("syndrome_buffer", format!("cap{}", buffer_size)),
buffer_size,
|b, &size| {
let mut buffer = SyndromeBuffer::new(size);
// Pre-fill to capacity
for i in 0..(size as u64) {
let round = SyndromeRound::new(i, i, i * 1000, DetectorBitmap::new(64), 0);
buffer.push(round);
}
let mut round_id = size as u64;
b.iter(|| {
for _ in 0..1000 {
let round = SyndromeRound::new(
round_id,
round_id,
round_id * 1000,
DetectorBitmap::new(64),
0,
);
buffer.push(round);
round_id += 1;
}
black_box(buffer.len())
});
},
);
// Window extraction under pressure
group.bench_with_input(
BenchmarkId::new("window_extraction", format!("cap{}", buffer_size)),
buffer_size,
|b, &size| {
let mut buffer = SyndromeBuffer::new(size);
for i in 0..(size as u64) {
let round = SyndromeRound::new(i, i, i * 1000, DetectorBitmap::new(64), 0);
buffer.push(round);
}
let window_size = (size / 10).max(10);
b.iter(|| {
let window = buffer.window(window_size);
black_box(window)
});
},
);
}
group.finish();
}
// ============================================================================
// CRITERION GROUPS
// ============================================================================
criterion_group!(
scaling_benches,
bench_latency_vs_distance,
bench_latency_vs_qubit_count,
bench_latency_vs_tile_count,
bench_throughput_vs_size,
bench_latency_vs_density,
bench_memory_pressure,
);
criterion_main!(scaling_benches);

View File

@@ -0,0 +1,251 @@
//! Benchmarks for syndrome processing performance.
//!
//! Run with: `cargo bench -p ruqu`
use criterion::{black_box, criterion_group, criterion_main, Criterion, Throughput};
use ruqu::syndrome::{DetectorBitmap, SyndromeBuffer, SyndromeDelta, SyndromeRound};
/// Benchmark DetectorBitmap operations
fn bench_bitmap_operations(c: &mut Criterion) {
let mut group = c.benchmark_group("DetectorBitmap");
// Benchmark set operation
group.throughput(Throughput::Elements(1024));
group.bench_function("set_all_1024", |b| {
let mut bitmap = DetectorBitmap::new(1024);
b.iter(|| {
for i in 0..1024 {
bitmap.set(i, true);
}
black_box(&bitmap);
});
});
// Benchmark get operation
group.bench_function("get_all_1024", |b| {
let mut bitmap = DetectorBitmap::new(1024);
for i in (0..1024).step_by(3) {
bitmap.set(i, true);
}
b.iter(|| {
let mut count = 0usize;
for i in 0..1024 {
if bitmap.get(i) {
count += 1;
}
}
black_box(count);
});
});
// Benchmark popcount
group.bench_function("popcount_sparse", |b| {
let mut bitmap = DetectorBitmap::new(1024);
for i in (0..1024).step_by(100) {
bitmap.set(i, true);
}
b.iter(|| black_box(bitmap.popcount()));
});
group.bench_function("popcount_dense", |b| {
let mut bitmap = DetectorBitmap::new(1024);
for i in 0..512 {
bitmap.set(i, true);
}
b.iter(|| black_box(bitmap.popcount()));
});
// Benchmark XOR
group.bench_function("xor_1024", |b| {
let mut a = DetectorBitmap::new(1024);
let mut bb = DetectorBitmap::new(1024);
for i in (0..512).step_by(2) {
a.set(i, true);
}
for i in (256..768).step_by(2) {
bb.set(i, true);
}
b.iter(|| black_box(a.xor(&bb)));
});
// Benchmark iter_fired
group.bench_function("iter_fired_sparse", |b| {
let mut bitmap = DetectorBitmap::new(1024);
for i in (0..1024).step_by(100) {
bitmap.set(i, true);
}
b.iter(|| {
let count: usize = bitmap.iter_fired().count();
black_box(count);
});
});
group.bench_function("iter_fired_dense", |b| {
let mut bitmap = DetectorBitmap::new(1024);
for i in 0..100 {
bitmap.set(i, true);
}
b.iter(|| {
let count: usize = bitmap.iter_fired().count();
black_box(count);
});
});
group.finish();
}
/// Benchmark SyndromeBuffer operations
fn bench_buffer_operations(c: &mut Criterion) {
let mut group = c.benchmark_group("SyndromeBuffer");
// Benchmark push (main hot path)
group.throughput(Throughput::Elements(1));
group.bench_function("push", |b| {
let mut buffer = SyndromeBuffer::new(1024);
let mut round_id = 0u64;
b.iter(|| {
let mut detectors = DetectorBitmap::new(64);
detectors.set((round_id % 64) as usize, true);
let round = SyndromeRound::new(round_id, round_id, round_id * 1000, detectors, 0);
buffer.push(round);
round_id = round_id.wrapping_add(1);
black_box(&buffer);
});
});
// Benchmark window extraction
group.bench_function("window_10", |b| {
let mut buffer = SyndromeBuffer::new(1024);
for i in 0..1000 {
let round = SyndromeRound::new(i, i, i * 1000, DetectorBitmap::new(64), 0);
buffer.push(round);
}
b.iter(|| black_box(buffer.window(10)));
});
group.bench_function("window_100", |b| {
let mut buffer = SyndromeBuffer::new(1024);
for i in 0..1000 {
let round = SyndromeRound::new(i, i, i * 1000, DetectorBitmap::new(64), 0);
buffer.push(round);
}
b.iter(|| black_box(buffer.window(100)));
});
// Benchmark get by round_id
group.bench_function("get_recent", |b| {
let mut buffer = SyndromeBuffer::new(1024);
for i in 0..1000 {
let round = SyndromeRound::new(i, i, i * 1000, DetectorBitmap::new(64), 0);
buffer.push(round);
}
b.iter(|| black_box(buffer.get(995)));
});
group.bench_function("get_old", |b| {
let mut buffer = SyndromeBuffer::new(1024);
for i in 0..1000 {
let round = SyndromeRound::new(i, i, i * 1000, DetectorBitmap::new(64), 0);
buffer.push(round);
}
b.iter(|| black_box(buffer.get(100)));
});
group.finish();
}
/// Benchmark SyndromeDelta computation
fn bench_delta_operations(c: &mut Criterion) {
let mut group = c.benchmark_group("SyndromeDelta");
// Create test rounds
let mut d1 = DetectorBitmap::new(1024);
let mut d2 = DetectorBitmap::new(1024);
for i in (0..512).step_by(2) {
d1.set(i, true);
}
for i in (256..768).step_by(2) {
d2.set(i, true);
}
let round1 = SyndromeRound::new(1, 100, 1000, d1, 0);
let round2 = SyndromeRound::new(2, 101, 2000, d2, 0);
// Benchmark delta computation
group.bench_function("compute", |b| {
b.iter(|| black_box(SyndromeDelta::compute(&round1, &round2)));
});
// Benchmark activity level
let delta = SyndromeDelta::compute(&round1, &round2);
group.bench_function("activity_level", |b| {
b.iter(|| black_box(delta.activity_level()));
});
// Benchmark is_quiet
group.bench_function("is_quiet", |b| {
b.iter(|| black_box(delta.is_quiet()));
});
group.finish();
}
/// Benchmark full pipeline throughput
fn bench_pipeline_throughput(c: &mut Criterion) {
let mut group = c.benchmark_group("Pipeline");
group.throughput(Throughput::Elements(1000));
group.bench_function("ingest_1000_rounds", |b| {
b.iter(|| {
let mut buffer = SyndromeBuffer::new(1024);
for i in 0..1000u64 {
let mut detectors = DetectorBitmap::new(64);
// Simulate sparse detector firings
if i % 10 == 0 {
detectors.set((i % 64) as usize, true);
}
let round = SyndromeRound::new(i, i, i * 1000, detectors, 0);
buffer.push(round);
}
black_box(&buffer);
});
});
group.bench_function("ingest_and_delta_1000", |b| {
b.iter(|| {
let mut buffer = SyndromeBuffer::new(1024);
let mut prev_round: Option<SyndromeRound> = None;
let mut delta_count = 0usize;
for i in 0..1000u64 {
let mut detectors = DetectorBitmap::new(64);
if i % 10 == 0 {
detectors.set((i % 64) as usize, true);
}
let round = SyndromeRound::new(i, i, i * 1000, detectors, 0);
if let Some(prev) = &prev_round {
let delta = SyndromeDelta::compute(prev, &round);
if !delta.is_quiet() {
delta_count += 1;
}
}
prev_round = Some(round.clone());
buffer.push(round);
}
black_box(delta_count);
});
});
group.finish();
}
criterion_group!(
benches,
bench_bitmap_operations,
bench_buffer_operations,
bench_delta_operations,
bench_pipeline_throughput,
);
criterion_main!(benches);

View File

@@ -0,0 +1,703 @@
//! Throughput benchmarks for ruQu Coherence Gate.
//!
//! Performance Targets:
//! - Syndrome ingestion rate: **1M rounds/sec**
//! - Gate decisions per second: **250K decisions/sec**
//! - Permit token generation rate: **100K tokens/sec**
//!
//! Run with: `cargo bench -p ruqu --bench throughput_bench`
use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion, Throughput};
use ruqu::filters::{FilterConfig, FilterPipeline, SystemState};
use ruqu::syndrome::{DetectorBitmap, SyndromeBuffer, SyndromeDelta, SyndromeRound};
use ruqu::tile::{
GateDecision, GateThresholds, PatchGraph, PermitToken, ReceiptLog,
SyndromeDelta as TileSyndromeDelta, TileReport, TileZero, WorkerTile,
};
// ============================================================================
// HELPER FUNCTIONS
// ============================================================================
/// Create a syndrome round with specified firing pattern
fn create_syndrome_round(round_id: u64, detector_count: usize, firing_rate: f64) -> SyndromeRound {
let mut detectors = DetectorBitmap::new(detector_count);
let num_fired = ((detector_count as f64) * firing_rate) as usize;
for i in 0..num_fired {
detectors.set(i * (detector_count / num_fired.max(1)), true);
}
SyndromeRound::new(round_id, round_id, round_id * 1000, detectors, 0)
}
/// Create a worker tile with pre-populated graph
fn create_worker_tile(tile_id: u8, num_vertices: u16, num_edges: u16) -> WorkerTile {
let mut tile = WorkerTile::new(tile_id);
for i in 0..num_vertices.min(255) {
tile.patch_graph.ensure_vertex(i);
}
let mut edges_added = 0u16;
'outer: for i in 0..num_vertices.saturating_sub(1) {
for j in (i + 1)..num_vertices.min(i + 4) {
if edges_added >= num_edges {
break 'outer;
}
if tile.patch_graph.add_edge(i, j, 1000).is_some() {
edges_added += 1;
}
}
}
tile.patch_graph.recompute_components();
tile
}
// ============================================================================
// SYNDROME INGESTION THROUGHPUT
// ============================================================================
/// Benchmark syndrome ingestion rate (target: 1M rounds/sec)
fn bench_syndrome_ingestion(c: &mut Criterion) {
let mut group = c.benchmark_group("syndrome_ingestion");
// Single round ingestion
group.throughput(Throughput::Elements(1));
group.bench_function("single_round", |b| {
let mut buffer = SyndromeBuffer::new(4096);
let mut round_id = 0u64;
b.iter(|| {
let round = create_syndrome_round(round_id, 64, 0.1);
buffer.push(round);
round_id += 1;
black_box(&buffer);
});
});
// Batch ingestion (1000 rounds)
group.throughput(Throughput::Elements(1000));
group.bench_function("batch_1000_rounds", |b| {
let mut buffer = SyndromeBuffer::new(4096);
let mut round_id = 0u64;
b.iter(|| {
for _ in 0..1000 {
let round = create_syndrome_round(round_id, 64, 0.1);
buffer.push(round);
round_id += 1;
}
black_box(&buffer);
});
});
// Large batch ingestion (10000 rounds)
group.throughput(Throughput::Elements(10_000));
group.bench_function("batch_10000_rounds", |b| {
let mut buffer = SyndromeBuffer::new(16384);
let mut round_id = 0u64;
b.iter(|| {
for _ in 0..10_000 {
let round = create_syndrome_round(round_id, 64, 0.1);
buffer.push(round);
round_id += 1;
}
black_box(&buffer);
});
});
// Varying detector counts
for detector_count in [64, 256, 512, 1024].iter() {
group.throughput(Throughput::Elements(1000));
group.bench_with_input(
BenchmarkId::new("batch_1000_detectors", detector_count),
detector_count,
|b, &count| {
let mut buffer = SyndromeBuffer::new(4096);
let mut round_id = 0u64;
b.iter(|| {
for _ in 0..1000 {
let round = create_syndrome_round(round_id, count, 0.1);
buffer.push(round);
round_id += 1;
}
black_box(&buffer);
});
},
);
}
// Varying firing rates
for firing_rate in [0.01, 0.05, 0.1, 0.25].iter() {
group.throughput(Throughput::Elements(1000));
group.bench_with_input(
BenchmarkId::new(
"batch_1000_firing_rate",
format!("{:.0}pct", firing_rate * 100.0),
),
firing_rate,
|b, &rate| {
let mut buffer = SyndromeBuffer::new(4096);
let mut round_id = 0u64;
b.iter(|| {
for _ in 0..1000 {
let round = create_syndrome_round(round_id, 256, rate);
buffer.push(round);
round_id += 1;
}
black_box(&buffer);
});
},
);
}
group.finish();
}
// ============================================================================
// GATE DECISION THROUGHPUT
// ============================================================================
/// Benchmark gate decisions per second
fn bench_gate_decision_throughput(c: &mut Criterion) {
let mut group = c.benchmark_group("gate_decisions");
// Single decision
group.throughput(Throughput::Elements(1));
group.bench_function("single_decision", |b| {
let mut tile = create_worker_tile(1, 64, 128);
let thresholds = GateThresholds::default();
let mut tilezero = TileZero::new(thresholds);
b.iter(|| {
let delta = TileSyndromeDelta::new(0, 1, 100);
let report = tile.tick(&delta);
let reports = vec![report; 10];
let decision = tilezero.merge_reports(reports);
black_box(decision)
});
});
// Batch decisions (100)
group.throughput(Throughput::Elements(100));
group.bench_function("batch_100_decisions", |b| {
let mut tile = create_worker_tile(1, 64, 128);
let thresholds = GateThresholds::default();
let mut tilezero = TileZero::new(thresholds);
b.iter(|| {
for i in 0..100 {
let delta = TileSyndromeDelta::new(0, 1, i as u16);
let report = tile.tick(&delta);
let reports = vec![report; 10];
let decision = tilezero.merge_reports(reports);
black_box(decision);
}
});
});
// Batch decisions (1000)
group.throughput(Throughput::Elements(1000));
group.bench_function("batch_1000_decisions", |b| {
let mut tile = create_worker_tile(1, 64, 128);
let thresholds = GateThresholds::default();
let mut tilezero = TileZero::new(thresholds);
b.iter(|| {
for i in 0..1000 {
let delta = TileSyndromeDelta::new(0, 1, (i % 256) as u16);
let report = tile.tick(&delta);
let reports = vec![report; 10];
let decision = tilezero.merge_reports(reports);
black_box(decision);
}
});
});
// Decisions with varying tile counts
for tile_count in [10, 50, 100, 255].iter() {
group.throughput(Throughput::Elements(100));
group.bench_with_input(
BenchmarkId::new("batch_100_tile_count", tile_count),
tile_count,
|b, &count| {
let mut tile = create_worker_tile(1, 64, 128);
let thresholds = GateThresholds::default();
let mut tilezero = TileZero::new(thresholds);
let base_reports: Vec<TileReport> = (1..=count)
.map(|i| {
let mut report = TileReport::new(i as u8);
report.local_cut = 10.0;
report.shift_score = 0.1;
report.e_value = 200.0;
report
})
.collect();
b.iter(|| {
for _ in 0..100 {
let delta = TileSyndromeDelta::new(0, 1, 100);
let _ = tile.tick(&delta);
let decision = tilezero.merge_reports(base_reports.clone());
black_box(decision);
}
});
},
);
}
group.finish();
}
// ============================================================================
// PERMIT TOKEN GENERATION THROUGHPUT
// ============================================================================
/// Benchmark permit token generation rate
fn bench_permit_token_throughput(c: &mut Criterion) {
let mut group = c.benchmark_group("permit_tokens");
// Single token
group.throughput(Throughput::Elements(1));
group.bench_function("single_token", |b| {
let thresholds = GateThresholds::default();
let tilezero = TileZero::new(thresholds);
let decision = GateDecision::Permit;
b.iter(|| {
let token = tilezero.issue_permit(&decision);
black_box(token)
});
});
// Batch tokens (1000)
group.throughput(Throughput::Elements(1000));
group.bench_function("batch_1000_tokens", |b| {
let thresholds = GateThresholds::default();
let tilezero = TileZero::new(thresholds);
let decision = GateDecision::Permit;
b.iter(|| {
for _ in 0..1000 {
let token = tilezero.issue_permit(&decision);
black_box(&token);
}
});
});
// Token validation throughput
group.throughput(Throughput::Elements(1000));
group.bench_function("validate_1000_tokens", |b| {
let thresholds = GateThresholds::default();
let tilezero = TileZero::new(thresholds);
let token = tilezero.issue_permit(&GateDecision::Permit);
let now_ns = token.timestamp + 1000;
b.iter(|| {
for _ in 0..1000 {
let valid = token.is_valid(now_ns);
black_box(valid);
}
});
});
group.finish();
}
// ============================================================================
// RECEIPT LOG THROUGHPUT
// ============================================================================
/// Benchmark receipt log operations
fn bench_receipt_log_throughput(c: &mut Criterion) {
let mut group = c.benchmark_group("receipt_log");
// Append throughput
group.throughput(Throughput::Elements(1000));
group.bench_function("append_1000", |b| {
let mut log = ReceiptLog::new();
let witness_hash = [0u8; 32];
b.iter(|| {
for i in 0..1000 {
log.append(GateDecision::Permit, i, i * 1000, witness_hash);
}
black_box(&log);
});
});
// Lookup throughput
group.throughput(Throughput::Elements(1000));
group.bench_function("lookup_1000", |b| {
let mut log = ReceiptLog::new();
let witness_hash = [0u8; 32];
for i in 0..10000 {
log.append(GateDecision::Permit, i, i * 1000, witness_hash);
}
b.iter(|| {
for i in 0..1000 {
let entry = log.get(i * 10);
black_box(entry);
}
});
});
group.finish();
}
// ============================================================================
// WORKER TILE THROUGHPUT
// ============================================================================
/// Benchmark worker tile tick throughput
fn bench_worker_tile_throughput(c: &mut Criterion) {
let mut group = c.benchmark_group("worker_tile");
// Single tick
group.throughput(Throughput::Elements(1));
group.bench_function("single_tick", |b| {
let mut tile = create_worker_tile(1, 64, 128);
b.iter(|| {
let delta = TileSyndromeDelta::new(0, 1, 100);
let report = tile.tick(&delta);
black_box(report)
});
});
// Batch ticks (1000)
group.throughput(Throughput::Elements(1000));
group.bench_function("batch_1000_ticks", |b| {
let mut tile = create_worker_tile(1, 64, 128);
b.iter(|| {
for i in 0..1000 {
let delta = TileSyndromeDelta::new(0, 1, (i % 256) as u16);
let report = tile.tick(&delta);
black_box(&report);
}
});
});
// Sustained throughput (10000 ticks)
group.throughput(Throughput::Elements(10_000));
group.bench_function("sustained_10000_ticks", |b| {
let mut tile = create_worker_tile(1, 64, 128);
b.iter(|| {
for i in 0..10_000 {
let delta = TileSyndromeDelta::new(0, 1, (i % 256) as u16);
let report = tile.tick(&delta);
black_box(&report);
}
});
});
// Varying graph sizes
for (vertices, edges) in [(32, 64), (64, 128), (128, 256), (200, 400)].iter() {
group.throughput(Throughput::Elements(1000));
group.bench_with_input(
BenchmarkId::new("batch_1000_graph", format!("v{}e{}", vertices, edges)),
&(*vertices, *edges),
|b, &(v, e)| {
let mut tile = create_worker_tile(1, v, e);
b.iter(|| {
for i in 0..1000 {
let delta = TileSyndromeDelta::new(0, 1, (i % 256) as u16);
let report = tile.tick(&delta);
black_box(&report);
}
});
},
);
}
group.finish();
}
// ============================================================================
// FILTER PIPELINE THROUGHPUT
// ============================================================================
/// Benchmark filter pipeline throughput
fn bench_filter_pipeline_throughput(c: &mut Criterion) {
let mut group = c.benchmark_group("filter_pipeline");
// Create a pre-warmed pipeline
let create_pipeline = || {
let config = FilterConfig::default();
let mut pipeline = FilterPipeline::new(config);
for i in 0..50u64 {
let _ = pipeline.structural_mut().insert_edge(i, i + 1, 1.0);
}
pipeline.structural_mut().build();
for region in 0..10 {
for _ in 0..50 {
pipeline.shift_mut().update(region, 0.5);
}
}
for _ in 0..20 {
pipeline.evidence_mut().update(1.5);
}
pipeline
};
// Single evaluation
group.throughput(Throughput::Elements(1));
group.bench_function("single_evaluation", |b| {
let pipeline = create_pipeline();
let state = SystemState::new(100);
b.iter(|| {
let result = pipeline.evaluate(&state);
black_box(result)
});
});
// Batch evaluations (1000)
group.throughput(Throughput::Elements(1000));
group.bench_function("batch_1000_evaluations", |b| {
let pipeline = create_pipeline();
let state = SystemState::new(100);
b.iter(|| {
for _ in 0..1000 {
let result = pipeline.evaluate(&state);
black_box(&result);
}
});
});
group.finish();
}
// ============================================================================
// SYNDROME DELTA COMPUTATION THROUGHPUT
// ============================================================================
/// Benchmark syndrome delta computation throughput
fn bench_syndrome_delta_throughput(c: &mut Criterion) {
let mut group = c.benchmark_group("syndrome_delta");
// Create test rounds
let create_rounds = |count: usize| -> Vec<SyndromeRound> {
(0..count)
.map(|i| create_syndrome_round(i as u64, 256, 0.1))
.collect()
};
// Single delta computation
group.throughput(Throughput::Elements(1));
group.bench_function("single_delta", |b| {
let round1 = create_syndrome_round(0, 256, 0.1);
let round2 = create_syndrome_round(1, 256, 0.1);
b.iter(|| {
let delta = SyndromeDelta::compute(&round1, &round2);
black_box(delta)
});
});
// Batch delta computation (1000)
group.throughput(Throughput::Elements(999));
group.bench_function("batch_1000_deltas", |b| {
let rounds = create_rounds(1000);
b.iter(|| {
for i in 0..999 {
let delta = SyndromeDelta::compute(&rounds[i], &rounds[i + 1]);
black_box(&delta);
}
});
});
// Varying detector counts
for detector_count in [64, 256, 512, 1024].iter() {
group.throughput(Throughput::Elements(999));
group.bench_with_input(
BenchmarkId::new("batch_1000_detectors", detector_count),
detector_count,
|b, &count| {
let rounds: Vec<SyndromeRound> = (0..1000)
.map(|i| create_syndrome_round(i as u64, count, 0.1))
.collect();
b.iter(|| {
for i in 0..999 {
let delta = SyndromeDelta::compute(&rounds[i], &rounds[i + 1]);
black_box(&delta);
}
});
},
);
}
group.finish();
}
// ============================================================================
// PATCH GRAPH THROUGHPUT
// ============================================================================
/// Benchmark patch graph operation throughput
fn bench_patch_graph_throughput(c: &mut Criterion) {
let mut group = c.benchmark_group("patch_graph_throughput");
// Edge insertion throughput
group.throughput(Throughput::Elements(1000));
group.bench_function("insert_1000_edges", |b| {
b.iter_batched(
PatchGraph::new,
|mut graph| {
for i in 0..1000u16 {
let v1 = i % 256;
let v2 = (i + 1) % 256;
if v1 != v2 {
let _ = graph.add_edge(v1, v2, 1000);
}
}
black_box(graph.num_edges)
},
criterion::BatchSize::SmallInput,
);
});
// Delta application throughput
group.throughput(Throughput::Elements(1000));
group.bench_function("apply_1000_deltas", |b| {
b.iter_batched(
|| {
let mut graph = PatchGraph::new();
for i in 0..100u16 {
let _ = graph.add_edge(i, (i + 1) % 100, 1000);
}
graph
},
|mut graph| {
for i in 0..1000u16 {
let delta = TileSyndromeDelta::new(i % 100, (i + 1) % 100, 100);
graph.apply_delta(&delta);
}
black_box(graph.num_edges)
},
criterion::BatchSize::SmallInput,
);
});
// Component recomputation throughput
group.throughput(Throughput::Elements(100));
group.bench_function("recompute_100_times", |b| {
b.iter_batched(
|| {
let mut graph = PatchGraph::new();
for i in 0..200u16 {
let _ = graph.add_edge(i, (i + 1) % 200, 1000);
}
graph
},
|mut graph| {
let mut count = 0u16;
for _ in 0..100 {
graph.status |= PatchGraph::STATUS_DIRTY;
count = graph.recompute_components();
}
black_box(count)
},
criterion::BatchSize::SmallInput,
);
});
group.finish();
}
// ============================================================================
// DETECTOR BITMAP THROUGHPUT
// ============================================================================
/// Benchmark detector bitmap throughput
fn bench_bitmap_throughput(c: &mut Criterion) {
let mut group = c.benchmark_group("bitmap_throughput");
// XOR throughput
group.throughput(Throughput::Elements(1000));
group.bench_function("xor_1000", |b| {
let mut a = DetectorBitmap::new(1024);
let mut bb = DetectorBitmap::new(1024);
for i in (0..512).step_by(2) {
a.set(i, true);
}
for i in (256..768).step_by(2) {
bb.set(i, true);
}
b.iter(|| {
for _ in 0..1000 {
let result = a.xor(&bb);
black_box(&result);
}
});
});
// Popcount throughput
group.throughput(Throughput::Elements(1000));
group.bench_function("popcount_1000", |b| {
let mut bitmap = DetectorBitmap::new(1024);
for i in (0..512).step_by(2) {
bitmap.set(i, true);
}
b.iter(|| {
let mut total = 0usize;
for _ in 0..1000 {
total += bitmap.popcount();
}
black_box(total)
});
});
// Iterator throughput
group.throughput(Throughput::Elements(1000));
group.bench_function("iter_fired_1000", |b| {
let mut bitmap = DetectorBitmap::new(1024);
for i in 0..100 {
bitmap.set(i * 10, true);
}
b.iter(|| {
let mut total = 0usize;
for _ in 0..1000 {
total += bitmap.iter_fired().count();
}
black_box(total)
});
});
group.finish();
}
// ============================================================================
// CRITERION GROUPS
// ============================================================================
criterion_group!(
throughput_benches,
bench_syndrome_ingestion,
bench_gate_decision_throughput,
bench_permit_token_throughput,
bench_receipt_log_throughput,
bench_worker_tile_throughput,
bench_filter_pipeline_throughput,
bench_syndrome_delta_throughput,
bench_patch_graph_throughput,
bench_bitmap_throughput,
);
criterion_main!(throughput_benches);