Download code/scripts/profile_ops.py from changh95/frnet-p150: direct link, hf CLI and curl.
- Browser
- Download file 9.24 kB
-
https://huggingface.co/changh95/frnet-p150/resolve/main/code/scripts/profile_ops.py
- Command line
-
hf download hf://changh95/frnet-p150/code/scripts/profile_ops.py
-
curl -L -o profile_ops.py https://huggingface.co/changh95/frnet-p150/resolve/main/code/scripts/profile_ops.py
9.24 kB
| #!/usr/bin/env python3 | |
| # SPDX-License-Identifier: Apache-2.0 | |
| """Device profile of frnet-p150: one eager forward and one traced replay of the whole graph between Tracy | |
| signposts, for one serve profile (``--variant ot128 | qt128``). | |
| ROOT=/home/ubuntu/experiments/tt-models | |
| $ROOT/bin/devrun -t 3600 -- python -m tracy -r -p -v --no-web-server --op-support-count 4000 \\ | |
| -o $ROOT/generated/profiler/frnet_baseline code/scripts/profile_ops.py --variant ot128 | |
| tt-perf-report <ops_perf_results_*.csv> --start-signpost trace --end-signpost trace_end --tracing-mode | |
| python code/scripts/profile_summary.py <the same csv> profile_summary.json --md profile_summary.md | |
| Sections (signposts): ``eager`` .. ``eager_end``, one eager forward after the warm-up (``TraceRunner.run_eager``: | |
| the same graph and programs as the trace, nothing compiled), and ``trace`` .. ``trace_end``, ``--replays`` replays | |
| of the captured ``default`` trace (the served device path; the uploaded inputs are the plan of ``--input``). The | |
| device profiler buffer is flushed (``ttnn.ReadDeviceProfiler``) before and after each section. Inside the eager | |
| section every module of the graph opens a signpost (``m:<module>``: ``encoder``, ``segmax``, ``frustum2pixel``, | |
| ``pixel2point``, ``stem``, ``point_stem``, ``fusion_stem``, ``layer<l>``, ``point_fusion<l>``, ``pixel_fusion<l>``, | |
| ``attention<l>``, ``decoder.up``, ``decoder.fuse1``, ``decoder.fuse2``, ``point_fuse``, ``head``) and every layer a | |
| layer signpost (``c:<layer>``; ``--no-layer-signposts`` turns them off). Ops that the graph issues inline after a | |
| module (the residual ``add`` + ``hardswish`` of a BasicBlock, ``addcmul`` after the attention, the softmax after the | |
| logits) belong to the module signposted last. Through the identical op order the ops of the replay inherit the | |
| eager attribution (``profile_summary.py``). The output folder must be absolute and outside the bundle (PLAN.md 5.1). | |
| """ | |
| from __future__ import annotations | |
| import argparse | |
| import json | |
| import os | |
| import time | |
| from pathlib import Path | |
| from typing import Callable, List, Sequence | |
| from tt_frnet import FRNet | |
| from tt_frnet.ttaw.profiling import read_device_profiler, signpost, signposted | |
| CODE = Path(__file__).resolve().parents[1] | |
| SAMPLES = CODE / "tt_frnet" / "samples" # the shipped synthetic_<variant>.npz | |
| class _Signposted: | |
| """A callable stand-in for a module that emits its signposts, then calls the module (attributes forward).""" | |
| def __init__(self, inner, labels: Sequence[str]): | |
| self.inner, self._labels = inner, list(labels) | |
| def __call__(self, *args, **kwargs): | |
| for label in self._labels: | |
| signpost(label) | |
| return self.inner(*args, **kwargs) | |
| def __getattr__(self, name): | |
| return getattr(self.inner, name) | |
| def module_signposts(graph, layers: bool = True) -> Callable[[], None]: | |
| """Wrap the modules of a ``tt.model.TtFRNet`` (and the C19 gathers its forward calls) with signposting | |
| stand-ins; returns the function that restores them.""" | |
| from tt_frnet.tt import model as tt_model | |
| undo: List[Callable[[], None]] = [] | |
| def labels(module: str, layer: str) -> List[str]: | |
| return [f"m:{module}"] + ([f"c:{layer}"] if layers else []) | |
| def swap(obj, attr, lab): | |
| inner = getattr(obj, attr) | |
| setattr(obj, attr, _Signposted(inner, lab)) | |
| undo.append(lambda: setattr(obj, attr, inner)) | |
| def swap_item(seq, key, lab): | |
| inner = seq[key] | |
| seq[key] = _Signposted(inner, lab) | |
| undo.append(lambda: seq.__setitem__(key, inner)) | |
| for fn_name in ("frustum2pixel", "pixel2point"): | |
| orig = getattr(tt_model, fn_name) | |
| def wrapped(*args, _orig=orig, _name=fn_name, **kwargs): | |
| signpost(f"m:{_name}") | |
| return _orig(*args, **kwargs) | |
| setattr(tt_model, fn_name, wrapped) | |
| undo.append(lambda _n=fn_name, _o=orig: setattr(tt_model, _n, _o)) | |
| for i in range(len(graph.ffe)): | |
| swap_item(graph.ffe, i, labels("encoder", f"encoder.ffe{i}")) | |
| swap(graph, "compression", labels("encoder", "encoder.compression")) | |
| swap(graph, "segmax_encoder", labels("segmax", "segmax.encoder")) | |
| swap(graph, "segmax_points", labels("segmax", "segmax.points")) | |
| for i in range(len(graph.stem)): | |
| swap_item(graph.stem, i, labels("stem", f"backbone.stem{i}")) | |
| swap(graph, "point_stem", labels("point_stem", "backbone.point_stem")) | |
| swap(graph, "fusion_stem", labels("fusion_stem", "backbone.fusion_stem")) | |
| for st in graph.stages: | |
| l = st["index"] | |
| blocks = st["blocks"] | |
| for b, (c1, c2, ds) in enumerate(blocks): | |
| pre = f"backbone.layer{l}.block{b}" | |
| new = (_Signposted(c1, labels(f"layer{l}", f"{pre}.conv1")), | |
| _Signposted(c2, labels(f"layer{l}", f"{pre}.conv2")), | |
| None if ds is None else _Signposted(ds, labels(f"layer{l}", f"{pre}.downsample"))) | |
| blocks[b] = new | |
| undo.append(lambda _bl=blocks, _b=b, _o=(c1, c2, ds): _bl.__setitem__(_b, _o)) | |
| swap_item(st, "point_fusion", labels(f"point_fusion{l}", f"backbone.point_fusion{l}")) | |
| swap_item(st, "pixel_fusion", labels(f"pixel_fusion{l}", f"backbone.pixel_fusion{l}")) | |
| for j in range(len(st["attention"])): | |
| swap_item(st["attention"], j, labels(f"attention{l}", f"backbone.attention{l}.conv{j}")) | |
| for s in list(graph.ups): | |
| swap_item(graph.ups, s, labels("decoder.up", f"decoder.up{s}")) | |
| swap(graph, "fuse1", labels("decoder.fuse1", "decoder.fuse1")) | |
| swap(graph, "fuse2", labels("decoder.fuse2", "decoder.fuse2")) | |
| swap(graph, "point_fuse1", labels("point_fuse", "decoder.point_fuse1")) | |
| swap(graph, "point_fuse2", labels("point_fuse", "decoder.point_fuse2")) | |
| for i in range(len(graph.mlps)): | |
| swap_item(graph.mlps, i, labels("head", f"head.mlp{i}")) | |
| swap(graph, "logits", labels("head", "head.logits")) | |
| def restore() -> None: | |
| for fn in reversed(undo): | |
| fn() | |
| return restore | |
| def calibration_for(path: Path, spec=None) -> dict: | |
| """The ``calibration`` of ``path`` (the rules of ``bench.py calibration_for``).""" | |
| if spec: | |
| if spec == "none": | |
| return {"ego_crop_box": None} | |
| if spec.startswith("preset:"): | |
| return {"preset": spec[len("preset:"):]} | |
| return json.loads(Path(spec).read_text()) | |
| stem = path.name.split(".")[0] | |
| if (CODE / "tt_frnet" / "calib" / f"{stem}.json").is_file(): | |
| return {"preset": stem} | |
| side = path.parent / f"{stem}.calib.json" | |
| return json.loads(side.read_text()) if side.is_file() else {"ego_crop_box": None} | |
| def main() -> None: | |
| ap = argparse.ArgumentParser(description=__doc__.split("\n")[0]) | |
| ap.add_argument("--input", default=None, help="default: the shipped synthetic sample of the variant") | |
| ap.add_argument("--calib", default=None, help="preset:<name> | <calibration .json> | none (bench.py rules)") | |
| ap.add_argument("--variant", default=None, help="ot128 | qt128 (default: FRNET_VARIANT or ot128)") | |
| ap.add_argument("--dispatch", default=None, choices=["eth", "worker"]) | |
| ap.add_argument("--cqs", type=int, default=None, choices=[1, 2]) | |
| ap.add_argument("--replays", type=int, default=1, help="replays inside the trace section") | |
| ap.add_argument("--no-eager", dest="eager", action="store_false") | |
| ap.add_argument("--no-layer-signposts", dest="layers", action="store_false") | |
| a = ap.parse_args() | |
| import ttnn | |
| src = Path(a.input or SAMPLES / f"synthetic_{a.variant or os.environ.get('FRNET_VARIANT', 'ot128')}.npz") | |
| calib = calibration_for(src, a.calib) | |
| t0 = time.perf_counter() | |
| with FRNet.from_pretrained(variant=a.variant, dispatch=a.dispatch, num_command_queues=a.cqs) as model: | |
| tr, dev = model.runner, model.device | |
| print("loaded in %.1f s:" % (time.perf_counter() - t0), json.dumps(model.device_info), flush=True) | |
| prepared = model._prepare(points=str(src), calibration=calib) | |
| out = model(str(src), calibration=calib) # one served frame: upload + replay + readback | |
| print(f"variant {model.variant}: N {prepared.plan.n} M {prepared.plan.m} (N_cap {prepared.plan.n_cap}, " | |
| f"M_cap {prepared.plan.m_cap}); {len(out.label_ids)} labelled points", flush=True) | |
| ttnn.synchronize_device(dev) | |
| read_device_profiler(dev) # warm-up / capture / first frame out of the buffer | |
| inputs = model.staging.write(prepared.plan) | |
| if a.eager: | |
| restore = module_signposts(model.graph, layers=a.layers) | |
| try: | |
| with signposted("eager"): | |
| tr.run_eager("default", inputs=inputs) | |
| ttnn.synchronize_device(dev) | |
| finally: | |
| restore() | |
| read_device_profiler(dev) | |
| tr.upload(inputs) | |
| ttnn.synchronize_device(dev) | |
| read_device_profiler(dev) | |
| with signposted("trace"): | |
| tr.replay("default", n=a.replays) | |
| ttnn.synchronize_device(dev) | |
| read_device_profiler(dev) | |
| print("trace:", json.dumps(tr.describe()), flush=True) | |
| if __name__ == "__main__": | |
| main() | |