Skip to content

Share whole-variable liveness keys and memoize UnionType::hasTemplateOrLateResolvableType() - #6990

Open
ondrejmirtes wants to merge 2 commits into
2.3.xfrom
faster-literal-key-union-write
Open

ondrejmirtes wants to merge 2 commits into
2.3.xfrom
faster-literal-key-union-write

Conversation

@ondrejmirtes

Copy link
Copy Markdown
Member

tests/bench/data/literal-key-union-offset-write.php (1000 × $b = $a; $b['lN'] = N; echo $b['lN']; with $a keyed by a union of 2000 literals) is the slowest benchmark in the CodSpeed workflow (#6974), at about 13 minutes under the CPU simulation. Two costs that grow with that shape:

VariableLivenessResolver (d0b1cf1). Every write that replaces the whole variable observes and kills the read keys of all of its slots. compileAccesses() copied all of them into each such write, so 1000 writes × ~2000 keys of $b. Going backwards over the function, each of those writes then looped over all 2000 kill keys and all 1001 marker slots of $b, while the live set at that point holds only a handful of keys. The keys are now built once per variable and shared, which PHP's copy-on-write makes free. Removing keys and markers from the live set, and matching observed keys against it, now walks the smaller of the two sets, the way the per-slot markers already did. markersByName holds the markers of all slots of a variable, so a whole-variable write removes them in one pass.

UnionType::hasTemplateOrLateResolvableType() (339d009). TypeUtils::resolveLateResolvableTypes() asks this for every resolved type, so every assignment scanned the whole 2000-member key union. It is now memoized in the union, like isNull() and isCallable().

Both commits port their change to the turbo-ext C++ twin, with the regenerated headers (UnionType.h, TemplateUnionType.h, TemplateBenevolentUnionType.h, VariableLivenessResolver.h).

Measurements

Instruction counts from CodSpeed's CPU simulation, using the harness from #6974 on an Amp orb with PHP 8.5.12 (absolute numbers differ slightly from the CI runner's):

benchmark 2.3.x this PR change
literal-key-union-offset-write 19.297 G 10.529 G −45.4 %
nullsafe-chain-walk 17.377 G 17.378 G +0.0 %
bug-10538 15.521 G 15.522 G +0.0 %
bug-14624 15.264 G 15.261 G −0.0 %
bug-11297 8.708 G 8.686 G −0.3 %
bug-7903 7.778 G 7.767 G −0.1 %
bug-8215 5.368 G 5.369 G +0.0 %
wordpress-user 2.638 G 2.636 G −0.1 %

Peak memory changes by +0.0 to +0.4 %. Under the simulation, literal-key-union-offset-write now takes 4.3 minutes instead of 6.2 on this machine.

Wall clock: bin/phpstan analyse --debug on literal-key-union-offset-write, median of 7 interleaved runs per side, result cache and container cleared before each run (about 0.9 s of each run is building the container), no turbo extension. Amp orb, Intel Xeon @ 2.60 GHz, 16 vCPUs, PHP 8.5.12 NTS: 3.70 s → 2.82 s.

Checks

  • make tests, with and without the turbo extension loaded (23,547 tests). Both runs have one failure, OverridingMethodRuleConfigPhpTest::testTentativeReturnTypesInPhpVersionRange, which fails the same way on unchanged 2.3.x.
  • turbo-ext/tests/walk-trace.php: identical (702,499 trace lines).
  • turbo-ext strict build, smoke.php ALL OK, signature-parity.php OK, side-by-side.php OK.
  • The raw analysis output of all 86 bench files is identical to 2.3.x's, both without and with the extension.
  • make phpstan and phpcs are clean.

… set

Every write replacing a whole variable observes and kills the read keys
of all its slots, and compileAccesses() copied them into each such write:
quadratic in the writes and reads of a variable. They are now built once
per variable and shared. Removing them, the markers of a variable and the
observed keys from the live set walks the smaller of the two sets, like
the per-slot markers already did.

Amp-Thread-ID: https://ampcode.com/threads/T-01a1ad6c-dec2-72ff-bbe8-5b3b62746c94
TypeUtils::resolveLateResolvableTypes() asks for every resolved type, so
an array keyed by a union of thousands of literals scanned the whole
union on every assignment.

Amp-Thread-ID: https://ampcode.com/threads/T-01a1ad6c-dec2-72ff-bbe8-5b3b62746c94
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant