Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 0 additions & 38 deletions .github/actions/ci-env/action.yml

This file was deleted.

37 changes: 37 additions & 0 deletions .github/actions/codecov/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
name: Generate and deploy Codecov results
description: >-
Collect llvm-cov coverage data and upload it to Codecov. Assumes the repo
is already checked out with `submodules: recursive` and a Rust toolchain
with caching is already set up, e.g. via
`actions-rust-lang/setup-rust-toolchain`.

inputs:
packages:
description: List of prerequisite Ubuntu packages, separated by whitespace
required: false
default: ''
runs:
using: composite
steps:
- uses: $/.github/actions/install-deps
if: inputs.packages != ''
with:
packages: "${{ inputs.packages }}"
- shell: bash
run: rustup component add llvm-tools-preview
- uses: taiki-e/install-action@nextest
- name: Install cargo-llvm-cov
uses: taiki-e/install-action@cargo-llvm-cov
- name: Clean the workspace
shell: bash
run: cargo llvm-cov clean --workspace
- name: Build
shell: bash
run: cargo build --workspace --release
- name: Collect coverage data
shell: bash
run: cargo llvm-cov nextest --lcov --output-path lcov.info --profile ci --release --workspace
- name: Upload coverage data to codecov
uses: codecov/codecov-action@v7
with:
files: lcov.info
49 changes: 49 additions & 0 deletions .github/actions/create-issue/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
name: Create or update issue
description:
Create an issue in the current repository, updating the body of an existing
open issue with the same title instead of filing a duplicate. Requires
`issues` write permission and the gh CLI on the runner.

inputs:
title:
description: Issue title, also used to match an existing open issue
required: true
body:
description:
Issue body text. Exactly one of `body` and `body-file` must be set.
required: false
default: ''
body-file:
description:
Path to a file containing the issue body, e.g. a generated report
required: false
default: ''
labels:
description:
Comma-separated labels to apply on creation. Best-effort — a label that
cannot be applied logs a warning instead of failing.
required: false
default: ''
update-existing:
description: Update the existing open issue's body instead of skipping it
required: false
default: 'true'
token:
description: GitHub token with `issues` write access
required: false
default: ${{ github.token }}

runs:
using: composite
steps:
- name: Create or update issue
shell: bash
env:
GH_TOKEN: ${{ inputs.token }}
INPUT_TITLE: ${{ inputs.title }}
INPUT_BODY: ${{ inputs.body }}
INPUT_BODY_FILE: ${{ inputs.body-file }}
INPUT_LABELS: ${{ inputs.labels }}
INPUT_UPDATE_EXISTING: ${{ inputs.update-existing }}
ACTION_PATH: ${{ github.action_path }}
run: python3 "$ACTION_PATH/create_issue.py"
56 changes: 56 additions & 0 deletions .github/actions/create-issue/create_issue.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
"""Create or update a GitHub issue via the gh CLI.

Reads its configuration from the INPUT_* environment variables set in
action.yml.
"""

import json
import os
import subprocess
import sys


def gh(*args):
return subprocess.run(
["gh", *args], check=True, text=True, stdout=subprocess.PIPE
).stdout


def main():
repo = os.environ["GITHUB_REPOSITORY"]
title = os.environ["INPUT_TITLE"]
labels = os.environ.get("INPUT_LABELS", "")
update_existing = os.environ.get("INPUT_UPDATE_EXISTING", "true") == "true"

body = os.environ.get("INPUT_BODY", "")
body_file = os.environ.get("INPUT_BODY_FILE", "")
if bool(body) == bool(body_file):
sys.exit("::error::Set exactly one of `body` and `body-file`")
if body_file:
with open(body_file) as f:
body = f.read()

issues = json.loads(
gh("issue", "list", "--repo", repo, "--state", "open",
"--limit", "100", "--json", "number,title")
)
existing = [issue["number"] for issue in issues if issue["title"] == title]
if existing:
if update_existing:
gh("issue", "edit", str(existing[0]), "--repo", repo, "--body", body)
print(f"Updated existing issue #{existing[0]}")
else:
print(f"Open issue #{existing[0]} already exists, skipping")
return

url = gh("issue", "create", "--repo", repo, "--title", title, "--body", body).strip()
print(f"Created issue {url}")
if labels:
try:
gh("issue", "edit", url, "--repo", repo, "--add-label", labels)
except subprocess.CalledProcessError:
print(f"::warning::Could not apply labels {labels!r}")


if __name__ == "__main__":
main()
165 changes: 165 additions & 0 deletions .github/actions/gpu-bench/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
name: Comparative benchmarks on GPU
description: >-
Run comparative criterion benchmarks against the base branch on a CUDA GPU,
posting the results as a commit comment. On a regression >= 10%, open an
issue instead of committing the bench result to `gh-pages`; the merge is
not blocked either way. Performs its own checkouts, so no prior checkout is
needed, but assumes a Rust toolchain with caching is already set up, e.g.
via `actions-rust-lang/setup-rust-toolchain`. Run on the `merge_group` trigger only, from a job with a
self-hosted Nvidia GPU runner (typically labeled `gpu-bench`) and
`contents` and `issues` write permissions — `contents: write` covers the
`gh-pages` push and the commit comment. Prerequisites — `cuda` Cargo
features, benchmarks formatted for `criterion-table` via
`${REPOSITORY_NAME}_BENCH_OUTPUT=commit-comment` (e.g.
`$LURK_BENCH_OUTPUT=commit-comment`), and a pre-existing `gh-pages` branch.

inputs:
packages:
description: List of prerequisite Ubuntu packages, separated by whitespace
required: false
default: ''
runs:
using: composite
steps:
- uses: $/.github/actions/gpu-setup
with:
gpu-framework: 'cuda'
- uses: $/.github/actions/install-deps
if: inputs.packages != ''
with:
packages: "${{ inputs.packages }}"
# `git-auto-commit-action` below pushes to `gh-pages` with the credentials
# this checkout persists, so they cannot be disabled here.
- uses: actions/checkout@v7 # zizmor: ignore[artipacked]
- name: Install criterion
shell: bash
run: |
cargo install cargo-criterion
cargo install criterion-table
- name: Set env vars
shell: bash
run: |
REPOSITORY_NAME=$(echo '${{ github.repository }}' | awk -F'/' '{ print toupper($2) }')
echo "${REPOSITORY_NAME}_BENCH_OUTPUT=commit-comment" | tee -a $GITHUB_ENV
echo "BASE_COMMIT=${{ github.event.merge_group.base_sha }}" | tee -a $GITHUB_ENV
echo "GPU_ID=$(echo $GPU_NAME | awk '{ print $NF }')" | tee -a $GITHUB_ENV
# Checkout gh-pages to check for cached bench result
- name: Checkout gh-pages
uses: actions/checkout@v7
with:
ref: gh-pages
path: gh-pages
persist-credentials: false
- name: Check for cached bench result
id: cached-bench
shell: bash
run: |
if [ -f "$BASE_COMMIT-$GPU_ID.json" ]
then
echo "cached=true" | tee -a $GITHUB_OUTPUT
cp "$BASE_COMMIT-$GPU_ID.json" "../$BASE_COMMIT.json"
else
echo "cached=false" | tee -a $GITHUB_OUTPUT
fi
working-directory: ${{ github.workspace }}/gh-pages
# Checkout base branch for comparative bench
- uses: actions/checkout@v7
if: steps.cached-bench.outputs.cached == 'false'
with:
ref: ${{ github.base_ref }}
path: ${{ github.base_ref }}
persist-credentials: false
- name: Run GPU bench on base branch
if: steps.cached-bench.outputs.cached == 'false'
shell: bash
run: |
# Run benchmark
cargo criterion --features "cuda" --message-format=json > "$BASE_COMMIT.json"
# Copy bench output to PR branch
cp "$BASE_COMMIT.json" ..
working-directory: ${{ github.workspace }}/${{ github.base_ref }}
- name: Run GPU bench on PR branch
shell: bash
run: |
cargo criterion --features "cuda" --message-format=json > ${{ github.sha }}.json
cp ${{ github.sha }}.json ..
working-directory: ${{ github.workspace }}/benches
- name: copy the benchmark template and prepare it with data
shell: bash
run: |
cp .github/tables.toml .
# Get CPU model
CPU_MODEL=$(grep '^model name' /proc/cpuinfo | head -1 | awk -F ': ' '{ print $2 }')
# Get num vCPUS
NUM_VCPUS="$(nproc --all) vCPUs"
# Get total RAM in GB
TOTAL_RAM=$(grep MemTotal /proc/meminfo | awk '{$2=$2/(1024^2); print int($2), "GB RAM";}')

# Use conditionals to ensure that only non-empty variables are inserted
[[ ! -z "$GPU_NAME" ]] && sed -i "/^\"\"\"$/i $GPU_NAME" tables.toml
[[ ! -z "$CPU_MODEL" ]] && sed -i "/^\"\"\"$/i $CPU_MODEL" tables.toml
[[ ! -z "$NUM_VCPUS" ]] && sed -i "/^\"\"\"$/i $NUM_VCPUS" tables.toml
[[ ! -z "$TOTAL_RAM" ]] && sed -i "/^\"\"\"$/i $TOTAL_RAM" tables.toml
sed -i "/^\"\"\"$/i Workflow run: $GITHUB_SERVER_URL/$REPO/actions/runs/$RUN_ID" tables.toml
working-directory: ${{ github.workspace }}
env:
REPO: ${{ github.repository }}
RUN_ID: ${{ github.run_id }}
# Create a `criterion-table` and write in commit comment
- name: Run `criterion-table`
shell: bash
run: cat "$BASE_COMMIT.json" "$GITHUB_SHA.json" | criterion-table > BENCHMARKS.md
- name: Write bench on commit comment
uses: peter-evans/commit-comment@v4
with:
body-path: BENCHMARKS.md
# Check for a slowdown >= 10%. If so, open an issue but don't block merge.
# Reported as a step output because `continue-on-error` is silently
# ignored on composite action steps, so a failing step would abort here.
- name: Check for perf regression
id: regression-check
shell: bash
run: |
regressions=$(awk -F'[*x]' '/slower/{print $12}' BENCHMARKS.md)

echo $regressions

regression=false
for r in $regressions
do
if (( $(echo "$r >= 1.10" | bc -l) ))
then
regression=true
fi
done
echo "regression=$regression" | tee -a $GITHUB_OUTPUT
# Not possible to use ${{ github.event.number }} with the `merge_group` trigger
- name: Get PR number from merge branch
shell: bash
env:
HEAD_REF: ${{ github.event.merge_group.head_ref }}
run: |
echo "PR_NUMBER=$(echo "$HEAD_REF" | sed -e 's/.*pr-\(.*\)-.*/\1/')" | tee -a $GITHUB_ENV
- name: Open issue on regression
if: steps.regression-check.outputs.regression == 'true'
uses: $/.github/actions/create-issue
with:
title: ':rotating_light: Performance regression detected for PR #${{ env.PR_NUMBER }}'
labels: P-Performance,automated issue
body: |
Regression >= 10% found during merge for PR #${{ env.PR_NUMBER }}
Commit: ${{ github.sha }}
Workflow run: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
- name: Remove old base bench
shell: bash
run: |
rm "$BASE_COMMIT.json"
mv "$GITHUB_SHA.json" "$GITHUB_SHA-$GPU_ID.json"
working-directory: ${{ github.workspace }}
- name: Commit bench result to `gh-pages` branch if no regression
if: steps.regression-check.outputs.regression != 'true'
uses: stefanzweifel/git-auto-commit-action@v7
with:
branch: gh-pages
commit_message: '[automated] GPU Benchmark from PR #${{ env.PR_NUMBER }}'
file_pattern: '${{ github.sha }}-${{ env.GPU_ID }}.json'
Loading
Loading