use criterion::{BenchmarkId, Criterion, black_box, criterion_group, criterion_main};
use tower_lsp::lsp_types::Url;
use php_lsp::document_store::DocumentStore;
const SMALL: &str = include_str!("fixtures/small_class.php");
const MEDIUM: &str = include_str!("fixtures/medium_class.php");
const LARGE_IFACE: &str = include_str!("fixtures/interface_large.php");
fn bench_index_single(c: &mut Criterion) {
let mut group = c.benchmark_group("index/single");
for (name, source) in [
("small_class", SMALL),
("medium_class", MEDIUM),
("interface_large", LARGE_IFACE),
] {
let uri = Url::parse("file:///bench/file.php").unwrap();
group.bench_with_input(BenchmarkId::from_parameter(name), source, |b, src| {
b.iter(|| {
let store = DocumentStore::new();
store.index(uri.clone(), src);
});
});
}
group.finish();
}
fn bench_get_doc(c: &mut Criterion) {
let store = DocumentStore::new();
let uri = Url::parse("file:///bench/medium.php").unwrap();
store.index(uri.clone(), MEDIUM);
c.bench_function("index/get_doc", |b| {
b.iter(|| black_box(store.get_doc_salsa(&uri)));
});
}
fn bench_all_docs(c: &mut Criterion) {
let store = DocumentStore::new();
let urls: Vec<Url> = (0..10)
.map(|i| Url::parse(&format!("file:///bench/file{i}.php")).unwrap())
.collect();
for u in &urls {
store.index(u.clone(), SMALL);
}
c.bench_function("index/all_docs_10", |b| {
b.iter(|| black_box(store.docs_for(&urls)));
});
}
fn bench_workspace_scan(c: &mut Criterion) {
let fixtures: &[(&str, &str)] = &[
("small_class", SMALL),
("medium_class", MEDIUM),
("interface_large", LARGE_IFACE),
];
let mut group = c.benchmark_group("index/workspace_scan");
for &n in &[1usize, 10, 50] {
let uris: Vec<Url> = (0..n)
.map(|i| Url::parse(&format!("file:///bench/scan_{i}.php")).unwrap())
.collect();
group.bench_with_input(
BenchmarkId::from_parameter(format!("{n}_files")),
&n,
|b, &n| {
b.iter(|| {
let store = DocumentStore::new();
for i in 0..n {
let (_, src) = fixtures[i % fixtures.len()];
store.index(uris[i].clone(), src);
}
});
},
);
}
group.finish();
}
fn bench_workspace_scan_laravel(c: &mut Criterion) {
let fixture_dir =
std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("benches/fixtures/laravel/src");
if !fixture_dir.exists() {
eprintln!(
"Laravel fixture not found — run `scripts/setup_laravel_fixture.sh` to enable this benchmark"
);
return;
}
let php_files: Vec<(tower_lsp::lsp_types::Url, String)> = walkdir::WalkDir::new(&fixture_dir)
.into_iter()
.filter_map(|e| e.ok())
.filter(|e| e.path().extension().map_or(false, |x| x == "php"))
.filter_map(|e| {
let url = tower_lsp::lsp_types::Url::from_file_path(e.path()).ok()?;
let src = std::fs::read_to_string(e.path()).ok()?;
Some((url, src))
})
.collect();
eprintln!("Laravel fixture: {} PHP files", php_files.len());
let mut group = c.benchmark_group("index/workspace_scan");
group.sample_size(10);
group.bench_function("laravel_framework", |b| {
b.iter(|| {
let store = DocumentStore::new();
for (url, src) in &php_files {
store.index(url.clone(), src);
}
});
});
group.finish();
}
fn bench_mirror_same_text_contended(c: &mut Criterion) {
use std::sync::Arc;
let store = Arc::new(DocumentStore::new());
let uri = Url::parse("file:///bench/mirror.php").unwrap();
store.index(uri.clone(), MEDIUM);
let threads = 8usize;
let iters_per_thread = 500usize;
c.bench_function("index/mirror_same_text_contended_8x500", |b| {
b.iter(|| {
let mut handles = Vec::with_capacity(threads);
for _ in 0..threads {
let store = Arc::clone(&store);
let uri = uri.clone();
handles.push(std::thread::spawn(move || {
for _ in 0..iters_per_thread {
store.index(black_box(uri.clone()), black_box(MEDIUM));
}
}));
}
for h in handles {
h.join().unwrap();
}
});
});
}
fn bench_get_doc_repeated(c: &mut Criterion) {
let store = DocumentStore::new();
let uri = Url::parse("file:///bench/hotdoc.php").unwrap();
store.index(uri.clone(), MEDIUM);
let _warm = store.get_doc_salsa(&uri);
c.bench_function("index/get_doc_repeated", |b| {
b.iter(|| black_box(store.get_doc_salsa(black_box(&uri))));
});
}
criterion_group!(
benches,
bench_index_single,
bench_get_doc,
bench_all_docs,
bench_workspace_scan,
bench_workspace_scan_laravel,
bench_mirror_same_text_contended,
bench_get_doc_repeated
);
criterion_main!(benches);