File size: 11,109 Bytes
51defdc
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
#!/usr/bin/env python3
# SPDX-License-Identifier: Apache-2.0
"""Minimal standalone repro (plain ttnn, no METEOR code): the separable bilinear x2 resize of the METEOR lift,
captured in one trace and replayed in a tight loop. On a Blackhole p150 with ETH dispatch the replay intermittently
never completes (logs/meteor/hangfix/ETH_DISPATCH_HANG_ISSUE.md).

    bin/devrun -t 600 -- python -u repro_resize2d_eth.py --dispatch eth --iters 3000 --jsonl out.jsonl
    bin/devrun -t 600 -- python -u repro_resize2d_eth.py --dispatch worker ...        # A/B

Op chain (= ttaw.ops.upsample.Resize2d, in (400, 250) -> out (800, 500), C = 96, N = 1, fp32 operands, HiFi4 + fp32
accumulation), on an input x [1, 1, 100000, 96] bf16 TILE in DRAM:

    RM -> reshape [1, 400, 250, 96] -> TILE -> typecast fp32 -> transpose(-2, -1) [1, 400, 96, 250]
    -> matmul A_w^T [250, 500] -> transpose(-2, -1) [1, 400, 500, 96] -> RM -> reshape [1, 1, 400, 48000] -> TILE
    -> matmul A_h [800, 400] @ [1, 1, 400, 48000] -> [1, 1, 800, 48000] fp32 -> typecast bf16 -> RM
    -> reshape [1, 1, 400000, 96] -> TILE

``--part w`` stops after the W pass, ``--part h`` runs only the H-pass matmul (on a persistent [1, 1, 400, 48000]
fp32 TILE input). Each replay is followed by ``synchronize_device`` and a fsync'd JSON heartbeat; outputs are hashed
every ``--check-every`` replays; faulthandler dumps the stack when a replay stalls. No tt-triage, no op timeout.
"""
from __future__ import annotations

import argparse
import faulthandler
import hashlib
import json
import os
import sys
import time

import numpy as np

H, W, H2, W2 = 400, 250, 800, 500


def interp(n_in: int, n_out: int) -> np.ndarray:
    """1-D bilinear, align_corners=False (half-pixel, clamped at 0), as F.interpolate."""
    s = n_in / n_out
    src = np.maximum((np.arange(n_out) + 0.5) * s - 0.5, 0.0)
    i0 = np.minimum(np.floor(src).astype(np.int64), n_in - 1)
    i1 = np.minimum(i0 + 1, n_in - 1)
    lam = src - i0
    a = np.zeros((n_out, n_in))
    np.add.at(a, (np.arange(n_out), i0), 1.0 - lam)
    np.add.at(a, (np.arange(n_out), i1), lam)
    return a.astype(np.float32)


def log(fh, **rec):
    rec["t"] = round(time.time(), 3)
    line = json.dumps(rec)
    print(line, flush=True)
    if fh:
        fh.write(line + "\n")
        fh.flush()
        os.fsync(fh.fileno())


def main() -> int:
    ap = argparse.ArgumentParser()
    ap.add_argument("--dispatch", choices=["eth", "worker"], default="eth")
    ap.add_argument("--cqs", type=int, default=1)
    ap.add_argument("--iters", type=int, default=3000)
    ap.add_argument("--per-trace", type=int, default=1, help="resize chains per trace")
    ap.add_argument("--part", choices=["full", "w", "h", "w_rm", "wh", "tail", "mid", "mid_h", "h_tail",
                                           "t_typecast", "t_untilize", "t_reshape", "t_tilize", "t_reshape_tile"], default="full",
                    help="w: up to the W-pass transpose; w_rm: + RM/reshape/TILE; wh: + H matmul (no tail); "
                         "tail: typecast/RM/reshape/TILE of a persistent z; mid: RM/reshape/TILE of a persistent "
                         "[1,400,500,C] y; mid_h: mid + H matmul; h_tail: H matmul + tail; t_*: ONE tail op on a "
                         "persistent input (typecast fp32->bf16 TILE [800, 500C]; untilize bf16 [800, 500C]; RM "
                         "reshape [800, 500C] -> [400000, C]; tilize RM [400000, C])")
    ap.add_argument("--channels", type=int, default=96, help="C (the H-pass matmul N is 500 * C; 96 -> 48000)")
    ap.add_argument("--dtype", choices=["float32", "bfloat16"], default="float32", help="matmul operand dtype")
    ap.add_argument("--fidelity", choices=["HiFi4", "HiFi3", "HiFi2", "LoFi"], default="HiFi4")
    ap.add_argument("--no-fp32-acc", action="store_true", help="fp32_dest_acc_en=False")
    ap.add_argument("--dest-cols", type=int, default=0,
                    help="t_reshape: destination last dim (default C: [800, 500C] -> [400000, C]); dest page = 2 B x it")
    ap.add_argument("--reshape", choices=["rm", "tile", "tail_tile"], default="rm",
                    help="rm: TILE -> RM -> reshape -> TILE (as Resize2d); tile: ttnn.reshape on the TILE tensor")
    ap.add_argument("--check-every", type=int, default=200)
    ap.add_argument("--stall-s", type=float, default=60.0)
    ap.add_argument("--jsonl")
    a = ap.parse_args()
    faulthandler.enable()
    fh = open(a.jsonl, "a") if a.jsonl else None
    faulthandler.dump_traceback_later(600, repeat=True)

    import torch
    import ttnn

    core = ttnn.DispatchCoreType.ETH if a.dispatch == "eth" else ttnn.DispatchCoreType.WORKER
    dev = ttnn.open_device(device_id=0, dispatch_core_config=ttnn.DispatchCoreConfig(core),
                           num_command_queues=a.cqs, l1_small_size=32768, trace_region_size=64 << 20)
    rc = 0
    try:
        g = dev.compute_with_storage_grid_size()
        C = a.channels
        dt = ttnn.float32 if a.dtype == "float32" else ttnn.bfloat16
        log(fh, ev="open", dispatch=a.dispatch, cqs=a.cqs, grid=f"{g.x}x{g.y}", part=a.part, per_trace=a.per_trace,
            channels=C, n=W2 * C, dtype=a.dtype, fidelity=a.fidelity, fp32_acc=not a.no_fp32_acc,
            reshape=a.reshape, dest_cols=a.dest_cols or None)
        cfg = ttnn.init_device_compute_kernel_config(dev.arch(), math_fidelity=getattr(ttnn.MathFidelity, a.fidelity),
                                                     fp32_dest_acc_en=not a.no_fp32_acc, packer_l1_acc=False,
                                                     math_approx_mode=False)
        to_dev = lambda arr, dt, lay: ttnn.from_torch(torch.from_numpy(np.ascontiguousarray(arr)), dtype=dt,  # noqa
                                                      layout=lay, device=dev, memory_config=ttnn.DRAM_MEMORY_CONFIG)
        rng = np.random.default_rng(0)
        aw_t = to_dev(interp(W, W2).T, dt, ttnn.TILE_LAYOUT)                     # [250, 500]
        ah = to_dev(interp(H, H2), dt, ttnn.TILE_LAYOUT)                         # [800, 400]
        x_in = to_dev(rng.standard_normal((1, 1, H * W, C)).astype(np.float32), ttnn.bfloat16, ttnn.TILE_LAYOUT)
        y_in = to_dev(rng.standard_normal((1, 1, H, W2 * C)).astype(np.float32), dt, ttnn.TILE_LAYOUT)

        z_in = to_dev(rng.standard_normal((1, 1, H2, W2 * C)).astype(np.float32), dt, ttnn.TILE_LAYOUT) \
            if a.part == "tail" else None
        yw_in = to_dev(rng.standard_normal((1, H, W2, C)).astype(np.float32), dt, ttnn.TILE_LAYOUT) \
            if a.part in ("mid", "mid_h") else None

        one = None
        if a.part.startswith("t_"):
            z32 = rng.standard_normal((1, 1, H2, W2 * C)).astype(np.float32)
            one = {"t_typecast": lambda: to_dev(z32, ttnn.float32, ttnn.TILE_LAYOUT),
                   "t_untilize": lambda: to_dev(z32, ttnn.bfloat16, ttnn.TILE_LAYOUT),
                   "t_reshape": lambda: to_dev(z32, ttnn.bfloat16, ttnn.ROW_MAJOR_LAYOUT),
                   "t_tilize": lambda: to_dev(z32.reshape(1, 1, H2 * W2, C), ttnn.bfloat16, ttnn.ROW_MAJOR_LAYOUT),
                   "t_reshape_tile": lambda: to_dev(z32, ttnn.bfloat16, ttnn.TILE_LAYOUT),
                   }[a.part]()

        def tail(z):
            if z.dtype != ttnn.bfloat16:
                z = ttnn.typecast(z, ttnn.bfloat16)
            if a.reshape in ("tile", "tail_tile"):
                return ttnn.reshape(z, (1, 1, H2 * W2, C))
            z = ttnn.to_layout(z, ttnn.ROW_MAJOR_LAYOUT)
            z = ttnn.reshape(z, (1, 1, H2 * W2, C))
            return ttnn.to_layout(z, ttnn.TILE_LAYOUT)

        def mid(y):
            if a.reshape == "tile":
                return ttnn.reshape(y, (1, 1, H, W2 * C))
            y = ttnn.to_layout(y, ttnn.ROW_MAJOR_LAYOUT)
            y = ttnn.reshape(y, (1, 1, H, W2 * C))
            return ttnn.to_layout(y, ttnn.TILE_LAYOUT)

        def chain():
            if a.part == "t_typecast":
                return ttnn.typecast(one, ttnn.bfloat16)
            if a.part == "t_untilize":
                return ttnn.to_layout(one, ttnn.ROW_MAJOR_LAYOUT)
            if a.part == "t_reshape":
                d = a.dest_cols or C
                return ttnn.reshape(one, (1, 1, H2 * W2 * C // d, d))
            if a.part == "t_reshape_tile":
                return ttnn.reshape(one, (1, 1, H2 * W2, C))
            if a.part == "t_tilize":
                return ttnn.to_layout(one, ttnn.TILE_LAYOUT)
            if a.part == "h":
                return ttnn.matmul(ah, y_in, compute_kernel_config=cfg)
            if a.part == "h_tail":
                return tail(ttnn.matmul(ah, y_in, compute_kernel_config=cfg))
            if a.part == "tail":
                return tail(z_in)
            if a.part == "mid":
                return mid(yw_in)
            if a.part == "mid_h":
                return ttnn.matmul(ah, mid(yw_in), compute_kernel_config=cfg)
            x = ttnn.to_layout(x_in, ttnn.ROW_MAJOR_LAYOUT)
            x = ttnn.reshape(x, (1, H, W, C))
            x = ttnn.to_layout(x, ttnn.TILE_LAYOUT)
            if dt != ttnn.bfloat16:
                x = ttnn.typecast(x, dt)
            xt = ttnn.transpose(x, -2, -1)
            y = ttnn.matmul(xt, aw_t, compute_kernel_config=cfg)
            y = ttnn.transpose(y, -2, -1)
            if a.part == "w":
                return y
            y = mid(y)
            if a.part == "w_rm":
                return y
            z = ttnn.matmul(ah, y, compute_kernel_config=cfg)
            if a.part == "wh":
                return z
            return tail(z)

        warm = [chain() for _ in range(a.per_trace)]          # compile, then capture
        ttnn.synchronize_device(dev)
        del warm
        tid = ttnn.begin_trace_capture(dev, cq_id=0)
        outs = [chain() for _ in range(a.per_trace)]
        ttnn.end_trace_capture(dev, tid, cq_id=0)
        ttnn.synchronize_device(dev)
        log(fh, ev="captured")
        ref = None
        for i in range(a.iters):
            faulthandler.dump_traceback_later(a.stall_s, repeat=True)
            t0 = time.perf_counter()
            ttnn.execute_trace(dev, tid, cq_id=0, blocking=False)
            ttnn.synchronize_device(dev)
            rec = {"ev": "iter", "i": i, "ms": round((time.perf_counter() - t0) * 1e3, 2)}
            if i % a.check_every == 0 or i == a.iters - 1:
                h = hashlib.sha256()
                for t in outs:
                    h.update(ttnn.to_torch(t).float().numpy().tobytes())
                d = h.hexdigest()[:16]
                rec["digest"] = d
                ref = ref or d
                if d != ref:
                    rec["MISMATCH"] = ref
                    rc = 1
            log(fh, **rec)
        faulthandler.cancel_dump_traceback_later()
        log(fh, ev="done", iters=a.iters, rc=rc)
        ttnn.release_trace(dev, tid)
    finally:
        faulthandler.dump_traceback_later(300, repeat=True)
        ttnn.close_device(dev)
        faulthandler.cancel_dump_traceback_later()
        log(fh, ev="closed")
    return rc


if __name__ == "__main__":
    sys.exit(main())