Download groot1/bench.py from marcyano/Groot-1: direct link, hf CLI and curl.
- Browser
- Download file 4.01 kB
-
https://huggingface.co/marcyano/Groot-1/resolve/main/groot1/bench.py
- Command line
-
hf download hf://marcyano/Groot-1/groot1/bench.py
-
curl -L -o bench.py https://huggingface.co/marcyano/Groot-1/resolve/main/groot1/bench.py
4.01 kB
| """Latency benchmark for Groot checkpoints (and, optionally, the Jev API). | |
| groot-bench --model runs/groot-1-s/final [--device cpu --threads 8] [--jev] | |
| Measures p50/p95 end-to-end latency of `GrootEngine.answer` for synthetic | |
| requests at several state sizes and question counts. With --jev and a | |
| TYPESAFE_API_KEY in the environment, the same requests are sent to | |
| https://api.typesafe.ai/v1/systemone for a side-by-side table. | |
| """ | |
| from __future__ import annotations | |
| import argparse | |
| import json | |
| import os | |
| import statistics | |
| import time | |
| import torch | |
| from .schema import SystemOneRequest | |
| LOREM = ( | |
| "The customer reported that the invoice was charged twice on the corporate card and asked for a refund. " | |
| "Support confirmed the duplicate transaction and escalated the case to billing with high priority. " | |
| ) | |
| def synthetic_request(state_words: int, n_questions: int) -> SystemOneRequest: | |
| words = (LOREM * (state_words // len(LOREM.split()) + 1)).split()[:state_words] | |
| state = " ".join(words) | |
| questions = {} | |
| kinds = ["choice", "score", "noul"] | |
| for i in range(n_questions): | |
| k = kinds[i % 3] | |
| if k == "choice": | |
| questions[f"q{i}"] = {"type": "choice", "instructions": "Which team should handle this?", "criteria": {"billing": "payments", "shipping": "delivery", "access": "login", "other": "anything else"}} | |
| elif k == "score": | |
| questions[f"q{i}"] = {"type": "score", "instructions": "How urgent is it?", "criteria": ["can wait", "this week", "today"]} | |
| else: | |
| questions[f"q{i}"] = {"type": "noul", "instructions": "Is the customer asking for a refund?"} | |
| return SystemOneRequest(state=state, questions=questions) | |
| def time_fn(fn, warmup: int = 2, runs: int = 10) -> dict: | |
| for _ in range(warmup): | |
| fn() | |
| times = [] | |
| for _ in range(runs): | |
| t0 = time.perf_counter() | |
| fn() | |
| times.append((time.perf_counter() - t0) * 1000) | |
| times.sort() | |
| return {"p50_ms": round(statistics.median(times), 1), "p95_ms": round(times[int(0.95 * (len(times) - 1))], 1), "min_ms": round(times[0], 1)} | |
| def main(argv: list[str] | None = None) -> None: | |
| ap = argparse.ArgumentParser(prog="groot-bench") | |
| ap.add_argument("--model", required=True) | |
| ap.add_argument("--device", default=None) | |
| ap.add_argument("--threads", type=int, default=None) | |
| ap.add_argument("--runs", type=int, default=10) | |
| ap.add_argument("--jev", action="store_true") | |
| args = ap.parse_args(argv) | |
| if args.threads: | |
| torch.set_num_threads(args.threads) | |
| from .serve import GrootEngine | |
| engine = GrootEngine(args.model, args.device) | |
| grid = [(64, 1), (256, 1), (256, 5), (1024, 5), (1024, 20), (4096, 5)] | |
| rows = [] | |
| for words, nq in grid: | |
| req = synthetic_request(words, nq) | |
| enc = (engine.members[0] if engine.members else engine).ser.encode_request(req.state, req.questions) | |
| res = time_fn(lambda: engine.answer(req.state, req.questions), runs=args.runs) | |
| row = {"state_tokens": enc.state.tokens, "chunks": len(enc.state.chunks), "questions": nq, **{f"groot_{k}": v for k, v in res.items()}} | |
| if args.jev and os.environ.get("TYPESAFE_API_KEY"): | |
| import httpx | |
| body = {"model": "jev-latest", "state": req.state, "questions": {k: v.model_dump(exclude_none=True) for k, v in req.questions.items()}} | |
| headers = {"Authorization": f"Bearer {os.environ['TYPESAFE_API_KEY']}"} | |
| with httpx.Client(timeout=30) as client: | |
| res_j = time_fn(lambda: client.post("https://api.typesafe.ai/v1/systemone", json=body, headers=headers).raise_for_status(), warmup=1, runs=max(3, args.runs // 2)) | |
| row.update({f"jev_{k}": v for k, v in res_j.items()}) | |
| rows.append(row) | |
| print(json.dumps(row), flush=True) | |
| print(json.dumps({"device": str(engine.device), "threads": torch.get_num_threads(), "model": engine.name, "rows": rows}, indent=2)) | |
| if __name__ == "__main__": | |
| main() | |