ci / fmt (pull_request) Successful in 31s
bench / build (pull_request) Successful in 1m1s
ci / clippy (pull_request) Successful in 1m55s
bench / measure (pull_request) Successful in 2m59s
ci / test (pull_request) Successful in 9m7s
ci / doc (pull_request) Successful in 1m42s
Gitea's concurrency group did not serialise two runs on benjy: the second run's scp failed with ETXTBSY on the binary the first was executing (safe, it happened before the miner was touched, but the run was lost). The pause, lock, measure and resume sequence now lives in bench-on-host.sh, executed over one ssh call under flock on the host, with per-run binary and record names so staging never collides. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CBgs2nSi4H2mdh8kD8vMX5
143 lines
5.5 KiB
YAML
143 lines
5.5 KiB
YAML
---
|
|
# GPU hashrate and parity measurement on a fleet mining host (quantus/miner#2).
|
|
#
|
|
# Runner containers on the GPU hosts have no device passthrough (gongfoo gives
|
|
# them `devices: None`), so the benchmark cannot run inside the runner. The
|
|
# binary is built on a runner and executed on the host over ssh as gitea_ci,
|
|
# which has the GPU device nodes (0666) and the Vulkan ICD, exactly like the
|
|
# miner. The host's quantus-miner.service is stopped for the window and started
|
|
# again afterwards, under a trap, so a failed run cannot leave the host idle.
|
|
#
|
|
# Benjy (4090, dedicated) is the reference card; beast (2x 5090) also serves
|
|
# inference and is only benchmarked by manual dispatch.
|
|
name: bench
|
|
|
|
on:
|
|
workflow_dispatch:
|
|
inputs:
|
|
host:
|
|
description: mining host to measure
|
|
default: benjy.hanzalova.internal
|
|
type: choice
|
|
options:
|
|
- benjy.hanzalova.internal
|
|
- quadbrat.hanzalova.internal
|
|
- beast.hanzalova.internal
|
|
duration_secs:
|
|
description: seconds per timed window
|
|
default: "30"
|
|
runs:
|
|
description: timed windows (median reported)
|
|
default: "5"
|
|
batch_size:
|
|
description: nonces per GPU batch
|
|
default: "1000000"
|
|
workers:
|
|
description: worker threads (one per card)
|
|
default: "1"
|
|
pull_request:
|
|
paths:
|
|
- crates/engine-gpu/**
|
|
- crates/engine-cpu/**
|
|
- crates/pow-core/**
|
|
- crates/miner-service/**
|
|
- crates/bench-harness/**
|
|
- Cargo.lock
|
|
- .gitea/workflows/bench.yaml
|
|
|
|
env:
|
|
CARGO_TERM_COLOR: always
|
|
BENCH_HOST: ${{ github.event.inputs.host || 'benjy.hanzalova.internal' }}
|
|
DURATION: ${{ github.event.inputs.duration_secs || '30' }}
|
|
RUNS: ${{ github.event.inputs.runs || '5' }}
|
|
BATCH: ${{ github.event.inputs.batch_size || '1000000' }}
|
|
WORKERS: ${{ github.event.inputs.workers || '1' }}
|
|
|
|
# One measurement per host at a time; a second one would share the card.
|
|
concurrency:
|
|
group: bench-${{ github.event.inputs.host || 'benjy.hanzalova.internal' }}
|
|
cancel-in-progress: false
|
|
|
|
jobs:
|
|
build:
|
|
# cuda-13.0, not rust: the rust image is Fedora 44 and the mining hosts are
|
|
# Fedora 43, so a binary built there needs a glibc the hosts do not have
|
|
# (observed: "GLIBC_2.43 not found"). cuda-13.0 is Fedora 43 based, matches
|
|
# the hosts, and is what the deploy in #8 builds on anyway.
|
|
runs-on: cuda-13.0
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- name: build quantus-bench
|
|
env:
|
|
MINER_BUILD_SHA: ${{ github.event.pull_request.head.sha || github.sha }}
|
|
run: cargo build --release --locked -p bench-harness
|
|
- uses: actions/upload-artifact@v3
|
|
with:
|
|
name: quantus-bench
|
|
path: target/release/quantus-bench
|
|
|
|
measure:
|
|
runs-on: fedora-43
|
|
needs: build
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- uses: actions/download-artifact@v3
|
|
with: { name: quantus-bench, path: _bin }
|
|
|
|
- name: write ssh key
|
|
run: |
|
|
set -euo pipefail
|
|
install -d -m 0700 ~/.ssh
|
|
printf '%s\n' "${{ secrets.RSYNC_SSH_KEY }}" > ~/.ssh/id_gitea_ci
|
|
chmod 0600 ~/.ssh/id_gitea_ci
|
|
|
|
- name: measure on ${{ env.BENCH_HOST }}
|
|
run: |
|
|
set -euo pipefail
|
|
SSHOPTS="-i $HOME/.ssh/id_gitea_ci -o StrictHostKeyChecking=accept-new"
|
|
run() { ssh $SSHOPTS gitea_ci@"$BENCH_HOST" "$@"; }
|
|
|
|
# Per-run file names: two runs may overlap on the host until the
|
|
# host-side flock serialises them, and scp over a running binary
|
|
# fails with ETXTBSY.
|
|
id="${{ github.run_id }}"
|
|
dir=/var/lib/gitea_ci/bench
|
|
run "install -d -m 0750 $dir"
|
|
scp $SSHOPTS -q _bin/quantus-bench gitea_ci@"$BENCH_HOST":$dir/quantus-bench.$id
|
|
scp $SSHOPTS -q crates/bench-harness/bench-on-host.sh gitea_ci@"$BENCH_HOST":$dir/bench-on-host.$id.sh
|
|
run "chmod 0755 $dir/quantus-bench.$id"
|
|
cleanup() { ssh $SSHOPTS gitea_ci@"$BENCH_HOST" "rm -f $dir/quantus-bench.$id $dir/bench-on-host.$id.sh $dir/record.$id.json"; }
|
|
trap cleanup EXIT
|
|
|
|
label="${{ github.event.pull_request.number && format('pr-{0}', github.event.pull_request.number) || github.ref_name }}"
|
|
# The host script pauses the miner, holds the per-host lock, measures,
|
|
# and resumes the miner under its own trap.
|
|
run "bash $dir/bench-on-host.$id.sh $dir/quantus-bench.$id $DURATION $RUNS $BATCH $WORKERS $label $dir/record.$id.json" | tee bench.log
|
|
# The summary is everything from the harness's markdown header on.
|
|
sed -n '/^## quantus-bench/,$p' bench.log > bench.md
|
|
scp $SSHOPTS -q gitea_ci@"$BENCH_HOST":$dir/record.$id.json record.json
|
|
{ echo; cat bench.md; } >> "$GITHUB_STEP_SUMMARY"
|
|
|
|
- uses: actions/upload-artifact@v3
|
|
if: always()
|
|
with:
|
|
name: bench-record-${{ env.BENCH_HOST }}
|
|
path: |
|
|
record.json
|
|
bench.md
|
|
|
|
- name: comment on the pull request
|
|
if: github.event_name == 'pull_request'
|
|
run: |
|
|
set -euo pipefail
|
|
body=$(python3 - <<'PY'
|
|
import json, pathlib
|
|
print(json.dumps({"body": pathlib.Path("bench.md").read_text()}))
|
|
PY
|
|
)
|
|
curl -fsS -X POST \
|
|
-H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
|
|
-H "Content-Type: application/json" \
|
|
"${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/issues/${{ github.event.pull_request.number }}/comments" \
|
|
-d "$body" > /dev/null
|