Call graph: cache Rust module parents and caller paths per refresh
A call-graph refresh after an edit runs on a lazy index, where the eager crate-wide memo is disabled. So every Rust module_parent lookup scanned all module-declaring files, and each caller's module path was walked again for every resolution. That cost about 14% of daemon CPU in this morning's performance sweep.
This builds one parent map per refresh snapshot, on first use, and memoizes each caller's module path for that snapshot. There's no format change.
Measured on the whole aft crate (1,226 Rust files, 880,141 references) on a 16-core build server:
| Before | After | |
|---|---|---|
| Refresh after a one-line edit | 98.8 s | 72.4 s |
| module_parent time | 15.0 s | 1.27 s |
| Caller module-path time | 15.8 s | 1.31 s |
| Parent inventory scans | 484,074 | 1 |
All 184,964 stored edge rows, after both a cold build and the refresh, matched column for column against the original scan path (ignored benchmark bench_rust_module_paths_whole_crate_refresh). Cold builds aren't faster.
Checks: formatting, Linux compile with warnings denied, and the Windows compile check passed on the build server. That run's call-graph test filter hit its time limit on a loaded server before printing results, so CI is the gate for the tests.
Summary by cubic
Speeds up call-graph refreshes after edits by caching Rust module parents and caller module paths per refresh snapshot, instead of rescanning module-declaring files on every lookup.
- Refresh after a one-line edit of the whole aft crate drops from 98.8 s to 72.4 s: module_parent time goes 15.0 s → 1.27 s, caller module-path time 15.8 s → 1.31 s, and parent inventory scans drop from 484,074 to 1.
- Stored results are unchanged: all 184,964 edge rows match column for column before and after a refresh, and cold builds aren't faster. There's no format change.
- Adds an ignored whole-crate parity benchmark and an AFT slice descriptor covering the refresh path.