use codesearch::symbols::{
Symbol, SymbolIndex, SymbolKind, SymbolVisibility,
extractor::{SymbolExtractor, extract_symbols_from_file},
relationships::RelationshipGraph,
};
use criterion::{BenchmarkId, Criterion, black_box, criterion_group, criterion_main};
use std::collections::HashMap;
use std::fs;
use tempfile::tempdir;
const SAMPLE_RUST: &str = r#"
pub struct Config {
name: String,
value: i32,
}
impl Config {
pub fn new(name: &str) -> Self {
Config {
name: name.to_string(),
value: 0,
}
}
pub fn get_name(&self) -> &str {
&self.name
}
pub fn set_value(&mut self, val: i32) {
self.value = val;
}
}
pub fn process_config(config: &Config) -> String {
format!("Config: {}", config.get_name())
}
pub fn main() {
let config = Config::new("test");
println!("{}", process_config(&config));
}
"#;
const SAMPLE_PYTHON: &str = r#"
class DataProcessor:
def __init__(self, name):
self.name = name
self.data = []
def process(self, items):
result = []
for item in items:
result.append(item * 2)
return result
def get_summary(self):
return f"Processor: {self.name}"
def main():
processor = DataProcessor("test")
result = processor.process([1, 2, 3])
print(result)
if __name__ == "__main__":
main()
"#;
const SAMPLE_JS: &str = r#"
class ApiHandler {
constructor(baseUrl) {
this.baseUrl = baseUrl;
}
async get(endpoint) {
return fetch(`${this.baseUrl}${endpoint}`);
}
async post(endpoint, data) {
return fetch(`${this.baseUrl}${endpoint}`, {
method: 'POST',
body: JSON.stringify(data)
});
}
}
function createHandler(url) {
return new ApiHandler(url);
}
module.exports = { ApiHandler, createHandler };
"#;
fn benchmark_symbol_extraction_rust(c: &mut Criterion) {
let dir = tempdir().unwrap();
let file_path = dir.path().join("test.rs");
fs::write(&file_path, SAMPLE_RUST).unwrap();
c.bench_function("symbol_extract_rust", |b| {
b.iter(|| black_box(extract_symbols_from_file(black_box(&file_path)).unwrap()))
});
}
fn benchmark_symbol_extraction_python(c: &mut Criterion) {
let dir = tempdir().unwrap();
let file_path = dir.path().join("test.py");
fs::write(&file_path, SAMPLE_PYTHON).unwrap();
c.bench_function("symbol_extract_python", |b| {
b.iter(|| black_box(extract_symbols_from_file(black_box(&file_path)).unwrap()))
});
}
fn benchmark_symbol_extraction_js(c: &mut Criterion) {
let dir = tempdir().unwrap();
let file_path = dir.path().join("test.js");
fs::write(&file_path, SAMPLE_JS).unwrap();
c.bench_function("symbol_extract_js", |b| {
b.iter(|| black_box(extract_symbols_from_file(black_box(&file_path)).unwrap()))
});
}
fn benchmark_symbol_index_add(c: &mut Criterion) {
let index = SymbolIndex::new();
let symbols: Vec<Symbol> = (0..100)
.map(|i| Symbol {
id: format!("symbol_{}", i),
name: format!("function_{}", i),
kind: SymbolKind::Function,
file_path: "src/main.rs".to_string(),
line: i,
column: 0,
end_line: i + 5,
signature: "()".to_string(),
documentation: None,
visibility: SymbolVisibility::Public,
parent: None,
type_info: None,
generics: vec![],
annotations: vec![],
attributes: vec![],
metadata: HashMap::new(),
})
.collect();
c.bench_function("symbol_index_add_100", |b| {
b.iter(|| {
let idx = SymbolIndex::new();
for symbol in &symbols {
idx.add_symbol(black_box(symbol.clone()));
}
black_box(idx);
})
});
}
fn benchmark_symbol_index_search(c: &mut Criterion) {
let index = SymbolIndex::new();
for i in 0..1000 {
let symbol = Symbol {
id: format!("symbol_{}", i),
name: format!("func_{}", i),
kind: if i % 5 == 0 {
SymbolKind::Class
} else if i % 3 == 0 {
SymbolKind::Method
} else {
SymbolKind::Function
},
file_path: format!("src/file_{}.rs", i % 10),
line: i,
column: 0,
end_line: i + 5,
signature: "()".to_string(),
documentation: None,
visibility: if i % 2 == 0 {
SymbolVisibility::Public
} else {
SymbolVisibility::Private
},
parent: if i % 3 == 0 {
Some("ParentClass".to_string())
} else {
None
},
type_info: None,
generics: vec![],
annotations: vec![],
attributes: vec![],
metadata: HashMap::new(),
};
index.add_symbol(symbol);
}
let mut group = c.benchmark_group("symbol_index_search");
group.bench_function("find_by_name_exact", |b| {
b.iter(|| black_box(index.find_by_name("func_500")))
});
group.bench_function("find_by_name_pattern", |b| {
b.iter(|| black_box(index.find_by_name_pattern("func_5.*").unwrap()))
});
group.bench_function("get_symbols_by_kind", |b| {
b.iter(|| black_box(index.get_symbols_by_kind(&SymbolKind::Function)))
});
group.bench_function("search_symbols_multi", |b| {
b.iter(|| {
black_box(index.search_symbols(
Some("func_"),
Some(&SymbolKind::Function),
None,
Some(&SymbolVisibility::Public),
))
})
});
group.finish();
}
fn benchmark_relationship_graph(c: &mut Criterion) {
let graph = RelationshipGraph::new();
let mut symbols = Vec::new();
for i in 0..500 {
let symbol = Symbol {
id: format!("sym_{}", i),
name: format!("symbol_{}", i),
kind: SymbolKind::Function,
file_path: "test.rs".to_string(),
line: i,
column: 0,
end_line: i + 5,
signature: "()".to_string(),
documentation: None,
visibility: SymbolVisibility::Public,
parent: None,
type_info: None,
generics: vec![],
annotations: vec![],
attributes: vec![],
metadata: HashMap::new(),
};
graph.add_symbol(symbol.clone());
symbols.push(symbol);
}
for i in 0..symbols.len() {
if i > 0 {
graph.add_relationship(
&symbols[i].id,
&symbols[i - 1].id,
codesearch::symbols::SymbolRelationType::Calls,
1.0,
);
}
if i > 10 {
graph.add_relationship(
&symbols[i].id,
&symbols[i - 10].id,
codesearch::symbols::SymbolRelationType::References,
0.8,
);
}
}
let mut group = c.benchmark_group("relationship_graph");
group.bench_function("get_relationships", |b| {
b.iter(|| black_box(graph.get_relationships("sym_250")))
});
group.bench_function("find_callers", |b| {
b.iter(|| black_box(graph.find_callers("sym_100")))
});
group.bench_function("find_callees", |b| {
b.iter(|| black_box(graph.find_callees("sym_200")))
});
group.bench_function("find_hierarchy", |b| {
b.iter(|| black_box(graph.find_hierarchy("sym_50")))
});
group.finish();
}
fn benchmark_symbol_extractor_creation(c: &mut Criterion) {
c.bench_function("symbol_extractor_new", |b| {
b.iter(|| black_box(SymbolExtractor::new()))
});
}
fn benchmark_multi_language_comparison(c: &mut Criterion) {
let dir = tempdir().unwrap();
let rust_path = dir.path().join("sample.rs");
let py_path = dir.path().join("sample.py");
let js_path = dir.path().join("sample.js");
fs::write(&rust_path, SAMPLE_RUST).unwrap();
fs::write(&py_path, SAMPLE_PYTHON).unwrap();
fs::write(&js_path, SAMPLE_JS).unwrap();
let mut group = c.benchmark_group("symbol_extract_comparison");
group.bench_with_input(
BenchmarkId::new("language", "rust"),
&rust_path,
|b, path| b.iter(|| black_box(extract_symbols_from_file(path).unwrap())),
);
group.bench_with_input(
BenchmarkId::new("language", "python"),
&py_path,
|b, path| b.iter(|| black_box(extract_symbols_from_file(path).unwrap())),
);
group.bench_with_input(BenchmarkId::new("language", "js"), &js_path, |b, path| {
b.iter(|| black_box(extract_symbols_from_file(path).unwrap()))
});
group.finish();
}
criterion_group!(
benches,
benchmark_symbol_extraction_rust,
benchmark_symbol_extraction_python,
benchmark_symbol_extraction_js,
benchmark_symbol_index_add,
benchmark_symbol_index_search,
benchmark_relationship_graph,
benchmark_symbol_extractor_creation,
benchmark_multi_language_comparison,
);
criterion_main!(benches);