()
| 542 | |
| 543 | |
| 544 | def main() -> int: |
| 545 | ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) |
| 546 | default_bin = os.environ.get("JCODE_BENCH_BIN") or shutil.which("jcode") or "./target/release/jcode" |
| 547 | ap.add_argument("binary", nargs="?", default=default_bin) |
| 548 | ap.add_argument("--runs", type=int, default=5) |
| 549 | ap.add_argument("--sample-interval-ms", type=float, default=2.0) |
| 550 | ap.add_argument("--client-hold-s", type=float, default=2.5, |
| 551 | help="how long to keep the cold client alive while sampling") |
| 552 | ap.add_argument("--json", type=str, default=None, help="write full results to a JSON file") |
| 553 | ap.add_argument("--skip-client", action="store_true") |
| 554 | ap.add_argument("--skip-server", action="store_true") |
| 555 | args = ap.parse_args() |
| 556 | |
| 557 | binary = args.binary |
| 558 | if not os.path.exists(binary): |
| 559 | resolved = shutil.which(binary) |
| 560 | if resolved: |
| 561 | binary = resolved |
| 562 | else: |
| 563 | print(f"Binary not found: {binary}", file=sys.stderr) |
| 564 | return 1 |
| 565 | binary = os.path.realpath(binary) |
| 566 | |
| 567 | size = os.path.getsize(binary) |
| 568 | print("=" * 64) |
| 569 | print("jcode spawn profile: time + resource utilization") |
| 570 | print("=" * 64) |
| 571 | print(f"Binary : {binary}") |
| 572 | print(f"Size : {size/1048576:.1f} MB") |
| 573 | print(f"Runs : {args.runs} sample interval: {args.sample_interval_ms} ms") |
| 574 | print(f"Host : {os.cpu_count()} CPUs, isolated JCODE_HOME per run") |
| 575 | |
| 576 | interval = args.sample_interval_ms / 1000.0 |
| 577 | results: dict = {"binary": binary, "binary_size_bytes": size, "runs": args.runs} |
| 578 | |
| 579 | # warm the page cache / binary load |
| 580 | subprocess.run([binary, "--version"], capture_output=True, check=False) |
| 581 | |
| 582 | if not args.skip_server: |
| 583 | print(f"\n[1/2] Profiling server daemon spawn ({args.runs} runs)...") |
| 584 | server_runs = [] |
| 585 | ready = [] |
| 586 | for i in range(args.runs): |
| 587 | r = profile_server_spawn(binary, interval) |
| 588 | if r: |
| 589 | server_runs.append(r) |
| 590 | if r.get("ready_ms"): |
| 591 | ready.append(r["ready_ms"]) |
| 592 | print(f" run {i+1}: ready={r.get('ready_ms', 0):.1f}ms " |
| 593 | f"peak_rss={mb(r.get('peak_rss_bytes')).strip()}MB " |
| 594 | f"threads={r.get('peak_threads')} fds={r.get('peak_fds')}") |
| 595 | results["server"] = server_runs |
| 596 | if ready: |
| 597 | print(f"\n Server ready (socket connectable): " |
| 598 | f"min={min(ready):.1f} median={statistics.median(ready):.1f} " |
| 599 | f"max={max(ready):.1f} ms") |
| 600 | print_resource_block("Server daemon resource footprint", server_runs) |
| 601 |
no test coverage detected