"""Measure native (M1) simulator throughput on the bundled demo firmware. Run standalone (from anywhere): python3 examples/scripts/sim_bench.py # untraced baseline python3 examples/scripts/sim_bench.py --trace # baseline + R4 trace overhead Reports MIPS for a fixed instruction budget -- the budget ``SimClient.continue_(timeout=1.0)`` derives from SIM_IPS = 1e6. With ``--trace`` a second run enables the exec/access trace and reports the per-instruction slowdown, so the non-trace path can be checked for regressions. """ from __future__ import annotations import argparse import sys import time from pathlib import Path ROOT = Path(__file__).resolve().parents[2] if str(ROOT) not in sys.path: sys.path.insert(0, str(ROOT)) import greeneye as ge from greeneye.errors import RspTimeout from sim.trace import Trace DEMO = ROOT / "firmware" / "uproc_demo" / "build" / "uproc_demo.elf" def measure(budget: int, *, trace: bool = False) -> tuple[int, float]: """Run a fixed instruction budget; return (instructions, seconds).""" session = ge.launch(kernel=str(DEMO), backend="native", name="sim-bench") machine = session.client.machine if trace: session.client.trace = Trace() # recorder stays null: nothing is written try: machine.insns = 0 started = time.perf_counter() try: session.client.continue_(timeout=budget / 1_000_000.0) except RspTimeout: pass # budget exhausted: the benchmark workload completed elapsed = time.perf_counter() - started banner = session.target.read_uart().splitlines() print(f"uart: {banner[0] if banner else ''!r}") return machine.insns, elapsed finally: session.close() def main(argv: list[str] | None = None) -> int: parser = argparse.ArgumentParser(description=__doc__.splitlines()[0]) parser.add_argument("--budget", type=int, default=1_000_000) parser.add_argument("--trace", action="store_true", help="also measure traced throughput") parser.add_argument("--trace-budget", type=int, default=200_000) args = parser.parse_args(argv) insns, elapsed = measure(args.budget) mips = insns / elapsed / 1e6 if elapsed else float("inf") print(f"native M1: {insns} instructions in {elapsed:.3f}s = {mips:.2f} MIPS (untraced)") if args.trace: t_insns, t_elapsed = measure(args.trace_budget, trace=True) t_mips = t_insns / t_elapsed / 1e6 if t_elapsed else float("inf") overhead = (mips / t_mips - 1.0) if t_mips else float("inf") print( f"native M1 trace: {t_insns} instructions in {t_elapsed:.3f}s = {t_mips:.2f} MIPS " f"({overhead * 100:+.1f}% per-instruction vs untraced)" ) return 0 if __name__ == "__main__": raise SystemExit(main())