magicciv/tools/batch-walltime.sh
Natalie e71cd3ca92 feat(@projects/@magic-civilization): add batch status monitoring tools
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-04-18 09:17:11 -07:00

83 lines
2.4 KiB
Bash
Executable file

#!/usr/bin/env bash
# batch-walltime.sh — Aggregate per-batch wall-clock statistics.
#
# Usage:
# tools/batch-walltime.sh <batch_dir>
# tools/batch-walltime.sh apricot:<remote_batch_dir>
# tools/batch-walltime.sh apricot:<parent_dir> # multi-mode → per-subdir
#
# Prints: "mode: n=NNN total=XX.Xs avg=YY.Ys/game victories=N/N"
# If TARGET has a parent of {gpu-true,gpu-false,clan-*} subdirs, iterates
# all of them so you can do a single call on the gpu-walltime parent.
set -euo pipefail
TARGET="${1:?usage: tools/batch-walltime.sh <batch_dir | apricot:/path>}"
read -r -d '' QUERY <<'EOF' || true
set -e
: "${DIR:?DIR must be set}"
summarize() {
local dir="$1"
local label="$2"
python3 - "$dir" "$label" <<'PY'
import json, os, sys, pathlib
root = pathlib.Path(sys.argv[1])
label = sys.argv[2]
games = sorted(root.glob("game_*"))
n = 0
total_wc = 0.0
victories = 0
turns = []
for g in games:
stats = g / "turn_stats.jsonl"
if not stats.is_file() or stats.stat().st_size == 0:
continue
try:
last = None
with open(stats) as f:
for line in f:
if line.strip(): last = line
if not last: continue
d = json.loads(last)
except Exception:
continue
n += 1
wc = d.get("wall_clock_sec")
if isinstance(wc, (int, float)): total_wc += float(wc)
if d.get("outcome") == "victory": victories += 1
t = d.get("turn")
if isinstance(t, int): turns.append(t)
if n == 0:
print(f"{label}: no games")
sys.exit(0)
avg = total_wc / n if n else 0
tmin = min(turns) if turns else "?"
tmax = max(turns) if turns else "?"
print(f"{label}: n={n} total={total_wc:.1f}s avg={avg:.1f}s/game victories={victories}/{n} turns={tmin}-{tmax}")
PY
}
# Is DIR itself a batch dir (has game_* children) or a parent of mode-subdirs?
if compgen -G "$DIR/game_*" > /dev/null; then
summarize "$DIR" "$(basename "$DIR")"
else
# Iterate mode-subdirs
found=0
for sub in "$DIR"/gpu-* "$DIR"/clan-* "$DIR"/smoke; do
[ -d "$sub" ] || continue
compgen -G "$sub/game_*" > /dev/null || continue
summarize "$sub" "$(basename "$sub")"
found=1
done
[ "$found" -eq 0 ] && { echo "no batches found under $DIR" >&2; exit 2; }
fi
EOF
if [[ "$TARGET" == apricot:* ]]; then
REMOTE_PATH="${TARGET#apricot:}"
ssh apricot "DIR='${REMOTE_PATH}' bash -s" <<< "$QUERY"
else
DIR="$TARGET" bash -c "$QUERY"
fi