Skip to content

perf(index): apply the warm-load cache by move — cut the startup RSS peak#207

Merged
Hessesian merged 1 commit into
refactor/unified-resolutionfrom
perf/f0-warm-load-peak
Jul 6, 2026
Merged

perf(index): apply the warm-load cache by move — cut the startup RSS peak#207
Hessesian merged 1 commit into
refactor/unified-resolutionfrom
perf/f0-warm-load-peak

Conversation

@Hessesian

Copy link
Copy Markdown
Owner

Summary

F0 of the memory plan: the warm-load apply path held the deserialized WorkspaceIndexResult AND a second copy of every FileData simultaneously (file_contributions cloned into the Indexer's Arcs while result.files still owned the originals), and jemalloc retained the freed transients — users saw ~2× the necessary RSS after startup.

  • apply_workspace_result now consumes result by value; file_contributions_owned moves each FileData into its Arc (Arc::new(result.data), no clone).
  • The Arc<WorkspaceIndexResult> wrapper is gone — it existed only so send_progress_end could read counts after the spawn; the three counts are captured before the move. Review verified the sequentiality (apply is awaited before progress-end; no other holder), and that the old code never used the Arc for retry either.
  • A second candidate (cache_entry_to_file_result copy) was implemented, measured at 0 MB peak effect (both builders free-as-they-go), and reverted — deferred to pair with the interning work.

Measured (the probe in the base PR is the meter)

RSS apply peak: 487.1 → 408.1 MB (−79 MB); unaccounted slack 207.9 → 129.1 MB — the delta matches the eliminated single FileData duplication; the remaining slack is the genuinely-derived FileContributions maps, which the planned file-id interning targets next.

1440 tests green, clippy -D warnings clean. Task-scoped review: Approved (no Critical/Important).

Stacked on the probe PR.

🤖 Generated with Claude Code

https://claude.ai/code/session_01EJsyQ1UgF8uJYR4HgpxEBB

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR reduces warm-load startup memory by eliminating a second in-memory copy of each FileData during the apply phase. It does this by making apply_workspace_result consume WorkspaceIndexResult and moving each FileData directly into the Indexer’s Arc<FileData>, while still emitting the same progress summary by capturing counts before the move.

Changes:

  • Change Indexer::apply_workspace_result to take WorkspaceIndexResult by value and drain result.files, moving FileData into Arc<FileData> via a new file_contributions_owned.
  • Remove the Arc<WorkspaceIndexResult> wrapper in the warm-apply path and capture progress summary counts up-front instead.
  • Update tests/docs in apply_tests and memory_probe_tests to reflect the new ownership/move behavior and expected apply-phase RSS profile.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated no comments.

File Description
src/indexer/scan.rs Captures progress counts before spawning apply, and passes WorkspaceIndexResult by value into apply_workspace_result to enable move-based apply.
src/indexer/memory_probe_tests.rs Updates the probe narrative and call site to reflect apply_workspace_result consuming the result (no post-apply drop(result)).
src/indexer/apply.rs Introduces file_contributions_owned and changes apply_workspace_result to consume and drain result.files, moving FileData into Arc without deep clones.
src/indexer/apply_tests.rs Updates tests to pass WorkspaceIndexResult by value to the new apply_workspace_result signature.

@Hessesian Hessesian force-pushed the perf/f0-warm-load-peak branch 2 times, most recently from 38ce988 to 43297e0 Compare July 6, 2026 12:25
@Hessesian Hessesian changed the base branch from perf/memory-probe to refactor/unified-resolution July 6, 2026 12:28
…S peak

The warm-load path deep-cloned every file's FileData twice: once into the
FileIndexResult vec (cache_entry_to_file_result) and again into the Indexer's
Arc (file_contributions -> Arc::new(result.data.clone())). At apply time both
copies coexisted, so the deserialized workspace footprint doubled and jemalloc
retained the freed transient — users saw ~2x the necessary RSS after startup.

Add file_contributions_owned, which moves FileData straight into its Arc, and
make apply_workspace_result take WorkspaceIndexResult by value: it now drains
result.files into the maps instead of cloning them, so the two full copies never
coexist. finalize_workspace_scan drops its Arc<WorkspaceIndexResult> wrapping
(the apply task and send_progress_end run sequentially, not concurrently) and
captures the progress-end summary counts before moving result into apply.

memory_retainer_profile apply peak: 487.1 MB -> 408.1 MB (-79 MB), with the
eliminated ~130 MB of duplicated FileData exactly matching the drop in
jemalloc-retained slack (207.9 MB -> 129.1 MB unaccounted).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EJsyQ1UgF8uJYR4HgpxEBB
@Hessesian Hessesian force-pushed the perf/f0-warm-load-peak branch from 43297e0 to 7c95d9d Compare July 6, 2026 12:28
@Hessesian Hessesian merged commit 607a923 into refactor/unified-resolution Jul 6, 2026
4 checks passed
@Hessesian Hessesian deleted the perf/f0-warm-load-peak branch July 6, 2026 12:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants