igneum/tools/ladder/verify-bench-remote.sh

81 lines
5.3 KiB
Bash
Executable file

#!/usr/bin/env bash
# The latency ladder's verifier bound, measured on igneum-build-1 (docs/design/latency-ladder.md section 4): for every rung
# of the ladder, the cold verify of one 32-lane warp of class v4 at that rung on the reference core (core 40, 3.8 GHz under
# schedutil, nice 19), alone and with its SMT sibling (core 88) running the same bench, plus the average of 50 warps. The
# figure a rung's admissibility reads is the cold run with the sibling loaded; the gate is 10 ms.
#
# tools/ladder/verify-bench-remote.sh [--rungs "27 35 53 88 173 267"] [--pow <path on the box>] [--out <dir on the Mac>]
#
# Runs ON the box under its measure hold (infra/build-server/remote-run.sh BR_MEASURE=1: waits for every running build, blocks
# new ones and the capacity layer until it ends; one JSONL line of kind measure in /srv/builds/_log/builds.jsonl). The binary is
# the box's own build of igneum-pow from this worktree (tools/build-remote.sh from igneum-pow/ puts it at
# /srv/builds/<worktree>/igneum-pow/target/release/igneum-pow); the results come back as one text file per run plus a table.
# A number taken beside another build is not a number (CLAUDE.md), which is what the hold is for.
set -euo pipefail
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
BS_TOOL=ladder-verify-bench
# shellcheck source=../../infra/build-server/lib.sh
. "$HERE/../../infra/build-server/lib.sh"
RUNGS="27 35 53 88 173 267"; POW=""; OUT=""
while [ $# -gt 0 ]; do
case "$1" in
--rungs) RUNGS="$2"; shift 2 ;; --pow) POW="$2"; shift 2 ;; --out) OUT="$2"; shift 2 ;;
*) echo "unknown argument $1" >&2; exit 2 ;;
esac
done
bs_host
# the context of the igneum-pow crate (BS_WT, BS_WT_ROOT, branch and sha for the JSONL line); the binary is already on the box
( cd "$HERE/../../igneum-pow" ) || bs_die "no igneum-pow beside tools/"
pushd "$HERE/../../igneum-pow" > /dev/null; bs_context; popd > /dev/null
WT_ROOT="$BS_WT_ROOT"
WT_NAME="$BS_WT"
POW="${POW:-/srv/builds/$WT_NAME/igneum-pow/target/release/igneum-pow}"
OUT="${OUT:-$WT_ROOT/docs/design/latency-ladder-bench}"
STAMP=$(date -u +%Y%m%dT%H%M%SZ)
REMOTE_OUT="/srv/builds/_log/ladder-bench/$STAMP"
mkdir -p "$OUT"
# the command remote-run.sh evals under the measure hold: per rung, the quiet-core run, then the two siblings at once
read -r -d '' CMD <<EOF || true
set -u; mkdir -p '$REMOTE_OUT'; cd '$REMOTE_OUT'
echo "host \$(hostname) load \$(cut -d' ' -f1-3 /proc/loadavg) freq40 \$(cat /sys/devices/system/cpu/cpu40/cpufreq/scaling_cur_freq 2>/dev/null || echo ?) kHz siblings \$(cat /sys/devices/system/cpu/cpu40/topology/thread_siblings_list) pow \$(sha256sum '$POW' | cut -c1-16)" > meta.txt
for r in $RUNGS; do
nice -n 19 taskset -c 40 '$POW' bench --seed igneum-genesis --day 2026-10-03 --class mx8+sh256x\$r --warps 50 > alone-\$r.txt 2>&1
# the sibling's load must outlast the measured run (the first run of this script, 22:06Z: a 50-warp sibling finished during
# the measured run's own cache fill, so the "loaded" warps ran alone; 4,000 warps is 20 to 40 s, killed when the run ends)
nice -n 19 taskset -c 88 '$POW' bench --seed igneum-genesis --day 2026-10-03 --class mx8+sh256x\$r --warps 4000 > sibling-\$r.txt 2>&1 &
sib=\$!
sleep 1.5
nice -n 19 taskset -c 40 '$POW' bench --seed igneum-genesis --day 2026-10-03 --class mx8+sh256x\$r --warps 50 > loaded-\$r.txt 2>&1
kill \$sib 2>/dev/null; wait \$sib 2>/dev/null
echo "rung reps=\$r done \$(date -u +%H:%M:%SZ)"
done
echo "load after \$(cut -d' ' -f1-3 /proc/loadavg)" >> meta.txt
EOF
bs_log "measuring rungs $RUNGS on $BS_HOST under the measure hold (pow $POW); results to $OUT"
BR_MEASURE=1 BR_KIND=measure BR_COMMAND="igneum-pow bench per ladder rung (core 40 alone, then with core 88 loaded)" BR_TARGET=x86_64-unknown-linux-gnu \
bs_remote_run "$(dirname "$POW")" "latency ladder verifier bench rungs $RUNGS" "$CMD"
bs_rsync -a "$BS_HOST:$REMOTE_OUT/" "$OUT/$STAMP/"
# the table: cold = the "warp base 0: single cold run" line, avg = the "CPU verify" line
python3 - "$OUT/$STAMP" $RUNGS <<'PY'
import re, sys, os
d = sys.argv[1]; rungs = sys.argv[2:]
def read(name):
try: t = open(os.path.join(d, name)).read()
except FileNotFoundError: return (None, None, None)
cold = re.search(r"warp base 0: single cold run ([\d.]+) ms", t)
avg = re.search(r"CPU verify: ([\d.]+) ms per 32-lane warp", t)
shadow = re.search(r"(\d+) shadow instructions per hash", t)
return (float(cold.group(1)) if cold else None, float(avg.group(1)) if avg else None, int(shadow.group(1)) if shadow else None)
out = [open(os.path.join(d, "meta.txt")).read().strip()]
out.append("| Rung | reps | Shadow instrs per hash | Counted ops (approx) | Cold, core alone (ms) | Avg of 50, alone (ms) | Cold, sibling loaded (ms) | Avg of 50, sibling loaded (ms) | Under 10 ms loaded |")
out.append("|---|---|---|---|---|---|---|---|---|")
for i, r in enumerate(rungs):
a = read(f"alone-{r}.txt"); l = read(f"loaded-{r}.txt")
ops = 930 + (8 * 256 * int(r) * 183 + 99) // 100
f = lambda x: "?" if x is None else f"{x:.2f}"
ok = "?" if l[0] is None else ("yes" if l[0] < 10.0 else "NO")
out.append(f"| {i} | {r} | {a[2] or '?'} | {ops:,} | {f(a[0])} | {f(a[1])} | {f(l[0])} | {f(l[1])} | {ok} |")
print("\n".join(out))
open(os.path.join(d, "table.md"), "w").write("\n".join(out) + "\n")
PY
bs_log "done; raw lines in $OUT/$STAMP"