Skip to content

Measure benchmarks by counted instructions with CodSpeed instead of phpbench - #6974

Open
ondrejmirtes wants to merge 1 commit into
2.3.xfrom
codspeed-benchmarks
Open

ondrejmirtes wants to merge 1 commit into
2.3.xfrom
codspeed-benchmarks

Conversation

@ondrejmirtes

Copy link
Copy Markdown
Member

The phpbench job measures wall-clock time on shared GitHub runners, so its numbers move with the runner rather than with the code. It currently fails on 2.3.x itself. This PR replaces it with a Benchmarks workflow that counts executed instructions under CodSpeed's CPU simulation (Valgrind with a fixed simulated cache). The counts do not depend on the runner, so a change in them comes from the code.

How a benchmark is measured

  • tests/bench/measure.php boots PHPStan in-process, analyses tests/bench/Warmup.php to load and compile the common code paths, then measures only the analysis of the benchmark file. Start/stop comes from CodSpeed's instrument-hooks library, called over FFI. Boot (about 5 G instructions) is no longer part of every number.
  • tests/bench/run.php runs one benchmark. First it runs natively, with a 60 s timeout, to fill the caches in a tmpDir of its own and catch infinite loops early. Then it runs under codspeed run --mode=simulation with a 90 min timeout. It records instructions, estimated cycles (instructions + 10 × L1 misses + 100 × LL misses) and peak memory.
  • tests/bench/phpstan.neon is the config the benchmarks are analysed with: level 8, bleedingEdge, inferPrivatePropertyTypeFromConstructor. tests/bench/data stays excluded from PHPStan's own analysis.

The workflow

  • shard.php splits tests/bench/data into 4 shards by expected instruction count (longest first). Each shard runs its benchmarks in parallel, one per CPU.
  • compare.php compares everything with tests/bench/expected.neon. A metric may differ by 5 % (10 % for memory), and by at least 25 M instructions / 35 M estimated cycles, so the quickest benchmarks, which mostly run shared code, are not held to a fraction of a millisecond. I picked that floor from measuring every benchmark under 1 G at 2.3.0, 2.3.5 and 2.3.x: files under 200 M drifted by up to 11 M between releases, while the real regressions from eea09a949/4dab5341b (40–170 M) still fail.
  • Only slower and not measured benchmarks fail the job. Faster and new ones pass but ask for expected.neon to be updated, because until then a later slowdown back to the old numbers would go unnoticed.
  • The report is printed in the job log and the job summary, with the full table collapsed. Each benchmark that differs is also annotated on its file in tests/bench/data. The report ends with a reproducer command and the two commands that accept the new numbers from the benchmark-measured artifact.

Numbers

Also in this PR

  • Removed phpbench: bench.yml, phpbench.json, BenchCase, RegressionBench, the stored baseline, the Makefile targets and the dependency in tests/composer.json.
  • The parser, comparator, report and sharding are pure classes covered by unit tests in tests/PHPStan/Benchmark. All I/O stays in the scripts.
  • CLAUDE.md explains the benchmarks and how to analyse a file from tests/bench/data.

Comment thread .github/workflows/benchmarks.yml Fixed
Comment thread .github/workflows/benchmarks.yml Fixed
Comment thread .github/workflows/benchmarks.yml Fixed
Comment thread .github/workflows/benchmarks.yml Fixed
Comment thread .github/workflows/benchmarks.yml Fixed
Comment thread .github/workflows/benchmarks.yml Fixed
Comment thread .github/workflows/benchmarks.yml Fixed
Comment thread .github/workflows/benchmarks.yml Fixed
Comment thread .github/workflows/benchmarks.yml Fixed
Comment thread .github/workflows/benchmarks.yml Outdated
Comment on lines +51 to +52
- name: "Checkout"
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
Comment on lines +83 to +84
- name: "Checkout"
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
Comment thread .github/workflows/benchmarks.yml Fixed
Comment thread .github/workflows/benchmarks.yml Fixed
Comment thread .github/workflows/benchmarks.yml Fixed
Comment thread .github/workflows/benchmarks.yml Fixed
Comment thread .github/workflows/benchmarks.yml Fixed
Comment thread .github/workflows/benchmarks.yml Fixed
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

- name: "Install PHP"
uses: "shivammathur/setup-php@eb7c497e18156a6bbabfef1d3a82760b9eda3962" # 2.40.0
ini-file: production
ini-values: memory_limit=-1

- uses: "ramsey/composer-install@65e4f84970763564f46a70b8a54b90d033b3bdda" # v4.0.0
- uses: "ramsey/composer-install@65e4f84970763564f46a70b8a54b90d033b3bdda" # v4.0.0

- name: "Install CodSpeed"
run: |
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

- name: "Install PHP"
uses: "shivammathur/setup-php@eb7c497e18156a6bbabfef1d3a82760b9eda3962" # 2.40.0
coverage: "none"
php-version: "8.5"

- uses: "ramsey/composer-install@65e4f84970763564f46a70b8a54b90d033b3bdda" # v4.0.0
…hpbench

Counts the executed instructions of only the benchmark file's analysis
under CodSpeed's CPU simulation, so the numbers no longer depend on the
runner. Compares them with tests/bench/expected.neon in a sharded
Benchmarks workflow and removes phpbench.

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.

2 participants