use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion, Throughput};
use debtmap::analyzers::batch::{analyze_files_effect, validate_files};
use debtmap::config::{BatchAnalysisConfig, DebtmapConfig};
use debtmap::effects::run_validation;
use debtmap::{analyzers::get_analyzer, core::Language, run_effect};
use rayon::prelude::*;
use std::hint::black_box;
use std::path::PathBuf;
use tempfile::TempDir;
fn create_test_files(num_files: usize) -> (TempDir, Vec<PathBuf>) {
let temp_dir = TempDir::new().unwrap();
let mut paths = Vec::with_capacity(num_files);
for i in 0..num_files {
let file_path = temp_dir.path().join(format!("file_{}.rs", i));
let content = format!(
r#"
/// A function with moderate complexity for benchmarking.
pub fn calculate_{i}(x: i32, y: i32, z: i32) -> i32 {{
if x > 0 {{
if y > 0 {{
if z > 0 {{
x + y + z
}} else {{
x + y - z
}}
}} else {{
x - y + z.abs()
}}
}} else {{
(-x).saturating_add(y).saturating_sub(z)
}}
}}
/// Another function to increase file complexity.
pub fn process_{i}(data: &[i32]) -> Vec<i32> {{
data.iter()
.filter(|&&n| n > 0)
.map(|&n| n * 2)
.collect()
}}
/// A function with a loop for additional complexity metrics.
pub fn aggregate_{i}(values: &[i32]) -> i32 {{
let mut sum = 0;
for value in values {{
if *value > 0 {{
sum += value;
}} else {{
sum -= value;
}}
}}
sum
}}
#[cfg(test)]
mod tests {{
use super::*;
#[test]
fn test_calculate_{i}() {{
assert_eq!(calculate_{i}(1, 2, 3), 6);
}}
}}
"#,
i = i
);
std::fs::write(&file_path, content).unwrap();
paths.push(file_path);
}
(temp_dir, paths)
}
fn benchmark_batch_sequential(c: &mut Criterion) {
let mut group = c.benchmark_group("batch_analysis_sequential");
for &size in &[5, 10, 25, 50] {
let (_temp_dir, paths) = create_test_files(size);
group.throughput(Throughput::Elements(size as u64));
group.bench_with_input(BenchmarkId::from_parameter(size), &paths, |b, paths| {
b.iter(|| {
let results: Vec<_> = paths
.iter()
.map(|path| {
let content = std::fs::read_to_string(path).unwrap();
let language = Language::from_path(path);
let analyzer = get_analyzer(language);
let ast = analyzer.parse(&content, path.clone()).unwrap();
analyzer.analyze(&ast)
})
.collect();
black_box(results)
});
});
}
group.finish();
}
fn benchmark_batch_parallel(c: &mut Criterion) {
let mut group = c.benchmark_group("batch_analysis_parallel");
for &size in &[5, 10, 25, 50] {
let (_temp_dir, paths) = create_test_files(size);
group.throughput(Throughput::Elements(size as u64));
group.bench_with_input(BenchmarkId::from_parameter(size), &paths, |b, paths| {
b.iter(|| {
let results: Vec<_> = paths
.par_iter()
.map(|path| {
let content = std::fs::read_to_string(path).unwrap();
let language = Language::from_path(path);
let analyzer = get_analyzer(language);
let ast = analyzer.parse(&content, path.clone()).unwrap();
analyzer.analyze(&ast)
})
.collect();
black_box(results)
});
});
}
group.finish();
}
fn benchmark_sequential_vs_parallel_comparison(c: &mut Criterion) {
let mut group = c.benchmark_group("batch_seq_vs_parallel");
let size = 25;
let (_temp_dir, paths) = create_test_files(size);
group.throughput(Throughput::Elements(size as u64));
group.bench_with_input(BenchmarkId::new("sequential", size), &paths, |b, paths| {
b.iter(|| {
let results: Vec<_> = paths
.iter()
.map(|path| {
let content = std::fs::read_to_string(path).unwrap();
let language = Language::from_path(path);
let analyzer = get_analyzer(language);
let ast = analyzer.parse(&content, path.clone()).unwrap();
analyzer.analyze(&ast)
})
.collect();
black_box(results)
});
});
group.bench_with_input(BenchmarkId::new("parallel", size), &paths, |b, paths| {
b.iter(|| {
let results: Vec<_> = paths
.par_iter()
.map(|path| {
let content = std::fs::read_to_string(path).unwrap();
let language = Language::from_path(path);
let analyzer = get_analyzer(language);
let ast = analyzer.parse(&content, path.clone()).unwrap();
analyzer.analyze(&ast)
})
.collect();
black_box(results)
});
});
group.finish();
}
fn benchmark_validation_accumulation(c: &mut Criterion) {
let mut group = c.benchmark_group("batch_validation");
for &size in &[5, 10, 25] {
let (_temp_dir, paths) = create_test_files(size);
group.throughput(Throughput::Elements(size as u64));
group.bench_with_input(BenchmarkId::from_parameter(size), &paths, |b, paths| {
b.iter(|| {
let validation = validate_files(paths);
let result = run_validation(validation);
black_box(result)
});
});
}
group.finish();
}
fn benchmark_effect_with_timing(c: &mut Criterion) {
let mut group = c.benchmark_group("batch_effect_timing");
for &size in &[5, 10, 25] {
let (_temp_dir, paths) = create_test_files(size);
group.throughput(Throughput::Elements(size as u64));
group.bench_with_input(BenchmarkId::from_parameter(size), &paths, |b, paths| {
b.iter(|| {
let config = DebtmapConfig {
batch_analysis: Some(BatchAnalysisConfig::default().with_timing()),
..Default::default()
};
let effect = analyze_files_effect(paths.clone());
let result = run_effect(effect, config);
black_box(result)
});
});
}
group.finish();
}
criterion_group!(
benches,
benchmark_batch_sequential,
benchmark_batch_parallel,
benchmark_sequential_vs_parallel_comparison,
benchmark_validation_accumulation,
benchmark_effect_with_timing
);
criterion_main!(benches);