performance_smoke.py (2845B)
1 #!/usr/bin/env python3 2 """Run each host smoke in a fresh, resource-limited process. 3 4 Three samples are descriptive, not a statistically controlled benchmark. 5 Every child validates its complete result before reporting a timing. 6 """ 7 import hashlib 8 import json 9 import os 10 from pathlib import Path 11 import platform 12 import statistics 13 import subprocess 14 import sys 15 import time 16 17 runner, fixtures, output = sys.argv[1:] 18 fixtures = Path(fixtures) 19 20 21 # The C executable applies limits before initializing the runtime. 22 23 cases = [(kind, n, 5) for kind in ("id", "append") for n in (256, 4096, 16384)] 24 cases += [(kind, n, 1) for kind in ("deep", "distribution") for n in (1000, 10000, 100000)] 25 cases += [("append", 4096, 256)] 26 report = { 27 "platform": platform.platform(), 28 "machine": platform.machine(), "cpu_count": os.cpu_count(), 29 "cpu_model": next((line.split(":", 1)[1].strip() for line in 30 Path("/proc/cpuinfo").read_text().splitlines() 31 if line.startswith("model name")), "unknown"), 32 "rss_measurement": "Linux /proc/self/status VmHWM (peak), /proc/self/statm (mid/end)", 33 "limits": {"address_space_mib": 512, "stack_mib": 8, "wall_seconds": 65, "cpu_seconds": 60}, 34 "samples_per_case": 3, 35 "runner": runner, 36 "fixture_sha256": {name: hashlib.sha256((fixtures / name).read_bytes()).hexdigest() 37 for name in ("id.arboricx", "append.arboricx")}, 38 "cases": [], 39 } 40 for kind, n, rounds in cases: 41 samples = [] 42 for _ in range(3): 43 fixture = fixtures / ("append.arboricx" if kind == "append" else "id.arboricx") 44 started = time.monotonic() 45 completed = subprocess.run([runner, kind, str(n), str(rounds), str(fixture)], 46 text=True, capture_output=True, timeout=65) 47 if completed.returncode: 48 raise RuntimeError(f"{kind}/{n}/{rounds}: exit {completed.returncode}\n{completed.stderr}") 49 sample = json.loads(completed.stdout) 50 if "host_mode" not in report: 51 report["host_mode"] = sample["host_mode"] 52 if sample["host_mode"] != report["host_mode"]: 53 raise RuntimeError("mixed runtime optimization modes") 54 sample["process_wall_s"] = time.monotonic() - started 55 samples.append(sample) 56 entry = {"kind": kind, "n": n, "rounds": rounds, "samples": samples} 57 entry["median_ms_per_iteration"] = { 58 field: statistics.median(s[field + "_s"] for s in samples) * 1000 / rounds 59 for field in ("build", "reduce", "observe", "gc")} 60 entry["max_peak_rss_mib"] = max(s["peak_rss_kib"] for s in samples) / 1024 61 report["cases"].append(entry) 62 print(f"{kind}/{n} x{rounds}: {entry['median_ms_per_iteration']} peak={entry['max_peak_rss_mib']:.1f} MiB", flush=True) 63 Path(output).write_text(json.dumps(report, indent=2) + "\n")