oxc/.github/workflows/benchmark.yml
overlookmotel f0cbbbe28c
ci: build each benchmark only with deps it needs (#3221)
This PR builds on #3201 to further speed up the benchmarks and reduce CI
time.

* Build and run each benchmark as separate job (like before).
* But now each bench is only built with the dependencies it needs.
* For linter benchmarks, build benchmark in 1 job (like #3201 does).
* Run each linter fixture in a separate job as they're slow.

This reduces total time to complete benchmarks from between 6m-7m to
~4m40s.

All the individual jobs complete in under 1m30s, except for building
linter benchmark which takes 2m30s. So there won't be the problem of
blocking the CI queue that there was before.

NB: I did try this before, and didn't see a benefit. But I realized
today what I was doing wrong - it only works once the caches are
populated by a previous run on main branch.

So the CI times in this PR won't look good, but once it's merged to
main, it will take effect. Here it is running on main branch of my fork:

https://github.com/overlookmotel/oxc/actions/runs/9030511348

I also added a step to delete the temp artefacts which aren't needed
once the run has completed.
2024-05-10 22:01:24 +08:00

339 lines
11 KiB
YAML

# Benchmarks are sharded.
#
# Each benchmark (parser, transformer, etc) runs in parallel in a separate job.
# Linter benchmarks are much slower to build and run than the rest, so linter benchmark
# is built in 1 job, and then run on each fixture in parallel in separate jobs.
# When all jobs are complete, a final job uploads all the results to CodSpeed.
#
# Sharding is not natively supported by CodSpeed, so we use a hacky method to achieve it.
# 1. Intercept the data which `CodSpeedHQ/action` would normally upload to CodSpeed for each job.
# 2. Once all runs are complete, combine the data for all the runs together.
# 3. Upload the combined data to CodSpeed as one.
# This is performed by some short NodeJS scripts in `tasks/benchmark/codspeed`.
name: Benchmark
on:
workflow_dispatch:
pull_request:
types: [opened, synchronize]
paths:
- '**/*.rs'
- 'napi/parser/**/*.js'
- 'napi/parser/**/*.mjs'
- 'Cargo.lock'
- '.github/workflows/benchmark.yml'
- 'tasks/benchmark/codspeed/*.mjs'
push:
branches:
- main
paths:
- '**/*.rs'
- 'napi/parser/**/*.js'
- 'napi/parser/**/*.mjs'
- 'Cargo.lock'
- '.github/workflows/benchmark.yml'
- 'tasks/benchmark/codspeed/*.mjs'
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }}
cancel-in-progress: true
jobs:
# Build and run benchmarks for all components except linter
benchmark:
name: Benchmark
runs-on: ubuntu-latest
strategy:
fail-fast: true
matrix:
component:
- lexer
- parser
- transformer
- semantic
- minifier
- codegen_sourcemap
- sourcemap
steps:
- name: Checkout Branch
uses: taiki-e/checkout-action@v1
- name: Install Rust Toolchain
uses: ./.github/actions/rustup
with:
shared-key: benchmark-${{ matrix.component }}
save-cache: ${{ github.ref_name == 'main' }}
- name: Install codspeed
uses: taiki-e/install-action@v2
with:
tool: cargo-codspeed
- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version: 20
registry-url: 'https://registry.npmjs.org'
- name: Start bench results interceptor server
working-directory: ./tasks/benchmark/codspeed
env:
COMPONENT: ${{ matrix.component }}
run: |
corepack enable
pnpm install
node capture.mjs &
- name: Build benchmark
env:
RUSTFLAGS: "-C debuginfo=1 -C strip=none -g --cfg codspeed"
shell: bash
run: |
cargo build --release -p oxc_benchmark --bench ${{ matrix.component }} \
--no-default-features --features ${{ matrix.component }} --features codspeed
mkdir -p target/codspeed/oxc_benchmark
mv target/release/deps/${{ matrix.component }}-* target/codspeed/oxc_benchmark
rm target/codspeed/oxc_benchmark/*.d
- name: Run benchmark
uses: CodSpeedHQ/action@v2
timeout-minutes: 30
with:
# Dummy token for tokenless runs, to suppress logging hash of metadata JSON (see `upload.mjs`)
token: ${{ secrets.CODSPEED_TOKEN || 'dummy' }}
upload-url: http://localhost:${{ env.INTERCEPT_PORT }}/upload
run: cargo codspeed run
- name: Upload bench data artefact
uses: actions/upload-artifact@v4
with:
name: result-${{ matrix.component }}
path: ${{ env.DATA_DIR }} # env.DATA_DIR from `capture.mjs`
if-no-files-found: error
retention-days: 1
# Build linter benchmark.
# Linter benchmarks are much slower than the rest, so we run each fixture in a separate job.
# But only build the linter benchmark once.
build-linter:
name: Build Linter Benchmark
runs-on: ubuntu-latest
steps:
- name: Checkout Branch
uses: taiki-e/checkout-action@v1
- name: Install Rust Toolchain
uses: ./.github/actions/rustup
with:
shared-key: benchmark-linter
save-cache: ${{ github.ref_name == 'main' }}
- name: Build benchmark
env:
RUSTFLAGS: "-C debuginfo=1 -C strip=none -g --cfg codspeed"
shell: bash
run: |
cargo build --release -p oxc_benchmark --bench linter \
--no-default-features --features linter --features codspeed
mkdir -p target/codspeed/oxc_benchmark
mv target/release/deps/linter-* target/codspeed/oxc_benchmark
rm target/codspeed/oxc_benchmark/*.d
- name: Upload Binary
uses: actions/upload-artifact@v4
with:
if-no-files-found: error
name: benchmark-linter
path: ./target/codspeed/oxc_benchmark
retention-days: 1
# Run linter benchmarks. Each fixture in a separate job.
benchmark-linter:
name: Benchmark linter
needs: build-linter
runs-on: ubuntu-latest
strategy:
fail-fast: true
matrix:
fixture:
- 0
- 1
steps:
- name: Checkout Branch
uses: taiki-e/checkout-action@v1
- name: Download Binary
uses: actions/download-artifact@v4
with:
name: benchmark-linter
path: ./target/codspeed/oxc_benchmark
- name: Fix permission loss
shell: bash
run: |
ls ./target/codspeed/oxc_benchmark
chmod +x ./target/codspeed/oxc_benchmark/*
- name: Install codspeed
uses: taiki-e/install-action@v2
with:
tool: cargo-codspeed
- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version: 20
registry-url: 'https://registry.npmjs.org'
- name: Start bench results interceptor server
working-directory: ./tasks/benchmark/codspeed
env:
COMPONENT: linter
FIXTURE: ${{ matrix.fixture }}
run: |
corepack enable
pnpm install
node capture.mjs &
- name: Run benchmark
uses: CodSpeedHQ/action@v2
timeout-minutes: 30
env:
FIXTURE: ${{ matrix.fixture }}
with:
# Dummy token for tokenless runs, to suppress logging hash of metadata JSON (see `upload.mjs`)
token: ${{ secrets.CODSPEED_TOKEN || 'dummy' }}
upload-url: http://localhost:${{ env.INTERCEPT_PORT }}/upload
run: cargo codspeed run
- name: Upload bench data artefact
uses: actions/upload-artifact@v4
with:
name: result-linter${{ matrix.fixture }}
path: ${{ env.DATA_DIR }} # env.DATA_DIR from `capture.mjs`
if-no-files-found: error
retention-days: 1
# benchmark-napi:
# name: Benchmark NAPI parser
# runs-on: ubuntu-latest
# if: false
# steps:
# - name: Checkout Branch
# uses: taiki-e/checkout-action@v1
# - name: Install Rust Toolchain
# uses: ./.github/actions/rustup
# with:
# shared-key: 'benchmark_napi'
# save-cache: ${{ github.ref_name == 'main' }}
# - name: Install codspeed
# uses: taiki-e/install-action@v2
# with:
# tool: cargo-codspeed
# - name: Install Node.js
# uses: actions/setup-node@v4
# with:
# node-version: 20
# registry-url: 'https://registry.npmjs.org'
# - name: Start bench results interceptor server
# working-directory: ./tasks/benchmark/codspeed
# env:
# COMPONENT: 'parser_napi'
# run: |
# corepack enable
# pnpm install
# node capture.mjs &
# # CodSpeed gets measurements completely off for NAPI if run in `CodSpeedHQ/action`,
# # so instead run real benchmark without CodSpeed's instrumentation and save the results.
# # Then "Run Rust benchmark" step below runs a loop of some simple Rust code the number
# # of times required to take same amount of time as the real benchmark took.
# # This is all a workaround for https://github.com/CodSpeedHQ/action/issues/96
# - name: Build NAPI Benchmark
# working-directory: ./napi/parser
# run: |
# corepack enable
# pnpm install
# pnpm build
# - name: Run NAPI Benchmark
# working-directory: ./napi/parser
# run: node parse.bench.mjs
# - name: Build Rust benchmark
# env:
# RUSTFLAGS: "-C debuginfo=2 -C strip=none -g --cfg codspeed"
# shell: bash
# run: |
# cargo build --release -p oxc_benchmark --bench parser_napi --no-default-features --features codspeed_napi
# mkdir -p target/codspeed/oxc_benchmark/
# mv target/release/deps/parser_napi-* target/codspeed/oxc_benchmark
# rm -rf target/codspeed/oxc_benchmark/*.d
# - name: Run Rust benchmark
# uses: CodSpeedHQ/action@v2
# timeout-minutes: 30
# with:
# run: cargo codspeed run
# # Dummy token for tokenless runs, to suppress logging hash of metadata JSON (see `upload.mjs`)
# token: ${{ secrets.CODSPEED_TOKEN || 'dummy' }}
# upload-url: http://localhost:${{ env.INTERCEPT_PORT }}/upload
# - name: Upload bench data artefact
# uses: actions/upload-artifact@v4
# with:
# name: 'parser_napi'
# path: ${{ env.DATA_DIR }}
# if-no-files-found: error
# retention-days: 1
# Upload combined benchmark results to CodSpeed
upload:
name: Upload benchmarks
# needs: [benchmark, benchmark-linter, benchmark-napi]
needs: [benchmark, benchmark-linter]
runs-on: ubuntu-latest
steps:
- name: Checkout Branch
uses: taiki-e/checkout-action@v1
- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version: 20
registry-url: 'https://registry.npmjs.org'
- name: Create temp dir
working-directory: ./tasks/benchmark/codspeed
run: |
corepack enable
pnpm install
node create_temp_dir.mjs
- name: Download artefacts
uses: actions/download-artifact@v4
with:
merge-multiple: true
pattern: result-*
path: ${{ env.DATA_DIR }} # env.DATA_DIR from `create_temp_dir.mjs`
- name: Upload to Codspeed
working-directory: ./tasks/benchmark/codspeed
env:
CODSPEED_TOKEN: ${{ secrets.CODSPEED_TOKEN }}
run: node upload.mjs
- name: Delete temporary artefacts
uses: geekyeggo/delete-artifact@v5
with:
name: |
result-*
benchmark-linter
failOnError: false