Repository navigation
perf(resolve): enumerate imported members without walking their routes - #1348
Merged
Merged
Conversation
Co-Authored-By: jason.han <hanhuijun@gmail.com>
Contributor
Author
|
I'll fix CI failures and address comments from users with write access. I'll skip comments containing "(aside)".
|
Co-Authored-By: jason.han <hanhuijun@gmail.com>
…e walk Co-Authored-By: jason.han <hanhuijun@gmail.com>
4 of 6 tasks
This was referenced Oct 21, 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.
What and why
The 0.11.0 performance record (measured against
v0.10.1) finds the language server'sReferencesColdat 44.5k → 6.28M allocs/op andRenameWarmat 14.9k → 12.4M. Bisected over the pull-request merges to #1062 (hidden colliding imports, 45k → 17.4M), later cut to 6.28M by #1158 and #1209. The allocation profile puts three quarters of what is left inResolver.namespaceChildren, which for every index entry under an imported namespace:Index.GetFQNto decide whether it is a re-export (reexportedUnder);withoutHiddenImports, which answered from the memoizedhidesBelowthat nothing on the route is hidden (hiddenOnRoute);[][]ElementFilterthroughIndex.ReexportGates, whichadmitsUnderNamethen read once, accepting the first unfiltered route (gateRoutes).This change keeps the decisions and drops the allocations:
hiddenOnRouteaskshidesBelowfirst;reexportedUndercompares the owner chain against the prefix segment by segment with the newsymbols.FQNIs;admitsUnderNamewalks the routes in place through the newIndex.EachReexportGate(ReexportGatesstays for its other caller andgateRoutesreturns nil when no route qualifies).ReferencesWarm,WorkspaceUpdateandFormatEditsare unchanged.What this does not recover: both rows still allocate about 1 GiB/op and take over a second, against 49 ms and 23 ms before #1062. The space profile puts that in
elementList.add: each query re-enumerates the members every wildcard import brings (importedMembersInto, whose per-edge memoenumMembersis cleared when the outermost enumeration returns), per occurrence inImportedBesidefor a rename and per document for the references index, and the enumeration itself is what #1062 made expensive. Keeping that enumeration across a query is a design change in the same code the in-flightperf/name-first-recursive-import-matchingbranch reworks, so it is left to a follow-up rather than done here.How it was verified
TestImportedMemberEnumerationDoesNotWalkRoutesPerMember(internal/semantic/resolve): a differentialtesting.AllocsPerRunbound — enumerating a member re-exported through a second wildcard import (RimportsQ::*,QimportsL::*; namespaces of 2n and n members, per member) costs at most 3 allocations. Ondevelopbefore this change it costs 6.23 and the test fails; with it 2.23.TestFQNIsMatchesFQNOfandTestEachReexportGateWalksReexportGates(internal/semantic/symbols): the new helpers answer asFQNOfandReexportGatesdo, stop at the first accepting visit, and allocate nothing.Benchmark A/B, six interleaved rounds on the record's machine, pre-regression commit
0af9fcb36(the parent of the fix(resolve): hide imported memberships that collide by name #1062 merge) in a scratch worktree |developb60a18c20| this branch:ReferencesColdRenameWarmReferencesWarmWorkspaceUpdatego test ./internal/semantic/... ./internal/frontend/lsp ./internal/workspace/... ./tests/resolve ./internal/check/...pass (the two failures without the pilot and training corpora present pass with them),go build ./...,go veton the touched packages,gofmt -l .empty,python3 scripts/changelog.py check.Checklist
make testandmake lintpass locally (the packages above; the full suite runs in CI)changes/unreleased/<slug>.<section>.md, not as an edit toCHANGELOG.mdmake docs-countsrun if a gate count moved (none moved)F4,K5) in the body, docs, or changelogLink to Devin session: https://nasa-jpl-demo.devinenterprise.com/sessions/cf22d7a746814d20b2f0eaf11682fef9
Open in Devin Desktop: https://nasa-jpl-demo.devinenterprise.com/desktop/session/cf22d7a746814d20b2f0eaf11682fef9?variant=devin
Requested by: @HuiJun