kernel-fun 0.2.0.dev1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- kernel_fun/__init__.py +67 -0
- kernel_fun/_common/__init__.py +11 -0
- kernel_fun/_common/cache.py +99 -0
- kernel_fun/_common/compat.py +168 -0
- kernel_fun/_common/support.py +267 -0
- kernel_fun/cconv/__init__.py +24 -0
- kernel_fun/cconv/_kernels/__init__.py +6 -0
- kernel_fun/cconv/_kernels/strip.py +450 -0
- kernel_fun/cconv/_provenance.py +11 -0
- kernel_fun/cconv/ops.py +256 -0
- kernel_fun/kda/__init__.py +23 -0
- kernel_fun/kda/_kernels/__init__.py +7 -0
- kernel_fun/kda/_kernels/bwd_dhu.py +994 -0
- kernel_fun/kda/_kernels/bwd_intra.py +1089 -0
- kernel_fun/kda/_kernels/bwd_intra_triton.py +328 -0
- kernel_fun/kda/_kernels/bwd_scan.py +1105 -0
- kernel_fun/kda/_kernels/bwd_wy.py +320 -0
- kernel_fun/kda/_kernels/bwd_wy_t.py +309 -0
- kernel_fun/kda/_kernels/fwd_intra_triton.py +101 -0
- kernel_fun/kda/_kernels/fwd_state.py +1065 -0
- kernel_fun/kda/_provenance.py +18 -0
- kernel_fun/kda/autograd.py +114 -0
- kernel_fun/kda/chain.py +150 -0
- kernel_fun/kda/ops.py +342 -0
- kernel_fun-0.2.0.dev1.dist-info/METADATA +347 -0
- kernel_fun-0.2.0.dev1.dist-info/RECORD +30 -0
- kernel_fun-0.2.0.dev1.dist-info/WHEEL +4 -0
- kernel_fun-0.2.0.dev1.dist-info/licenses/LICENSE +201 -0
- kernel_fun-0.2.0.dev1.dist-info/licenses/NOTICE +60 -0
- kernel_fun-0.2.0.dev1.dist-info/licenses/THIRD_PARTY_NOTICES.md +175 -0
|
@@ -0,0 +1,994 @@
|
|
|
1
|
+
"""Phase 2 B2a — CuTe port of fla's `chunk_gated_delta_rule_bwd_dhu` (stage 4), the
|
|
2
|
+
reverse state scan. No fusion: B1's dq experiment measured the cross-CTA reduce tax at
|
|
3
|
+
~2x DRAM per fused K-shaped output (ALGORITHM.md "B1 verdict"), so this port keeps
|
|
4
|
+
fla's stage boundary and takes the scan win alone (gdn 003's dhu port was its 1.87x
|
|
5
|
+
stage; kernel_scan.py mirrored is the template).
|
|
6
|
+
|
|
7
|
+
Per (b, hv, v_tile) CTA, dh^T [BV, K] fp32 resident in SIMT registers (init dht),
|
|
8
|
+
chunks newest first; per chunk c = NT-1 .. 0:
|
|
9
|
+
|
|
10
|
+
dhck[c] = dh (bf16 -> HBM pre-update, COL_MAJOR trans staging)
|
|
11
|
+
DV = kg @ dh_c^T (MMA; kg pre-scaled by recompute — no gate here)
|
|
12
|
+
dv2 = DV + dv_in (SIMT) -> dv2 HBM + WD's A operand (bf16)
|
|
13
|
+
QD = do^T @ qg (MMA, both operands straight TMA loads)
|
|
14
|
+
WD = dv2^T @ w (MMA)
|
|
15
|
+
dh = exp2(gd) * dh + scale*QD - WD (SIMT regs, per-dim K-vector decay)
|
|
16
|
+
after chunk 0: dh0 = dh (fp32 scatter)
|
|
17
|
+
|
|
18
|
+
Two UPD accumulators instead of gdn's folded dog = do*scale operand: fla multiplies the
|
|
19
|
+
fp32 q-side dot by scale AFTER accumulation, and keeping that order reproduces fla's
|
|
20
|
+
arithmetic exactly (only MMA-vs-tl.dot reassociation remains, ~1e-6) — and it makes
|
|
21
|
+
do^T a pure mn-major TMA operand (V is contiguous in gmem, so do^T [BV, BT] is
|
|
22
|
+
M-contiguous as-is; no SIMT scaling pass at all). Costs one extra [BV,K] accumulator
|
|
23
|
+
(tmem 320/512 cols) and one extra t2r per chunk, overlapped: QD commits while SIMT is
|
|
24
|
+
still assembling dv2.
|
|
25
|
+
|
|
26
|
+
Logical gmem mode order (M/N, K_contract, rest) per operand:
|
|
27
|
+
kg: (T, K, HV, B) A of DV, k-major
|
|
28
|
+
do^T: (V, T, HV, B) A of QD, mn-major (same storage as do)
|
|
29
|
+
qg^T: (K, T, HV, B) B of QD, mn-major (same storage as qg)
|
|
30
|
+
w^T: (K, T, HV, B) B of WD, mn-major (same storage as w)
|
|
31
|
+
dv_in: (T, V, HV, B) SIMT-only, linear smem
|
|
32
|
+
gd: (K, NT, HV, B) fp32 per-chunk decay vectors (g2 last rows)
|
|
33
|
+
dht: (K, V, HV, B) fp32 scalar gather, once per CTA
|
|
34
|
+
dv2: (T, V, HV, B) TMA store
|
|
35
|
+
dhck: (V, K, NT, HV, B) TMA store (bf16), COL_MAJOR staging (TMA can't transpose)
|
|
36
|
+
dh0: (K, V, HV, B) fp32 scalar scatter, once per CTA
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
from __future__ import annotations
|
|
40
|
+
|
|
41
|
+
from typing import Type
|
|
42
|
+
|
|
43
|
+
import torch
|
|
44
|
+
|
|
45
|
+
import cuda.bindings.driver as cuda
|
|
46
|
+
import cutlass
|
|
47
|
+
import cutlass.cute as cute
|
|
48
|
+
import cutlass.pipeline as pipeline
|
|
49
|
+
import cutlass.utils as utils
|
|
50
|
+
import cutlass.utils.blackwell_helpers as sm100_utils
|
|
51
|
+
from cutlass.cute.nvgpu import cpasync, tcgen05
|
|
52
|
+
from cutlass.pipeline import pipeline_init_arrive, pipeline_init_wait
|
|
53
|
+
|
|
54
|
+
# The call cache lives in _common: one copy of the marshal/poke/release scheme for
|
|
55
|
+
# every kernel in this package. In the research ladder each kernel carried its own,
|
|
56
|
+
# which is how the keepalive leak was fixed in one of them and missed in three.
|
|
57
|
+
from ..._common.cache import ( # noqa: F401
|
|
58
|
+
alloc_outs as _alloc_outs,
|
|
59
|
+
cute_view as _cute_view,
|
|
60
|
+
out_specs as _out_specs,
|
|
61
|
+
release_keepalives as _release_keepalives,
|
|
62
|
+
retarget as _retarget,
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class KdaB2aDhuKernel:
|
|
67
|
+
"""Reverse dh scan. One CTA per (b, hv, v_tile); serial over chunks, newest first.
|
|
68
|
+
|
|
69
|
+
Warps: 0 = TMA producer, 1 = MMA, 4..7 = SIMT recurrence (dv2, dh update, dh0,
|
|
70
|
+
checkpoint/dv2 stores). Warps 2,3 idle through the role branch and only join
|
|
71
|
+
alloc/dealloc barriers.
|
|
72
|
+
"""
|
|
73
|
+
|
|
74
|
+
def __init__(self, io_dtype: Type[cutlass.Numeric], K: int, V_TILE: int):
|
|
75
|
+
self.io_dtype = io_dtype
|
|
76
|
+
self.acc_dtype = cutlass.Float32
|
|
77
|
+
self.BT = 64
|
|
78
|
+
self.K = K
|
|
79
|
+
self.BV = V_TILE
|
|
80
|
+
|
|
81
|
+
assert K in (64, 128), "K must be 64 or 128"
|
|
82
|
+
assert self.BV == 64, "V tile is 64"
|
|
83
|
+
|
|
84
|
+
# MMA tile shapes (M, N, K_contract)
|
|
85
|
+
self.tile_dv = (self.BT, self.BV, self.K) # kg @ dh^T
|
|
86
|
+
self.tile_upd = (self.BV, self.K, self.BT) # do^T @ qg / dv2^T @ w
|
|
87
|
+
|
|
88
|
+
self.cta_group = tcgen05.CtaGroup.ONE
|
|
89
|
+
|
|
90
|
+
self.tma_warp_id = 0
|
|
91
|
+
self.mma_warp_id = 1
|
|
92
|
+
self.simt_warp_id = (4, 5, 6, 7)
|
|
93
|
+
self.threads_per_cta = 32 * 8
|
|
94
|
+
|
|
95
|
+
self.input_stages = 2
|
|
96
|
+
self.dh_stages = 2
|
|
97
|
+
|
|
98
|
+
self.simt_sync_barrier = pipeline.NamedBarrier(
|
|
99
|
+
barrier_id=1, num_threads=32 * len(self.simt_warp_id)
|
|
100
|
+
)
|
|
101
|
+
self.tmem_dealloc_sync_barrier = pipeline.NamedBarrier(
|
|
102
|
+
barrier_id=2, num_threads=self.threads_per_cta
|
|
103
|
+
)
|
|
104
|
+
self.smem_capacity = utils.get_smem_capacity_in_bytes("sm_100")
|
|
105
|
+
|
|
106
|
+
# ---------------------------------------------------------------------------------
|
|
107
|
+
|
|
108
|
+
def _make_tiled_mmas(self):
|
|
109
|
+
io, acc, grp = self.io_dtype, self.acc_dtype, self.cta_group
|
|
110
|
+
mma_dv = sm100_utils.make_trivial_tiled_mma(
|
|
111
|
+
io, tcgen05.OperandMajorMode("k"), tcgen05.OperandMajorMode("k"),
|
|
112
|
+
acc, grp, self.tile_dv[:2], tcgen05.OperandSource.SMEM,
|
|
113
|
+
)
|
|
114
|
+
# QD: A = do^T [BV, BT] mn-major (V contiguous in gmem == M contiguous here)
|
|
115
|
+
mma_qd = sm100_utils.make_trivial_tiled_mma(
|
|
116
|
+
io, tcgen05.OperandMajorMode("mn"), tcgen05.OperandMajorMode("mn"),
|
|
117
|
+
acc, grp, self.tile_upd[:2], tcgen05.OperandSource.SMEM,
|
|
118
|
+
)
|
|
119
|
+
# WD: A = dv2^T [BV, BT] k-major (SIMT trans-stmatrix built)
|
|
120
|
+
mma_wd = sm100_utils.make_trivial_tiled_mma(
|
|
121
|
+
io, tcgen05.OperandMajorMode("k"), tcgen05.OperandMajorMode("mn"),
|
|
122
|
+
acc, grp, self.tile_upd[:2], tcgen05.OperandSource.SMEM,
|
|
123
|
+
)
|
|
124
|
+
return mma_dv, mma_qd, mma_wd
|
|
125
|
+
|
|
126
|
+
def _setup_attributes(self):
|
|
127
|
+
mma_dv, mma_qd, mma_wd = self._make_tiled_mmas()
|
|
128
|
+
BT, K, BV = self.BT, self.K, self.BV
|
|
129
|
+
|
|
130
|
+
self.kg_smem_layout = sm100_utils.make_smem_layout_a(
|
|
131
|
+
mma_dv, self.tile_dv, self.io_dtype, self.input_stages
|
|
132
|
+
)
|
|
133
|
+
self.dot_smem_layout = sm100_utils.make_smem_layout_a(
|
|
134
|
+
mma_qd, self.tile_upd, self.io_dtype, self.input_stages
|
|
135
|
+
)
|
|
136
|
+
self.qgt_smem_layout = sm100_utils.make_smem_layout_b(
|
|
137
|
+
mma_qd, self.tile_upd, self.io_dtype, self.input_stages
|
|
138
|
+
)
|
|
139
|
+
self.wt_smem_layout = sm100_utils.make_smem_layout_b(
|
|
140
|
+
mma_wd, self.tile_upd, self.io_dtype, self.input_stages
|
|
141
|
+
)
|
|
142
|
+
# dv_in is SIMT-only: plain row-major (BT, BV), BV contiguous
|
|
143
|
+
self.dvi_smem_layout = cute.make_layout(
|
|
144
|
+
(BT, BV, self.input_stages), stride=(BV, 1, BT * BV)
|
|
145
|
+
)
|
|
146
|
+
self.gd_smem_layout = cute.make_layout((K, self.input_stages))
|
|
147
|
+
|
|
148
|
+
# dh^T as B operand of DV ([BV, K] k-major, staged); written by SIMT through
|
|
149
|
+
# the ROW_MAJOR epi view of the same bytes.
|
|
150
|
+
self.dh_smem_layout = sm100_utils.make_smem_layout_b(
|
|
151
|
+
mma_dv, self.tile_dv, self.io_dtype, self.dh_stages
|
|
152
|
+
)
|
|
153
|
+
self.dh_epi_layout = sm100_utils.make_smem_layout_epi(
|
|
154
|
+
self.io_dtype, utils.LayoutEnum.ROW_MAJOR, (BV, K), self.dh_stages
|
|
155
|
+
)
|
|
156
|
+
# checkpoint staging: COL_MAJOR (BV, K) in gmem order (TMA cannot transpose)
|
|
157
|
+
self.cks_smem_layout = sm100_utils.make_smem_layout_epi(
|
|
158
|
+
self.io_dtype, utils.LayoutEnum.COL_MAJOR, (BV, K), 1
|
|
159
|
+
)
|
|
160
|
+
# dv2 as A operand of WD ([BV, BT] k-major); written via transpose stmatrix
|
|
161
|
+
# through the COL_MAJOR (BT, BV) epi view of the same bytes.
|
|
162
|
+
self.dv2n_smem_layout = sm100_utils.make_smem_layout_a(
|
|
163
|
+
mma_wd, self.tile_upd, self.io_dtype, 1
|
|
164
|
+
)
|
|
165
|
+
self.ops_epi_layout = sm100_utils.make_smem_layout_epi(
|
|
166
|
+
self.io_dtype, utils.LayoutEnum.COL_MAJOR, (BT, BV), 1
|
|
167
|
+
)
|
|
168
|
+
# dv2 store staging: (BT, BV) BV-contiguous to match gmem
|
|
169
|
+
self.dv2s_smem_layout = cute.make_layout((BT, BV, 1), stride=(BV, 1, BT * BV))
|
|
170
|
+
|
|
171
|
+
self.num_mma_load_bytes = sum(
|
|
172
|
+
cute.size_in_bytes(
|
|
173
|
+
self.io_dtype, cute.slice_(lay, (None, None, None, 0))
|
|
174
|
+
)
|
|
175
|
+
for lay in (
|
|
176
|
+
self.kg_smem_layout, self.dot_smem_layout,
|
|
177
|
+
self.qgt_smem_layout, self.wt_smem_layout,
|
|
178
|
+
)
|
|
179
|
+
)
|
|
180
|
+
self.num_simt_load_bytes = (
|
|
181
|
+
cute.size_in_bytes(
|
|
182
|
+
self.io_dtype, cute.slice_(self.dvi_smem_layout, (None, None, 0))
|
|
183
|
+
)
|
|
184
|
+
+ cute.size_in_bytes(
|
|
185
|
+
cutlass.Float32, cute.slice_(self.gd_smem_layout, (None, 0))
|
|
186
|
+
)
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
(
|
|
190
|
+
self.tmem_dv_offset,
|
|
191
|
+
self.tmem_qd_offset,
|
|
192
|
+
self.tmem_wd_offset,
|
|
193
|
+
self.num_tmem_cols,
|
|
194
|
+
) = self._plan_tmem(mma_dv, mma_qd, mma_wd)
|
|
195
|
+
|
|
196
|
+
def _plan_tmem(self, mma_dv, mma_qd, mma_wd):
|
|
197
|
+
def acc_cols(mma, tile):
|
|
198
|
+
shape = mma.partition_shape_C(tile[:2])
|
|
199
|
+
fake = mma.make_fragment_C(cute.append(shape, 1))
|
|
200
|
+
return tcgen05.find_tmem_tensor_col_offset(fake)
|
|
201
|
+
|
|
202
|
+
dv = acc_cols(mma_dv, self.tile_dv)
|
|
203
|
+
qd = acc_cols(mma_qd, self.tile_upd)
|
|
204
|
+
wd = acc_cols(mma_wd, self.tile_upd)
|
|
205
|
+
off_dv = 0
|
|
206
|
+
off_qd = off_dv + dv
|
|
207
|
+
off_wd = off_qd + qd
|
|
208
|
+
total_ = off_wd + wd
|
|
209
|
+
total = 1
|
|
210
|
+
while total < total_:
|
|
211
|
+
total *= 2
|
|
212
|
+
assert total <= 512, f"tmem overflow: {total_} cols"
|
|
213
|
+
return off_dv, off_qd, off_wd, total
|
|
214
|
+
|
|
215
|
+
# ---------------------------------------------------------------------------------
|
|
216
|
+
|
|
217
|
+
@cute.jit
|
|
218
|
+
def __call__(
|
|
219
|
+
self,
|
|
220
|
+
kg: cute.Tensor, # (T, K, HV, B)
|
|
221
|
+
dot: cute.Tensor, # (V, T, HV, B) — same storage as do
|
|
222
|
+
qgt: cute.Tensor, # (K, T, HV, B) — same storage as qg
|
|
223
|
+
wt: cute.Tensor, # (K, T, HV, B) — same storage as w
|
|
224
|
+
dvi: cute.Tensor, # (T, V, HV, B)
|
|
225
|
+
gd: cute.Tensor, # (K, NT, HV, B) fp32
|
|
226
|
+
dht: cute.Tensor, # (K, V, HV, B) fp32
|
|
227
|
+
dv2: cute.Tensor, # (T, V, HV, B) out
|
|
228
|
+
dhck: cute.Tensor, # (V, K, NT, HV, B) out, io dtype
|
|
229
|
+
dh0: cute.Tensor, # (K, V, HV, B) fp32 out
|
|
230
|
+
scale: cutlass.Float32,
|
|
231
|
+
stream: cuda.CUstream,
|
|
232
|
+
):
|
|
233
|
+
self._setup_attributes()
|
|
234
|
+
mma_dv, mma_qd, mma_wd = self._make_tiled_mmas()
|
|
235
|
+
BT, K, BV = self.BT, self.K, self.BV
|
|
236
|
+
cluster_vmnk = (1, 1, 1, 1)
|
|
237
|
+
|
|
238
|
+
tma_kg, tma_tensor_kg = cute.nvgpu.make_tiled_tma_atom_A(
|
|
239
|
+
cpasync.CopyBulkTensorTileG2SOp(), kg,
|
|
240
|
+
cute.slice_(self.kg_smem_layout, (None, None, None, 0)),
|
|
241
|
+
self.tile_dv, mma_dv, cluster_vmnk,
|
|
242
|
+
)
|
|
243
|
+
tma_dot, tma_tensor_dot = cute.nvgpu.make_tiled_tma_atom_A(
|
|
244
|
+
cpasync.CopyBulkTensorTileG2SOp(), dot,
|
|
245
|
+
cute.slice_(self.dot_smem_layout, (None, None, None, 0)),
|
|
246
|
+
self.tile_upd, mma_qd, cluster_vmnk,
|
|
247
|
+
)
|
|
248
|
+
tma_qgt, tma_tensor_qgt = cute.nvgpu.make_tiled_tma_atom_B(
|
|
249
|
+
cpasync.CopyBulkTensorTileG2SOp(), qgt,
|
|
250
|
+
cute.slice_(self.qgt_smem_layout, (None, None, None, 0)),
|
|
251
|
+
self.tile_upd, mma_qd, cluster_vmnk,
|
|
252
|
+
)
|
|
253
|
+
tma_wt, tma_tensor_wt = cute.nvgpu.make_tiled_tma_atom_B(
|
|
254
|
+
cpasync.CopyBulkTensorTileG2SOp(), wt,
|
|
255
|
+
cute.slice_(self.wt_smem_layout, (None, None, None, 0)),
|
|
256
|
+
self.tile_upd, mma_wd, cluster_vmnk,
|
|
257
|
+
)
|
|
258
|
+
tma_dvi, tma_tensor_dvi = cpasync.make_tiled_tma_atom(
|
|
259
|
+
cpasync.CopyBulkTensorTileG2SOp(), dvi,
|
|
260
|
+
cute.slice_(self.dvi_smem_layout, (None, None, 0)),
|
|
261
|
+
(BT, BV),
|
|
262
|
+
)
|
|
263
|
+
gd_cta_v_layout = cute.slice_(
|
|
264
|
+
cute.make_identity_layout(gd.shape), (None, 0, 0, 0)
|
|
265
|
+
)
|
|
266
|
+
tma_gd, tma_tensor_gd = cpasync.make_tiled_tma_atom(
|
|
267
|
+
cpasync.CopyBulkTensorTileG2SOp(), gd,
|
|
268
|
+
cute.slice_(self.gd_smem_layout, (None, 0)),
|
|
269
|
+
gd_cta_v_layout,
|
|
270
|
+
)
|
|
271
|
+
tma_dv2, tma_tensor_dv2 = cpasync.make_tiled_tma_atom(
|
|
272
|
+
cpasync.CopyBulkTensorTileS2GOp(), dv2,
|
|
273
|
+
cute.slice_(self.dv2s_smem_layout, (None, None, 0)),
|
|
274
|
+
(BT, BV),
|
|
275
|
+
)
|
|
276
|
+
tma_ck, tma_tensor_ck = cpasync.make_tiled_tma_atom(
|
|
277
|
+
cpasync.CopyBulkTensorTileS2GOp(), dhck,
|
|
278
|
+
cute.slice_(self.cks_smem_layout, (None, None, 0)),
|
|
279
|
+
(BV, K),
|
|
280
|
+
)
|
|
281
|
+
|
|
282
|
+
B = cute.size(kg, mode=[3])
|
|
283
|
+
HV = cute.size(kg, mode=[2])
|
|
284
|
+
NV = cute.size(dvi, mode=[1]) // BV
|
|
285
|
+
grid = (B * HV * NV, 1, 1)
|
|
286
|
+
|
|
287
|
+
swz_align, lin_align = 1024, 128
|
|
288
|
+
|
|
289
|
+
# Every `*_full` range backs both halves of a pipeline's mbarrier array —
|
|
290
|
+
# 2 * num_stages Int64s (under-sizing aliases the next pipeline).
|
|
291
|
+
@cute.struct
|
|
292
|
+
class SharedStorage:
|
|
293
|
+
mmain_full: cute.struct.MemRange[cutlass.Int64, self.input_stages * 2] # type: ignore
|
|
294
|
+
simtin_full: cute.struct.MemRange[cutlass.Int64, self.input_stages * 2] # type: ignore
|
|
295
|
+
dh_full: cute.struct.MemRange[cutlass.Int64, self.dh_stages * 2] # type: ignore
|
|
296
|
+
dv2n_full: cute.struct.MemRange[cutlass.Int64, 2] # type: ignore
|
|
297
|
+
dvf_full: cute.struct.MemRange[cutlass.Int64, 2] # type: ignore
|
|
298
|
+
qdf_full: cute.struct.MemRange[cutlass.Int64, 2] # type: ignore
|
|
299
|
+
wdf_full: cute.struct.MemRange[cutlass.Int64, 2] # type: ignore
|
|
300
|
+
tmem_holding_buf: cutlass.Int32
|
|
301
|
+
smem_kg: cute.struct.Align[
|
|
302
|
+
cute.struct.MemRange[self.io_dtype, cute.cosize(self.kg_smem_layout)], swz_align # type: ignore
|
|
303
|
+
]
|
|
304
|
+
smem_dot: cute.struct.Align[
|
|
305
|
+
cute.struct.MemRange[self.io_dtype, cute.cosize(self.dot_smem_layout)], swz_align # type: ignore
|
|
306
|
+
]
|
|
307
|
+
smem_qgt: cute.struct.Align[
|
|
308
|
+
cute.struct.MemRange[self.io_dtype, cute.cosize(self.qgt_smem_layout)], swz_align # type: ignore
|
|
309
|
+
]
|
|
310
|
+
smem_wt: cute.struct.Align[
|
|
311
|
+
cute.struct.MemRange[self.io_dtype, cute.cosize(self.wt_smem_layout)], swz_align # type: ignore
|
|
312
|
+
]
|
|
313
|
+
smem_dvi: cute.struct.Align[
|
|
314
|
+
cute.struct.MemRange[self.io_dtype, cute.cosize(self.dvi_smem_layout)], lin_align # type: ignore
|
|
315
|
+
]
|
|
316
|
+
smem_gd: cute.struct.Align[
|
|
317
|
+
cute.struct.MemRange[cutlass.Float32, cute.cosize(self.gd_smem_layout)], lin_align # type: ignore
|
|
318
|
+
]
|
|
319
|
+
smem_dh: cute.struct.Align[
|
|
320
|
+
cute.struct.MemRange[self.io_dtype, cute.cosize(self.dh_smem_layout)], swz_align # type: ignore
|
|
321
|
+
]
|
|
322
|
+
smem_cks: cute.struct.Align[
|
|
323
|
+
cute.struct.MemRange[self.io_dtype, cute.cosize(self.cks_smem_layout)], swz_align # type: ignore
|
|
324
|
+
]
|
|
325
|
+
smem_dv2n: cute.struct.Align[
|
|
326
|
+
cute.struct.MemRange[self.io_dtype, cute.cosize(self.dv2n_smem_layout)], swz_align # type: ignore
|
|
327
|
+
]
|
|
328
|
+
smem_dv2s: cute.struct.Align[
|
|
329
|
+
cute.struct.MemRange[self.io_dtype, cute.cosize(self.dv2s_smem_layout)], lin_align # type: ignore
|
|
330
|
+
]
|
|
331
|
+
|
|
332
|
+
self.shared_storage = SharedStorage
|
|
333
|
+
if cutlass.const_expr(self.shared_storage.size_in_bytes() > self.smem_capacity):
|
|
334
|
+
raise ValueError(
|
|
335
|
+
f"smem {self.shared_storage.size_in_bytes()} > {self.smem_capacity}"
|
|
336
|
+
)
|
|
337
|
+
|
|
338
|
+
self.kda_cute_dhu(
|
|
339
|
+
tma_kg, tma_tensor_kg,
|
|
340
|
+
tma_dot, tma_tensor_dot,
|
|
341
|
+
tma_qgt, tma_tensor_qgt,
|
|
342
|
+
tma_wt, tma_tensor_wt,
|
|
343
|
+
tma_dvi, tma_tensor_dvi,
|
|
344
|
+
tma_gd, tma_tensor_gd,
|
|
345
|
+
tma_dv2, tma_tensor_dv2,
|
|
346
|
+
tma_ck, tma_tensor_ck,
|
|
347
|
+
dht,
|
|
348
|
+
dh0,
|
|
349
|
+
scale,
|
|
350
|
+
).launch(grid=grid, block=[self.threads_per_cta, 1, 1], stream=stream)
|
|
351
|
+
|
|
352
|
+
# ---------------------------------------------------------------------------------
|
|
353
|
+
|
|
354
|
+
@cute.kernel
|
|
355
|
+
def kda_cute_dhu(
|
|
356
|
+
self,
|
|
357
|
+
tma_kg: cute.CopyAtom, mKG: cute.Tensor,
|
|
358
|
+
tma_dot: cute.CopyAtom, mDOT: cute.Tensor,
|
|
359
|
+
tma_qgt: cute.CopyAtom, mQGT: cute.Tensor,
|
|
360
|
+
tma_wt: cute.CopyAtom, mWT: cute.Tensor,
|
|
361
|
+
tma_dvi: cute.CopyAtom, mDVI: cute.Tensor,
|
|
362
|
+
tma_gd: cute.CopyAtom, mGd: cute.Tensor,
|
|
363
|
+
tma_dv2: cute.CopyAtom, mDV2: cute.Tensor,
|
|
364
|
+
tma_ck: cute.CopyAtom, mCK: cute.Tensor,
|
|
365
|
+
mDHT: cute.Tensor,
|
|
366
|
+
mDH0: cute.Tensor,
|
|
367
|
+
scale: cutlass.Float32,
|
|
368
|
+
):
|
|
369
|
+
BT, K, BV = self.BT, self.K, self.BV
|
|
370
|
+
io = self.io_dtype
|
|
371
|
+
f32 = self.acc_dtype
|
|
372
|
+
# Layouts/TiledMma from the host trace cannot cross the region boundary — rebuild.
|
|
373
|
+
self._setup_attributes()
|
|
374
|
+
mma_dv, mma_qd, mma_wd = self._make_tiled_mmas()
|
|
375
|
+
warp_idx = cute.arch.make_warp_uniform(cute.arch.warp_idx())
|
|
376
|
+
tidx, _, _ = cute.arch.thread_idx()
|
|
377
|
+
local_tidx = tidx % 128
|
|
378
|
+
|
|
379
|
+
if warp_idx == self.tma_warp_id:
|
|
380
|
+
for atom in [tma_kg, tma_dot, tma_qgt, tma_wt, tma_dvi, tma_gd, tma_dv2, tma_ck]:
|
|
381
|
+
cpasync.prefetch_descriptor(atom)
|
|
382
|
+
|
|
383
|
+
bidx, _, _ = cute.arch.block_idx()
|
|
384
|
+
HV = cute.size(mKG, mode=[2])
|
|
385
|
+
NV = cute.size(mDVI, mode=[1]) // BV
|
|
386
|
+
T = cute.size(mKG, mode=[0])
|
|
387
|
+
NT = T // BT
|
|
388
|
+
v_idx = bidx % NV
|
|
389
|
+
hv_idx = (bidx // NV) % HV
|
|
390
|
+
b_idx = bidx // (NV * HV)
|
|
391
|
+
|
|
392
|
+
smem = utils.SmemAllocator()
|
|
393
|
+
storage = smem.allocate(self.shared_storage)
|
|
394
|
+
|
|
395
|
+
sKG = storage.smem_kg.get_tensor(self.kg_smem_layout.outer, swizzle=self.kg_smem_layout.inner)
|
|
396
|
+
sDOT = storage.smem_dot.get_tensor(self.dot_smem_layout.outer, swizzle=self.dot_smem_layout.inner)
|
|
397
|
+
sQGT = storage.smem_qgt.get_tensor(self.qgt_smem_layout.outer, swizzle=self.qgt_smem_layout.inner)
|
|
398
|
+
sWT = storage.smem_wt.get_tensor(self.wt_smem_layout.outer, swizzle=self.wt_smem_layout.inner)
|
|
399
|
+
sDVI = storage.smem_dvi.get_tensor(self.dvi_smem_layout)
|
|
400
|
+
sGd = storage.smem_gd.get_tensor(self.gd_smem_layout)
|
|
401
|
+
sDH = storage.smem_dh.get_tensor(self.dh_smem_layout.outer, swizzle=self.dh_smem_layout.inner)
|
|
402
|
+
sDH_epi = storage.smem_dh.get_tensor(self.dh_epi_layout.outer, swizzle=self.dh_epi_layout.inner)
|
|
403
|
+
sCKS = storage.smem_cks.get_tensor(self.cks_smem_layout.outer, swizzle=self.cks_smem_layout.inner)
|
|
404
|
+
sDV2N = storage.smem_dv2n.get_tensor(self.dv2n_smem_layout.outer, swizzle=self.dv2n_smem_layout.inner)
|
|
405
|
+
sDV2N_epi = storage.smem_dv2n.get_tensor(self.ops_epi_layout.outer, swizzle=self.ops_epi_layout.inner)
|
|
406
|
+
sDV2S = storage.smem_dv2s.get_tensor(self.dv2s_smem_layout)
|
|
407
|
+
|
|
408
|
+
# ---- pipelines ----
|
|
409
|
+
simt_threads = 32 * len(self.simt_warp_id)
|
|
410
|
+
mmain_pipe = pipeline.PipelineTmaUmma.create(
|
|
411
|
+
num_stages=self.input_stages,
|
|
412
|
+
producer_group=pipeline.CooperativeGroup(pipeline.Agent.Thread),
|
|
413
|
+
consumer_group=pipeline.CooperativeGroup(pipeline.Agent.Thread),
|
|
414
|
+
tx_count=self.num_mma_load_bytes,
|
|
415
|
+
barrier_storage=storage.mmain_full.data_ptr(),
|
|
416
|
+
defer_sync=True,
|
|
417
|
+
)
|
|
418
|
+
simtin_pipe = pipeline.PipelineTmaMultiConsumersAsync.create(
|
|
419
|
+
num_stages=self.input_stages,
|
|
420
|
+
producer_group=pipeline.CooperativeGroup(pipeline.Agent.Thread),
|
|
421
|
+
consumer_group_umma=pipeline.CooperativeGroup(pipeline.Agent.Thread),
|
|
422
|
+
consumer_group_async=pipeline.CooperativeGroup(
|
|
423
|
+
pipeline.Agent.Thread, simt_threads
|
|
424
|
+
),
|
|
425
|
+
tx_count=self.num_simt_load_bytes,
|
|
426
|
+
barrier_storage=storage.simtin_full.data_ptr(),
|
|
427
|
+
defer_sync=True,
|
|
428
|
+
)
|
|
429
|
+
|
|
430
|
+
def make_simt_to_mma_pipe(ptr, stages):
|
|
431
|
+
return pipeline.PipelineAsyncUmma.create(
|
|
432
|
+
num_stages=stages,
|
|
433
|
+
producer_group=pipeline.CooperativeGroup(
|
|
434
|
+
pipeline.Agent.Thread, simt_threads
|
|
435
|
+
),
|
|
436
|
+
consumer_group=pipeline.CooperativeGroup(pipeline.Agent.Thread),
|
|
437
|
+
barrier_storage=ptr,
|
|
438
|
+
defer_sync=True,
|
|
439
|
+
)
|
|
440
|
+
|
|
441
|
+
def make_mma_to_simt_pipe(ptr):
|
|
442
|
+
return pipeline.PipelineUmmaAsync.create(
|
|
443
|
+
num_stages=1,
|
|
444
|
+
producer_group=pipeline.CooperativeGroup(pipeline.Agent.Thread),
|
|
445
|
+
consumer_group=pipeline.CooperativeGroup(
|
|
446
|
+
pipeline.Agent.Thread, simt_threads
|
|
447
|
+
),
|
|
448
|
+
barrier_storage=ptr,
|
|
449
|
+
defer_sync=True,
|
|
450
|
+
)
|
|
451
|
+
|
|
452
|
+
dh_pipe = make_simt_to_mma_pipe(storage.dh_full.data_ptr(), self.dh_stages)
|
|
453
|
+
dv2n_pipe = make_simt_to_mma_pipe(storage.dv2n_full.data_ptr(), 1)
|
|
454
|
+
dvf_pipe = make_mma_to_simt_pipe(storage.dvf_full.data_ptr())
|
|
455
|
+
qdf_pipe = make_mma_to_simt_pipe(storage.qdf_full.data_ptr())
|
|
456
|
+
wdf_pipe = make_mma_to_simt_pipe(storage.wdf_full.data_ptr())
|
|
457
|
+
|
|
458
|
+
pipeline_init_arrive(cluster_shape_mn=(1, 1, 1), is_relaxed=True)
|
|
459
|
+
pipeline_init_wait(cluster_shape_mn=(1, 1, 1))
|
|
460
|
+
|
|
461
|
+
# ---- tmem ----
|
|
462
|
+
tmem_alloc_barrier = pipeline.NamedBarrier(
|
|
463
|
+
barrier_id=0, num_threads=self.threads_per_cta
|
|
464
|
+
)
|
|
465
|
+
tmem = utils.TmemAllocator(
|
|
466
|
+
storage.tmem_holding_buf.ptr,
|
|
467
|
+
barrier_for_retrieve=tmem_alloc_barrier,
|
|
468
|
+
allocator_warp_id=self.simt_warp_id[0],
|
|
469
|
+
)
|
|
470
|
+
tmem.allocate(self.num_tmem_cols)
|
|
471
|
+
tmem.wait_for_alloc()
|
|
472
|
+
tmem_ptr_base = tmem.retrieve_ptr(self.acc_dtype)
|
|
473
|
+
|
|
474
|
+
def acc_tensor(mma, tile, offset):
|
|
475
|
+
shape = mma.partition_shape_C(tile[:2])
|
|
476
|
+
fake = mma.make_fragment_C(cute.append(shape, 1))
|
|
477
|
+
return cute.make_tensor(tmem_ptr_base + offset, fake.layout)
|
|
478
|
+
|
|
479
|
+
tDV = acc_tensor(mma_dv, self.tile_dv, self.tmem_dv_offset)
|
|
480
|
+
tQD = acc_tensor(mma_qd, self.tile_upd, self.tmem_qd_offset)
|
|
481
|
+
tWD = acc_tensor(mma_wd, self.tile_upd, self.tmem_wd_offset)
|
|
482
|
+
|
|
483
|
+
# ---- global tiles (per (b, hv, v_idx), open over chunks) ----
|
|
484
|
+
gKG = cute.local_tile(mKG, (BT, K), (None, 0, hv_idx, b_idx)) # (BT,K,NT)
|
|
485
|
+
gDOT = cute.local_tile(mDOT, (BV, BT), (v_idx, None, hv_idx, b_idx)) # (BV,BT,NT)
|
|
486
|
+
gQGT = cute.local_tile(mQGT, (K, BT), (0, None, hv_idx, b_idx)) # (K,BT,NT)
|
|
487
|
+
gWT = cute.local_tile(mWT, (K, BT), (0, None, hv_idx, b_idx))
|
|
488
|
+
gDVI = cute.local_tile(mDVI, (BT, BV), (None, v_idx, hv_idx, b_idx)) # (BT,BV,NT)
|
|
489
|
+
gGd = mGd[(None, None, hv_idx, b_idx)] # (K, NT)
|
|
490
|
+
gDV2 = cute.local_tile(mDV2, (BT, BV), (None, v_idx, hv_idx, b_idx))
|
|
491
|
+
gCK = cute.local_tile(mCK, (BV, K), (v_idx, 0, None, hv_idx, b_idx)) # (BV,K,NT)
|
|
492
|
+
|
|
493
|
+
# ==========================================================================
|
|
494
|
+
# TMA warp — chunks walked newest-first
|
|
495
|
+
# ==========================================================================
|
|
496
|
+
if warp_idx == self.tma_warp_id:
|
|
497
|
+
thr_mma_dv = mma_dv.get_slice(0)
|
|
498
|
+
thr_mma_qd = mma_qd.get_slice(0)
|
|
499
|
+
thr_mma_wd = mma_wd.get_slice(0)
|
|
500
|
+
|
|
501
|
+
tKG_mma = thr_mma_dv.partition_A(gKG)
|
|
502
|
+
tDOT_mma = thr_mma_qd.partition_A(gDOT)
|
|
503
|
+
tQGT_mma = thr_mma_qd.partition_B(gQGT)
|
|
504
|
+
tWT_mma = thr_mma_wd.partition_B(gWT)
|
|
505
|
+
|
|
506
|
+
cta1 = cute.make_layout(1)
|
|
507
|
+
tKGs, tKGg = cpasync.tma_partition(
|
|
508
|
+
tma_kg, 0, cta1, cute.group_modes(sKG, 0, 3), cute.group_modes(tKG_mma, 0, 3)
|
|
509
|
+
)
|
|
510
|
+
tDOTs, tDOTg = cpasync.tma_partition(
|
|
511
|
+
tma_dot, 0, cta1, cute.group_modes(sDOT, 0, 3), cute.group_modes(tDOT_mma, 0, 3)
|
|
512
|
+
)
|
|
513
|
+
tQGTs, tQGTg = cpasync.tma_partition(
|
|
514
|
+
tma_qgt, 0, cta1, cute.group_modes(sQGT, 0, 3), cute.group_modes(tQGT_mma, 0, 3)
|
|
515
|
+
)
|
|
516
|
+
tWTs, tWTg = cpasync.tma_partition(
|
|
517
|
+
tma_wt, 0, cta1, cute.group_modes(sWT, 0, 3), cute.group_modes(tWT_mma, 0, 3)
|
|
518
|
+
)
|
|
519
|
+
tDVIs, tDVIg = cpasync.tma_partition(
|
|
520
|
+
tma_dvi, 0, cta1, cute.group_modes(sDVI, 0, 2), cute.group_modes(gDVI, 0, 2)
|
|
521
|
+
)
|
|
522
|
+
tGds, tGdg = cpasync.tma_partition(
|
|
523
|
+
tma_gd, 0, cta1, cute.group_modes(sGd, 0, 1), cute.group_modes(gGd, 0, 1)
|
|
524
|
+
)
|
|
525
|
+
|
|
526
|
+
mmain_producer = pipeline.make_pipeline_state(
|
|
527
|
+
pipeline.PipelineUserType.Producer, self.input_stages
|
|
528
|
+
)
|
|
529
|
+
simtin_producer = pipeline.make_pipeline_state(
|
|
530
|
+
pipeline.PipelineUserType.Producer, self.input_stages
|
|
531
|
+
)
|
|
532
|
+
|
|
533
|
+
for rc in cutlass.range(NT, unroll=1):
|
|
534
|
+
c = NT - 1 - rc
|
|
535
|
+
mmain_pipe.producer_acquire(mmain_producer)
|
|
536
|
+
bar = mmain_pipe.producer_get_barrier(mmain_producer)
|
|
537
|
+
cute.copy(tma_kg, tKGg[None, c], tKGs[None, mmain_producer.index], tma_bar_ptr=bar)
|
|
538
|
+
cute.copy(tma_dot, tDOTg[None, c], tDOTs[None, mmain_producer.index], tma_bar_ptr=bar)
|
|
539
|
+
cute.copy(tma_qgt, tQGTg[None, c], tQGTs[None, mmain_producer.index], tma_bar_ptr=bar)
|
|
540
|
+
cute.copy(tma_wt, tWTg[None, c], tWTs[None, mmain_producer.index], tma_bar_ptr=bar)
|
|
541
|
+
mmain_producer.advance()
|
|
542
|
+
|
|
543
|
+
simtin_pipe.producer_acquire(simtin_producer)
|
|
544
|
+
sbar = simtin_pipe.producer_get_barrier(simtin_producer)
|
|
545
|
+
cute.copy(tma_dvi, tDVIg[None, c], tDVIs[None, simtin_producer.index], tma_bar_ptr=sbar)
|
|
546
|
+
cute.copy(tma_gd, tGdg[None, c], tGds[None, simtin_producer.index], tma_bar_ptr=sbar)
|
|
547
|
+
simtin_producer.advance()
|
|
548
|
+
|
|
549
|
+
mmain_pipe.producer_tail(mmain_producer)
|
|
550
|
+
simtin_pipe.producer_tail(simtin_producer)
|
|
551
|
+
|
|
552
|
+
# ==========================================================================
|
|
553
|
+
# MMA warp
|
|
554
|
+
# ==========================================================================
|
|
555
|
+
elif warp_idx == self.mma_warp_id:
|
|
556
|
+
tCrKG = mma_dv.make_fragment_A(sKG)
|
|
557
|
+
tCrDH = mma_dv.make_fragment_B(sDH)
|
|
558
|
+
tCrDOT = mma_qd.make_fragment_A(sDOT)
|
|
559
|
+
tCrQGT = mma_qd.make_fragment_B(sQGT)
|
|
560
|
+
tCrDV2N = mma_wd.make_fragment_A(sDV2N)
|
|
561
|
+
tCrWT = mma_wd.make_fragment_B(sWT)
|
|
562
|
+
|
|
563
|
+
mmain_consumer = pipeline.make_pipeline_state(
|
|
564
|
+
pipeline.PipelineUserType.Consumer, self.input_stages
|
|
565
|
+
)
|
|
566
|
+
simtin_mma_consumer = pipeline.make_pipeline_state(
|
|
567
|
+
pipeline.PipelineUserType.Consumer, self.input_stages
|
|
568
|
+
)
|
|
569
|
+
dh_consumer = pipeline.make_pipeline_state(
|
|
570
|
+
pipeline.PipelineUserType.Consumer, self.dh_stages
|
|
571
|
+
)
|
|
572
|
+
dv2n_consumer = pipeline.make_pipeline_state(pipeline.PipelineUserType.Consumer, 1)
|
|
573
|
+
dvf_producer = pipeline.make_pipeline_state(pipeline.PipelineUserType.Producer, 1)
|
|
574
|
+
qdf_producer = pipeline.make_pipeline_state(pipeline.PipelineUserType.Producer, 1)
|
|
575
|
+
wdf_producer = pipeline.make_pipeline_state(pipeline.PipelineUserType.Producer, 1)
|
|
576
|
+
|
|
577
|
+
for rc in cutlass.range(NT, unroll=1):
|
|
578
|
+
# DV = kg dh^T — heads the critical path (SIMT needs it for dv2)
|
|
579
|
+
mmain_pipe.consumer_wait(mmain_consumer)
|
|
580
|
+
dh_pipe.consumer_wait(dh_consumer)
|
|
581
|
+
dvf_pipe.producer_acquire(dvf_producer)
|
|
582
|
+
for kk in cutlass.range(cute.size(tCrDH, mode=[2]), unroll_full=True):
|
|
583
|
+
mma_dv.set(tcgen05.Field.ACCUMULATE, cutlass.Boolean(kk != 0))
|
|
584
|
+
cute.gemm(
|
|
585
|
+
mma_dv, tDV[None, None, None, 0],
|
|
586
|
+
tCrKG[None, None, kk, mmain_consumer.index],
|
|
587
|
+
tCrDH[None, None, kk, dh_consumer.index],
|
|
588
|
+
tDV[None, None, None, 0],
|
|
589
|
+
)
|
|
590
|
+
dvf_pipe.producer_commit(dvf_producer)
|
|
591
|
+
dvf_producer.advance()
|
|
592
|
+
dh_pipe.consumer_release(dh_consumer)
|
|
593
|
+
dh_consumer.advance()
|
|
594
|
+
|
|
595
|
+
# QD = do^T qg — pure TMA operands, lands while SIMT builds dv2
|
|
596
|
+
qdf_pipe.producer_acquire(qdf_producer)
|
|
597
|
+
for kk in cutlass.range(cute.size(tCrDOT, mode=[2]), unroll_full=True):
|
|
598
|
+
mma_qd.set(tcgen05.Field.ACCUMULATE, cutlass.Boolean(kk != 0))
|
|
599
|
+
cute.gemm(
|
|
600
|
+
mma_qd, tQD[None, None, None, 0],
|
|
601
|
+
tCrDOT[None, None, kk, mmain_consumer.index],
|
|
602
|
+
tCrQGT[None, None, kk, mmain_consumer.index],
|
|
603
|
+
tQD[None, None, None, 0],
|
|
604
|
+
)
|
|
605
|
+
qdf_pipe.producer_commit(qdf_producer)
|
|
606
|
+
qdf_producer.advance()
|
|
607
|
+
|
|
608
|
+
# WD = dv2^T w
|
|
609
|
+
dv2n_pipe.consumer_wait(dv2n_consumer)
|
|
610
|
+
wdf_pipe.producer_acquire(wdf_producer)
|
|
611
|
+
for kk in cutlass.range(cute.size(tCrDV2N, mode=[2]), unroll_full=True):
|
|
612
|
+
mma_wd.set(tcgen05.Field.ACCUMULATE, cutlass.Boolean(kk != 0))
|
|
613
|
+
cute.gemm(
|
|
614
|
+
mma_wd, tWD[None, None, None, 0],
|
|
615
|
+
tCrDV2N[None, None, kk, 0],
|
|
616
|
+
tCrWT[None, None, kk, mmain_consumer.index],
|
|
617
|
+
tWD[None, None, None, 0],
|
|
618
|
+
)
|
|
619
|
+
wdf_pipe.producer_commit(wdf_producer)
|
|
620
|
+
wdf_producer.advance()
|
|
621
|
+
dv2n_pipe.consumer_release(dv2n_consumer)
|
|
622
|
+
dv2n_consumer.advance()
|
|
623
|
+
mmain_pipe.consumer_release(mmain_consumer)
|
|
624
|
+
mmain_consumer.advance()
|
|
625
|
+
# umma half of simtin's empty arrive (data untouched by this warp)
|
|
626
|
+
simtin_pipe.consumer_wait(simtin_mma_consumer)
|
|
627
|
+
simtin_pipe.consumer_release(
|
|
628
|
+
simtin_mma_consumer, pipeline.PipelineOp.TCGen05Mma
|
|
629
|
+
)
|
|
630
|
+
simtin_mma_consumer.advance()
|
|
631
|
+
|
|
632
|
+
dvf_pipe.producer_tail(dvf_producer)
|
|
633
|
+
qdf_pipe.producer_tail(qdf_producer)
|
|
634
|
+
wdf_pipe.producer_tail(wdf_producer)
|
|
635
|
+
|
|
636
|
+
# ==========================================================================
|
|
637
|
+
# SIMT warps 4..7: dv2, the dh update, checkpoint/dv2 stores, dh0.
|
|
638
|
+
# ==========================================================================
|
|
639
|
+
elif (
|
|
640
|
+
warp_idx == self.simt_warp_id[0]
|
|
641
|
+
or warp_idx == self.simt_warp_id[1]
|
|
642
|
+
or warp_idx == self.simt_warp_id[2]
|
|
643
|
+
or warp_idx == self.simt_warp_id[3]
|
|
644
|
+
):
|
|
645
|
+
t2r_64_atom = cute.make_copy_atom(
|
|
646
|
+
tcgen05.Ld16x256bOp(tcgen05.Repetition(8), tcgen05.Pack.NONE), f32
|
|
647
|
+
)
|
|
648
|
+
f32_cp_atom = cute.make_copy_atom(cute.nvgpu.CopyUniversalOp(), f32)
|
|
649
|
+
io_cp_atom = cute.make_copy_atom(cute.nvgpu.CopyUniversalOp(), io)
|
|
650
|
+
|
|
651
|
+
# --- DV -> dv2 ---
|
|
652
|
+
tDV_2d = tDV[((None, None), 0, 0, None)]
|
|
653
|
+
tiled_t2r_dv = tcgen05.make_tmem_copy(t2r_64_atom, tDV_2d[None, None, 0])
|
|
654
|
+
thr_t2r_dv = tiled_t2r_dv.get_slice(local_tidx)
|
|
655
|
+
tTR_tDV = thr_t2r_dv.partition_S(tDV_2d)
|
|
656
|
+
tTR_rDV = cute.make_rmem_tensor(
|
|
657
|
+
thr_t2r_dv.partition_D(cute.make_identity_tensor((BT, BV))).shape, f32
|
|
658
|
+
)
|
|
659
|
+
tDVsDVI = thr_t2r_dv.partition_D(sDVI)
|
|
660
|
+
tDVrDVI = cute.make_rmem_tensor(
|
|
661
|
+
cute.slice_(tDVsDVI.shape, (None, None, None, 0)), io
|
|
662
|
+
)
|
|
663
|
+
r2s_x16t_atom = cute.make_copy_atom(
|
|
664
|
+
cute.nvgpu.warp.StMatrix8x8x16bOp(transpose=True, num_matrices=4), io
|
|
665
|
+
)
|
|
666
|
+
tiled_r2s_dv2n = cute.make_tiled_copy_D(r2s_x16t_atom, tiled_t2r_dv)
|
|
667
|
+
thr_r2s_dv2n = tiled_r2s_dv2n.get_slice(local_tidx)
|
|
668
|
+
tRS_sDV2N = thr_r2s_dv2n.partition_D(sDV2N_epi)
|
|
669
|
+
tRS_rDV2N = cute.make_rmem_tensor(
|
|
670
|
+
cute.slice_(tRS_sDV2N.shape, (None, None, None, 0)), io
|
|
671
|
+
)
|
|
672
|
+
r2s_x16_atom = cute.make_copy_atom(
|
|
673
|
+
cute.nvgpu.warp.StMatrix8x8x16bOp(transpose=False, num_matrices=4), io
|
|
674
|
+
)
|
|
675
|
+
tiled_r2s_dv2s = cute.make_tiled_copy_D(r2s_x16_atom, tiled_t2r_dv)
|
|
676
|
+
thr_r2s_dv2s = tiled_r2s_dv2s.get_slice(local_tidx)
|
|
677
|
+
tRS_sDV2S = thr_r2s_dv2s.partition_D(sDV2S)
|
|
678
|
+
tRS_rDV2S = cute.make_rmem_tensor(
|
|
679
|
+
cute.slice_(tRS_sDV2S.shape, (None, None, None, 0)), io
|
|
680
|
+
)
|
|
681
|
+
|
|
682
|
+
# --- QD/WD -> dh ---
|
|
683
|
+
tQD_2d = tQD[((None, None), 0, 0, None)]
|
|
684
|
+
tiled_t2r_upd = tcgen05.make_tmem_copy(t2r_64_atom, tQD_2d[None, None, 0])
|
|
685
|
+
thr_t2r_upd = tiled_t2r_upd.get_slice(local_tidx)
|
|
686
|
+
tTR_tQD = thr_t2r_upd.partition_S(tQD_2d)
|
|
687
|
+
coordUPD = thr_t2r_upd.partition_D(cute.make_identity_tensor((BV, K)))
|
|
688
|
+
tTR_rQD = cute.make_rmem_tensor(coordUPD.shape, f32)
|
|
689
|
+
tWD_2d = tWD[((None, None), 0, 0, None)]
|
|
690
|
+
tiled_t2r_wd = tcgen05.make_tmem_copy(t2r_64_atom, tWD_2d[None, None, 0])
|
|
691
|
+
thr_t2r_wd = tiled_t2r_wd.get_slice(local_tidx)
|
|
692
|
+
tTR_tWD = thr_t2r_wd.partition_S(tWD_2d)
|
|
693
|
+
tTR_rWD = cute.make_rmem_tensor(coordUPD.shape, f32)
|
|
694
|
+
tDHreg = cute.make_rmem_tensor(coordUPD.shape, f32)
|
|
695
|
+
sGd_bcast = cute.make_tensor(
|
|
696
|
+
sGd.iterator,
|
|
697
|
+
cute.make_layout((BV, K, self.input_stages), stride=(0, 1, K)),
|
|
698
|
+
)
|
|
699
|
+
tUPDsGd = thr_t2r_upd.partition_D(sGd_bcast)
|
|
700
|
+
tUPDrGd = cute.make_rmem_tensor(
|
|
701
|
+
cute.slice_(tUPDsGd.shape, (None, None, None, 0)), f32
|
|
702
|
+
)
|
|
703
|
+
r2s_dh_atom = cute.make_copy_atom(
|
|
704
|
+
cute.nvgpu.warp.StMatrix8x8x16bOp(transpose=False, num_matrices=4), io
|
|
705
|
+
)
|
|
706
|
+
tiled_r2s_dh = cute.make_tiled_copy_D(r2s_dh_atom, tiled_t2r_upd)
|
|
707
|
+
thr_r2s_dh = tiled_r2s_dh.get_slice(local_tidx)
|
|
708
|
+
tRS_sDH = thr_r2s_dh.partition_D(sDH_epi)
|
|
709
|
+
tRS_rDH = cute.make_rmem_tensor(
|
|
710
|
+
cute.slice_(tRS_sDH.shape, (None, None, None, 0)), io
|
|
711
|
+
)
|
|
712
|
+
r2s_ck_atom = cute.make_copy_atom(
|
|
713
|
+
cute.nvgpu.warp.StMatrix8x8x16bOp(transpose=True, num_matrices=4), io
|
|
714
|
+
)
|
|
715
|
+
tiled_r2s_ck = cute.make_tiled_copy_D(r2s_ck_atom, tiled_t2r_upd)
|
|
716
|
+
thr_r2s_ck = tiled_r2s_ck.get_slice(local_tidx)
|
|
717
|
+
tRS_sCKS = thr_r2s_ck.partition_D(sCKS)
|
|
718
|
+
tRS_rCKS = cute.make_rmem_tensor(
|
|
719
|
+
cute.slice_(tRS_sCKS.shape, (None, None, None, 0)), io
|
|
720
|
+
)
|
|
721
|
+
|
|
722
|
+
# TMA store plumbing (dv2 per chunk; dh checkpoint per chunk)
|
|
723
|
+
bSG_sDV2S, bSG_gDV2 = cpasync.tma_partition(
|
|
724
|
+
tma_dv2, 0, cute.make_layout(1),
|
|
725
|
+
cute.group_modes(sDV2S, 0, 2), cute.group_modes(gDV2, 0, 2),
|
|
726
|
+
)
|
|
727
|
+
bSG_sCK, bSG_gCK = cpasync.tma_partition(
|
|
728
|
+
tma_ck, 0, cute.make_layout(1),
|
|
729
|
+
cute.group_modes(sCKS, 0, 2), cute.group_modes(gCK, 0, 2),
|
|
730
|
+
)
|
|
731
|
+
tma_store_pipeline = pipeline.PipelineTmaStore.create(
|
|
732
|
+
num_stages=1,
|
|
733
|
+
producer_group=pipeline.CooperativeGroup(
|
|
734
|
+
pipeline.Agent.Thread, simt_threads
|
|
735
|
+
),
|
|
736
|
+
)
|
|
737
|
+
|
|
738
|
+
simtin_consumer = pipeline.make_pipeline_state(
|
|
739
|
+
pipeline.PipelineUserType.Consumer, self.input_stages
|
|
740
|
+
)
|
|
741
|
+
dvf_consumer = pipeline.make_pipeline_state(pipeline.PipelineUserType.Consumer, 1)
|
|
742
|
+
qdf_consumer = pipeline.make_pipeline_state(pipeline.PipelineUserType.Consumer, 1)
|
|
743
|
+
wdf_consumer = pipeline.make_pipeline_state(pipeline.PipelineUserType.Consumer, 1)
|
|
744
|
+
dh_producer = pipeline.make_pipeline_state(
|
|
745
|
+
pipeline.PipelineUserType.Producer, self.dh_stages
|
|
746
|
+
)
|
|
747
|
+
dv2n_producer = pipeline.make_pipeline_state(pipeline.PipelineUserType.Producer, 1)
|
|
748
|
+
|
|
749
|
+
# ---- dh := dht; checkpoint NT-1 is exactly that value ----
|
|
750
|
+
for i in cutlass.range(cute.size(tDHreg), unroll_full=True):
|
|
751
|
+
vv, kk = coordUPD[i]
|
|
752
|
+
tDHreg[i] = mDHT[(kk, v_idx * BV + vv, hv_idx, b_idx)]
|
|
753
|
+
dh_pipe.producer_acquire(dh_producer)
|
|
754
|
+
for i in cutlass.range(cute.size(tDHreg), unroll_full=True, vectorize=True):
|
|
755
|
+
vio = tDHreg[i].to(io)
|
|
756
|
+
tRS_rDH[i] = vio
|
|
757
|
+
tRS_rCKS[i] = vio
|
|
758
|
+
cute.copy(tiled_r2s_dh, tRS_rDH, tRS_sDH[None, None, None, dh_producer.index])
|
|
759
|
+
cute.copy(tiled_r2s_ck, tRS_rCKS, tRS_sCKS[None, None, None, 0])
|
|
760
|
+
cute.arch.fence_proxy("async.shared", space="cta")
|
|
761
|
+
self.simt_sync_barrier.arrive_and_wait()
|
|
762
|
+
# commit dh BEFORE the checkpoint store (record-006 deferral): the store's
|
|
763
|
+
# completion wait lands on the next dv2-store acquire, off the dh chain.
|
|
764
|
+
dh_pipe.producer_commit(dh_producer)
|
|
765
|
+
dh_producer.advance()
|
|
766
|
+
if warp_idx == self.simt_warp_id[0]:
|
|
767
|
+
cute.copy(tma_ck, bSG_sCK[None, 0], bSG_gCK[None, NT - 1])
|
|
768
|
+
tma_store_pipeline.producer_commit()
|
|
769
|
+
|
|
770
|
+
for rc in cutlass.range(NT, unroll=1):
|
|
771
|
+
c = NT - 1 - rc
|
|
772
|
+
simtin_pipe.consumer_wait(simtin_consumer)
|
|
773
|
+
scrd = (None, None, None, simtin_consumer.index)
|
|
774
|
+
cute.copy(io_cp_atom, tDVsDVI[scrd], tDVrDVI)
|
|
775
|
+
|
|
776
|
+
# dv2 = DV + dv_in
|
|
777
|
+
dvf_pipe.consumer_wait(dvf_consumer)
|
|
778
|
+
cute.copy(tiled_t2r_dv, tTR_tDV[None, None, None, 0], tTR_rDV)
|
|
779
|
+
cute.arch.fence_view_async_tmem_load()
|
|
780
|
+
dvf_pipe.consumer_release(dvf_consumer)
|
|
781
|
+
dvf_consumer.advance()
|
|
782
|
+
dv2n_pipe.producer_acquire(dv2n_producer)
|
|
783
|
+
for i in cutlass.range(
|
|
784
|
+
cute.size(tTR_rDV), unroll_full=True, vectorize=True
|
|
785
|
+
):
|
|
786
|
+
v2 = (tTR_rDV[i] + tDVrDVI[i].to(f32)).to(io)
|
|
787
|
+
tRS_rDV2N[i] = v2
|
|
788
|
+
tRS_rDV2S[i] = v2
|
|
789
|
+
# the MMA operand first: it unblocks WD
|
|
790
|
+
cute.copy(tiled_r2s_dv2n, tRS_rDV2N, tRS_sDV2N[None, None, None, 0])
|
|
791
|
+
cute.arch.fence_proxy("async.shared", space="cta")
|
|
792
|
+
self.simt_sync_barrier.arrive_and_wait()
|
|
793
|
+
dv2n_pipe.producer_commit(dv2n_producer)
|
|
794
|
+
dv2n_producer.advance()
|
|
795
|
+
# acquire FIRST: waits on last chunk's stores, not the fresh ones
|
|
796
|
+
if warp_idx == self.simt_warp_id[0]:
|
|
797
|
+
tma_store_pipeline.producer_acquire()
|
|
798
|
+
self.simt_sync_barrier.arrive_and_wait()
|
|
799
|
+
cute.copy(tiled_r2s_dv2s, tRS_rDV2S, tRS_sDV2S[None, None, None, 0])
|
|
800
|
+
cute.arch.fence_proxy("async.shared", space="cta")
|
|
801
|
+
self.simt_sync_barrier.arrive_and_wait()
|
|
802
|
+
if warp_idx == self.simt_warp_id[0]:
|
|
803
|
+
cute.copy(tma_dv2, bSG_sDV2S[None, 0], bSG_gDV2[None, c])
|
|
804
|
+
tma_store_pipeline.producer_commit()
|
|
805
|
+
|
|
806
|
+
# dh update: dh = exp2(gd) * dh + scale*QD - WD (per-dim decay)
|
|
807
|
+
cute.copy(f32_cp_atom, tUPDsGd[scrd], tUPDrGd)
|
|
808
|
+
qdf_pipe.consumer_wait(qdf_consumer)
|
|
809
|
+
cute.copy(tiled_t2r_upd, tTR_tQD[None, None, None, 0], tTR_rQD)
|
|
810
|
+
cute.arch.fence_view_async_tmem_load()
|
|
811
|
+
qdf_pipe.consumer_release(qdf_consumer)
|
|
812
|
+
qdf_consumer.advance()
|
|
813
|
+
wdf_pipe.consumer_wait(wdf_consumer)
|
|
814
|
+
cute.copy(tiled_t2r_wd, tTR_tWD[None, None, None, 0], tTR_rWD)
|
|
815
|
+
cute.arch.fence_view_async_tmem_load()
|
|
816
|
+
wdf_pipe.consumer_release(wdf_consumer)
|
|
817
|
+
wdf_consumer.advance()
|
|
818
|
+
for i in cutlass.range(
|
|
819
|
+
cute.size(tDHreg), unroll_full=True, vectorize=True
|
|
820
|
+
):
|
|
821
|
+
dec = cute.math.exp2(tUPDrGd[i], fastmath=True)
|
|
822
|
+
tDHreg[i] = dec * tDHreg[i] + (scale * tTR_rQD[i] - tTR_rWD[i])
|
|
823
|
+
if c > 0:
|
|
824
|
+
dh_pipe.producer_acquire(dh_producer)
|
|
825
|
+
for i in cutlass.range(
|
|
826
|
+
cute.size(tDHreg), unroll_full=True, vectorize=True
|
|
827
|
+
):
|
|
828
|
+
vio = tDHreg[i].to(io)
|
|
829
|
+
tRS_rDH[i] = vio
|
|
830
|
+
tRS_rCKS[i] = vio
|
|
831
|
+
cute.copy(
|
|
832
|
+
tiled_r2s_dh, tRS_rDH, tRS_sDH[None, None, None, dh_producer.index]
|
|
833
|
+
)
|
|
834
|
+
cute.copy(tiled_r2s_ck, tRS_rCKS, tRS_sCKS[None, None, None, 0])
|
|
835
|
+
cute.arch.fence_proxy("async.shared", space="cta")
|
|
836
|
+
self.simt_sync_barrier.arrive_and_wait()
|
|
837
|
+
dh_pipe.producer_commit(dh_producer)
|
|
838
|
+
dh_producer.advance()
|
|
839
|
+
if warp_idx == self.simt_warp_id[0]:
|
|
840
|
+
cute.copy(tma_ck, bSG_sCK[None, 0], bSG_gCK[None, c - 1])
|
|
841
|
+
tma_store_pipeline.producer_commit()
|
|
842
|
+
|
|
843
|
+
simtin_pipe.consumer_release(
|
|
844
|
+
simtin_consumer, pipeline.PipelineOp.AsyncThread
|
|
845
|
+
)
|
|
846
|
+
simtin_consumer.advance()
|
|
847
|
+
|
|
848
|
+
# ---- dh0 (fp32): once-per-kernel plain global scatter ----
|
|
849
|
+
for i in cutlass.range(cute.size(tDHreg), unroll_full=True):
|
|
850
|
+
vv, kk = coordUPD[i]
|
|
851
|
+
mDH0[(kk, v_idx * BV + vv, hv_idx, b_idx)] = tDHreg[i]
|
|
852
|
+
|
|
853
|
+
tma_store_pipeline.producer_tail()
|
|
854
|
+
|
|
855
|
+
tmem.relinquish_alloc_permit()
|
|
856
|
+
self.tmem_dealloc_sync_barrier.arrive_and_wait()
|
|
857
|
+
tmem.free(tmem_ptr_base)
|
|
858
|
+
return
|
|
859
|
+
|
|
860
|
+
|
|
861
|
+
# --------------------------------------------------------------------------------------------
|
|
862
|
+
# host wrapper
|
|
863
|
+
# --------------------------------------------------------------------------------------------
|
|
864
|
+
|
|
865
|
+
_COMPILE_CACHE: dict = {}
|
|
866
|
+
|
|
867
|
+
|
|
868
|
+
# Layout-keyed call cache with ctypes pointer retargeting; outputs are NEVER cached —
|
|
869
|
+
# allocated per call and retargeted (see kernel_fwd.py's note above _CALL_CACHE).
|
|
870
|
+
_CALL_CACHE: dict = {}
|
|
871
|
+
|
|
872
|
+
|
|
873
|
+
def _call_key(qg, kg, w, g2, dht, do, dv, scale):
|
|
874
|
+
def sig(t):
|
|
875
|
+
return (t.shape, t.stride(), t.dtype)
|
|
876
|
+
|
|
877
|
+
return (sig(qg), sig(kg), sig(w), sig(g2), sig(dht), sig(do), sig(dv), scale,
|
|
878
|
+
torch.cuda.current_stream().cuda_stream)
|
|
879
|
+
|
|
880
|
+
|
|
881
|
+
def kda_cute_dhu_call(
|
|
882
|
+
qg: torch.Tensor, # [B,T,HV,K] bf16/fp16 — q * exp2(g2), from recompute
|
|
883
|
+
kg: torch.Tensor, # [B,T,HV,K] — k * exp2(G - g2)
|
|
884
|
+
w: torch.Tensor, # [B,T,HV,K]
|
|
885
|
+
g2: torch.Tensor, # [B,T,HV,K] fp32 (only last rows used)
|
|
886
|
+
dht: torch.Tensor, # [B,HV,K,V] fp32
|
|
887
|
+
do: torch.Tensor, # [B,T,HV,V]
|
|
888
|
+
dv: torch.Tensor, # [B,T,HV,V] — stage 3's dv
|
|
889
|
+
scale: float,
|
|
890
|
+
) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
|
|
891
|
+
"""Returns (dh checkpoints [B,NT,HV,K,V] bf16, dh0 [B,HV,K,V] fp32, dv2 [B,T,HV,V])."""
|
|
892
|
+
key = _call_key(qg, kg, w, g2, dht, do, dv, scale)
|
|
893
|
+
ent = _CALL_CACHE.get(key)
|
|
894
|
+
outs = None
|
|
895
|
+
if ent is None:
|
|
896
|
+
B, T, HV, K = kg.shape
|
|
897
|
+
V = do.shape[3]
|
|
898
|
+
NT = T // 64
|
|
899
|
+
assert T % 64 == 0
|
|
900
|
+
assert V % 64 == 0
|
|
901
|
+
|
|
902
|
+
dh = torch.empty(B, NT, HV, K, V, device=kg.device, dtype=kg.dtype)
|
|
903
|
+
dh0 = torch.empty(B, HV, K, V, device=kg.device, dtype=torch.float32)
|
|
904
|
+
dv2 = torch.empty(B, T, HV, V, device=kg.device, dtype=dv.dtype)
|
|
905
|
+
gdc = torch.empty(B, HV, NT, K, device=g2.device, dtype=g2.dtype)
|
|
906
|
+
|
|
907
|
+
io_dtype = cutlass.BFloat16 if kg.dtype == torch.bfloat16 else cutlass.Float16
|
|
908
|
+
compile_key = (io_dtype, K, V)
|
|
909
|
+
|
|
910
|
+
ckg = _cute_view(kg, (1, 3, 2, 0), (0, 2, 3))
|
|
911
|
+
cdot = _cute_view(do, (3, 1, 2, 0), (1, 2, 3))
|
|
912
|
+
cqgt = _cute_view(qg, (3, 1, 2, 0), (1, 2, 3))
|
|
913
|
+
cwt = _cute_view(w, (3, 1, 2, 0), (1, 2, 3))
|
|
914
|
+
cdvi = _cute_view(dv, (1, 3, 2, 0), (0, 2, 3))
|
|
915
|
+
cgd = _cute_view(gdc, (3, 2, 1, 0), (1, 2, 3))
|
|
916
|
+
cdht = _cute_view(dht, (2, 3, 1, 0), (2, 3))
|
|
917
|
+
cdv2 = _cute_view(dv2, (1, 3, 2, 0), (0, 2, 3))
|
|
918
|
+
cdhck = _cute_view(dh, (4, 3, 1, 2, 0), (2, 3, 4))
|
|
919
|
+
cdh0 = _cute_view(dh0, (2, 3, 1, 0), (2, 3))
|
|
920
|
+
|
|
921
|
+
stream = cuda.CUstream(torch.cuda.current_stream().cuda_stream)
|
|
922
|
+
compiled = _COMPILE_CACHE.get(compile_key)
|
|
923
|
+
if compiled is None:
|
|
924
|
+
kernel_obj = KdaB2aDhuKernel(io_dtype, K, V_TILE=64)
|
|
925
|
+
compiled = cute.compile(
|
|
926
|
+
kernel_obj, ckg, cdot, cqgt, cwt, cdvi, cgd, cdht, cdv2, cdhck, cdh0,
|
|
927
|
+
cutlass.Float32(scale), stream,
|
|
928
|
+
)
|
|
929
|
+
_COMPILE_CACHE[compile_key] = compiled
|
|
930
|
+
# See kernel_fwd._release_keepalives: dh alone is 2 GiB at prod8192 and this entry
|
|
931
|
+
# would pin it, plus kg/do/qg/w/dv/dht, for the life of the process.
|
|
932
|
+
_release_keepalives(ckg, cdot, cqgt, cwt, cdvi, cgd, cdht, cdv2, cdhck, cdh0)
|
|
933
|
+
args = (ckg, cdot, cqgt, cwt, cdvi, cgd, cdht, cdv2, cdhck, cdh0,
|
|
934
|
+
cutlass.Float32(scale), stream)
|
|
935
|
+
if len(_CALL_CACHE) >= 64:
|
|
936
|
+
_CALL_CACHE.clear()
|
|
937
|
+
outs = (dh, dh0, dv2)
|
|
938
|
+
out_specs = tuple((tuple(t.shape), t.dtype) for t in outs)
|
|
939
|
+
ent = (compiled, args, out_specs, gdc)
|
|
940
|
+
_CALL_CACHE[key] = ent
|
|
941
|
+
|
|
942
|
+
compiled, args, out_specs, gdc = ent
|
|
943
|
+
ckg, cdot, cqgt, cwt, cdvi, _, cdht, cdv2, cdhck, cdh0, _, _ = args
|
|
944
|
+
if outs is None:
|
|
945
|
+
outs = tuple(
|
|
946
|
+
torch.empty(shape, device=kg.device, dtype=dtype)
|
|
947
|
+
for shape, dtype in out_specs
|
|
948
|
+
)
|
|
949
|
+
dh, dh0, dv2 = outs
|
|
950
|
+
_retarget(cdhck, dh)
|
|
951
|
+
_retarget(cdh0, dh0)
|
|
952
|
+
_retarget(cdv2, dv2)
|
|
953
|
+
_retarget(ckg, kg)
|
|
954
|
+
_retarget(cdot, do)
|
|
955
|
+
_retarget(cqgt, qg)
|
|
956
|
+
_retarget(cwt, w)
|
|
957
|
+
_retarget(cdvi, dv)
|
|
958
|
+
_retarget(cdht, dht)
|
|
959
|
+
gdc.copy_(g2[:, 63::64].transpose(1, 2))
|
|
960
|
+
compiled(*args)
|
|
961
|
+
return dh, dh0, dv2
|
|
962
|
+
|
|
963
|
+
|
|
964
|
+
# Serial scans need a full GPU (gdn 003 record 005's lesson); below the floor fall back
|
|
965
|
+
# to fla. KDA002_B2A=cutedsl forces past it for dbg-sized shapes; =fla pins fla.
|
|
966
|
+
_MIN_CTAS = 256
|
|
967
|
+
|
|
968
|
+
|
|
969
|
+
def kda_dhu_b2a(qg, kg, w, g2, h0, dht, do, dv, scale, chunk_size):
|
|
970
|
+
"""Stage-4 dispatcher, argument-for-argument fla's chunk_gated_delta_rule_bwd_dhu."""
|
|
971
|
+
B, T, HV, K = kg.shape
|
|
972
|
+
V = do.shape[-1]
|
|
973
|
+
supported = (
|
|
974
|
+
chunk_size == 64
|
|
975
|
+
and T % 64 == 0
|
|
976
|
+
and K in (64, 128)
|
|
977
|
+
and V % 64 == 0
|
|
978
|
+
and kg.dtype in (torch.bfloat16, torch.float16)
|
|
979
|
+
# dht is None when the caller did not ask for a final state; this kernel requires
|
|
980
|
+
# it, so that call takes fla's dhu and gives up ~0.7ms.
|
|
981
|
+
and dht is not None
|
|
982
|
+
and B * HV * (V // 64) >= _MIN_CTAS
|
|
983
|
+
)
|
|
984
|
+
if not supported:
|
|
985
|
+
from fla.ops.common.chunk_delta_h import chunk_gated_delta_rule_bwd_dhu
|
|
986
|
+
|
|
987
|
+
return chunk_gated_delta_rule_bwd_dhu(
|
|
988
|
+
q=qg, k=kg, w=w, gk=g2, h0=h0, dht=dht, do=do, dv=dv,
|
|
989
|
+
scale=scale, chunk_size=chunk_size,
|
|
990
|
+
)
|
|
991
|
+
dh, dh0, dv2 = kda_cute_dhu_call(qg, kg, w, g2, dht, do, dv, float(scale))
|
|
992
|
+
# fla's contract: dh0 exists only when h0 was provided (it is h0's gradient; the
|
|
993
|
+
# kernel computes it unconditionally since its value never depends on h0).
|
|
994
|
+
return dh, (dh0 if h0 is not None else None), dv2
|