File size: 17,596 Bytes
4d9b003 be62f78 4d9b003 | 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 | # SPDX-License-Identifier: Apache-2.0
"""Device-side constants and the precision policy of the ttnn graph (COMPILE parameters, PLAN.md 0.4).
Shapes. The masked attentions run on tile-aligned key counts: a user mask with an unaligned key count lets the
padded keys of the last tile into the softmax (``ttaw.ops.attention``, C20), so the fusion transformer runs on
:data:`TOKENS` = 576 tokens (564 + 12 zero pad tokens, masked as keys) and the decoder on :data:`AGENTS` = 352 agents
(321 + 31 extra rows, masked as self-attention keys). Both counts are what the tiles hold anyway (no extra compute).
Rows past the real counts are computed (finite) and never read back. The cross-attention keys are the real 564
tokens (no mask: the op masks its own padding element by element).
Solver state. ``y = x * mask0`` (the state with the t = 0 columns, the prefix-constrained current state, zeroed) is
the fp32 state of the on-device DPM-Solver++(2M) loop; the decoder input is ``x = y + cs`` (``cs`` = current
states in the t = 0 columns) and the last projection's t = 0 output columns are zeroed in the weights, so the
model output is ``m * mask0`` and every update ``y' = A y - B m_k + C m_(k-1)`` keeps ``y * mask0 = y``. This is
the node's update + ``apply_prefix_constraint`` exactly (``m``'s t = 0 slot is never used: the correction
overwrites it).
Numerics options (:data:`KNOBS`, measured on the device, PORT_LOG.md 5.2): the fused ``ttnn.layer_norm`` loses
the per-entity signal on offset-dominated rows (fp32 decomposition for the mixers and the decoder: ``LN_FP32``); a
device fp32 matmul truncates its operands TF32-like, which the mixers amplify ~100x and which, compounded over the 11
decoder evaluations, moves the plan (split hi / lo matmuls for the mixer inputs and every decoder linear:
``SPLIT_MATMUL``); and the bf16 SDPA kernel is the largest encoder error left on the sensitive nuScenes instants
(fp32 matmul attention in the fusion and the decoder: ``ATTN_MATMUL``). With the defaults the 27 e2e scenes (7
research + 20 nuScenes instants) stay within ego max 0.31 m / mean 0.14 m / neighbour median-max 0.09 m of the fp32
reference (gates 1.0 / 0.3 / 1.5 m); the defaults of the first device round failed the mean on one instant (0.35 m).
Precision (:data:`DEFAULT_PRECISION`, ``ttaw.precision.PrecisionPolicy`` rules, first match wins; override with
``DIFFUSION_PLANNER_PRECISION="dec.*=HiFi2+fp32:a=bf16"``): every matmul / LayerNorm gets an explicit compute
config; ``w=`` is the weight dtype, ``a=`` the dtype of the module's residual stream (sub-layer outputs are produced
in it and LayerNorm keeps it). The ego / neighbour pre-projection island is fp32 and pad-relative (probe P12, a
device fp32 matmul is TF32-like); the decoder pre-projection reads the fp32 solver state with fp32 weights.
"""
from __future__ import annotations
from typing import Dict
from ..reference import config as C
__all__ = ["TOKENS", "TOKENS_REAL", "AGENTS", "AGENTS_REAL", "STATE_COLS", "STATE_COLS_T0", "TILE",
"ISLAND_ROWS", "LANE_AUX_DIM", "NEIGHBOR_AUX_DIM", "POS_AUG_DIM", "DEFAULT_PRECISION", "MIXER_T",
"MIXER_CIN", "KNOBS", "globs"]
TILE = 32
def _aligned(n: int) -> int:
return -(-n // TILE) * TILE
TOKENS_REAL = C.ENCODING_TOKEN_NUM # 564 encoder tokens (the cross-attention keys)
TOKENS = _aligned(TOKENS_REAL) # 576: fusion rows / masked keys
AGENTS_REAL = C.MAX_NUM_AGENTS # 321
AGENTS = _aligned(AGENTS_REAL) # 352: decoder rows / masked self-attention keys
STATE_COLS = C.DIT_INPUT_DIM # 324 = 81 points x 4
STATE_COLS_T0 = C.POSE_DIM # columns 0..3 hold the t = 0 point (prefix constraint)
# pre-projection island rows (the only non-zero time rows after the in-graph truncation, SPEC 3.8)
ISLAND_ROWS = {"ego": tuple(range(C.EGO_HISTORY_KEEP.start, C.EGO_HISTORY_KEEP.stop)),
"neighbor": tuple(range(C.NEIGHBOR_HISTORY_KEEP.start, C.NEIGHBOR_HISTORY_KEEP.stop))}
# mixer token axis length (T) and input channels per category (host features)
MIXER_T = {"ego": len(ISLAND_ROWS["ego"]), "neighbor": len(ISLAND_ROWS["neighbor"]), "lane": C.POINTS_PER_SEGMENT,
"route": C.POINTS_PER_SEGMENT, "polygon": C.POINTS_PER_POLYGON, "line_string": C.POINTS_PER_LINE_STRING}
MIXER_CIN = {"ego": C.POSE_DIM, "neighbor": C.NEIGHBOR_FEATURE_DIM, "lane": C.LANE_FEATURE_DIM,
"route": C.LANE_FEATURE_DIM, "polygon": C.POLYGON_FEATURE_DIM,
"line_string": C.LINE_STRING_FEATURE_DIM}
# exact affine rewrites of the small embeddings (tt.params): the host builds the input columns
NEIGHBOR_AUX_DIM = 3 + 1 # [type one-hot (3), 1] @ [W_type; b_type]
LANE_AUX_DIM = 3 + C.LANE_ATTRIBUTE_DIM + 1 # [speed*has, has, 1-has, attributes (25), 1] @ [w_s; b_s; unk; W_a; b_a]
POS_AUG_DIM = C.POS_FEATURE_DIM + 1 # [pos (14), token valid] @ [W_pos; b_pos]
DEFAULT_PRECISION: Dict[str, str] = {
"enc.island.*": "HiFi4+fp32:w=fp32:a=fp32",
"enc.mixer.*": "HiFi4+fp32:w=bf16:a=fp32",
"enc.fusion*": "HiFi4+fp32:w=bf16:a=fp32",
"enc.*": "HiFi4+fp32:w=bf16:a=fp32",
"dec.preproj.fc1": "HiFi4+fp32:w=fp32:a=fp32",
"dec.*": "HiFi4+fp32:w=bf16:a=fp32",
"turn": "HiFi4+fp32:w=fp32:a=fp32",
}
def _knobs():
from ..ttaw.knobs import Knob, Knobs
return Knobs("DIFFUSION_PLANNER", [
Knob("LN_FP32", "enc.mixer.*,dec.*", "comma-separated module globs whose LayerNorms run as an fp32 "
"decomposition (mean, variance, rsqrt as fp32 SFPU ops) instead of ttnn.layer_norm, whose error on "
"offset-dominated rows (|mean| / std 10-30) is rel-L2 0.03 (PORT_LOG 5.2); 'none' = empty"),
Knob("HIDDEN_FP32", "", "comma-separated module globs whose hidden MLP activations are fp32 (default "
"bf16); 'none' = empty"),
Knob("SPLIT_MATMUL", "enc.island.*,enc.pre.*,dec.*", "comma-separated module globs whose fp32 "
"matmuls are split into bf16 hi / fp32 lo parts (2-3 matmuls, ~1e-5 relative instead of the TF32-like "
"~1e-3; their hidden activations stay fp32): the mixer inputs and every decoder linear; 'none' = "
"empty"),
Knob("ATTN_FP32_ACC", "", "comma-separated module globs whose SDPA runs with fp32 accumulation (probe P7: "
"half the max error, ~1.4x the time); 'none' = empty"),
Knob("ATTN_MATMUL", "enc.fusion.attn,dec.*", "comma-separated module globs (enc.fusion.attn, "
"dec.self_attn, dec.cross_attn) whose attention runs as fp32 matmuls + softmax (C20 attention_matmul) "
"instead of the bf16 SDPA kernel; 'none' = empty"),
Knob("ENC_CH2D", True, "the encoder linears on batched [1, E, T, C] activations run as one 2-D matmul "
"over all rows (free [1, 1, E*T, C] view for T = 64, else a 1-D program config with fuse_batch) "
"instead of the stock per-element tiling on 4-8 cores (OPT round 1 item 1); 0 = the stock call"),
Knob("ATTN_FAST", 1, "fp32 matmul attention variant (tt/attention.py, OPT round 1 item 4a): 0 = "
"ttaw.ops.attention.attention_matmul; 1 = P.V on one output tile per core (bit-identical); 2 = 1 + "
"the scale on Q; 3 = 1 + the scale fused into the mask add (not bit-identical, rejected)",
choices=(0, 1, 2, 3)),
Knob("DEC_MMCFG", True, "explicit 2-D multicast program configs for the decoder's 352-row matmuls "
"(tt/layers.py DEC_MM_CONFIGS, the fastest bit-identical config per shape and split pass of the device "
"sweep, OPT round 1 item 2a); 0 = the auto config"),
Knob("LN_KERNEL", 2, "the fp32 LayerNorm decomposition (LN_FP32 modules) as one fused generic_op "
"program (tt/ln_kernel.py, the same SFPU LLK sequence, bit-identical: OPT round 2 item 3) instead of "
"7-9 stock programs: 1 = per-tile unpacker / SFPU inits, 2 = one init per phase; 0 = the stock "
"decomposition", choices=(0, 1, 2)),
Knob("LN_RESID", True, "with LN_KERNEL: the residual add in front of a fused fp32 LayerNorm (mixer blocks: "
"x + y, x + ch2; decoder: h + gate * a, h + m2b, ...) in the same program, which writes the new stream "
"and the normalised rows (OPT round 2 item 5); 0 = separate add / multiply programs"),
Knob("SPLIT_KCAT", 2, "decoder split matmuls (SPLIT_MATMUL dec.*, tile-aligned K) as one fp32 matmul over "
"the concatenated K axis [x_hi|x_hi|x_lo|1] @ [w_hi;w_lo;w_hi;b] (OPT round 2 item 2): 0 = three "
"passes + adds (the published numerics); 1 = stock-op operand build (typecast / subtract / concat); 2 = "
"the operand built by one generic_op (tt/kcat_kernel.py); not bit-identical to 0 (one fp32 accumulation, "
"exact bias), gates green",
choices=(0, 1, 2)),
Knob("ATTN_SMASK", True, "fp32 matmul attention (ATTN_FAST=1) with a mask: the score scale multiply and the "
"mask add as one generic_op (tt/smask_kernel.py, the same SFPU LLK calls: bit-identical; OPT round 2 "
"item 4b) instead of two binary_ng programs; 0 = the two programs"),
Knob("ATTN_SMSM", 1, "with ATTN_SMASK: the score scale, the mask add and the softmax as one generic_op "
"(tt/smsm_kernel.py: the smask SFPU sequence, then the stock softmax kernel_lib calls on the row in L1; "
"OPT round 3 item 1, bit-identical) instead of the smask program + ttnn.softmax: 1 = the scale as a "
"multiply by a scale tile (mul_binary_tile, as smask), 2 = the same fp32 SFPU multiply by an immediate "
"(mul_unary_tile, no scale tile copy); 0 = the two programs", choices=(0, 1, 2)),
Knob("ATTN_FUSED", True, "with ATTN_SMSM: each fp32 matmul attention (decoder self / cross, fusion) as one "
"generic_op (tt/fattn_kernel.py: Q K^T into DEST, the smsm phases, P V accumulated in DEST, Q / K / V "
"read in place and the heads merged on write; OPT round 3 item 4) instead of head split, Q K^T, smsm, "
"P V and head merge; 0 = those programs"),
Knob("KCAT_EMIT", True, "with SPLIT_KCAT=2: the producers of the decoder's K-concatenated split linears' "
"inputs (the split-row LayerNorms -> qkv / mlp fc1 / cross q, the fused attention -> attn out / cross "
"out) write the split operand [x_hi | x_hi | x_lo | 1] themselves (the kcat LLK calls on their output "
"tile; OPT round 3 item 5) instead of x + a kcat operand program; 0 = the operand programs"),
Knob("LN_TR", True, "with LN_KERNEL and LN_RESID: the two transposes around each mixer block's token-mixing "
"MLP done inside the fused LayerNorm programs (n1 writes its output per-entity transposed, n2 reads the "
"token-mixing output transposed; the stock transpose LLK, exact; OPT round 3 item 6) instead of two "
"ttnn.transpose programs (mixer trunks on the one-core-per-row LN kernel); 0 = the transpose programs"),
Knob("ENC_KCAT", True, "with SPLIT_KCAT=2: the encoder's split linears (SPLIT_MATMUL enc.pre.*, "
"enc.island.*; any K, blocks padded to whole tiles) as one K-concatenated fp32 matmul too (OPT round 3 "
"item 2; one fp32 accumulation, exact bias: a precision change of the SPLIT_KCAT kind); 0 = three "
"passes + adds"),
Knob("LIN_ACT", False, "the GELU of the encoder mixer linears (token / channel MLP fc1, bf16 output) in the "
"matmul epilogue (an explicit copy of the stock auto config + fused_activation, OPT round 3 item 3) "
"instead of a unary program on the rounded bf16 output; a precision change (GELU before the bf16 "
"rounding); 0 = the unary program"),
Knob("KCAT_ACT", True, "with SPLIT_KCAT=2: the GELU of a decoder linear that feeds another K-concatenated "
"split linear (preproj.fc1 -> fc2, mlp fc1 -> fc2) applied inside the next one's operand build (the "
"same SFPU LLK: bit-identical) instead of its own unary program; 0 = the unary program"),
Knob("LN_SPLIT", True, "with LN_KERNEL: a fused fp32 LayerNorm over few tile rows (the decoder's 11) spread "
"over Wt cores per row (tt/ln_kernel.py layer_norm_fp32_split: the root core folds the gathered tiles "
"in order and broadcasts the statistics, bit-identical) instead of one core per row; 0 = one core per "
"row"),
Knob("KCAT_L1", True, "with SPLIT_KCAT=2: the decoder's K = 1024 split operands (mlp fc2, final p4) written "
"L1 block-sharded by the operand build and read in place by a 2-D multicast matmul with in0 sharded "
"(tt/layers.py KCAT_L1_CONFIGS; OPT round 4 item 1, bit-identical) instead of a DRAM round trip of "
"the 4.7 MB operand; 0 = DRAM interleaved"),
Knob("KCAT_ACT_ONCE", False, "with KCAT_ACT: the operand build applies the deferred GELU to one DEST copy of "
"each tile and copies it to the second (copy_dest_values; OPT round 4 item 2, bit-identical) instead "
"of running the GELU on both copies (measured, not kept: +0.06 ms); 0 = twice"),
Knob("ATTN_L1", 2, "with ATTN_FUSED: the fused attention's inputs in L1 (interleaved) instead of DRAM: the "
"decoder qkv / cross q projections write L1, the hoisted cross K / V heads and the masks are L1-resident "
"(OPT round 4 item 3, bit-identical; the 11 query-row cores of a head re-read the same K / V tiles); "
"1 = also the fusion q / kv projections (the stock linear then runs on 6 cores with a separate bias "
"add: +0.3 ms, item 8); 2 = not those; 0 = DRAM", choices=(0, 1, 2)),
Knob("DEC_L1", 2, "the decoder blocks' intermediates (the split-row LN outputs and stream, the fused "
"attention outputs, the out / mlp / cross-out linear outputs) in L1 (interleaved) instead of DRAM (OPT "
"round 4 item 4, bit-identical); 2 = also the pre-projection and the final layer (item 6); 0 = DRAM",
choices=(0, 1, 2)),
Knob("ENC_L1", False, "the encoder mixer blocks' intermediates (the fused LN outputs and stream, the token / "
"channel MLP outputs) in L1 (interleaved) instead of DRAM (OPT round 4 item 5, bit-identical; measured, "
"not kept: +1.67 ms); 0 = DRAM"),
Knob("FUS_L1", False, "the encoder fusion blocks' intermediates (LN outputs, stream adds, attention / out / "
"MLP outputs) in L1 (interleaved) instead of DRAM (OPT round 4 item 7; measured, not kept: +0.39 ms and "
"not bit-identical, the stock ops pick other programs for L1 outputs); 0 = DRAM"),
Knob("COMPACT", 2, "exact compaction (OPT round 5): besides the full-capacity plan, one trace per agent "
"bucket (AGENT_BUCKETS), picked per request from the host masks. 1 = the decoder runs on the first R "
"rows only (the needed rows: ego, every valid self-attention key, every emitted neighbour, every valid "
"neighbour token; the rows past R are masked keys and never read; item 1); 2 = also the encoder's "
"neighbour trunk and head on the first R neighbours, the other neighbour tokens zero (invalid: "
"token_valid zeroes them anyway; item 2). The matmul configs keep their K blocking, so the needed rows "
"are the same values; 0 = the full 352 rows / 320 neighbours always", choices=(0, 1, 2)),
Knob("AGENT_BUCKETS", "32,64,96,128,192", "with COMPACT: the decoder row buckets (multiples of 32 below "
"352), one captured trace each"),
Knob("HOST_FAST", True, "the host pre- and post-processing vectorised (OPT round 5 item 3: the normalisation "
"computes only the rows that are not all-small, the neighbour features only the 6 kept history rows, "
"the trajectory's velocity smoothing / force stop / acceleration as array operations in the same "
"float64 / float32 order, the denormalisation and predicted paths on the emitted rows only); bit-exact "
"(the host suite compares the arrays); 0 = the first port's *_ref functions"),
Knob("INPUT_TRIM", True, "per-request uploads trimmed (OPT round 5 item 4, H5): the solver's current states "
"``cs`` uploaded as one tile column [352, 32] (the 4 t = 0 columns + zeros) and widened to 324 columns in "
"the trace by a tile-aligned concat with a zero block (the same values), and an input whose array is all "
"+0.0 (``y0`` at temperature 0, ``static_x``) not uploaded again while its device buffer already holds "
"zeros from this model's last upload; 0 = every input every plan, ``cs`` at full width"),
Knob("LN_SFPU_BCAST", True, "with LN_KERNEL: the row statistics written to every column by the SFPU row "
"reduce itself (kernels/ln32_sfpu.h, the stock reduce's arithmetic with extra stores: bit-identical) "
"instead of a RISC-V column fill of the packed column-0 tile; 0 = the fill"),
])
KNOBS = _knobs()
def globs(text: str):
"""``"a.*, b"`` -> ``("a.*", "b")``; ``""`` / ``"none"`` -> ``()``."""
t = (text or "").strip()
if t.lower() in ("", "none"):
return ()
return tuple(g.strip() for g in t.split(",") if g.strip())
|