-
Notifications
You must be signed in to change notification settings - Fork 8
Description
After having spent time looking at the implementation of the PR benchmark workflow and in-tree benchmarks in fastify/point-of-view, fastify/fastify, and fastify/workflows, I've managed to pull it all together. Hopefully the result is more usable with less code duplication, and provides better actionable information to PR reviewers.
I feel like things are in a stable place now, so I'm seeking feedback on if this is going to be valuable to reviewers, my approach, and the changes to each of the three repos needed. Open to any and all feedback before I spent any more time on it.
Demo
fastify
point-of-view
Required changes
fastify/workflows: mweberxyz@9cee011
fastify/fastify: mweberxyz/fastify@053330e
fastify/point-of-view: mweberxyz/point-of-view@07b17c8
Sample PRs
fastify/fastify: PR from fork
Merges code from a fork into my fork, to demonstrate that the "base" benchmark are run against the target of the PR. Additionally, it shows warnings in the comment because the "head" aka PR branch does not run the parser.js correctly and all requests return 404s.
fastify/point-of-view: PR from same repo with performance degredation
Merges code from a branch into the default branch of the same fork. It reverts a performance improvement, to demonstrate what it looks like when a PR really tanks performance.
Approach
- Everything needed to run, parse, send comments, and remove the benchmark label is contained in the re-usable workflow
- Re-usable workflows and custom actions each come with their pros and cons. In the end, I decided to keep the entirety of the logic in a re-usable workflow for ease of maintenance, though I admit the JS-in-YAML is a bit unwieldy.
- Some benefits are we don't need to pass around GITHUB_TOKEN, we avoid a build step, and it fits in better with the rest of the workflows already defined in this repo
- Each file in the input
benchmarks-dirdirectory is executed (except any file specified in the inputfiles-to-ignore), then autocannon is run 3 times* for 5 seconds* each against each file, taking the maximum result of mean req/s of the three runs- Following conclusion of benchmark runs, a table is sent to the PR as a comment
- Any results that differ by 5% or more are bolded
- Autocannon is loaded via
npx- Any plugin/core can use the workflow without regard for whether
autocannonis a listed dep, or installed - When invoked this way,
autocannonwrites it's table output to stderr, so the raw results can be seen if needed in the action logs- Example: https://github.com/mweberxyz/fastify/actions/runs/8040188150/job/21958002386 (expand one of the "Run Benchmark" steps
- Any plugin/core can use the workflow without regard for whether
- Autocannon's
--on-portis used to spawn benchmark scripts- removes the need for the logic currently in
fastify/point-of-view/benchmark.js
- removes the need for the logic currently in
- Selection of node versions is moved to the
node-versionsinput- the current
fastify/workflowsworkflow uses different versions of Node for benchmarks than is currently implemented infastify/fastify
- the current
- Static outputs removed, results moved to GHA artifacts
- in part due to the previous, but also to keep history of these runs over time
- If any benchmark needs to add autocannon arguments, they can be defined in a comment in the benchmark file itself
- example:
examples/benchmark/parser.jsinfastify/fastify
- example:
Lessons
- fix(plugins-benchmark-pr): run comparison benchmark against target #120 still has potentially incorrect logic
- When commits are added to
mainafter the PR is created, thegithub.event.pull_request.base.shais not updated. That is to say, when running thebasebenchmarks, they always run against themaincommit as-of-the-time the PR was created. - Fixed in POC by using the
github.event.pull_request.base.refinstead
- When commits are added to
Future work
- Test error and failure states more extensively
- Add input-configurable
timeout-minutesto the benchmark steps - Correctly handle when a PR adds or removes a benchmark
- Add input-configurable
- Experiment with self-hosted runners as a strategy to reduce run-to-run variance
- Make benchmark run and benchmark duration input-configurable (see * above in Approach)
- Factor out logic used in GHA to be usable by developers locally
- Would be nice as a developer to run
npm run benchmarkand see the same type of output
- Would be nice as a developer to run