training / scripts /bench_threads.py
cazyundee's picture
fix: disable gradio SSR (node server crash)
11f47cf verified
Raw History Blame Contribute Delete
3.18 kB
#!/usr/bin/env python3
"""
Measure self-play throughput for (parallel experiments x threads each).
The research machine has 16 cores. The question is whether one 16-thread job or
several narrow jobs give more total positions/second. Tiny models usually
parallelise badly across threads, so N x 1-thread often wins -- but measure it.
python scripts/bench_threads.py --total-cores 16 --games 8 --plies 40
"""
from __future__ import annotations
import argparse
import itertools
import json
import multiprocessing as mp
import os
import sys
import time
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
def worker(threads, games, plies, steps, seed, q):
os.environ["OMP_NUM_THREADS"] = str(threads)
os.environ["MKL_NUM_THREADS"] = str(threads)
import torch
torch.set_num_threads(threads)
from tinychess.config import baseline_config
from tinychess.model import TinyChess
from tinychess.selfplay import SelfPlayConfig, play_games
torch.manual_seed(seed)
m = TinyChess(baseline_config())
t = time.time()
_, st = play_games(m, cfg=SelfPlayConfig(n_games=games, max_plies=plies,
steps=steps, seed=seed,
adjudicate="material"))
q.put({"moves": st["total_moves"], "seconds": time.time() - t})
def run(nproc, threads, games, plies, steps):
q = mp.Queue()
ps = [mp.Process(target=worker, args=(threads, games, plies, steps, 100 + i, q))
for i in range(nproc)]
t0 = time.time()
for p in ps:
p.start()
res = [q.get() for _ in ps]
for p in ps:
p.join()
wall = time.time() - t0
moves = sum(r["moves"] for r in res)
return {"procs": nproc, "threads": threads, "wall": round(wall, 2),
"moves": moves, "moves_per_s": round(moves / wall, 1)}
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--total-cores", type=int, default=os.cpu_count())
ap.add_argument("--games", type=int, default=8)
ap.add_argument("--plies", type=int, default=40)
ap.add_argument("--steps", type=int, default=4)
ap.add_argument("--out", default="runs/bench_threads.json")
a = ap.parse_args()
C = a.total_cores
combos = []
n = 1
while n <= C:
combos.append((n, max(1, C // n)))
n *= 2
print(f"cores={C} benchmarking {len(combos)} configurations")
print(f"{'procs':>6} {'thr':>4} {'wall(s)':>9} {'moves':>8} {'moves/s':>9}")
rows = []
for nproc, thr in combos:
r = run(nproc, thr, a.games, a.plies, a.steps)
rows.append(r)
print(f"{r['procs']:>6} {r['threads']:>4} {r['wall']:>9.2f} "
f"{r['moves']:>8} {r['moves_per_s']:>9}")
best = max(rows, key=lambda r: r["moves_per_s"])
print(f"\nBEST: {best['procs']} process(es) x {best['threads']} thread(s) "
f"= {best['moves_per_s']} moves/s")
os.makedirs(os.path.dirname(os.path.abspath(a.out)), exist_ok=True)
json.dump({"cores": C, "rows": rows, "best": best}, open(a.out, "w"), indent=2)
if __name__ == "__main__":
mp.set_start_method("spawn", force=True)
main()