02dce1093b
Every PR of the startup batch adds a different tools/profile_client_startup.py, so any two of them conflict. This one measures only Behaviours::load, so it becomes tools/profile_behaviour_cache.py, and --cache-dir passes the snapshot folder to the example so warm loads can be measured. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
58 lines
2.6 KiB
Python
58 lines
2.6 KiB
Python
#!/usr/bin/env python3
|
|
"""Measure the resource-dependent Behaviours::load portion of client startup.
|
|
|
|
Build the behaviour_load_profile example in the checkout to be measured first:
|
|
cargo build --release -p horizon-world --example behaviour_load_profile
|
|
|
|
Run the profiler from the repository root with the matching example binary.
|
|
This measures behaviour loading, not time to first rendered frame. With
|
|
--cache-dir, the example reads and refreshes the resolved snapshot there, as the
|
|
client does with its .horizon-cache folder: the first load of a run builds it
|
|
when it is missing, the next loads restore it.
|
|
"""
|
|
import argparse
|
|
import json
|
|
import re
|
|
import statistics
|
|
import subprocess
|
|
from pathlib import Path
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
parser.add_argument("--binary", type=Path, default=Path("target/release/examples/behaviour_load_profile"))
|
|
parser.add_argument("--resources", type=Path, default=Path(".local/horizon-client/26.3"))
|
|
parser.add_argument("--cache-dir", type=Path,
|
|
help="snapshot folder (default: no snapshot, every load is uncached)")
|
|
parser.add_argument("--repeats", type=int, default=5)
|
|
parser.add_argument("--output", type=Path)
|
|
args = parser.parse_args()
|
|
if args.repeats < 1:
|
|
parser.error("--repeats must be at least one")
|
|
samples = []
|
|
for _ in range(args.repeats):
|
|
command = [str(args.binary.resolve()), str(args.resources.resolve())]
|
|
if args.cache_dir:
|
|
command.append(str(args.cache_dir.resolve()))
|
|
run = subprocess.run(command, text=True, capture_output=True, check=True)
|
|
values = {key: float(value) for key, value in
|
|
re.findall(r"^(registry_ms|behaviours_[0-2]_ms)=([\d.]+)$", run.stderr, re.M)}
|
|
if len(values) != 4:
|
|
raise RuntimeError(f"incomplete profile output: {run.stderr}")
|
|
samples.append(values)
|
|
summary = {key: {"median_ms": round(statistics.median(row[key] for row in samples), 3),
|
|
"samples_ms": [row[key] for row in samples]}
|
|
for key in samples[0]}
|
|
result = {"scope": "behaviour loader only; not full client startup",
|
|
"binary": str(args.binary), "resources": str(args.resources), "repeats": args.repeats,
|
|
"cache_dir": str(args.cache_dir) if args.cache_dir else None,
|
|
"metrics": summary}
|
|
print(json.dumps(result, indent=2))
|
|
if args.output:
|
|
args.output.parent.mkdir(parents=True, exist_ok=True)
|
|
args.output.write_text(json.dumps(result, indent=2) + "\n")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|