Repository navigation
Avoid concatenated cache keys in CompilingMatcher - #186
Conversation
| if (!isset(self::$compiledCheckerCache[$operator][$constraintString])) { | ||
| $code = $constraint->compile($operator); | ||
| self::$compiledCheckerCache[$cacheKey] = $function = eval('return function($v, $b){return '.$code.';};'); | ||
| self::$compiledCheckerCache[$operator][$constraintString] = $function = eval('return function($v, $b){return '.$code.';};'); | ||
| } else { | ||
| $function = self::$compiledCheckerCache[$cacheKey]; | ||
| $function = self::$compiledCheckerCache[$operator][$constraintString]; | ||
| } |
There was a problem hiding this comment.
Is there really a point in splitting the cacheKey here into sub-arrays? That just creates more arrays without benefit AFAICT?
There was a problem hiding this comment.
There is a tradeoff: nested arrays avoid constructing concatenated string keys on every call but introduce more arrays and extra lookups. But accessing by reference then improved bucket lookup time again. My measurements showed that nested arrays alone saved roughly 30 ms in pool optimization.
So there was a small measured benefit on my project but I mean, it's minimal and we can drop it. Just figured CompilingMatcher is built for the hot path so it's probably justified to go the extra mile to squeeze out even more perf 😇
There was a problem hiding this comment.
Yeah that makes sense for $resultCache IMO, but self::$compiledCheckerCache is not using the reference trick, and you access once in isset() then once on read at line 92, so you do two extra hash lookups and gain zero benefits except the lack of a string concat to build $cacheKey? Not a huge deal either way tho..
There was a problem hiding this comment.
Ok I did the change I meant.. I can't prove it's any better because this is way too jittery on my machine, none of the changes are meaningfully faster enough to really tell.
Co-authored-by: Jordi Boggiano <j.boggiano@seld.be>
Co-authored-by: Jordi Boggiano <j.boggiano@seld.be>
CompilingMatchercaches match results but currently builds a combined string key on every call, even when the result is already cached.For example, checking whether
2.1.0satisfies>=2.0.0:Checking the same constraint against
2.2.0etc. repeats this work, including concatenating the unchanged operator and constraint.This PR stores results in nested arrays instead:
Each call accesses the operator/constraint bucket by reference, then looks up the version within it. This avoids constructing the combined string key over and over again.
The reference bit might look strange at first, that's why I've added a comment explaining it. It basically lets the lookup and any subsequent write reuse the same bucket and it has a measurable positive impact on performance.
On a real project's
composer update, pool optimization took 678 ms instead of 723 ms (~6% faster) so really this complexity is all about optimizing the hot path 😊