magicciv/tools/profiling-report.py

71 lines
2.5 KiB
Python
Raw Normal View History

#!/usr/bin/env python3
"""p2-84: dev compute profiling report tool.
Ingests the per-turn profiling JSONL produced by mc-profiling::drain_turn
(called from WorldSim::step and other hot paths when the "profiling" Cargo feature
is enabled) plus optional godot-side blocks appended via the Gd bridge.
Ranks features by total wall time, max, alloc delta. Use to drive p2-83-style
parallelism decisions and p1-22 huge-map budgets.
Usage:
python tools/profiling-report.py profiling.jsonl
(or the combined turn_stats + profiling stream)
The JSONL lines are of form:
{"turn": N, "spans": [ {"name": "worldsim.ecology", "calls": C, "total_ms": T, "max_ms": M, "alloc_delta": A, "alloc_peak": P}, ... ]}
"""
import json
import sys
import collections
from pathlib import Path
def main(argv):
if len(argv) < 2:
print(__doc__)
print("No input file; using stdin or default 'profiling.jsonl' if present.")
p = Path("profiling.jsonl")
if p.exists():
lines = p.read_text().splitlines()
else:
lines = sys.stdin.read().splitlines()
else:
p = Path(argv[1])
lines = p.read_text().splitlines()
by_name = collections.defaultdict(lambda: {"calls": 0, "total_ms": 0.0, "max_ms": 0.0, "alloc_delta": 0, "alloc_peak": 0})
turns = 0
for ln in lines:
ln = ln.strip()
if not ln:
continue
try:
rec = json.loads(ln)
except json.JSONDecodeError:
continue
turns += 1
for sp in rec.get("spans", []):
n = sp.get("name", "unknown")
c = by_name[n]
c["calls"] += int(sp.get("calls", 1))
c["total_ms"] += float(sp.get("total_ms", 0.0))
c["max_ms"] = max(c["max_ms"], float(sp.get("max_ms", 0.0)))
c["alloc_delta"] += int(sp.get("alloc_delta", 0))
c["alloc_peak"] = max(c["alloc_peak"], int(sp.get("alloc_peak", 0)))
if not by_name:
print("No spans found. (Run with --features profiling and drive some turns.)")
return
ranked = sorted(by_name.items(), key=lambda kv: -kv[1]["total_ms"])
print(f"Profiling report over {turns} turn(s) with data:")
print("name | calls | total_ms | max_ms | alloc_delta | alloc_peak")
print("-" * 70)
for name, c in ranked[:15]:
print(f"{name} | {c['calls']} | {c['total_ms']:.2f} | {c['max_ms']:.2f} | {c['alloc_delta']} | {c['alloc_peak']}")
print("\n(Top 15 by total wall time. Use this to target p2-83 parallelism or budgets.)")
if __name__ == "__main__":
main(sys.argv)