Skip to content

Share whole-variable liveness keys and walk the smaller set - #6990

Merged
ondrejmirtes merged 1 commit into
2.3.xfrom
faster-literal-key-union-write
Nov 7, 2026
Merged

ondrejmirtes merged 1 commit into
2.3.xfrom
faster-literal-key-union-write

Conversation

@ondrejmirtes

@ondrejmirtes ondrejmirtes commented Nov 6, 2026 •

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 one of the slowest benchmarks in the CodSpeed workflow (#6974).

VariableLivenessResolver. 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.

The commit ports the change to the turbo-ext C++ twin, with the regenerated VariableLivenessResolver.h.

This PR first also memoized UnionType::hasTemplateOrLateResolvableType(). ec1df78 landed the same memo on 2.3.x, so that commit was dropped in the rebase and the numbers below are against 2.3.x with it.

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 14.441 G 10.082 G −30.2 %
bug-11297 8.685 G 8.685 G +0.0 %
wordpress-user 2.636 G 2.636 G +0.0 %

Peak memory of literal-key-union-offset-write drops from 475 MB to 240 MB.

Wall clock: bin/phpstan analyse --debug on literal-key-union-offset-write, level 8 + bleedingEdge, median of 7 interleaved runs per side after one warm-up pair, tmpDir removed 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: 2.73 s → 2.28 s (−16 %; ranges 2.63–2.95 s and 2.18–2.40 s).

Checks

After the rebase:

  • make tests, with and without the turbo extension loaded (23,561 tests). Both runs have one failure, OverridingMethodRuleConfigPhpTest::testTentativeReturnTypesInPhpVersionRange, which fails the same way on unchanged 2.3.x in this environment.
  • turbo-ext build, smoke.php ALL OK, signature-parity.php OK, side-by-side.php OK.
  • make phpstan is clean.

Before the rebase, turbo-ext/tests/walk-trace.php was identical (702,499 trace lines), and the raw analysis output of all 86 bench files was identical to 2.3.x's, both without and with the extension.

… 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
@ondrejmirtes
ondrejmirtes force-pushed the faster-literal-key-union-write branch from 339d009 to 133b195 Compare November 7, 2026 08:14
@ondrejmirtes ondrejmirtes changed the title Share whole-variable liveness keys and memoize UnionType::hasTemplateOrLateResolvableType() Share whole-variable liveness keys and walk the smaller set Nov 7, 2026
@ondrejmirtes
ondrejmirtes merged commit 6bba813 into 2.3.x Nov 7, 2026
1038 of 1043 checks passed
@ondrejmirtes
ondrejmirtes deleted the faster-literal-key-union-write branch November 7, 2026 08:33
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