Repository navigation
fix(symbols): stop the index merge rescanning the whole index per file - #53
Merged
Merged
Conversation
For every changed file, _index_folder_with_snapshot rebuilt the entire symbol dict and every occurrence bucket to drop that file's old entries, so a build of k files cost k x index size. A cold build of concord (500 files) spent 108 s of 134 s profiled in those comprehensions. The CPU-bound loop holds the GIL and slows the shared daemon's searches. _IndexMerge indexes symbols and occurrence buckets by source file, and keeps each bucket as runs of consecutive same-file rows, so removing a file touches only its own entries. The published index is unchanged, including key and row order, emptied-bucket removal, and duplicate-id suffixing. The test-file flag is now computed once per file instead of once per occurrence. Measured on concord, against the 3.7.2 release under load average 17-24: cold build 97-99 s -> 18-24 s, full re-parse 136-145 s -> 24.5 s; the saved index bodies are identical. Refs: LGREP-52
Splitting every touched bucket into runs on the first removal made a one-file refresh slower than the old full filter: on a 427 MB index (981,821 rows), the busiest file's refresh took 0.58 s against 0.21 s. Most of that was the cyclic garbage collector rescanning the large heap while about 67,000 run containers were allocated. A bucket's first removal now filters it into a list the merge owns. Only a second removal splits it into runs, and appends go to plain lists, so cold builds never split. Measured on the same index: one file 0.20 -> 0.25 s, 10 files 3.52 -> 0.87 s, 100 files 34.9 -> 1.44 s. On concord: cold build 81.6 -> 15.1 s, full re-parse 118.8 -> 21.2 s, with identical saved index bodies. Refs: LGREP-52
This was referenced Oct 25, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
For each changed file,
_index_folder_with_snapshot(src/lgrep/tools/index_folder.py) rebuilt the whole symbol dict and every occurrence bucket to drop that file's old entries. A build of k files therefore cost k × index size. Profiling a cold build of a 500-file repository showed 108 s of 134 s in those comprehensions (122 milliondict.getcalls), against about 12 s for parsing. The loop is CPU-bound Python, so it holds the GIL and slows every other search in the shared daemon (LGREP-52).Change
_IndexMergeindexes symbols and occurrence buckets by source file, so a removal touches only the buckets that hold that file's rows.@start_byteduplicate-id suffixing. Lists of the loaded index, which the cache may share, are never mutated.Evidence
tests/test_index_folder_merge.pycompares the published index with the previous algorithm over randomized refresh histories. The histories cover modified, deleted, and added files, truncated walks, forced full re-parses, interleaved rows, empty buckets, and colliding symbol ids. They also check that the loaded index is not mutated.ruff checkandruff format --checkare clean.Timing against the 3.7.2 release:
The 427 MB rows time the merge step alone, on 981,821 rows. The saved concord index bodies were identical, field for field.