perf(kernel): compute a call's shape facts once per reference
Indexing JetBrains Exposed, a Kotlin project, takes a median 31.2 s instead of 37.3 s (16% faster, 5 alternating runs per build).
| Metric | Before | After | Change |
|---|---|---|---|
Exposed, codegraph init | 37.3 s | 31.2 s | 16% lower |
| nocodb (no Kotlin, control) | 6.81 s | 7.06 s | no clear change (run ranges overlap) |
Median of 5 runs per build, baseline run twice in the same alternation (Exposed baseline against baseline: 0.0%). Every Exposed run on the new build (30.65 to 32.09 s) is faster than every baseline run (36.07 to 40.26 s). On nocodb the new build's runs (6.65 to 7.40 s) overlap the baseline's (6.74 to 7.06 s) and none of the changed code runs there, so I read it as run-to-run noise, not a slowdown. Measured on d59ca2b3 plus this change, before it was rebased onto #488, which does not touch these files. Node 24.21.0, same dist/, only the kernel binary differs.
What changes
When a call could reach several same-named functions, call_shape_target is asked about each candidate in turn. For Kotlin it re-read the call site, re-ran the unknown-receiver check, re-found the infix site and re-typed the receiver every time, though all four depend only on the reference. A probe build earlier put 7.3 s of Exposed's 9.8 s in call_shape_target into those four.
They now live in a small RefFacts value that retain_lang_scope_tracked creates once per reference and each candidate fills on first use. Rust, Go, Swift and Scala share the call-site part of it.
The output is unchanged: Exposed 28,689 nodes, 82,842 edges and 36,202 call edges, nocodb 24,570 nodes, 48,763 edges and 15,039 call edges, with no call edge only in either build (scripts/index-metrics.mjs).
Checks
- Full vitest suite on the rebased head: 702 files, 8,950 passed, 39 skipped, 0 failed; kernel golden dumps unchanged.
cargo test --release: 70 passed, 1 ignored. The kernel build's clippy gate passes. - No new test: the behavior is unchanged, and the golden dumps and Kotlin resolver tests already cover what the facts feed.
- README: checked the "About this fork" tables and performance paragraphs; none quotes an Exposed index time or this path, so no row changes. CHANGELOG entry added under Fixes.
Not covered: the other per-candidate work in the Kotlin scope steps (kotlin_member_reachable, kotlin_unknown_extension_target) is untouched.
Summary by cubic
Speeds up Kotlin indexing by computing a call's shape facts once per reference and sharing them with all candidates, instead of recomputing the call site, unknown-receiver check, infix site, and receiver type for each same-named candidate. Indexing JetBrains Exposed takes a median 31.2 s instead of 37.3 s (16% faster); the nocodb control shows no clear change, and the graph output is unchanged on both.
Benchmarks
- Across 5 alternating runs per build, every new-build Exposed run (30.65–32.09 s) beat every baseline run (36.07–40.26 s).
- nocodb runs (6.65–7.40 s) overlap the baseline's (6.74–7.06 s), so that's run-to-run noise, not a slowdown.
- Exposed keeps 28,689 nodes, 82,842 edges, and 36,202 call edges; nocodb keeps 24,570 nodes, 48,763 edges, and 15,039 call edges — no call edge exists in only one build.
Summary by CodeRabbit
- Performance
- Improved Kotlin project indexing when calls have multiple same-named candidates: indexing time decreased from a 37.3-second median to 31.2 seconds, with the same number of call edges.