use super::*;
const FIND_BENCH_PREFIX: &str = "fffind_benchmark ";
const FIND_BENCH_DEFAULT_REPETITIONS: usize = 25;
const FIND_BENCH_DEFAULT_LIMIT: usize = 50;
const FIND_BENCH_MAX_LIMIT: usize = 200;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum FindBenchBaseline {
IgnoreWalk,
None,
}
impl FindBenchBaseline {
fn from_env() -> Self {
match std::env::var("FFFIND_BENCH_BASELINE")
.unwrap_or_else(|_| "ignore_walk".to_string())
.trim()
{
"none" => Self::None,
"ignore_walk" | "" => Self::IgnoreWalk,
value => {
eprintln!(
"fffind_benchmark warning=unsupported_baseline value={value:?} using=ignore_walk"
);
Self::IgnoreWalk
}
}
}
}
#[derive(Debug)]
struct FindBenchConfig {
root: PathBuf,
root_label: String,
root_hash: String,
queries: Vec<String>,
paths: Vec<String>,
repetitions: usize,
limit: usize,
cwd_churn: usize,
baseline: FindBenchBaseline,
show_results: bool,
}
impl FindBenchConfig {
fn from_env() -> anyhow::Result<Self> {
let root = std::env::var("FFFIND_BENCH_ROOT")
.map(PathBuf::from)
.unwrap_or_else(|_| PathBuf::from(env!("CARGO_MANIFEST_DIR")))
.canonicalize()?;
let root_label = root
.file_name()
.and_then(|name| name.to_str())
.filter(|name| !name.is_empty())
.unwrap_or("root")
.to_string();
let root_hash = hash_root_label(&root);
let queries = comma_env("FFFIND_BENCH_QUERIES").unwrap_or_else(|| {
["lib", "tools", "profile", "readme", "fff"]
.into_iter()
.map(ToOwned::to_owned)
.collect()
});
let paths = comma_env("FFFIND_BENCH_PATHS")
.unwrap_or_else(|| ["src", "docs"].into_iter().map(ToOwned::to_owned).collect());
let repetitions = parse_usize_env(
"FFFIND_BENCH_REPETITIONS",
FIND_BENCH_DEFAULT_REPETITIONS,
1,
usize::MAX,
);
let limit = parse_usize_env(
"FFFIND_BENCH_LIMIT",
FIND_BENCH_DEFAULT_LIMIT,
1,
FIND_BENCH_MAX_LIMIT,
);
let cwd_churn = parse_usize_env("FFFIND_BENCH_CWD_CHURN", 0, 0, usize::MAX);
let show_results = std::env::var("FFFIND_BENCH_SHOW_RESULTS")
.ok()
.is_some_and(|value| value == "1");
Ok(Self {
root,
root_label,
root_hash,
queries,
paths,
repetitions,
limit,
cwd_churn,
baseline: FindBenchBaseline::from_env(),
show_results,
})
}
fn existing_filter_paths(&self) -> Vec<String> {
self.paths
.iter()
.filter_map(|path| {
let full_path = self.root.join(path);
if full_path.is_dir() {
Some(path.clone())
} else {
eprintln!(
"fffind_benchmark warning=missing_filter_path path={path:?} action=skipped"
);
None
}
})
.collect()
}
}
#[derive(Debug)]
struct FindBenchObservation {
elapsed: Vec<Duration>,
matches_returned: u64,
total_matched: u64,
truncated: bool,
index_ready: bool,
top_results: Option<Vec<String>>,
}
fn run_find_observation(
runtime: &ToolRuntime,
query: &str,
kind: &str,
path: Option<&str>,
limit: usize,
iterations: usize,
include_top_results: bool,
) -> anyhow::Result<FindBenchObservation> {
let mut elapsed = Vec::with_capacity(iterations);
let mut matches_returned = 0;
let mut total_matched = 0;
let mut truncated = false;
let mut index_ready = false;
let mut top_results = None;
for index in 0..iterations {
let arguments = match path {
Some(path) => json!({"query": query, "kind": kind, "path": path, "limit": limit}),
None => json!({"query": query, "kind": kind, "limit": limit}),
};
let started = Instant::now();
let result = runtime.dispatch("find", arguments);
elapsed.push(started.elapsed());
if !result.success {
anyhow::bail!("find benchmark dispatch failed: {}", result.content);
}
matches_returned = metadata_u64(&result.metadata, "matches_returned");
total_matched = metadata_u64(&result.metadata, "total_matched");
truncated = result
.metadata
.get("truncated")
.and_then(Value::as_bool)
.unwrap_or(false);
index_ready = result
.metadata
.get("index_ready")
.and_then(Value::as_bool)
.unwrap_or(false);
if include_top_results && index == 0 {
top_results = Some(
result
.content
.lines()
.take(10)
.map(ToOwned::to_owned)
.collect(),
);
}
}
Ok(FindBenchObservation {
elapsed,
matches_returned,
total_matched,
truncated,
index_ready,
top_results,
})
}
#[derive(Debug, Serialize)]
struct IgnoreWalkBaselineSummary {
engine: &'static str,
p50_ms: f64,
p95_ms: f64,
matches_returned: usize,
total_matched: usize,
}
fn run_ignore_walk_baseline(
root: &Path,
query: &str,
path_filter: Option<&str>,
limit: usize,
iterations: usize,
) -> anyhow::Result<IgnoreWalkBaselineSummary> {
let mut elapsed = Vec::with_capacity(iterations);
let mut matches_returned = 0;
let mut total_matched = 0;
for _ in 0..iterations {
let started = Instant::now();
let matches = ignore_walk_matches(root, query, path_filter, limit)?;
elapsed.push(started.elapsed());
matches_returned = matches.0;
total_matched = matches.1;
}
let stats = duration_stats(&elapsed);
Ok(IgnoreWalkBaselineSummary {
engine: "ignore_walk",
p50_ms: stats.p50,
p95_ms: stats.p95,
matches_returned,
total_matched,
})
}
fn ignore_walk_matches(
root: &Path,
query: &str,
path_filter: Option<&str>,
limit: usize,
) -> anyhow::Result<(usize, usize)> {
let start = path_filter
.map(|path| root.join(path))
.unwrap_or_else(|| root.to_path_buf());
let query = query.to_lowercase();
let mut returned = 0;
let mut total = 0;
for entry in ignore::WalkBuilder::new(start)
.standard_filters(true)
.build()
.filter_map(Result::ok)
{
if !entry
.file_type()
.is_some_and(|file_type| file_type.is_file())
{
continue;
}
let relative = entry
.path()
.strip_prefix(root)
.unwrap_or(entry.path())
.to_string_lossy()
.replace('\\', "/");
if relative.to_lowercase().contains(&query) {
total += 1;
if returned < limit {
returned += 1;
}
}
}
Ok((returned, total))
}
fn print_find_benchmark_line(value: Value) {
println!("{FIND_BENCH_PREFIX}{value}");
}
#[expect(
clippy::too_many_arguments,
reason = "benchmark output builder mirrors JSON fields for call-site clarity"
)]
fn find_benchmark_line(
cfg: &FindBenchConfig,
scenario: &str,
query: Option<&str>,
kind: Option<&str>,
path: Option<&str>,
observation: &FindBenchObservation,
resources: ResourceDelta,
baseline: Option<IgnoreWalkBaselineSummary>,
) -> Value {
let mut value = json!({
"schema": 1,
"scenario": scenario,
"root_label": cfg.root_label,
"root_hash": cfg.root_hash,
"query": query,
"kind": kind,
"path": path,
"iterations": observation.elapsed.len(),
"elapsed_ms": duration_stats(&observation.elapsed),
"matches_returned": observation.matches_returned,
"total_matched": observation.total_matched,
"truncated": observation.truncated,
"index_ready": observation.index_ready,
"rss_kb_delta": resources.rss_kb_delta,
"fd_delta": resources.fd_delta,
"thread_delta": resources.thread_delta,
"baseline": baseline,
});
if let Some(top_results) = observation.top_results.as_ref() {
value["top_results"] = json!(top_results);
}
value
}
fn baseline_for_file_scenario(
cfg: &FindBenchConfig,
query: &str,
path: Option<&str>,
iterations: usize,
) -> anyhow::Result<Option<IgnoreWalkBaselineSummary>> {
match cfg.baseline {
FindBenchBaseline::IgnoreWalk => {
run_ignore_walk_baseline(&cfg.root, query, path, cfg.limit, iterations).map(Some)
}
FindBenchBaseline::None => Ok(None),
}
}
pub(super) fn collect_churn_dirs(root: &Path, count: usize) -> Vec<PathBuf> {
if count == 0 {
return Vec::new();
}
let mut dirs = Vec::new();
for entry in ignore::WalkBuilder::new(root)
.max_depth(Some(3))
.standard_filters(true)
.build()
.filter_map(Result::ok)
{
if dirs.len() >= count {
break;
}
let path = entry.path();
if path != root
&& entry
.file_type()
.is_some_and(|file_type| file_type.is_dir())
{
dirs.push(path.to_path_buf());
}
}
dirs
}
fn run_cwd_churn_scenario(
cfg: &FindBenchConfig,
runtime: &ToolRuntime,
query: &str,
) -> anyhow::Result<Option<Value>> {
let dirs = collect_churn_dirs(&cfg.root, cfg.cwd_churn);
if cfg.cwd_churn == 0 {
return Ok(None);
}
if dirs.is_empty() {
eprintln!("fffind_benchmark warning=no_child_dirs_for_cwd_churn action=skipped");
return Ok(None);
}
let before = ResourceSnapshot::capture();
let mut elapsed = Vec::with_capacity(dirs.len());
let mut matches_returned = 0;
let mut total_matched = 0;
let mut truncated = false;
let mut index_ready = false;
for dir in &dirs {
let cwd_runtime = runtime.clone_for_cwd_with_subagent_depth(dir, 0)?;
let started = Instant::now();
let result = cwd_runtime.dispatch(
"find",
json!({"query": query, "kind": "files", "limit": cfg.limit}),
);
elapsed.push(started.elapsed());
if !result.success {
anyhow::bail!("find cwd churn dispatch failed: {}", result.content);
}
matches_returned = metadata_u64(&result.metadata, "matches_returned");
total_matched = metadata_u64(&result.metadata, "total_matched");
truncated = result
.metadata
.get("truncated")
.and_then(Value::as_bool)
.unwrap_or(false);
index_ready = result
.metadata
.get("index_ready")
.and_then(Value::as_bool)
.unwrap_or(false);
}
let resources = before.delta(ResourceSnapshot::capture());
Ok(Some(json!({
"schema": 1,
"scenario": "cwd_churn",
"root_label": cfg.root_label,
"root_hash": cfg.root_hash,
"query": query,
"kind": "files",
"path": null,
"iterations": elapsed.len(),
"configured_churn": cfg.cwd_churn,
"elapsed_ms": duration_stats(&elapsed),
"matches_returned": matches_returned,
"total_matched": total_matched,
"truncated": truncated,
"index_ready": index_ready,
"rss_kb_delta": resources.rss_kb_delta,
"fd_delta": resources.fd_delta,
"thread_delta": resources.thread_delta,
"baseline": null,
})))
}
fn print_find_summary(cfg: &FindBenchConfig, scenario_count: usize, observed_values: &[Value]) {
let max_warm_p95_ms = observed_values
.iter()
.filter(|value| {
value["scenario"].as_str().is_some_and(|scenario| {
scenario.starts_with("warm_") || scenario == "repeated_same_query"
})
})
.filter_map(|value| value["elapsed_ms"]["p95"].as_f64())
.max_by(f64::total_cmp)
.map(round_millis);
let baseline_speedup_observed = observed_values.iter().any(|value| {
let Some(fff_p95) = value["elapsed_ms"]["p95"].as_f64() else {
return false;
};
let Some(baseline_p95) = value["baseline"]["p95_ms"].as_f64() else {
return false;
};
fff_p95 < baseline_p95
});
let resource_growth_observed = observed_values.iter().any(|value| {
value["fd_delta"].as_i64().is_some_and(|delta| delta > 16)
|| value["thread_delta"]
.as_i64()
.is_some_and(|delta| delta > 16)
});
print_find_benchmark_line(json!({
"schema": 1,
"scenario": "summary",
"root_label": cfg.root_label,
"root_hash": cfg.root_hash,
"queries": cfg.queries,
"paths": cfg.paths,
"repetitions": cfg.repetitions,
"limit": cfg.limit,
"baseline": match cfg.baseline {
FindBenchBaseline::IgnoreWalk => "ignore_walk",
FindBenchBaseline::None => "none",
},
"cwd_churn": cfg.cwd_churn,
"scenario_lines": scenario_count,
"max_warm_p95_ms": max_warm_p95_ms,
"baseline_speedup_observed": baseline_speedup_observed,
"resource_growth_observed": resource_growth_observed,
"step4_exploration": {
"recommended": baseline_speedup_observed && !resource_growth_observed,
"caveat": "fffind path benchmark does not prove grep content-search compatibility"
},
"step5_exploration": {
"recommended": max_warm_p95_ms.is_some_and(|p95| p95 <= 50.0) && !resource_growth_observed,
"caveat": "validate threshold on production repo before TUI autocomplete work"
}
}));
}
#[test]
#[ignore = "diagnostic-only fffind benchmark; run explicitly with --ignored --nocapture"]
fn find_benchmark_measures_cold_warm_filtered_and_baseline() {
let cfg = FindBenchConfig::from_env().expect("fffind benchmark config");
let runtime = ToolRuntime::new(&cfg.root).expect("fffind benchmark runtime");
let existing_paths = cfg.existing_filter_paths();
let cold_query = cfg.queries.first().expect("at least one query").clone();
let mut scenario_count = 0;
let mut observed_values = Vec::new();
let before = ResourceSnapshot::capture();
let cold = run_find_observation(
&runtime,
&cold_query,
"files",
None,
cfg.limit,
1,
cfg.show_results,
)
.expect("cold fffind benchmark");
let resources = before.delta(ResourceSnapshot::capture());
let baseline = baseline_for_file_scenario(&cfg, &cold_query, None, 1).expect("cold baseline");
let value = find_benchmark_line(
&cfg,
"cold_unfiltered_files",
Some(&cold_query),
Some("files"),
None,
&cold,
resources,
baseline,
);
print_find_benchmark_line(value.clone());
observed_values.push(value);
scenario_count += 1;
for query in &cfg.queries {
let before = ResourceSnapshot::capture();
let observation = run_find_observation(
&runtime,
query,
"files",
None,
cfg.limit,
cfg.repetitions,
false,
)
.expect("warm unfiltered fffind benchmark");
let resources = before.delta(ResourceSnapshot::capture());
let baseline = baseline_for_file_scenario(&cfg, query, None, cfg.repetitions)
.expect("warm unfiltered baseline");
let value = find_benchmark_line(
&cfg,
"warm_unfiltered_files",
Some(query),
Some("files"),
None,
&observation,
resources,
baseline,
);
print_find_benchmark_line(value.clone());
observed_values.push(value);
scenario_count += 1;
}
for path in &existing_paths {
for query in &cfg.queries {
let before = ResourceSnapshot::capture();
let observation = run_find_observation(
&runtime,
query,
"files",
Some(path),
cfg.limit,
cfg.repetitions,
false,
)
.expect("warm filtered fffind benchmark");
let resources = before.delta(ResourceSnapshot::capture());
let baseline = baseline_for_file_scenario(&cfg, query, Some(path), cfg.repetitions)
.expect("warm filtered baseline");
let value = find_benchmark_line(
&cfg,
"warm_filtered_files",
Some(query),
Some("files"),
Some(path),
&observation,
resources,
baseline,
);
print_find_benchmark_line(value.clone());
observed_values.push(value);
scenario_count += 1;
}
}
for (scenario, kind) in [("warm_directories", "directories"), ("warm_mixed", "mixed")] {
for query in &cfg.queries {
let before = ResourceSnapshot::capture();
let observation = run_find_observation(
&runtime,
query,
kind,
None,
cfg.limit,
cfg.repetitions,
false,
)
.expect("warm kind fffind benchmark");
let resources = before.delta(ResourceSnapshot::capture());
let value = find_benchmark_line(
&cfg,
scenario,
Some(query),
Some(kind),
None,
&observation,
resources,
None,
);
print_find_benchmark_line(value.clone());
observed_values.push(value);
scenario_count += 1;
}
}
let before = ResourceSnapshot::capture();
let repeated = run_find_observation(
&runtime,
&cold_query,
"files",
None,
cfg.limit,
cfg.repetitions,
false,
)
.expect("repeated same query fffind benchmark");
let resources = before.delta(ResourceSnapshot::capture());
let baseline = baseline_for_file_scenario(&cfg, &cold_query, None, cfg.repetitions)
.expect("repeated same query baseline");
let value = find_benchmark_line(
&cfg,
"repeated_same_query",
Some(&cold_query),
Some("files"),
None,
&repeated,
resources,
baseline,
);
print_find_benchmark_line(value.clone());
observed_values.push(value);
scenario_count += 1;
if let Some(value) = run_cwd_churn_scenario(&cfg, &runtime, &cold_query).expect("cwd churn") {
print_find_benchmark_line(value.clone());
observed_values.push(value);
scenario_count += 1;
}
print_find_summary(&cfg, scenario_count, &observed_values);
}