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
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
144 lines
5.5 KiB
YAML
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
|