70 lines
2.5 KiB
Python
70 lines
2.5 KiB
Python
#!/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)
|