OverviewFilesCommitsIssuesDocsReleasesPeers

hyper-tps / scripts / frametime-load-test.sh

101 lines · bash ref:
#!/usr/bin/env bash
# frametime-load-test — does background tile generation cost frames?
#
# THE ONLY HARD CONSTRAINT on growing the world across peers is that generation
# must not affect gameplay. Everything else — throughput, peer counts, hours to
# bake — is secondary and has been argued about at length without this number.
#
# HOW IT WORKS. One run, three phases, so the comparison is within a single
# session rather than across runs:
#
#   1. BASELINE   game idle, no load
#   2. LOADED     N CPU tile generations in flight at `nice` level
#   3. RECOVERY   load stopped — proves frame time returns, i.e. what we saw
#                 was the load and not drift or thermal ramp
#
# WHY IN ONE RUN. The GPU throttles 2100 -> 1410 MHz at 86 C and the same config
# has timed 8.07 to 24.35 s across runs. Comparing two separate launches would
# measure the card's mood. Only interleaved phases survive that, and RECOVERY is
# what catches it if they do not.
#
# THE LOAD IS THE REAL GENERATOR, not a synthetic spinner: tiles fetched from
# the CPU inference service, which is what would actually run in the background.
# A busy-loop would exercise the scheduler but not the memory bandwidth and
# cache pressure that are the likely mechanism.
#
#   scripts/frametime-load-test.sh [--phase-seconds 60] [--jobs 2] [--nice 19]
#
# Then play normally — walk, fight, look around. An idle camera measures an idle
# renderer and answers nothing.
set -uo pipefail

PHASE=60
JOBS=2
NICE=19
URL="http://127.0.0.1:8830"
OUT="/tmp/frametime-$(date +%H%M%S).csv"

while [ $# -gt 0 ]; do
  case "$1" in
    --phase-seconds) PHASE="$2"; shift 2 ;;
    --jobs)          JOBS="$2"; shift 2 ;;
    --nice)          NICE="$2"; shift 2 ;;
    --url)           URL="$2"; shift 2 ;;
    --out)           OUT="$2"; shift 2 ;;
    *) echo "unknown arg: $1" >&2; exit 2 ;;
  esac
done

if ! curl -s -m 3 -o /dev/null "$URL/coarse?i1=0&j1=0&i2=2&j2=2"; then
  echo "FATAL: no CPU inference service at $URL" >&2
  echo "  start one:  cd ../terrain-diffusion && .venv/bin/python -m \\" >&2
  echo "    terrain_diffusion.inference.api --device cpu --no-compile \\" >&2
  echo "    --seed 1234 --port 8830" >&2
  exit 1
fi

echo "frametime-load-test"
echo "  phases      ${PHASE}s each: baseline / loaded / recovery"
echo "  load        ${JOBS} tile generations, nice ${NICE}, from ${URL}"
echo "  frame log   ${OUT}"
echo
echo "Launch the game in ANOTHER terminal with:"
echo
echo "    HYPER_TPS_FRAMELOG=${OUT} ./scripts/dev-run.sh"
echo
echo "Then PLAY — walk, fight, look around. An idle camera measures an idle"
echo "renderer and answers nothing."
echo
read -r -p "Press enter once the map has loaded and you are moving... " _

mark () { echo "# phase $1 $(date +%s%3N)" >> "$OUT.phases"; echo "  -> $1"; }

: > "$OUT.phases"
mark baseline
sleep "$PHASE"

mark loaded
PIDS=()
for i in $(seq 1 "$JOBS"); do
  (
    tx=$((1000 + i * 37))
    while :; do
      nice -n "$NICE" curl -s -m 600 -o /dev/null \
        "$URL/terrain?i1=$((tx * 256))&j1=0&i2=$(((tx + 1) * 256))&j2=256&scale=1"
      tx=$((tx + 1))
    done
  ) &
  PIDS+=($!)
done
sleep "$PHASE"

for p in "${PIDS[@]}"; do kill "$p" 2>/dev/null; done
# the curl children outlive their subshell; reap by process group
pkill -P $$ curl 2>/dev/null
mark recovery
sleep "$PHASE"

echo
echo "done. Stop the game, then:"
echo "    python3 scripts/frametime-report.py $OUT"