From 3f4a514bf8b539d9b882899187d772b748eed2ae Mon Sep 17 00:00:00 2001 From: Long Ho Date: Fri, 22 May 2026 09:12:48 -0400 Subject: [PATCH 1/2] perf: memoize import resolution --- crates/codescythe/analyze/resolver.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/crates/codescythe/analyze/resolver.rs b/crates/codescythe/analyze/resolver.rs index ec509a6..0135351 100644 --- a/crates/codescythe/analyze/resolver.rs +++ b/crates/codescythe/analyze/resolver.rs @@ -1,8 +1,11 @@ use super::*; +use std::cell::RefCell; + pub(super) struct ModuleResolver { resolver: ResolverGeneric, index_by_path: HashMap, + resolution_cache: RefCell>, } struct IgnoredResolverMetadataFileSystem { @@ -94,6 +97,7 @@ impl FileSystem for IgnoredResolverMetadataFileSystem { } } +#[derive(Clone, Copy)] pub(super) enum ImportResolution { Project(usize), External, @@ -149,10 +153,24 @@ impl ModuleResolver { Ok(Self { resolver, index_by_path, + resolution_cache: RefCell::new(HashMap::new()), }) } pub(super) fn resolve(&self, from: &FileData, specifier: &str) -> Result { + let cache_key = (from.relative.clone(), specifier.to_string()); + if let Some(resolution) = self.resolution_cache.borrow().get(&cache_key) { + return Ok(*resolution); + } + + let resolution = self.resolve_uncached(from, specifier)?; + self.resolution_cache + .borrow_mut() + .insert(cache_key, resolution); + Ok(resolution) + } + + fn resolve_uncached(&self, from: &FileData, specifier: &str) -> Result { match self.resolver.resolve_file(&from.path, specifier) { Ok(resolution) => { let path = normalize_path(resolution.path()); From 7c077a68ad3cb7b0a5a670737dd0ccc12c5517b8 Mon Sep 17 00:00:00 2001 From: Long Ho Date: Fri, 22 May 2026 09:33:07 -0400 Subject: [PATCH 2/2] docs: refresh benchmark numbers --- README.md | 8 ++++---- benchmarks/README.md | 18 +++++++++--------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 5479c4f..3dc26d5 100644 --- a/README.md +++ b/README.md @@ -36,10 +36,10 @@ produced: | Fixture | Benchmarked files | Codescythe | Knip | | --- | ---: | ---: | ---: | -| `microsoft/vscode` | 9,537 | 738.0ms | 5.76s | -| `grafana/grafana` | 8,701 | 771.0ms | 9.29s | -| `elastic/kibana` | 86,056 | 11.64s | 45.35s | -| `renovatebot/renovate` | 2,488 | 118.5ms | 846.1ms | +| `microsoft/vscode` | 9,398 | 1.11s | 4.22s | +| `grafana/grafana` | 8,358 | 833.2ms | 9.51s | +| `elastic/kibana` | 85,928 | 12.96s | 53.33s | +| `renovatebot/renovate` | 2,456 | 154.5ms | 900.5ms | Counts reflect each fixture's generated benchmark config after excludes. Run `pnpm benchmark` to measure the same fixtures locally. diff --git a/benchmarks/README.md b/benchmarks/README.md index 1b5b1b0..61fa0dd 100644 --- a/benchmarks/README.md +++ b/benchmarks/README.md @@ -63,7 +63,7 @@ unset. ## Current Numbers -Local run on May 20, 2026 with the checked-in fixture configs: +Local run on May 22, 2026 with the checked-in fixture configs: ```sh bazel build -c opt //crates/codescythe_cli:codescythe @@ -73,14 +73,14 @@ node --experimental-transform-types benchmarks/run.ts --samples 3 --warmups 1 -- ```text fixture tool mean rme samples ops/sec --------- ---------- --------- --------- ------- ------- -vscode codescythe 1468.2ms +/-4.42% 4 0.68 -vscode knip 4669.8ms +/-27.79% 3 0.21 -grafana codescythe 1031.1ms +/-3.69% 5 0.97 -grafana knip 10302.1ms +/-28.43% 3 0.10 -kibana codescythe 15932.2ms +/-10.85% 3 0.06 -kibana knip 61479.6ms +/-14.43% 3 0.02 -renovate codescythe 176.3ms +/-2.92% 17 5.67 -renovate knip 954.5ms +/-18.01% 5 1.05 +vscode codescythe 1111.9ms +/-1.36% 5 0.90 +vscode knip 4223.1ms +/-4.89% 3 0.24 +grafana codescythe 833.2ms +/-4.11% 5 1.20 +grafana knip 9513.4ms +/-56.75% 3 0.11 +kibana codescythe 12963.8ms +/-25.85% 3 0.08 +kibana knip 53327.5ms +/-33.69% 3 0.02 +renovate codescythe 154.5ms +/-4.79% 18 6.47 +renovate knip 900.5ms +/-6.09% 5 1.11 ``` ## Vendored Conformance