Something went wrong. Try again.
Backend environment for match hosting for lance.blue
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131#!/usr/bin/env python3"""Time a cold start: page open to the board being on screen.
scripts/perf/startup.py <debug-port> [url-substring]
Example:
./scripts/arena.sh up lance-blue/bench-4v4.mms -p 8080 google-chrome --headless=new --remote-debugging-port=9300 \\ --user-data-dir=/tmp/startup-chrome --no-first-run \\ --no-default-browser-check --disable-gpu \\ 'http://localhost:8080/did:plc:localdev/?perf=1' python3 scripts/perf/startup.py 9300 localdev
**Wait for the bytes, not for a paint.** The first paints are Suramadu's ownloading UI and land about two seconds in, while the board is still thirtyseconds away - timing to `px` records reports a cold start of 2s and is wrong byan order of magnitude. The board is the one large transfer, so this waits forcumulative `rx` to cross half a megabyte and reports the record that carried it.
Like latency-pilot.py it starts neither Chrome nor the match, and it wants aseat's path (`/<did>/`) rather than the bare root, which serves the war's ownindex and loads no perf.js.
Prints one JSON object:
ws_open_ms WebSocket open, ms since page load board_bytes_ms when cumulative rx crossed the threshold board_paint_ms the first paint at or after that largest_frame bytes in the biggest single frame, and when total_rx bytes received up to that point"""
import jsonimport osimport sysimport time
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))from cdp import CDP, find_page # noqa: E402
# The board arrives as a few hundred kilobytes in one or two frames; the loading# UI before it is a few kilobytes in total. Anything in between separates them.BOARD_BYTES = 500_000CONNECT_TIMEOUT = 120BOARD_TIMEOUT = 180SETTLE = 2.0
def connect(port, match): """Chrome may not be listening yet, and the page may not have opened.""" deadline = time.monotonic() + CONNECT_TIMEOUT last = None while time.monotonic() < deadline: try: page = find_page("localhost", port, match) # CDP wants the path, not the whole ws:// URL. ws_path = page["webSocketDebuggerUrl"].split(str(port), 1)[1] return CDP("localhost", port, ws_path) except Exception as exc: # connection refused, no target yet last = exc time.sleep(1) raise SystemExit(f"no page matching {match!r} after {CONNECT_TIMEOUT}s: {last}")
def received(cdp): return cdp.evaluate( "(()=>{try{return window.__arenaPerf.dump().records" ".filter(r=>r.k==='rx').reduce((a,r)=>a+(r.n||0),0)}catch(e){return -1}})()" )
def main(): if len(sys.argv) < 2: raise SystemExit(__doc__.strip().split("\n")[2].strip()) port = int(sys.argv[1]) match = sys.argv[2] if len(sys.argv) > 2 else ""
cdp = connect(port, match) cdp.call("Runtime.enable")
deadline = time.monotonic() + BOARD_TIMEOUT seen = -1 while time.monotonic() < deadline: seen = received(cdp) if isinstance(seen, (int, float)) and seen > BOARD_BYTES: break time.sleep(0.5) else: raise SystemExit( f"no board after {BOARD_TIMEOUT}s (last rx total {seen}); " "is perf.js loaded, and is this a seat's path rather than /?" ) # Let the paint that applies the board land before reading the buffer. time.sleep(SETTLE)
dump = json.loads(cdp.evaluate("JSON.stringify(window.__arenaPerf.dump())")) records = dump.get("records", []) opens = [r for r in records if r.get("k") == "ws" and r.get("e") == "open"] rx = [r for r in records if r.get("k") == "rx"] px = [r for r in records if r.get("k") == "px"]
total = 0 board = None for rec in rx: total += rec.get("n") or 0 if total > BOARD_BYTES: board = rec break biggest = max(rx, key=lambda r: r.get("n") or 0) if rx else None painted = [p["t"] for p in px if board and p["t"] >= board["t"]]
print( json.dumps( { "ws_open_ms": round(opens[0]["t"], 1) if opens else None, "board_bytes_ms": round(board["t"], 1) if board else None, "board_paint_ms": round(painted[0], 1) if painted else None, "largest_frame": biggest.get("n") if biggest else None, "largest_frame_ms": round(biggest["t"], 1) if biggest else None, "total_rx": total, "paints": len(px), } ) ) return 0
if __name__ == "__main__": sys.exit(main())