|
1 | | -// SPDX-FileCopyrightText: 2025 ECHIDNA Project Team |
| 1 | +// SPDX-FileCopyrightText: 2025-2026 Jonathan D.A. Jewell <j.d.a.jewell@open.ac.uk> |
2 | 2 | // SPDX-License-Identifier: PMPL-1.0-or-later |
3 | 3 |
|
4 | 4 | //! Performance benchmarks for ECHIDNA proof operations |
| 5 | +//! |
| 6 | +//! Run with: cargo bench |
| 7 | +//! Generates HTML reports in target/criterion/ |
5 | 8 |
|
6 | | -use criterion::{black_box, criterion_group, criterion_main, Criterion}; |
| 9 | +use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion}; |
7 | 10 |
|
8 | | -/// Benchmark simple proof search operations |
9 | | -fn bench_simple_arithmetic(c: &mut Criterion) { |
10 | | - c.bench_function("simple_goal_complexity", |b| { |
11 | | - b.iter(|| { |
12 | | - let goal = black_box("forall n : nat, n + 0 = n"); |
13 | | - // Simulate complexity calculation |
14 | | - goal.matches("forall").count() * 10 + goal.len() / 10 |
15 | | - }) |
16 | | - }); |
| 11 | +use echidna::core::{Context, Goal, Hypothesis, ProofState, Term}; |
| 12 | +use echidna::provers::{ProverConfig, ProverFactory, ProverKind}; |
| 13 | +use echidna::verification::axiom_tracker::AxiomTracker; |
| 14 | +use echidna::verification::confidence::{compute_trust_level, TrustFactors, TrustLevel}; |
| 15 | +use echidna::verification::mutation::MutationTester; |
| 16 | +use echidna::verification::pareto::{ParetoFrontier, ProofCandidate, ProofObjective}; |
| 17 | +use echidna::verification::statistics::StatisticsTracker; |
| 18 | +use echidna::verification::DangerLevel; |
| 19 | +use echidna::verification::portfolio::PortfolioConfidence; |
| 20 | + |
| 21 | +use std::collections::HashMap; |
| 22 | + |
| 23 | +// ============================================================================ |
| 24 | +// Core Type Benchmarks |
| 25 | +// ============================================================================ |
| 26 | + |
| 27 | +/// Benchmark ProofState construction with varying goal counts |
| 28 | +fn bench_proof_state_construction(c: &mut Criterion) { |
| 29 | + let mut group = c.benchmark_group("proof_state_construction"); |
| 30 | + for goal_count in [1, 5, 10, 50, 100] { |
| 31 | + group.bench_with_input( |
| 32 | + BenchmarkId::from_parameter(goal_count), |
| 33 | + &goal_count, |
| 34 | + |b, &n| { |
| 35 | + b.iter(|| { |
| 36 | + let goals: Vec<Goal> = (0..n) |
| 37 | + .map(|i| Goal { |
| 38 | + id: format!("goal_{i}"), |
| 39 | + hypotheses: vec![Hypothesis { |
| 40 | + name: format!("h_{i}"), |
| 41 | + ty: Term::Const("Prop".to_string()), |
| 42 | + body: None, |
| 43 | + }], |
| 44 | + target: Term::App { |
| 45 | + func: Box::new(Term::Const("eq".to_string())), |
| 46 | + args: vec![ |
| 47 | + Term::Var(format!("x_{i}")), |
| 48 | + Term::Var(format!("x_{i}")), |
| 49 | + ], |
| 50 | + }, |
| 51 | + }) |
| 52 | + .collect(); |
| 53 | + black_box(ProofState { |
| 54 | + goals, |
| 55 | + context: Context::default(), |
| 56 | + proof_script: vec![], |
| 57 | + metadata: HashMap::new(), |
| 58 | + }) |
| 59 | + }) |
| 60 | + }, |
| 61 | + ); |
| 62 | + } |
| 63 | + group.finish(); |
| 64 | +} |
| 65 | + |
| 66 | +/// Benchmark Term AST construction (deeply nested) |
| 67 | +fn bench_term_construction(c: &mut Criterion) { |
| 68 | + let mut group = c.benchmark_group("term_construction"); |
| 69 | + for depth in [1, 5, 10, 20] { |
| 70 | + group.bench_with_input(BenchmarkId::from_parameter(depth), &depth, |b, &d| { |
| 71 | + b.iter(|| { |
| 72 | + let mut term = Term::Const("base".to_string()); |
| 73 | + for i in 0..d { |
| 74 | + term = Term::App { |
| 75 | + func: Box::new(Term::Const(format!("f_{i}"))), |
| 76 | + args: vec![term], |
| 77 | + }; |
| 78 | + } |
| 79 | + black_box(term) |
| 80 | + }) |
| 81 | + }); |
| 82 | + } |
| 83 | + group.finish(); |
| 84 | +} |
| 85 | + |
| 86 | +// ============================================================================ |
| 87 | +// Prover Factory Benchmarks |
| 88 | +// ============================================================================ |
| 89 | + |
| 90 | +/// Benchmark prover creation for representative backends |
| 91 | +fn bench_prover_creation(c: &mut Criterion) { |
| 92 | + let provers = [ |
| 93 | + ("Agda", ProverKind::Agda), |
| 94 | + ("Coq", ProverKind::Coq), |
| 95 | + ("Lean", ProverKind::Lean), |
| 96 | + ("Z3", ProverKind::Z3), |
| 97 | + ("CVC5", ProverKind::CVC5), |
| 98 | + ("Idris2", ProverKind::Idris2), |
| 99 | + ("Vampire", ProverKind::Vampire), |
| 100 | + ("Metamath", ProverKind::Metamath), |
| 101 | + ]; |
| 102 | + |
| 103 | + let mut group = c.benchmark_group("prover_creation"); |
| 104 | + for (name, kind) in &provers { |
| 105 | + group.bench_function(*name, |b| { |
| 106 | + b.iter(|| { |
| 107 | + let config = ProverConfig::default(); |
| 108 | + black_box(ProverFactory::create(*kind, config).unwrap()) |
| 109 | + }) |
| 110 | + }); |
| 111 | + } |
| 112 | + group.finish(); |
| 113 | +} |
| 114 | + |
| 115 | +/// Benchmark prover kind detection from file extension |
| 116 | +fn bench_prover_detection(c: &mut Criterion) { |
| 117 | + let files = [ |
| 118 | + "test.agda", |
| 119 | + "test.v", |
| 120 | + "test.lean", |
| 121 | + "test.smt2", |
| 122 | + "test.thy", |
| 123 | + "test.idr", |
| 124 | + "test.p", |
| 125 | + "test.mm", |
| 126 | + ]; |
| 127 | + |
| 128 | + let mut group = c.benchmark_group("prover_detection"); |
| 129 | + for file in &files { |
| 130 | + group.bench_function(*file, |b| { |
| 131 | + let path = std::path::Path::new(file); |
| 132 | + b.iter(|| black_box(ProverFactory::detect_from_file(path))) |
| 133 | + }); |
| 134 | + } |
| 135 | + group.finish(); |
| 136 | +} |
| 137 | + |
| 138 | +// ============================================================================ |
| 139 | +// Trust Pipeline Benchmarks |
| 140 | +// ============================================================================ |
| 141 | + |
| 142 | +/// Benchmark trust level computation with varying scenarios |
| 143 | +fn bench_trust_computation(c: &mut Criterion) { |
| 144 | + let scenarios = [ |
| 145 | + ( |
| 146 | + "max_trust", |
| 147 | + TrustFactors { |
| 148 | + prover: ProverKind::Lean, |
| 149 | + confirming_provers: 3, |
| 150 | + has_certificate: true, |
| 151 | + certificate_verified: true, |
| 152 | + worst_axiom_danger: DangerLevel::Safe, |
| 153 | + solver_integrity_ok: true, |
| 154 | + portfolio_confidence: Some(PortfolioConfidence::CrossChecked), |
| 155 | + }, |
| 156 | + ), |
| 157 | + ( |
| 158 | + "single_prover", |
| 159 | + TrustFactors { |
| 160 | + prover: ProverKind::Z3, |
| 161 | + confirming_provers: 1, |
| 162 | + has_certificate: false, |
| 163 | + certificate_verified: false, |
| 164 | + worst_axiom_danger: DangerLevel::Safe, |
| 165 | + solver_integrity_ok: true, |
| 166 | + portfolio_confidence: None, |
| 167 | + }, |
| 168 | + ), |
| 169 | + ( |
| 170 | + "dangerous", |
| 171 | + TrustFactors { |
| 172 | + prover: ProverKind::Lean, |
| 173 | + confirming_provers: 2, |
| 174 | + has_certificate: true, |
| 175 | + certificate_verified: true, |
| 176 | + worst_axiom_danger: DangerLevel::Reject, |
| 177 | + solver_integrity_ok: true, |
| 178 | + portfolio_confidence: Some(PortfolioConfidence::CrossChecked), |
| 179 | + }, |
| 180 | + ), |
| 181 | + ]; |
| 182 | + |
| 183 | + let mut group = c.benchmark_group("trust_computation"); |
| 184 | + for (name, factors) in &scenarios { |
| 185 | + group.bench_function(*name, |b| { |
| 186 | + b.iter(|| black_box(compute_trust_level(factors))) |
| 187 | + }); |
| 188 | + } |
| 189 | + group.finish(); |
| 190 | +} |
| 191 | + |
| 192 | +/// Benchmark axiom danger scanning |
| 193 | +fn bench_axiom_scanning(c: &mut Criterion) { |
| 194 | + let test_contents = [ |
| 195 | + ("clean_lean", ProverKind::Lean, "theorem foo : True := trivial"), |
| 196 | + ( |
| 197 | + "sorry_lean", |
| 198 | + ProverKind::Lean, |
| 199 | + "theorem foo : True := by sorry", |
| 200 | + ), |
| 201 | + ( |
| 202 | + "admitted_coq", |
| 203 | + ProverKind::Coq, |
| 204 | + "Theorem foo : True. Proof. Admitted.", |
| 205 | + ), |
| 206 | + ( |
| 207 | + "believe_me_idris", |
| 208 | + ProverKind::Idris2, |
| 209 | + "foo : Bool\nfoo = believe_me True", |
| 210 | + ), |
| 211 | + ]; |
| 212 | + |
| 213 | + let mut group = c.benchmark_group("axiom_scanning"); |
| 214 | + for (name, prover, content) in &test_contents { |
| 215 | + group.bench_function(*name, |b| { |
| 216 | + let tracker = AxiomTracker::new(); |
| 217 | + b.iter(|| black_box(tracker.scan(*prover, content))) |
| 218 | + }); |
| 219 | + } |
| 220 | + group.finish(); |
17 | 221 | } |
18 | 222 |
|
19 | | -/// Benchmark proof state construction |
20 | | -fn bench_proof_state(c: &mut Criterion) { |
21 | | - c.bench_function("proof_state_creation", |b| { |
| 223 | +// ============================================================================ |
| 224 | +// Verification Module Benchmarks |
| 225 | +// ============================================================================ |
| 226 | + |
| 227 | +/// Benchmark mutation test generation |
| 228 | +fn bench_mutation_generation(c: &mut Criterion) { |
| 229 | + c.bench_function("mutation_generation", |b| { |
| 230 | + let term = Term::App { |
| 231 | + func: Box::new(Term::Const("forall".to_string())), |
| 232 | + args: vec![Term::Lambda { |
| 233 | + param: "x".to_string(), |
| 234 | + param_type: Some(Box::new(Term::Universe(0))), |
| 235 | + body: Box::new(Term::App { |
| 236 | + func: Box::new(Term::Const("eq".to_string())), |
| 237 | + args: vec![ |
| 238 | + Term::App { |
| 239 | + func: Box::new(Term::Const("add".to_string())), |
| 240 | + args: vec![ |
| 241 | + Term::Var("x".to_string()), |
| 242 | + Term::Const("0".to_string()), |
| 243 | + ], |
| 244 | + }, |
| 245 | + Term::Var("x".to_string()), |
| 246 | + ], |
| 247 | + }), |
| 248 | + }], |
| 249 | + }; |
22 | 250 | b.iter(|| { |
23 | | - let hypotheses = ["H1: P".to_string(), "H2: Q".to_string()]; |
24 | | - let conclusion = "P /\\ Q"; |
25 | | - // Simulate proof state construction |
26 | | - black_box(hypotheses.len() + conclusion.len()) |
| 251 | + let tester = MutationTester::new(); |
| 252 | + black_box(tester.generate_mutations(&term)) |
27 | 253 | }) |
28 | 254 | }); |
29 | 255 | } |
30 | 256 |
|
31 | | -/// Benchmark tactic application simulation |
32 | | -fn bench_tactic_application(c: &mut Criterion) { |
33 | | - c.bench_function("tactic_selection", |b| { |
| 257 | +/// Benchmark Pareto frontier computation |
| 258 | +fn bench_pareto_frontier(c: &mut Criterion) { |
| 259 | + let mut group = c.benchmark_group("pareto_frontier"); |
| 260 | + for n_points in [10, 50, 100] { |
| 261 | + group.bench_with_input( |
| 262 | + BenchmarkId::from_parameter(n_points), |
| 263 | + &n_points, |
| 264 | + |b, &n| { |
| 265 | + b.iter(|| { |
| 266 | + let mut candidates: Vec<ProofCandidate> = (0..n) |
| 267 | + .map(|i| ProofCandidate { |
| 268 | + id: format!("candidate_{i}"), |
| 269 | + objectives: ProofObjective { |
| 270 | + proof_time_ms: (i as u64) * 100, |
| 271 | + trust_level: TrustLevel::Level3, |
| 272 | + memory_bytes: (n as u64 - i as u64) * 1024, |
| 273 | + proof_steps: i * 5, |
| 274 | + }, |
| 275 | + is_pareto_optimal: false, |
| 276 | + }) |
| 277 | + .collect(); |
| 278 | + black_box(ParetoFrontier::compute(&mut candidates)) |
| 279 | + }) |
| 280 | + }, |
| 281 | + ); |
| 282 | + } |
| 283 | + group.finish(); |
| 284 | +} |
| 285 | + |
| 286 | +/// Benchmark statistics tracker updates |
| 287 | +fn bench_statistics_tracking(c: &mut Criterion) { |
| 288 | + c.bench_function("statistics_update_100", |b| { |
34 | 289 | b.iter(|| { |
35 | | - let goal = black_box("forall n : nat, n + 0 = n"); |
36 | | - // Simulate tactic selection heuristics |
37 | | - if goal.contains("forall") { |
38 | | - "intros" |
39 | | - } else if goal.contains("=") { |
40 | | - "reflexivity" |
41 | | - } else { |
42 | | - "auto" |
| 290 | + let mut tracker = StatisticsTracker::new(); |
| 291 | + for i in 0..100u64 { |
| 292 | + tracker.record_success(ProverKind::Z3, "arithmetic", i * 10); |
43 | 293 | } |
| 294 | + black_box(tracker.total_attempts()) |
44 | 295 | }) |
45 | 296 | }); |
46 | 297 | } |
47 | 298 |
|
48 | | -/// Benchmark ML confidence calculation |
49 | | -fn bench_ml_confidence(c: &mut Criterion) { |
50 | | - c.bench_function("confidence_calculation", |b| { |
| 299 | +// ============================================================================ |
| 300 | +// FFI Benchmarks |
| 301 | +// ============================================================================ |
| 302 | + |
| 303 | +/// Benchmark FFI prover kind mapping roundtrip |
| 304 | +fn bench_ffi_kind_mapping(c: &mut Criterion) { |
| 305 | + c.bench_function("ffi_kind_roundtrip_all_48", |b| { |
51 | 306 | b.iter(|| { |
52 | | - let goal_length = black_box(42); |
53 | | - let quantifiers = black_box(2); |
54 | | - // Simulate confidence score calculation |
55 | | - let complexity = quantifiers as f64 * 10.0 + goal_length as f64 / 10.0; |
56 | | - 1.0 / (1.0 + (-complexity / 100.0).exp()) |
| 307 | + for i in 0u8..49 { |
| 308 | + if let Some(k) = echidna::ffi::kind_from_u8(i) { |
| 309 | + black_box(echidna::ffi::kind_to_u8(k)); |
| 310 | + } |
| 311 | + } |
57 | 312 | }) |
58 | 313 | }); |
59 | 314 | } |
60 | 315 |
|
| 316 | +// ============================================================================ |
| 317 | +// Benchmark Groups |
| 318 | +// ============================================================================ |
| 319 | + |
| 320 | +criterion_group!( |
| 321 | + core_benches, |
| 322 | + bench_proof_state_construction, |
| 323 | + bench_term_construction, |
| 324 | +); |
| 325 | + |
| 326 | +criterion_group!( |
| 327 | + prover_benches, |
| 328 | + bench_prover_creation, |
| 329 | + bench_prover_detection, |
| 330 | +); |
| 331 | + |
| 332 | +criterion_group!( |
| 333 | + trust_benches, |
| 334 | + bench_trust_computation, |
| 335 | + bench_axiom_scanning, |
| 336 | +); |
| 337 | + |
61 | 338 | criterion_group!( |
62 | | - benches, |
63 | | - bench_simple_arithmetic, |
64 | | - bench_proof_state, |
65 | | - bench_tactic_application, |
66 | | - bench_ml_confidence |
| 339 | + verification_benches, |
| 340 | + bench_mutation_generation, |
| 341 | + bench_pareto_frontier, |
| 342 | + bench_statistics_tracking, |
| 343 | +); |
| 344 | + |
| 345 | +criterion_group!(ffi_benches, bench_ffi_kind_mapping,); |
| 346 | + |
| 347 | +criterion_main!( |
| 348 | + core_benches, |
| 349 | + prover_benches, |
| 350 | + trust_benches, |
| 351 | + verification_benches, |
| 352 | + ffi_benches, |
67 | 353 | ); |
68 | | -criterion_main!(benches); |
|
0 commit comments