Skip to content

Run the benchmark on PHP 7.4 in addition to PHP 8.5 - #6121

Merged
ondrejmirtes merged 6 commits into
2.2.xfrom
bench-php-7.4
Jul 28, 2026
Merged

Run the benchmark on PHP 7.4 in addition to PHP 8.5#6121
ondrejmirtes merged 6 commits into
2.2.xfrom
bench-php-7.4

Conversation

@ondrejmirtes

@ondrejmirtes ondrejmirtes commented Jul 28, 2026

Copy link
Copy Markdown
Member

Both the baseline and the test job are now matrixed over PHP 7.4 and 8.5. The 7.4 leg downgrades the source code the same way the tests-with-old-phpunit job does (downgrade root + tests/ dependencies, then ./.github/actions/downgrade-code).

What the 7.4 runtime forced:

  • phpbench is pinned to 1.2.14 there — 1.2.15+ requires ^8.1. That version has no aggregate-preview generator, which my-report extends, so the 7.4 leg uses the built-in aggregate report instead. Its mode column already inlines the percent diff against the baseline, so the output is still readable.
  • tests/composer.json pins the Composer platform to PHP 8.2, so even with --ignore-platform-reqs Composer resolved PHPBench's Symfony dependencies to 6.4, which cannot be parsed on PHP 7.4 (unexpected 'static' in UnicodeString.php). The other 7.4 workflows never hit this because they only install root dependencies. Unsetting the platform makes Composer resolve against the real PHP version and pick Symfony 5.4 throughout, which also makes pinning individual packages unnecessary.
  • PHPBench attributes are replaced with annotations in RegressionBench. tests/bench is not part of build/downgrade.php, so it has to be valid PHP 7.4 on its own, and a multi-line attribute is a syntax error there (single-line ones silently become comments and the benchmark would lose its revs/iterations/asserts). This is also why the file was excluded from parallel-lint — that exclusion is gone now, so the lint job checks it again. The assertion expression has to stay on one line, and no annotation name may appear anywhere else in the docblock: the annotation reader parses prose mentions too.
  • Data files that cannot be parsed on the running PHP version are skipped, through the same // lint first-line comment TypeInferenceTestCase uses. 10 of the 72 data files skip on 7.4 — the test job reports 62 assertions there and 72 on 8.5.

The baseline file is stored per PHP version: tests/bench/storage/baseline-8.5.xml (renamed from baseline.xml) and baseline-7.4.xml (taken from the phpbench-baseline-7.4 artifact of the baseline job).

Two things worth knowing for the future, both now documented in comments:

  • PHPBench does not fail when the file passed to --file does not exist. It warns and runs with an empty baseline, so every assertion passes and the job is green while testing nothing. The test job checks for the file first.
  • GitHub runners differ in speed by up to 2x. The first baseline-7.4.xml committed here came from a fast runner — it measured PHP 7.4 as 2x faster than PHP 8.5, and every variant of the test job then looked like a +100% regression. The <baseline-machine> values PHPBench records in each file (md5, file_rw) identify the machine class and should be compared against the currently committed baseline before replacing it.

Note that this variance is pre-existing and applies to the 8.5 leg too: both jobs compare a fresh run against a baseline recorded on a different runner, so a bad draw can exceed the 10-50% thresholds. Removing it would mean recording the baseline and running the test on the same machine within one job.

🤖 Generated with Claude Code

https://claude.ai/code/session_01HijxpJYQ5uGfdo6AkXV7TA


- name: "Run phpbench baseline"
run: "tests/vendor/bin/phpbench run --dump-file=tests/bench/storage/baseline.xml --ansi"
run: "tests/vendor/bin/phpbench run --dump-file=tests/bench/storage/baseline-${{ matrix.php-version }}.xml --ansi"

- name: "Run phpbench test"
run: "tests/vendor/bin/phpbench run --file=tests/bench/storage/baseline.xml --report=my-report --ansi"
run: "tests/vendor/bin/phpbench run --file=tests/bench/storage/baseline-${{ matrix.php-version }}.xml --report=${{ matrix.report }} --ansi"

- name: "Run phpbench test"
run: "tests/vendor/bin/phpbench run --file=tests/bench/storage/baseline.xml --report=my-report --ansi"
run: "tests/vendor/bin/phpbench run --file=tests/bench/storage/baseline-${{ matrix.php-version }}.xml --report=${{ matrix.report }} --ansi"
with:
working-directory: "tests/"

- uses: ./.github/actions/downgrade-code
Comment thread .github/workflows/bench.yml Fixed
@@ -1,4 +1,4 @@
<?php
<?php // lint >= 8.1

@staabm staabm Jul 28, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in #6122 I adjusted the RequiredPhpVersionCommentTest which checks the lint comments

# with an empty baseline and every assertion passes
- name: "Check that the baseline exists"
run: |
test -f tests/bench/storage/baseline-${{ matrix.php-version }}.xml \
- name: "Check that the baseline exists"
run: |
test -f tests/bench/storage/baseline-${{ matrix.php-version }}.xml \
|| (echo "Missing tests/bench/storage/baseline-${{ matrix.php-version }}.xml, download it from the phpbench-baseline-${{ matrix.php-version }} artifact of the Baseline job and commit it." && exit 1)
- name: "Check that the baseline exists"
run: |
test -f tests/bench/storage/baseline-${{ matrix.php-version }}.xml \
|| (echo "Missing tests/bench/storage/baseline-${{ matrix.php-version }}.xml, download it from the phpbench-baseline-${{ matrix.php-version }} artifact of the Baseline job and commit it." && exit 1)
with:
working-directory: "tests/"

- uses: ./.github/actions/downgrade-code
ondrejmirtes and others added 6 commits July 28, 2026 13:28
Reverts 030b59f and the Configurator part of 0587638. In the phar setup
the main process does not load the extension and workers get it via
-d extension=, so the key differed between them and a single run compiled two
containers - undoing 8fd0fda "Do not compile special DI container for
workers". The env unsets right above exist to keep main and workers on one
container.

Nothing in the compiled container depends on the stubs: every shadowed class is
a value object or static helper, none is a service, a constructor dependency of
one, or named in any conf/*.neon. Once a shadowed class is registered as a
service the container does differ, but only by an inert $wiring entry for the
stub's native parent class, and main and workers have to share one container
anyway - a container that genuinely depended on the stubs cannot be fixed by a
cache key.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EeBk5QAUD6JJecNn6RPYBJ
Both the baseline and the test job are matrixed over PHP 7.4 and 8.5. The
7.4 leg downgrades the source code the same way the test workflow does.

phpbench 1.2.14 is the last version that supports PHP 7.4, so that leg
pins it, and because it does not have the aggregate-preview report that
'my-report' extends, it uses the built-in 'aggregate' report - its mode
column already inlines the percent diff against the baseline.

PHPBench attributes are replaced with annotations so that RegressionBench
is valid PHP 7.4 - tests/bench is not downgraded, and a multi-line
attribute is a syntax error there. That also lets parallel-lint check the
file again.

Data files that cannot be parsed on the PHP version the benchmark runs on
are skipped through the '// lint' comment, the same way
TypeInferenceTestCase does it.

The baseline is now stored per PHP version. baseline-7.4.xml has to be
taken from the phpbench-baseline-7.4 artifact and committed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HijxpJYQ5uGfdo6AkXV7TA
The prose in the class docblock mentioned an annotation name, which the
annotation reader tried to parse as an actual annotation with a null value.

tests/composer.json pins the Composer platform to PHP 8.2, so Composer
resolved PHPBench's Symfony dependencies to 6.4 even with
--ignore-platform-reqs, and those cannot be parsed on PHP 7.4. Unsetting
the platform makes Composer resolve against the real PHP version, which
also makes pinning the individual Symfony packages unnecessary.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HijxpJYQ5uGfdo6AkXV7TA
The baseline comes from the phpbench-baseline-7.4 artifact of the
Baseline job.

PHPBench does not fail when the file passed to --file does not exist - it
warns and runs with an empty baseline, so every assertion passes and the
job is green while testing nothing. The test job now checks for the file
first.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HijxpJYQ5uGfdo6AkXV7TA
The first committed baseline came from a runner that was twice as fast as
the one that recorded baseline-8.5.xml - PHP 7.4 measured 2x faster than
PHP 8.5, which is not possible. Every variant of the test job then looked
like a +100% regression.

The replacement comes from a runner whose <baseline-machine> values match
the ones in baseline-8.5.xml.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HijxpJYQ5uGfdo6AkXV7TA
@ondrejmirtes
ondrejmirtes merged commit 4a8955b into 2.2.x Jul 28, 2026
174 of 176 checks passed
@ondrejmirtes
ondrejmirtes deleted the bench-php-7.4 branch July 28, 2026 11:29
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.

3 participants