File size: 10,381 Bytes
0c2d7e9 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 | # SPDX-FileCopyrightText: © 2026 Tenstorrent USA, Inc.
# SPDX-License-Identifier: Apache-2.0
"""Model start-up cost of ``Gr00tTT.from_pretrained`` per DiT backend (megakernel-default pass; WP-K5 review NB 7 /
§8 pre-condition 5): wall time of the weight upload and the module builds, the tensors and bytes ``TTWeights`` puts
on the device, and what the megakernel backend skips (``tt.model.megakernel_weight_names``).
Per invocation the device is opened once through ``tt.model.open_model_device(policy)`` and the model is built
``--rounds`` times for each configuration -- ``skip`` (the default: the megakernel packs the DiT / precompute / encoder
tensors into its arena and ``TTWeights`` leaves them out) and ``keep`` (``load_ttnn_dit_weights=True``: the full
Stage-1 upload next to the arena, what WP-K5 measured) -- alternating ``keep, skip, keep, skip, ...`` so both see the
same OS page-cache state of the ``.tensorbin`` tier; every model is released before the next build. With
``--dit-backend ttnn`` only the ``keep`` configuration exists (the ttnn head reads those weights).
Output ``benchmarks/results/bench_load_<version>_<ts>.json`` (schema ``gr00t-tt-perf/1``): stages
``from_pretrained/<cfg>`` (median ms over the rounds), ``weights_load/<cfg>``, ``head_build/<cfg>`` (the megakernel
arena plan + pack + upload sit inside it); ``meta.configs`` holds the per-round numbers, the tensor counts, the bytes
on the device and the skipped set, ``meta.device_mb`` the MB of plan tensors per configuration and ``meta.delta`` the
keep - skip savings.
Run::
DEVICE_LOCK_TIMEOUT=14400 /home/deepgadget/experiments/gr00t/bin/with-device.sh \\
python -m models.experimental.gr00t.benchmarks.bench_load --version n16 [--rounds 2] [--dit-backend ttnn]
"""
from __future__ import annotations
import argparse
import statistics
import time
from pathlib import Path
from typing import Any, Dict, List, Optional, Sequence
from models.experimental.gr00t.benchmarks import bench_e2e as E2E
from models.experimental.gr00t.benchmarks import bench_stage_ops as SO
from models.experimental.gr00t.tt import device as D
from models.experimental.gr00t.tt.policy import DEFAULT_DIT_BACKEND, DIT_BACKENDS, MK_ARENA_DTYPES
BENCH_NAME = "bench_load"
CONFIGS: Dict[str, Optional[bool]] = {"keep": True, "skip": None} # -> from_pretrained(load_ttnn_dit_weights=)
def configs_for(dit_backend: str) -> Dict[str, Optional[bool]]:
"""The configurations that exist for a backend: both for the megakernel, ``keep`` only for ttnn."""
if dit_backend == "megakernel":
return dict(CONFIGS)
return {"keep": None}
def one_build(
setup: Dict[str, Any], device: Any, cache_root: Optional[Path], load_ttnn: Optional[bool]
) -> Dict[str, Any]:
"""Build the model once, record the timings / weight facts, release it."""
t0 = time.perf_counter()
model = E2E.load_model(setup, device, cache_root, load_ttnn_dit_weights=load_ttnn)
total = time.perf_counter() - t0
try:
timing = {k: float(v) for k, v in model.timing.items() if k.endswith("_s")}
head = {k: float(v) for k, v in model.head.timing.items()}
rec = {
"from_pretrained_s": total,
"weights_load_s": timing.get("weights_load_s"),
"backbone_build_s": timing.get("backbone_build_s"),
"adapter_build_s": timing.get("adapter_build_s"),
"embed_gather_build_s": timing.get("embed_gather_build_s"),
"executor_and_head_build_s": timing.get("executor_and_head_build_s"),
"head_timing": head,
"n_tensors": len(model.W),
"device_bytes": float(model.W.device_bytes),
"load_path": model.W.load_stats.get("path"),
"n_warm": model.W.load_stats.get("n_warm"),
"n_cold": model.W.load_stats.get("n_cold"),
"skipped": dict(model.weights_skipped),
"dit_backend": model.policy.dit_backend,
"mk_arena_dtype": model.recipe.mk_arena_dtype,
"adapter_mem": model.adapter_mem,
}
finally:
model.release()
import ttnn
ttnn.synchronize_device(device)
return rec
def run(
device: Any,
*,
version: str,
policy: str = "mixed_dit",
trace_layout: str = "per_stage",
dit_backend: str = DEFAULT_DIT_BACKEND,
mk_arena_dtype: str = "auto",
rounds: int = 2,
cache_root: Optional[Path] = None,
verbose: bool = True,
) -> Dict[str, Any]:
if rounds <= 0:
raise ValueError("rounds must be positive")
setup = E2E.resolve_setup(
version, policy=policy, trace_layout=trace_layout, dit_backend=dit_backend, mk_arena_dtype=mk_arena_dtype
)
cfgs = configs_for(dit_backend)
per_cfg: Dict[str, List[Dict[str, Any]]] = {name: [] for name in cfgs}
for r in range(rounds):
for name, load_ttnn in cfgs.items():
rec = one_build(setup, device, cache_root, load_ttnn)
rec["round"] = r
per_cfg[name].append(rec)
if verbose:
print(
f"[bench_load] {version}/{dit_backend}/{name} round {r}: from_pretrained {rec['from_pretrained_s']:.2f} s "
f"(weights {rec['weights_load_s']:.2f} s, {rec['n_tensors']} tensors, {rec['device_bytes'] / 1e6:.0f} MB, "
f"{rec['load_path']}; head {rec['executor_and_head_build_s']:.2f} s"
f"{'; skipped ' + str(rec['skipped'].get('n_tensors')) + ' tensors / ' + format(rec['skipped'].get('device_bytes', 0) / 1e6, '.0f') + ' MB' if rec['skipped'] else ''})"
)
stages: Dict[str, Dict[str, Any]] = {}
for name, recs in per_cfg.items():
def med(key: str) -> float:
vals = [float(x[key]) for x in recs if x.get(key) is not None]
return statistics.median(vals) if vals else float("nan")
stages[f"from_pretrained/{name}"] = {
"metric": "ms",
"value": med("from_pretrained_s") * 1e3,
"min": min(float(x["from_pretrained_s"]) for x in recs) * 1e3,
"per_round": [round(float(x["from_pretrained_s"]) * 1e3, 1) for x in recs],
}
stages[f"weights_load/{name}"] = {"metric": "ms", "value": med("weights_load_s") * 1e3}
stages[f"head_build/{name}"] = {
"metric": "ms",
"value": med("executor_and_head_build_s") * 1e3,
"note": "executor + ActionHeadTT build (megakernel: arena plan + pack + upload + io inside)",
}
facts = D.device_facts(device)
facts["worker_l1_size"] = E2E._worker_l1_size(device)
meta = dict(
setup["meta"],
configs={name: recs for name, recs in per_cfg.items()},
device_mb={
name: {"mb": recs[0]["device_bytes"] / 1e6, "n_tensors": recs[0]["n_tensors"]}
for name, recs in per_cfg.items()
},
rounds=rounds,
worker_l1_size=facts["worker_l1_size"],
)
if "skip" in per_cfg and "keep" in per_cfg:
k, s = per_cfg["keep"][0], per_cfg["skip"][0]
meta["delta"] = {
"device_mb_saved": (k["device_bytes"] - s["device_bytes"]) / 1e6,
"n_tensors_saved": k["n_tensors"] - s["n_tensors"],
"from_pretrained_ms_saved_median": stages["from_pretrained/keep"]["value"]
- stages["from_pretrained/skip"]["value"],
"weights_load_ms_saved_median": stages["weights_load/keep"]["value"] - stages["weights_load/skip"]["value"],
}
payload = SO.perf_payload(
BENCH_NAME,
version,
dry_run=False,
args={
"version": version,
"policy": policy,
"trace_layout": trace_layout,
"dit_backend": dit_backend,
"mk_arena_dtype": mk_arena_dtype,
"rounds": rounds,
},
method={
"builds": f"{rounds} round(s) of alternating keep / skip Gr00tTT.from_pretrained on one open device, "
"each model released before the next build; medians over the rounds",
"clock": "host wall clock (time.perf_counter)",
},
stages=stages,
rows=[],
device=facts,
meta=meta,
)
return payload
def build_parser() -> argparse.ArgumentParser:
ap = argparse.ArgumentParser(description=__doc__.split("\n\n")[0])
ap.add_argument("--version", required=True, choices=E2E.VERSIONS)
ap.add_argument("--policy", default="mixed_dit", choices=E2E.POLICIES)
ap.add_argument("--trace-layout", default="per_stage", choices=E2E.TRACE_LAYOUTS)
ap.add_argument("--dit-backend", default=DEFAULT_DIT_BACKEND, choices=DIT_BACKENDS)
ap.add_argument("--mk-arena-dtype", default="auto", choices=MK_ARENA_DTYPES)
ap.add_argument("--rounds", type=int, default=2)
ap.add_argument("--cache-root", default=None)
ap.add_argument("--out", default=str(SO.RESULTS_DIR))
ap.add_argument("--trace-region-mb", type=int, default=64)
ap.add_argument("--num-cqs", type=int, default=2, choices=(1, 2))
return ap
def main(argv: Optional[Sequence[str]] = None) -> int:
args = build_parser().parse_args(argv)
if E2E.import_gr00t_tt() is None:
raise ImportError("tt.model.Gr00tTT is not importable")
SO.ensure_tt_metal_cache()
from models.experimental.gr00t.tt import model as M
from models.experimental.gr00t.tt.policy import TTPolicy
device = M.open_model_device(
TTPolicy(
dtype_policy=args.policy,
trace_layout=args.trace_layout,
dit_backend=args.dit_backend,
mk_arena_dtype=args.mk_arena_dtype,
),
trace_region_size=args.trace_region_mb << 20,
l1_small_size=D.DEFAULT_L1_SMALL_SIZE,
num_command_queues=args.num_cqs,
)
try:
payload = run(
device,
version=args.version,
policy=args.policy,
trace_layout=args.trace_layout,
dit_backend=args.dit_backend,
mk_arena_dtype=args.mk_arena_dtype,
rounds=args.rounds,
cache_root=Path(args.cache_root) if args.cache_root else None,
)
finally:
D.close_gr00t_device(device)
path = SO.write_perf_json(payload, Path(args.out))
print(f"[bench_load] wrote {path}")
return 0
if __name__ == "__main__":
raise SystemExit(main())
|