File size: 9,240 Bytes
fe51a63 | 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 | #!/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()
|