#!/usr/bin/env python3 """Summarise Suramadu's per-frame stats from a running match's container log. scripts/perf/stats.py [HH:MM:SS] [--session NAME] Suramadu logs one `stats,,,` line per metric per frame when allowStatisticsLogging is on, which suramadu.config.template sets. Those lines are the only per-frame server-side numbers there are, and reading them by eye does not scale past a few frames. The percentiles matter more than the medians here. A board repaint is rare and huge; a cursor move is constant and small. Anything that reports only a median will say a 16-unit match performs like a 4-unit one, which is false - see PERFORMANCE.md, "How a match scales with units on the board". """ import argparse import collections import re import statistics import subprocess import sys def main(): ap = argparse.ArgumentParser() ap.add_argument( "since", nargs="?", default="00:00:00", help="only lines at or after this UTC HH:MM:SS", ) ap.add_argument( "--session", default="perf", help="ARENA_SESSION of the container to read (default: perf)", ) args = ap.parse_args() ids = subprocess.run( ["docker", "ps", "-q", "-f", f"name=arena-{args.session}"], capture_output=True, text=True, ).stdout.split() if not ids: sys.exit(f"no running container named arena-{args.session}") out = subprocess.run(["docker", "logs", ids[0]], capture_output=True) log = out.stdout.decode("utf8", "replace") + out.stderr.decode("utf8", "replace") vals = collections.defaultdict(list) for line in log.splitlines(): m = re.search(r"(\d\d:\d\d:\d\d)\.\d+ stats,[^,]*,([A-Za-z]+),(-?\d+)", line) if m and m.group(1) >= args.since: vals[m.group(2)].append(int(m.group(3))) if not vals: sys.exit( "no stats lines found - is allowStatisticsLogging on, and has anyone connected?" ) print(f"{'metric':26s} {'n':>5s} {'median':>10s} {'p90':>10s} {'max':>10s}") for k in sorted(vals): v = sorted(vals[k]) p90 = v[min(len(v) - 1, int(len(v) * 0.9))] print(f"{k:26s} {len(v):5d} {statistics.median(v):10.0f} {p90:10d} {v[-1]:10d}") if __name__ == "__main__": main()