Skip to content

[perf] Use native instanceof in NodeGroup::isStmtAwareNode - #8746

Merged
TomasVotruba merged 1 commit into
mainfrom
tv-perf-stmt-aware-match
Oct 20, 2026
Merged

TomasVotruba merged 1 commit into
mainfrom
tv-perf-stmt-aware-match

Conversation

@TomasVotruba

@TomasVotruba TomasVotruba commented Oct 20, 2026 •

Copy link
Copy Markdown
Member

A fresh full-run profile (in-process, worker-faithful) put NodeGroup::isStmtAwareNode()'s closure at the top of the call-count table - it is called per node on the traversal hot path.

It used array_any(self::STMTS_AWARE, fn ($class) => $node instanceof $class) - a closure call plus a dynamic $node instanceof $class for each of the 18 classes, per node. Replaced with native instanceof via match (true): no per-element closure, no dynamic instanceof, same subclass semantics (verified equal on every node type).

match (true) is required in source: the active codeQuality set (SwitchTrueToMatchRector) rewrites any switch (true) back to match. For the published rector/rector the downgrade pipeline (DowngradeMatchToSwitchRector) converts it to switch (true), so downgraded (PHP 7.2) users run the switch form - which is the fastest of the three.

Numbers

Non-stmt-aware-heavy node mix (the worst case that exhausts every instanceof), CPU time, same run, 18.6M calls:

form us/call vs before
before: array_any + closure 3.22 1x
source: match (true) 0.74 ~4.4x
downgraded: switch (true) 0.43 ~7.5x

Correctness verified identical across all node types for all three forms. Full vendor/bin/phpunit (4990 tests) green, PHPStan and ECS clean.

isStmtAwareNode() is called per node on the traversal hot path (top call count in a full profile). It used array_any() over STMTS_AWARE with a closure doing a dynamic $node instanceof $class per element - a closure call plus dynamic instanceof for each of 18 classes.

Replace with native instanceof via match(true): no per-element closure, no dynamic instanceof, same subclass semantics.

bench/node_group_is_stmt_aware_bench.php (non-stmt-aware-heavy node mix): 3.34 -> 0.75 us/call (~4.5x).
@TomasVotruba
TomasVotruba merged commit 1dcaf15 into main Oct 20, 2026
49 checks passed
@TomasVotruba
TomasVotruba deleted the tv-perf-stmt-aware-match branch October 20, 2026 21:33
@TomasVotruba

Copy link
Copy Markdown
Member Author

LGTM 👍

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