Skip to content

Cache file-level invariants per file in AbstractRector::enterNode() - #8558

Closed
TomasVotruba wants to merge 1 commit into
mainfrom
worktree-enternode-file-hoist
Closed

TomasVotruba wants to merge 1 commit into
mainfrom
worktree-enternode-file-hoist

Conversation

@TomasVotruba

Copy link
Copy Markdown
Member

Proof of concept

AbstractRector::enterNode() runs for every node that matches a rule's getNodeTypes() (the traverser already filters by node type via getVisitorsForNode()). On each call it recomputed values that are constant for the whole file:

  • getFile() -> getFilePath()
  • is_a($this, HTMLAverseRectorInterface) + File::containsHTML()
  • Skipper::matchSkip($this, $filePath) (depends only on rule + file path, never on the node)

For a file with thousands of nodes and many applicable rules, these ran per node x rule although nothing changed within the file.

This PR caches them once per file (keyed on the File instance) and leaves only the genuinely per-node work in the hot path: shouldSkipCurrentNode(), the clone probe on a skip match, refactor(), and post-processing.

Behavior

No behavior change. matchSkip, HTMLAverse and file path are all file-level invariants, so caching them per file is equivalent. Core test suite green (the only failures are the pre-existing tests/Bin/RectorTest subprocess cases, unrelated).

Benchmark - honest result

Interleaved A/B, cold cache each run, single process, broad rule set (all PHP sets + deadCode + codeQuality + codingStyle + typeDeclarations) over src/:

round baseline cached delta
1 67.25s 67.82s +0.85%
2 68.43s 65.54s -4.22%
3 69.16s 70.40s +1.79%
4 69.27s 67.25s -2.92%

Average ~ -1%, and the sign flips between rounds. The saving is real but within measurement noise at the full-run level: the hoisted calls are cheap next to parsing, PHPStan scope resolution, refactor() bodies and printing, and matchSkip's resolve() is already memoized.

Opening as a proof of concept. The refactor is clean and behavior-neutral; the question is whether the per-node cleanup is worth keeping on its own merit given the macro benchmark is flat.

@TomasVotruba
TomasVotruba force-pushed the worktree-enternode-file-hoist branch from 24964e2 to 0115e7b Compare October 9, 2026 07:00
@TomasVotruba
TomasVotruba marked this pull request as draft October 9, 2026 07:01
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.

1 participant