Files
miner/.gitea/workflows/bench.yaml
rob thijssen b640d3a711
All checks were successful
ci / fmt (pull_request) Successful in 23s
bench / build (pull_request) Successful in 1m0s
ci / clippy (pull_request) Successful in 1m57s
bench / measure (pull_request) Successful in 2m51s
ci / doc (pull_request) Successful in 1m52s
ci / test (pull_request) Successful in 7m25s
engine-cuda: fold round constants into the preceding linear layer; bench on cuda paths
Each linear layer now folds the constant of the round that follows it into
its final deferred-carry reduction (or, for the internal rounds, into the
element-0 multiply-add), removing the per-element gf_add before every S-box.
Parity verified. On benjy the effect is within run-to-run noise: three
interleaved rounds gave medians of 418.5 vs 409.7 MH/s against the previous
default, with SM clock swinging 1935-2025 MHz across rounds. Kept on as
LAIR_FOLD_RC=1 for the instruction count; not claimed as a measured gain.

bench.yaml: crates/engine-cuda/** now triggers the harness on PRs. The
previous PR's kernel changes did not run it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CBgs2nSi4H2mdh8kD8vMX5
2026-09-03 15:56:23 +03:00

144 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-cuda/**
- 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