Repository navigation
perf(resolution): read a bare JS/TS call's local binding once per match — graph byte-identical - #2934
Merged
Merged
Conversation
…ch — graph byte-identical matchByExactName asked isLocallyBoundJsName for every cross-file candidate of a bare JavaScript or TypeScript call, though the answer depends only on the reference's name and file, which are the same for every candidate. The helper memoizes it per file and name, but each call still built the key and read the memo. The answer is now kept in a lazy local for the one match, at the same place in the filter, so candidates, order, scoring and confidence are untouched. Validation: fresh indexes of codegraph's own source, excalidraw, vben, express and zod dump byte-identical to main's (nodes, edges with their metadata, unresolved refs, files), and express stays identical through sync after a JS file is added, edited and deleted. A CPU profile of indexing codegraph's own source (all threads, two runs per side) puts isLocallyBoundJsName's self time at 150-173 ms on main and 45-46 ms here; the scans it guards (bindsAtSites) are unchanged. Whole-index CPU on vben, 10 interleaved runs, moves less than its noise. The PR's 8 tests pin the matcher's behaviour and pass here and on main. Original PR #2871 by @luojiyin1987. Fixes #2870 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 27, 2026
inth3shadows
added a commit
to inth3shadows/codegraph
that referenced
this pull request
Oct 29, 2026
Brings 4955eac..787eafb (colbymchenry#3014, colbymchenry#3017, colbymchenry#3020, colbymchenry#3018, colbymchenry#3023). Clean auto-merge. README: drop colbymchenry#2871 (closed upstream; colbymchenry#2934 merged in its place, and name-matcher already matches upstream).
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.
Summary
A bare JavaScript or TypeScript call no longer asks, once per candidate, whether its own file binds the name locally. The answer depends only on the reference's name and file, so
matchByExactNamenow keeps it in a lazy local for the one match. The graph is byte-identical.Root cause
matchByExactNamefilters its exact-name candidates, and for a bare JS/TS call it drops every cross-file candidate when the calling file binds the name itself (a parameter, aconst). That check,isLocallyBoundJsName(name, file), ran inside the per-candidate filter. It is memoized per file and name, so it never rescanned the file, but every candidate still built the memo key and read the memo. The issue measured 387,362 such checks on CodeGraph's own source, of which 15,392 were the first in their match.What came from the contributor, and what we changed
From @luojiyin1987 (#2871, issue #2870): the lazy local in
matchByExactName, read at the same place in the filter, and 8 tests that pin the matcher's behaviour (zero, one and tied candidates, a same-file winner, a parameter binding, import and member candidates, no carry-over between references, and nested scopes and import aliases through indexing and sync).Ours: the commit message only. No CHANGELOG bullet: nothing a user can see changes, and no index-time difference shows above noise (below).
Validation
Byte identity (nodes, edges with their metadata, unresolved refs and files, by natural keys), main vs this change, fresh indexes: codegraph's own source, excalidraw, vben, express and zod are identical. express also stays identical through
codegraph syncafter a JS file is added, edited and deleted. That ran on 4c572e0; sincematchByExactNameitself changed on main afterwards (#1936), the five fresh indexes were repeated on 4eb8c2a with the change rebased onto it: identical again. Later main commits touch no code this change reads.Tests: the PR's 8 pass, here and on main (they pin existing behaviour; nothing should change).
Cost. The saving is real but small. A CPU profile of a fresh index of CodeGraph's own source (all 17 threads, two runs per side, order alternated) puts
isLocallyBoundJsName's self time at 150 and 173 ms on main and 46 and 45 ms with this change.bindsAtSites, the scan the memo guards, is unchanged (749/755 vs 702/784 ms), as is the rest of the index (about 130 s of sampled thread time either way). That is about 0.1 s per index here, below what whole-index timing can resolve: vben, 10 interleaved runs (5 in each order), median process CPU 30.5 s on main vs 31.3 s with the change, where whichever side runs second in a pair is about 0.65 s slower and the paired differences spread by about 1.4 s.Suite on this commit (
CODEGRAPH_KERNEL_EXPECT=1 npx vitest run --maxWorkers=4 --minWorkers=1, Windows): 763 files passed, 6 skipped; 9,079 tests passed, 85 skipped, 0 failed.Fixes #2870
Lands #2871 by @luojiyin1987.
🤖 Generated with Claude Code