blob: 7a333a1a0222c3503e377021e01217d3b4066bb5 [file]
# This file defines a workflow that runs the libc++ benchmarks at the given commit.
# This workflow is intended to be triggered manually or via the Github API. As such,
# it requires several inputs that allow customizing its behavior.
name: "[libc++] Run benchmark suite against commit"
# Keep in sync with libcxx/utils/ci/lnt/dispatch-benchmarks
run-name: "[libc++] Run benchmark suite against ${{ inputs.commit }} on ${{ inputs.lnt-machine }}${{ !inputs.submit-lnt && ' (dry run)' || '' }}"
permissions:
contents: read
on:
workflow_dispatch:
inputs:
commit:
description: 'The libc++ commit to benchmark'
required: true
type: string
lnt-machine:
description: 'The LNT machine configuration to use when benchmarking and to report results as'
required: true
type: string
benchmark-suite-override:
description: |
Override the version of the benchmark suite to use (a LLVM monorepo SHA). By default, the version pinned
for this machine in machines.json is used. This override can be used to dry-run benchmarks with arbitrary
commits of the benchmark suite, but only the version pinned in machines.json can be used when actually
submitting to LNT.
required: false
type: string
filter:
description: 'An optional filter to determine which benchmarks to run'
required: false
type: string
submit-lnt:
description: 'Whether to submit the results to LNT -- dry-run unless opted in'
required: false
type: boolean
default: false
lnt-url:
description: 'The URL of the LNT instance to submit to'
required: false
type: string
default: https://lnt.llvm.org
jobs:
# Determine which configuration to run based on the `lnt-machine` input.
select-machine:
runs-on: ubuntu-26.04
outputs:
matrix: ${{ steps.select.outputs.matrix }}
steps:
- name: Checkout the machine definitions
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
# Disabling cone mode allows checking out exactly the single file we need.
sparse-checkout: libcxx/utils/ci/lnt/machines.json
sparse-checkout-cone-mode: false
- name: Select the configuration to benchmark on
id: select
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
env:
LNT_MACHINE: ${{ inputs.lnt-machine }}
BENCHMARK_SUITE_OVERRIDE: ${{ inputs.benchmark-suite-override }}
SUBMIT_LNT: ${{ inputs.submit-lnt }}
with:
script: |
const config = JSON.parse(require('fs').readFileSync('libcxx/utils/ci/lnt/machines.json', 'utf8'));
const requested = process.env.LNT_MACHINE;
const selected = config.filter(cfg => cfg['lnt-machine'] === requested);
if (selected.length === 0) {
const known = config.map(cfg => cfg['lnt-machine']).join(', ');
core.setFailed(`Unknown LNT machine '${requested}' (known machines: ${known})`);
return;
}
// Honor benchmark suite version override and make sure we don't submit if an incorrect
// override is provided.
const version_override = (process.env.BENCHMARK_SUITE_OVERRIDE || '').trim().toLowerCase();
for (const cfg of selected) {
const pinned = cfg['benchmark-suite-version'];
if (process.env.SUBMIT_LNT === 'true' && version_override && version_override !== pinned) {
core.setFailed(`Refusing to submit results for ${cfg['lnt-machine']}, since the benchmark suite was `
+ `overridden to version ${version_override}, which is different from the version `
+ `pinned in machines.json (${pinned}).`);
return;
}
if (version_override) {
cfg['benchmark-suite-version'] = version_override;
}
}
core.setOutput('matrix', JSON.stringify(selected));
run-benchmarks:
needs:
- select-machine
strategy:
matrix:
include: ${{ fromJSON(needs.select-machine.outputs.matrix) }}
fail-fast: false
runs-on: ${{ matrix.runner }}
env:
COMPILER: ${{ matrix.cxx }}
# Where the library is installed when we build it. This lives inside the workspace
# so that actions/checkout cleans it up between runs on self-hosted runners.
INSTALL_DIR: ${{ github.workspace }}/install
steps:
- name: Checkout the LLVM monorepo
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
# We benchmark arbitrary historical commits, which requires the full history to be available.
fetch-depth: 0
fetch-tags: true
- name: Install Python
if: runner.os == 'Linux' # installed via Homebrew on macOS
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: '3.14'
- name: Select Xcode
if: runner.os == 'macOS'
run: echo "DEVELOPER_DIR=/Applications/Xcode_${{ matrix.xcode-version }}.app/Contents/Developer" >> $GITHUB_ENV
- name: Install dependencies via Homebrew
if: runner.os == 'macOS'
run: |
brew update
brew install ninja cmake python@3.14
echo "$(brew --prefix python@3.14)/bin" >> "$GITHUB_PATH"
- name: Diagnose tools in use
run: |
cmake --version
ninja --version
"${COMPILER}" --version
python3 --version
- name: Setup virtual environment
run: |
python3 -m venv .venv
source .venv/bin/activate
pip install -r libcxx/utils/ci/lnt/requirements.txt
# Build the library. This is only done when the machine configuration provides a 'build' key,
# otherwise we assume that the configuration does not require building anything (e.g. a system
# standard library).
#
# A build failure is tolerated on purpose, since the library doesn't build at every historical commit.
# We still submit an empty run for such commits.
- name: Build libc++ at ${{ inputs.commit }}
if: ${{ matrix.build != '' }}
continue-on-error: true
uses: ./.github/workflows/libcxx/build-at-commit
with:
git-repo: ${{ github.workspace }}
commit: ${{ inputs.commit }}
install-dir: ${{ env.INSTALL_DIR }}
compiler: ${{ matrix.cxx }}
cmake-cache: ${{ matrix.build.cmake-cache }}
- name: Run the benchmarks
env:
BENCHMARK_SUITE_VERSION: ${{ matrix.benchmark-suite-version }}
BUILT_LIBCXX: ${{ matrix.build != '' }}
COMMIT: ${{ inputs.commit }}
FILTER: ${{ inputs.filter }}
LIT_PARAMS: ${{ join(matrix.lit-params, ' ') }}
LNT_MACHINE: ${{ matrix.lnt-machine }}
TEST_CONFIG: ${{ matrix.test-config }}
run: |
source .venv/bin/activate
filter_arg=()
if [ -n "${FILTER}" ]; then
filter_arg=(--filter "${FILTER}")
fi
read -ra configured_params <<< "${LIT_PARAMS}"
lit_params=()
for param in "${configured_params[@]}"; do
lit_params+=(--param "${param}")
done
# When the library is built in this workflow, point the testing configuration at the installation.
# Everything else needed by the configuration comes from machines.json.
if [ "${BUILT_LIBCXX}" = "true" ]; then
# If the library build failed, still create an install directory so we
# run the benchmarks. The benchmarks will fail, and we'll submit an empty
# LNT report.
mkdir -p "${INSTALL_DIR}"
lit_params+=(--param "libcxx_installation=${INSTALL_DIR}")
fi
libcxx/utils/ci/lnt/run-benchmarks \
--test-suite-commit "${BENCHMARK_SUITE_VERSION}" \
--machine "${LNT_MACHINE}" \
--compiler "${COMPILER}" \
--benchmark-commit "${COMMIT}" \
--test-config "${TEST_CONFIG}" \
"${filter_arg[@]}" \
--output "${COMMIT}.json" \
-- "${lit_params[@]}"
cat "${COMMIT}.json"
- name: Submit to LNT
if: ${{ inputs.submit-lnt }}
env:
COMMIT: ${{ inputs.commit }}
LNT_URL: ${{ inputs.lnt-url }}
run: |
source .venv/bin/activate
libcxx/utils/ci/lnt/submit-benchmarks \
--lnt-url "${LNT_URL}" \
--test-suite libcxx \
"${COMMIT}.json"