Resolve a node's file and package dependencies in one pass over unique reflections - #6475
Merged
Merged
Conversation
…e reflections The reflections a node depends on were walked twice - once for the analysed files, once for the non-analysed files and packages - normalizing every file path both times. They are now resolved in a single pass. The reflections are also collected keyed by their object id, so each is kept once: every level of a class hierarchy repeated the interfaces it inherits, and the classes one node references share most of their ancestors, so 55% of the reflections walked were duplicates. The versioned extension check looks the symbol up by name before asking whether it is built-in, which goes through several layers of reflection for nearly every dependency while nearly none of them is such a symbol. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0147ixdtEww7ewC6Jz6isghx
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.
After every analysed node,
FileAnalyserCallbackresolves the node's dependencies to the files and Composer packages the result cache records. Profilingbin/phpstan analyse -l 8 src/Analyser/ src/Rules/ src/Type/ --debugshowed three sources of avoidable work there:NodeDependencies::getFileDependencies()andgetNonAnalysedDependencies()were called back to back on the same list. Both calledgetFileName()andnormalizePath()on every reflection. They are now one method,getFileAndPackageDependencies(). It returns the analysed files, the non-analysed files and the packages in one pass, in the same order as before.buildClassDependencies()addedgetInterfaces()again at every parent level, even though it already returns inherited interfaces, and the classes one node references share most of their ancestors.DependencyResolvernow keys the reflections byspl_object_id()and merges them with+=instead ofarray_merge(). Deduplicating this way costs nothing:resolveDependencies()takes the same time as before.resolveVersionedExtensionPackage(), added in 677df31) calledisBuiltin()first for every dependency, which goes through several layers of BetterReflection calls. Only ds and couchbase symbols can match. It now looks the name up in that small map first and callsisBuiltin()only on a hit.Numbers
Measured on the command above, same session, OPcache on, without the turbo extension.
resolveDependencies()End to end, over 6 alternating base/head pairs, instructions retired dropped by 1.0% (432.7G → 428.3G). Every head run was below every base run (paired t ≈ −7.8). The wall-time difference (~0.4 s of ~52 s) is within the noise. The analysis output is identical to the base branch.
Verification
make testspasses.make phpstanand phpcs are clean.ext-ds-versionscripts were run locally on the base branch and on this one. Their results match. The few scripts that fail locally fail the same way on the base branch because of the local environment.🤖 Generated with Claude Code
https://claude.ai/code/session_0147ixdtEww7ewC6Jz6isghx