perf: Iterate through nullsafe expression chains#6084
Conversation
|
This pull request has been marked as ready for review. |
|
Hey Florian, nice to see you contributing :-). could you give us a small series of steps how this improvement can be reproduced? Thanks PS: I just rebased the PR, as it was missing ~150 commits of 2.2.x |
Replace recursive calls in NullsafeShortCircuitingHelper::getType() with a loop while preserving the same expression-chain cases and return behavior. This helper is a flat CPU hotspot under expression type resolution. Iteration avoids repeated PHP method calls and stack frames for chained property, method, array, and static accesses. Benchmark: uncached Sylius analysis of 2,366 files with eight workers, five runs per variant. Median elapsed time fell from 28.35 s to 27.22 s (-4.0%) and aggregate user+sys CPU from 172.90 s to 170.65 s (-1.3%). In the combined profile, helper CPU fell from 5.72 s to 2.70 s (-52.8%).
5097016 to
ef44511
Compare
So it boils down to me cloning https://github.com/sylius/sylius (because it is a huge code base that uses PHPStan), running PHPStan with |
|
PS: you could try latest 2.2.x-dev version on your project. it should be waaay faster than the latest stable release |
|
I benchmarked the |
|
thank you @realFlowControl |
Every property fetch / method call resolves its type by walking down to the chain root to detect a nullsafe operator (NullsafeShortCircuitingHelper), costing O(N²) walk steps per chain of depth N — with or without an actual nullsafe operator in the chain. Deep loop-wrapped plain chains make that walk dominate: 3.71s -> 3.14s wall (-15%), -18% user CPU from the recursion-to-loop rewrite. The real-world counterpart is Symfony TreeBuilder fluent chains (300+ calls in one statement) in Sylius bundle Configuration classes, which dropped up to 23% per file. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016szvNF5RXhACdfMQNc6DVL
While profiling PHPStan on the Sylius project, I found several optimization opportunities. This PR addresses the first one.
It replaces recursive calls in
NullsafeShortCircuitingHelper::getType()with a loop while preserving the same expression-chain handling and return behavior. This helper is a CPU hotspot during expression type resolution. Iteration avoids repeated PHP method calls and stack frames.In five uncached Sylius analysis runs per variant, covering 2,366 files with eight workers, median elapsed time fell from 28.35 s to 27.22 s (-4.0%), while median aggregate CPU time (user + sys) fell from 172.90 s to 170.65 s (-1.3%). In the combined candidate profile, CPU time attributed to this helper fell from 5.72 s to 2.70 s (-52.8%).
PHPBench showed -2.5% locally as well 🎉
Benchmarks were run on an Apple M4 Max with PHP 8.5.7