use codelens_engine::{
GraphCache, ProjectRoot, SymbolIndex, content_hash, find_circular_dependencies,
get_blast_radius, get_callers, get_symbols_overview, search_for_pattern, search_symbols_hybrid,
};
use criterion::{Criterion, black_box, criterion_group, criterion_main};
use std::fs;
fn create_fixture() -> (tempfile::TempDir, ProjectRoot) {
let dir = tempfile::tempdir().expect("tempdir");
let root = dir.path();
fs::create_dir_all(root.join("src")).unwrap();
fs::write(
root.join("src/service.py"),
r#"
from src.models import User
from src.utils import validate
class UserService:
def __init__(self, db):
self.db = db
def get_user(self, user_id: int) -> User:
validate(user_id)
return self.db.find(user_id)
def create_user(self, name: str, email: str) -> User:
user = User(name=name, email=email)
self.db.save(user)
return user
def delete_user(self, user_id: int) -> bool:
user = self.get_user(user_id)
return self.db.delete(user)
def list_users(self, limit: int = 100):
return self.db.query("SELECT * FROM users LIMIT %s", limit)
"#,
)
.unwrap();
fs::write(
root.join("src/models.py"),
r#"
class User:
def __init__(self, name: str, email: str, id: int = None):
self.name = name
self.email = email
self.id = id
def to_dict(self):
return {"name": self.name, "email": self.email, "id": self.id}
class Permission:
def __init__(self, role: str):
self.role = role
def can_access(self, resource: str) -> bool:
return self.role == "admin"
"#,
)
.unwrap();
fs::write(
root.join("src/utils.py"),
r#"
import re
def validate(value):
if value is None:
raise ValueError("value cannot be None")
return True
def sanitize(text: str) -> str:
return re.sub(r'[<>]', '', text)
def format_name(first: str, last: str) -> str:
return f"{first} {last}".strip()
"#,
)
.unwrap();
fs::write(
root.join("src/api.ts"),
r#"
import { UserService } from './service';
import { Request, Response } from 'express';
export class ApiController {
constructor(private userService: UserService) {}
async getUser(req: Request, res: Response): Promise<void> {
const user = await this.userService.findById(req.params.id);
res.json(user);
}
async createUser(req: Request, res: Response): Promise<void> {
const user = await this.userService.create(req.body);
res.status(201).json(user);
}
}
export function healthCheck(): { status: string } {
return { status: 'ok' };
}
"#,
)
.unwrap();
fs::write(
root.join("src/service.ts"),
r#"
export interface User {
id: string;
name: string;
email: string;
}
export class UserService {
async findById(id: string): Promise<User | null> {
return null;
}
async create(data: Partial<User>): Promise<User> {
return { id: '1', name: data.name || '', email: data.email || '' };
}
async delete(id: string): Promise<boolean> {
return true;
}
}
"#,
)
.unwrap();
fs::write(
root.join("src/lib.rs"),
r#"
pub struct Config {
pub name: String,
pub port: u16,
}
impl Config {
pub fn new(name: &str, port: u16) -> Self {
Self { name: name.to_owned(), port }
}
pub fn validate(&self) -> Result<(), String> {
if self.name.is_empty() { return Err("empty name".into()); }
if self.port == 0 { return Err("invalid port".into()); }
Ok(())
}
}
pub fn run(config: &Config) -> bool {
config.validate().is_ok()
}
pub trait Handler {
fn handle(&self, input: &str) -> String;
}
"#,
)
.unwrap();
fs::write(
root.join("src/Main.java"),
r#"
public class Main {
public static void main(String[] args) {
System.out.println("Hello");
}
public static int add(int a, int b) {
return a + b;
}
public static String greet(String name) {
return "Hello " + name;
}
}
"#,
)
.unwrap();
fs::write(
root.join("src/main.go"),
r#"
package main
type Server struct {
Port int
Host string
}
func NewServer(host string, port int) *Server {
return &Server{Port: port, Host: host}
}
func (s *Server) Start() error {
return nil
}
func (s *Server) Stop() {
}
"#,
)
.unwrap();
let project = ProjectRoot::new(root).expect("project");
(dir, project)
}
fn bench_refresh_all(c: &mut Criterion) {
let (_dir, project) = create_fixture();
c.bench_function("refresh_all (9 files, 5 langs)", |b| {
b.iter(|| {
let index = SymbolIndex::new_memory(black_box(project.clone()));
index.refresh_all().unwrap();
})
});
}
fn bench_find_symbol_exact(c: &mut Criterion) {
let (_dir, project) = create_fixture();
let index = SymbolIndex::new_memory(project.clone());
index.refresh_all().unwrap();
c.bench_function("find_symbol exact (UserService)", |b| {
b.iter(|| {
index
.find_symbol(black_box("UserService"), None, false, true, 10)
.unwrap();
})
});
}
fn bench_find_symbol_fuzzy(c: &mut Criterion) {
let (_dir, project) = create_fixture();
let index = SymbolIndex::new_memory(project.clone());
index.refresh_all().unwrap();
c.bench_function("find_symbol substring (user)", |b| {
b.iter(|| {
index
.find_symbol(black_box("user"), None, false, false, 50)
.unwrap();
})
});
}
fn bench_get_symbols_overview(c: &mut Criterion) {
let (_dir, project) = create_fixture();
c.bench_function("get_symbols_overview (service.py)", |b| {
b.iter(|| {
get_symbols_overview(black_box(&project), "src/service.py", 2).unwrap();
})
});
}
fn bench_search_for_pattern(c: &mut Criterion) {
let (_dir, project) = create_fixture();
c.bench_function("search_for_pattern (def.*user)", |b| {
b.iter(|| {
search_for_pattern(black_box(&project), r"def.*user", None, 50, 0, 0).unwrap();
})
});
}
fn bench_search_symbols_hybrid(c: &mut Criterion) {
let (_dir, project) = create_fixture();
let index = SymbolIndex::new_memory(project.clone());
index.refresh_all().unwrap();
c.bench_function("search_symbols_hybrid (Usr, fuzzy)", |b| {
b.iter(|| {
search_symbols_hybrid(black_box(&project), "Usr", 20, 0.6).unwrap();
})
});
}
fn bench_blast_radius(c: &mut Criterion) {
let (_dir, project) = create_fixture();
let cache = GraphCache::new(0);
c.bench_function("get_blast_radius (models.py)", |b| {
b.iter(|| {
let _ = get_blast_radius(black_box(&project), "src/models.py", 3, &cache);
})
});
}
fn bench_get_callers(c: &mut Criterion) {
let (_dir, project) = create_fixture();
let cache = GraphCache::new(0);
c.bench_function("get_callers (validate)", |b| {
b.iter(|| {
get_callers(black_box(&project), "validate", None, 50, Some(&cache)).unwrap();
})
});
}
fn bench_circular_deps(c: &mut Criterion) {
let (_dir, project) = create_fixture();
let cache = GraphCache::new(0);
c.bench_function("find_circular_dependencies", |b| {
b.iter(|| {
find_circular_dependencies(black_box(&project), 50, &cache).unwrap();
})
});
}
fn bench_content_hash(c: &mut Criterion) {
let data = vec![0u8; 100_000];
c.bench_function("content_hash (100KB)", |b| {
b.iter(|| {
content_hash(black_box(&data));
})
});
}
fn create_scoring_stress_fixture() -> (tempfile::TempDir, ProjectRoot) {
let dir = tempfile::tempdir().expect("tempdir");
let root = dir.path();
fs::create_dir_all(root.join("src")).unwrap();
for i in 0..20 {
let content = format!(
r#"
pub struct Widget{i} {{
pub name: String,
pub value: i32,
}}
impl Widget{i} {{
pub fn new(name: &str, value: i32) -> Self {{
Self {{ name: name.to_owned(), value }}
}}
pub fn process_data(&self) -> i32 {{
self.value * 2
}}
pub fn validate_input(&self) -> bool {{
!self.name.is_empty() && self.value > 0
}}
}}
"#,
i = i
);
fs::write(root.join(format!("src/widget_{i}.rs")), content).unwrap();
}
let mods: String = (0..20).map(|i| format!("pub mod widget_{i};\n")).collect();
fs::write(root.join("src/lib.rs"), mods).unwrap();
let project = ProjectRoot::new(root).expect("project");
(dir, project)
}
fn build_scoring_stress_index() -> (tempfile::TempDir, ProjectRoot, SymbolIndex) {
let (dir, project) = create_scoring_stress_fixture();
let index = SymbolIndex::new_memory(project.clone());
index.refresh_all().unwrap();
(dir, project, index)
}
fn bench_scoring_stress_nl(c: &mut Criterion) {
let (_dir, project, _index) = build_scoring_stress_index();
c.bench_function("search_hybrid_stress (NL, 20 modules)", |b| {
b.iter(|| {
search_symbols_hybrid(black_box(&project), "process data from widget", 20, 0.6)
.unwrap();
})
});
}
fn bench_scoring_stress_identifier(c: &mut Criterion) {
let (_dir, project, _index) = build_scoring_stress_index();
c.bench_function("search_hybrid_stress (ident, 20 modules)", |b| {
b.iter(|| {
search_symbols_hybrid(black_box(&project), "validate_input", 20, 0.6).unwrap();
})
});
}
fn bench_ranked_context_cached_stress_nl(c: &mut Criterion) {
let (_dir, _project, index) = build_scoring_stress_index();
c.bench_function("ranked_context_cached_stress (NL, 20 modules)", |b| {
b.iter(|| {
index
.get_ranked_context_cached(
black_box("process data from widget"),
None,
4000,
false,
2,
None,
std::collections::HashMap::new(),
)
.unwrap();
})
});
}
fn bench_ranked_context_cached_stress_identifier(c: &mut Criterion) {
let (_dir, _project, index) = build_scoring_stress_index();
c.bench_function("ranked_context_cached_stress (ident, 20 modules)", |b| {
b.iter(|| {
index
.get_ranked_context_cached(
black_box("validate_input"),
None,
4000,
false,
2,
None,
std::collections::HashMap::new(),
)
.unwrap();
})
});
}
fn bench_ranked_context_cache_hit(c: &mut Criterion) {
let (_dir, _project, index) = build_scoring_stress_index();
let _ = index
.get_ranked_context_cached(
"validate_input",
None,
4000,
false,
2,
None,
std::collections::HashMap::new(),
)
.unwrap();
c.bench_function("ranked_context_cached (cache hit, ident)", |b| {
b.iter(|| {
index
.get_ranked_context_cached(
black_box("validate_input"),
None,
4000,
false,
2,
None,
std::collections::HashMap::new(),
)
.unwrap();
})
});
}
fn bench_large_project_indexing(c: &mut Criterion) {
let dir = tempfile::tempdir().expect("tempdir");
let root = dir.path();
fs::create_dir_all(root.join("src")).unwrap();
for i in 0..100 {
let content = format!(
r#"
pub struct Service{i} {{
pub id: u64,
pub name: String,
}}
impl Service{i} {{
pub fn new(id: u64, name: &str) -> Self {{
Self {{ id, name: name.to_owned() }}
}}
pub fn process(&self) -> u64 {{
self.id * 2
}}
pub fn validate(&self) -> bool {{
self.id > 0 && !self.name.is_empty()
}}
pub fn transform(&self, input: &str) -> String {{
format!("{{}}-{{}}", self.name, input)
}}
}}
"#,
i = i
);
fs::write(root.join(format!("src/service_{i}.rs")), content).unwrap();
}
let mods: String = (0..100)
.map(|i| format!("pub mod service_{i};\n"))
.collect();
fs::write(root.join("src/lib.rs"), mods).unwrap();
let project = ProjectRoot::new(root).expect("project");
c.bench_function("refresh_all (100 modules, 500+ symbols)", |b| {
b.iter(|| {
let index = SymbolIndex::new_memory(black_box(project.clone()));
index.refresh_all().unwrap();
})
});
}
criterion_group!(
benches,
bench_refresh_all,
bench_find_symbol_exact,
bench_find_symbol_fuzzy,
bench_get_symbols_overview,
bench_search_for_pattern,
bench_search_symbols_hybrid,
bench_blast_radius,
bench_get_callers,
bench_circular_deps,
bench_content_hash,
bench_scoring_stress_nl,
bench_scoring_stress_identifier,
bench_ranked_context_cached_stress_nl,
bench_ranked_context_cached_stress_identifier,
bench_ranked_context_cache_hit,
bench_large_project_indexing,
);
criterion_main!(benches);