Skip to content

[perf] Add static node-rule map for O(1) visitor lookup - #8544

Draft
TomasVotruba wants to merge 6 commits into
mainfrom
worktree-static-node-rule-map
Draft

TomasVotruba wants to merge 6 commits into
mainfrom
worktree-static-node-rule-map

Conversation

@TomasVotruba

Copy link
Copy Markdown
Member

Why

RectorNodeTraverser::getVisitorsForNode() is cached per node class, but the first hit for each node class loops every active rule (~600-700) and calls getNodeTypes() + is_a() on each. Cold-cache cost on every distinct node class.

What

  • Precomputed static map config/node-rule-map.json: concrete node class -> rule FQCN list, is_a-resolved (so Node/ClassLike/CallLike subscribers are folded in per concrete node). Covers core rules + symfony/doctrine/phpunit.
  • getVisitorsForNode() now does an O(1) map lookup, keeps only the active rules, and orders them by their registration position. No per-node foreach over all rules.
  • Rules not in the map (e.g. third-party rules, or a node class absent from the map) fall back to the original is_a loop, so behavior is preserved.
  • Generator script build/build-node-rule-map.php (composer build-node-rule-map) reuses RectorClassFinder and filters to RectorInterface (PostRectors excluded). Output is deterministic (sorted).
  • CI step Node Rule Map Up To Date regenerates and fails on drift, so a listed rule changing its node types cannot silently go stale.

Tests

  • RectorNodeTraverserTest extended with a static-map resolution case; existing cases still pass.
  • Full rules-tests suite exercises the map path with real rules.

@TomasVotruba TomasVotruba changed the title [Performance] Add static node-rule map for O(1) visitor lookup [perf] Add static node-rule map for O(1) visitor lookup Oct 8, 2026
@TomasVotruba
TomasVotruba force-pushed the worktree-static-node-rule-map branch 3 times, most recently from ad378dd to fca12ef Compare October 8, 2026 20:59
@TomasVotruba
TomasVotruba marked this pull request as draft October 8, 2026 21:01
@TomasVotruba
TomasVotruba force-pushed the worktree-static-node-rule-map branch from fca12ef to 503127a Compare October 10, 2026 09:29
TomasVotruba and others added 6 commits October 10, 2026 11:41
Precompute a node-class to rule map for core + symfony/doctrine/phpunit
rules, shipped as config/node-rule-map.json. RectorNodeTraverser uses it
to resolve visitors per node class without looping all rules on cold cache.
Rules missing from the map (e.g. third-party) fall back to the is_a loop.
The static map file is immutable, decode it once instead of on every
prepareNodeVisitors() / refreshPhpRectors().
@TomasVotruba
TomasVotruba force-pushed the worktree-static-node-rule-map branch from fe529b4 to 2beb0ef Compare October 10, 2026 09:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants