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,320 @@
|
|
|
1
|
+
"""Phase 2 B2b — fla 0.5.2's `chunk_kda_bwd_wy_dqkg_fused`, restructured to run at full K.
|
|
2
|
+
|
|
3
|
+
fla's grid is already one CTA per (chunk, b·hv); the waste is entirely inside the CTA,
|
|
4
|
+
in `for i_k in range(cdiv(K, BK))`. Slabbing K costs three separate things:
|
|
5
|
+
|
|
6
|
+
1. `v_new`, `do` and `dv` are loaded in the i_v loop, which sits INSIDE the i_k loop,
|
|
7
|
+
so each is re-read NK times per CTA (h/dh/q/k/g/A are read once). At prod8192 that
|
|
8
|
+
is +3.2GB at BK=64 (+9.7GB at BK=32) on top of a ~15.5GB useful footprint.
|
|
9
|
+
2. Every inner dot is [BT,BV] @ [BV,BK] with BK ∈ {32,64} — a tiny N, three of them
|
|
10
|
+
chained on the same two operands. B1's probe already showed this stage is
|
|
11
|
+
latency-bound, not dot-bound (deleting one of the three dots bought 0.12ms of 6.15).
|
|
12
|
+
3. `tl.debug_barrier()` — fla marks it DO NOT REMOVE — is a full CTA barrier executed
|
|
13
|
+
NK·NV times, which is there only because the `if i_k == 0` block reuses the loop's
|
|
14
|
+
smem. It defeats whatever `num_stages` was going to pipeline.
|
|
15
|
+
|
|
16
|
+
So: BK = K. The i_k loop is gone, `h`/`dh` load as [BV, K], the V-shaped tiles are read
|
|
17
|
+
ONCE, the three dots widen to [BT,BV] @ [BV,K], the `i_k == 0` guard disappears (and with
|
|
18
|
+
it the reason for the barrier), and the whole exp2/dg/dA epilogue runs once instead of NK
|
|
19
|
+
times. Accumulators: dq/dk/dw [BT,K] fp32 + dA [BT,BT] = 448 of 512 tmem cols on Blackwell.
|
|
20
|
+
|
|
21
|
+
Numerics vs fla: the accumulation order over V is unchanged (i_v ascending, fp32 acc).
|
|
22
|
+
What moves is the reduction blocking inside each dot and `b_dA += dot(b_dw, kg^T)`, which
|
|
23
|
+
was NK partial sums over BK-wide slices and is now one sum over K — fp32 reassociations of
|
|
24
|
+
the same terms, no new exp2 factorization and no gate rewrite. Every store is idempotent
|
|
25
|
+
(the autotuner re-runs the kernel once per config trial, and any read-modify-write tensor
|
|
26
|
+
would corrupt on the first call per key — the NOTES-002 CachedAutotuner trap).
|
|
27
|
+
|
|
28
|
+
Varlen is not plumbed (the staged chain never passes cu_seqlens); off the supported shape
|
|
29
|
+
or below the CTA floor the dispatcher falls back to fla's fused kernel.
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
from __future__ import annotations
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
import torch
|
|
36
|
+
import triton
|
|
37
|
+
import triton.language as tl
|
|
38
|
+
|
|
39
|
+
from fla.ops.utils.cache import fla_cache_autotune
|
|
40
|
+
from fla.ops.utils.op import exp2
|
|
41
|
+
from fla.utils import autotune_cache_kwargs, check_shared_mem
|
|
42
|
+
|
|
43
|
+
# Measured on b300 (dbg_wysweep.py, prod8192): BV=128 blows smem at num_stages>=2 and
|
|
44
|
+
# never wins at 1; num_warps 16/32 spill ~2KB/thread and run 40-80x slower, so 8 warps is
|
|
45
|
+
# the ceiling. Everything viable lands within ~1% of 4.95ms, which is the register wall,
|
|
46
|
+
# not a tiling choice — keep the space small so autotune trials stay cheap.
|
|
47
|
+
BV_LIST = [32, 64] if check_shared_mem('ampere') else [16, 32]
|
|
48
|
+
|
|
49
|
+
# Serial-ish per-CTA kernels want a full GPU before they beat fla's autotuned one; below
|
|
50
|
+
# this many CTAs the launch is grid-starved and the comparison is noise (the same floor
|
|
51
|
+
# the other 002 stages use). KDA002_WY=triton forces past it so dbg-sized shapes still
|
|
52
|
+
# exercise this kernel.
|
|
53
|
+
_MIN_CTAS = 256
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@fla_cache_autotune(
|
|
57
|
+
configs=[
|
|
58
|
+
triton.Config({'BV': BV}, num_warps=num_warps, num_stages=num_stages)
|
|
59
|
+
for BV in BV_LIST
|
|
60
|
+
for num_warps in [4, 8]
|
|
61
|
+
for num_stages in [2, 3, 4]
|
|
62
|
+
],
|
|
63
|
+
key=['BT', 'HV', 'K', 'V', 'STATE_V_FIRST'],
|
|
64
|
+
**autotune_cache_kwargs,
|
|
65
|
+
)
|
|
66
|
+
@triton.jit(do_not_specialize=['T'])
|
|
67
|
+
def chunk_kda_bwd_kernel_wy_dqkg_wide(
|
|
68
|
+
q,
|
|
69
|
+
k,
|
|
70
|
+
v,
|
|
71
|
+
v_new,
|
|
72
|
+
g,
|
|
73
|
+
beta,
|
|
74
|
+
A,
|
|
75
|
+
h,
|
|
76
|
+
do,
|
|
77
|
+
dh,
|
|
78
|
+
dq,
|
|
79
|
+
dk,
|
|
80
|
+
dv,
|
|
81
|
+
dv2,
|
|
82
|
+
dg,
|
|
83
|
+
db,
|
|
84
|
+
dA,
|
|
85
|
+
scale,
|
|
86
|
+
T,
|
|
87
|
+
H: tl.constexpr,
|
|
88
|
+
HV: tl.constexpr,
|
|
89
|
+
K: tl.constexpr,
|
|
90
|
+
V: tl.constexpr,
|
|
91
|
+
BT: tl.constexpr,
|
|
92
|
+
BV: tl.constexpr,
|
|
93
|
+
STATE_V_FIRST: tl.constexpr,
|
|
94
|
+
PROBE_SKIP: tl.constexpr = 0,
|
|
95
|
+
):
|
|
96
|
+
# PROBE_SKIP is an attribution knob for dbg_wysweep.py only (the KDA002C_SKIP pattern
|
|
97
|
+
# from the CuTe intra kernel): 1 drops the dgk elementwise reduction, 2 drops the WY
|
|
98
|
+
# block, 3 drops the three main dots. Any nonzero value produces WRONG output — it
|
|
99
|
+
# exists to price a piece, not to run. The shipping path always passes 0.
|
|
100
|
+
i_t, i_bh = tl.program_id(0).to(tl.int64), tl.program_id(1)
|
|
101
|
+
i_b, i_hv = i_bh // HV, i_bh % HV
|
|
102
|
+
i_h = i_hv // (HV // H)
|
|
103
|
+
|
|
104
|
+
NT = tl.cdiv(T, BT)
|
|
105
|
+
i_tg = (i_b * NT + i_t).to(tl.int64)
|
|
106
|
+
bos = (i_b * T).to(tl.int64)
|
|
107
|
+
|
|
108
|
+
o_t = i_t * BT + tl.arange(0, BT)
|
|
109
|
+
m_t = o_t < T
|
|
110
|
+
m_last = (o_t == min(T, i_t * BT + BT) - 1)
|
|
111
|
+
|
|
112
|
+
q += (bos * H + i_h) * K
|
|
113
|
+
k += (bos * H + i_h) * K
|
|
114
|
+
v += (bos * HV + i_hv) * V
|
|
115
|
+
v_new += (bos * HV + i_hv) * V
|
|
116
|
+
g += (bos * HV + i_hv) * K
|
|
117
|
+
beta += bos * HV + i_hv
|
|
118
|
+
A += (bos * HV + i_hv) * BT
|
|
119
|
+
h += (i_tg * HV + i_hv) * K*V
|
|
120
|
+
do += (bos * HV + i_hv) * V
|
|
121
|
+
dh += (i_tg * HV + i_hv) * K*V
|
|
122
|
+
dq += (bos * HV + i_hv) * K
|
|
123
|
+
dk += (bos * HV + i_hv) * K
|
|
124
|
+
dv += (bos * HV + i_hv) * V
|
|
125
|
+
dv2 += (bos * HV + i_hv) * V
|
|
126
|
+
dg += (bos * HV + i_hv) * K
|
|
127
|
+
db += bos * HV + i_hv
|
|
128
|
+
dA += (bos * HV + i_hv) * BT
|
|
129
|
+
|
|
130
|
+
p_beta = beta + o_t * HV
|
|
131
|
+
b_beta = tl.load(p_beta, mask=m_t, other=0.0)
|
|
132
|
+
|
|
133
|
+
o_A = tl.arange(0, BT)
|
|
134
|
+
m_AT = (o_A[:, None] < BT) & m_t[None, :]
|
|
135
|
+
p_A = A + o_A[:, None] + o_t[None, :] * (HV * BT)
|
|
136
|
+
b_A = tl.load(p_A, mask=m_AT, other=0.0)
|
|
137
|
+
|
|
138
|
+
# K is the full head dim here — one slab, no i_k loop.
|
|
139
|
+
o_k = tl.arange(0, K)
|
|
140
|
+
m_tk = m_t[:, None] & (o_k < K)[None, :]
|
|
141
|
+
|
|
142
|
+
p_k = k + o_t[:, None] * (H*K) + o_k[None, :]
|
|
143
|
+
p_g = g + o_t[:, None] * (HV*K) + o_k[None, :]
|
|
144
|
+
b_k = tl.load(p_k, mask=m_tk, other=0.0)
|
|
145
|
+
b_g = tl.load(p_g, mask=m_tk, other=0.0).to(tl.float32)
|
|
146
|
+
|
|
147
|
+
p_gn = g + (min(T, i_t * BT + BT) - 1).to(tl.int64) * HV*K + o_k
|
|
148
|
+
b_gn = tl.load(p_gn, mask=o_k < K, other=0).to(tl.float32)
|
|
149
|
+
|
|
150
|
+
b_dA = tl.zeros([BT, BT], dtype=tl.float32)
|
|
151
|
+
b_db = tl.zeros([BT], dtype=tl.float32)
|
|
152
|
+
b_dq = tl.zeros([BT, K], dtype=tl.float32)
|
|
153
|
+
b_dk = tl.zeros([BT, K], dtype=tl.float32)
|
|
154
|
+
b_dw = tl.zeros([BT, K], dtype=tl.float32)
|
|
155
|
+
b_dgk = tl.zeros([K], dtype=tl.float32)
|
|
156
|
+
|
|
157
|
+
for i_v in range(tl.cdiv(V, BV)):
|
|
158
|
+
o_v = i_v * BV + tl.arange(0, BV)
|
|
159
|
+
m_tv = m_t[:, None] & (o_v[None, :] < V)
|
|
160
|
+
m_h = (o_v[:, None] < V) & (o_k < K)[None, :]
|
|
161
|
+
p_v_new = v_new + o_t[:, None] * (HV*V) + o_v[None, :]
|
|
162
|
+
p_do = do + o_t[:, None] * (HV*V) + o_v[None, :]
|
|
163
|
+
if STATE_V_FIRST:
|
|
164
|
+
p_h = h + o_v[:, None] * K + o_k[None, :]
|
|
165
|
+
p_dh = dh + o_v[:, None] * K + o_k[None, :]
|
|
166
|
+
else:
|
|
167
|
+
p_h = h + o_v[:, None] + o_k[None, :] * V
|
|
168
|
+
p_dh = dh + o_v[:, None] + o_k[None, :] * V
|
|
169
|
+
p_v = v + o_t[:, None] * (HV*V) + o_v[None, :]
|
|
170
|
+
p_dv = dv + o_t[:, None] * (HV*V) + o_v[None, :]
|
|
171
|
+
p_dv2 = dv2 + o_t[:, None] * (HV*V) + o_v[None, :]
|
|
172
|
+
# [BT, BV] — each read ONCE now, not once per K slab
|
|
173
|
+
b_v_new = tl.load(p_v_new, mask=m_tv, other=0.0)
|
|
174
|
+
b_do = tl.load(p_do, mask=m_tv, other=0.0)
|
|
175
|
+
b_v = tl.load(p_v, mask=m_tv, other=0.0)
|
|
176
|
+
b_dv = tl.load(p_dv, mask=m_tv, other=0.0)
|
|
177
|
+
# [BV, K]
|
|
178
|
+
b_h = tl.load(p_h, mask=m_h, other=0.0)
|
|
179
|
+
b_dh = tl.load(p_dh, mask=m_h, other=0.0)
|
|
180
|
+
|
|
181
|
+
if PROBE_SKIP != 1:
|
|
182
|
+
b_dgk += tl.sum(b_h * b_dh, axis=0)
|
|
183
|
+
if PROBE_SKIP != 3:
|
|
184
|
+
b_dq += tl.dot(b_do, b_h.to(b_do.dtype))
|
|
185
|
+
b_dk += tl.dot(b_v_new, b_dh.to(b_v_new.dtype))
|
|
186
|
+
b_dw += tl.dot(b_dv.to(b_v_new.dtype), b_h.to(b_v_new.dtype))
|
|
187
|
+
|
|
188
|
+
# fla ran this block under `if i_k == 0` to keep it from repeating per K slab;
|
|
189
|
+
# with the slab loop gone it is just the v-loop body, and the debug_barrier that
|
|
190
|
+
# guarded the smem reuse across that branch goes with it.
|
|
191
|
+
if PROBE_SKIP != 2:
|
|
192
|
+
b_dA += tl.dot(b_dv, tl.trans(b_v))
|
|
193
|
+
b_dvb = tl.dot(b_A, b_dv)
|
|
194
|
+
b_dv2 = b_dvb * b_beta[:, None]
|
|
195
|
+
b_db += tl.sum(b_dvb * b_v, 1)
|
|
196
|
+
tl.store(p_dv2, b_dv2.to(p_dv2.dtype.element_ty), mask=m_tv)
|
|
197
|
+
|
|
198
|
+
b_gk_exp = exp2(b_g)
|
|
199
|
+
b_gb = b_gk_exp * b_beta[:, None]
|
|
200
|
+
b_dgk *= exp2(b_gn)
|
|
201
|
+
b_dq = b_dq * b_gk_exp * scale
|
|
202
|
+
b_dk = b_dk * tl.where(m_t[:, None], exp2(b_gn[None, :] - b_g), 0)
|
|
203
|
+
|
|
204
|
+
b_kg = b_k * b_gk_exp
|
|
205
|
+
|
|
206
|
+
b_dw = -b_dw.to(b_A.dtype)
|
|
207
|
+
b_dA += tl.dot(b_dw, tl.trans(b_kg.to(b_A.dtype)))
|
|
208
|
+
|
|
209
|
+
b_dkgb = tl.dot(b_A, b_dw)
|
|
210
|
+
b_db += tl.sum(b_dkgb * b_kg, 1)
|
|
211
|
+
|
|
212
|
+
p_q = q + o_t[:, None] * (H*K) + o_k[None, :]
|
|
213
|
+
b_q = tl.load(p_q, mask=m_tk, other=0.0)
|
|
214
|
+
b_kdk = b_k * b_dk
|
|
215
|
+
b_dgk += tl.sum(b_kdk, axis=0)
|
|
216
|
+
b_dg = b_q * b_dq - b_kdk + m_last[:, None] * b_dgk + b_kg * b_dkgb * b_beta[:, None]
|
|
217
|
+
b_dk = b_dk + b_dkgb * b_gb
|
|
218
|
+
|
|
219
|
+
p_dq = dq + o_t[:, None] * (HV*K) + o_k[None, :]
|
|
220
|
+
p_dk = dk + o_t[:, None] * (HV*K) + o_k[None, :]
|
|
221
|
+
p_dg = dg + o_t[:, None] * (HV*K) + o_k[None, :]
|
|
222
|
+
tl.store(p_dq, b_dq.to(p_dq.dtype.element_ty), mask=m_tk)
|
|
223
|
+
tl.store(p_dk, b_dk.to(p_dk.dtype.element_ty), mask=m_tk)
|
|
224
|
+
tl.store(p_dg, b_dg.to(p_dg.dtype.element_ty), mask=m_tk)
|
|
225
|
+
|
|
226
|
+
m_A = (o_t[:, None] > o_t[None, :]) & (m_t[:, None] & m_t)
|
|
227
|
+
b_dA = tl.where(m_A, b_dA * b_beta[None, :], 0)
|
|
228
|
+
b_dA = tl.dot(b_dA.to(b_A.dtype), b_A)
|
|
229
|
+
b_dA = tl.dot(b_A, b_dA.to(b_A.dtype))
|
|
230
|
+
b_dA = tl.where(m_A, -b_dA, 0)
|
|
231
|
+
|
|
232
|
+
m_dA = m_t[:, None] & (o_A[None, :] < BT)
|
|
233
|
+
p_dA = dA + o_t[:, None] * (HV * BT) + o_A[None, :]
|
|
234
|
+
p_db = db + o_t * HV
|
|
235
|
+
tl.store(p_dA, b_dA.to(p_dA.dtype.element_ty), mask=m_dA)
|
|
236
|
+
tl.store(p_db, b_db.to(p_db.dtype.element_ty), mask=m_t)
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def _supported(K: int, V: int, BT: int, ctas: int) -> bool:
|
|
240
|
+
"""Full-K tiles need K to be a power of two `tl.arange` can produce and small enough
|
|
241
|
+
that dq/dk/dw fit tmem; V must tile by the smallest configured BV. The CTA floor keeps
|
|
242
|
+
grid-starved shapes on fla, where they are genuinely faster."""
|
|
243
|
+
return (
|
|
244
|
+
K in (32, 64, 128)
|
|
245
|
+
and V % min(BV_LIST) == 0
|
|
246
|
+
and BT == 64
|
|
247
|
+
and ctas >= _MIN_CTAS
|
|
248
|
+
)
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
def chunk_kda_bwd_wy_dqkg_wide(
|
|
252
|
+
q: torch.Tensor,
|
|
253
|
+
k: torch.Tensor,
|
|
254
|
+
v: torch.Tensor,
|
|
255
|
+
v_new: torch.Tensor,
|
|
256
|
+
g: torch.Tensor,
|
|
257
|
+
beta: torch.Tensor,
|
|
258
|
+
A: torch.Tensor,
|
|
259
|
+
h: torch.Tensor,
|
|
260
|
+
do: torch.Tensor,
|
|
261
|
+
dh: torch.Tensor,
|
|
262
|
+
dv: torch.Tensor,
|
|
263
|
+
scale: float | None = None,
|
|
264
|
+
state_v_first: bool = False,
|
|
265
|
+
chunk_size: int = 64,
|
|
266
|
+
):
|
|
267
|
+
B, T, H, K, HV, V = *k.shape, v.shape[2], v.shape[-1]
|
|
268
|
+
BT = chunk_size
|
|
269
|
+
NT = triton.cdiv(T, BT)
|
|
270
|
+
|
|
271
|
+
# (The tmem CuTe port of this stage lives in the research ladder and is NOT shipped:
|
|
272
|
+
# it is correct but measured 6.93ms against this kernel's 4.98 at prod8192.)
|
|
273
|
+
if state_v_first or not _supported(K, V, BT, NT * B * HV):
|
|
274
|
+
from fla.ops.kda.chunk_bwd import chunk_kda_bwd_wy_dqkg_fused
|
|
275
|
+
|
|
276
|
+
return chunk_kda_bwd_wy_dqkg_fused(
|
|
277
|
+
q=q, k=k, v=v, v_new=v_new, g=g, beta=beta, A=A, h=h,
|
|
278
|
+
do=do, dh=dh, dv=dv, scale=scale, state_v_first=state_v_first,
|
|
279
|
+
chunk_size=chunk_size,
|
|
280
|
+
)
|
|
281
|
+
|
|
282
|
+
# dq, dk are allocated at HV; the caller reduces to H if GVA. All outputs are fresh
|
|
283
|
+
# (never read-modify-write — autotune trials re-run the kernel).
|
|
284
|
+
dq = g.new_empty(B, T, HV, K, dtype=torch.float)
|
|
285
|
+
dk = g.new_empty(B, T, HV, K, dtype=torch.float)
|
|
286
|
+
dv2 = torch.empty_like(v)
|
|
287
|
+
dg = torch.empty_like(g, dtype=torch.float)
|
|
288
|
+
db = torch.empty_like(beta, dtype=torch.float)
|
|
289
|
+
dA = torch.empty_like(A, dtype=torch.float)
|
|
290
|
+
|
|
291
|
+
grid = (NT, B * HV)
|
|
292
|
+
chunk_kda_bwd_kernel_wy_dqkg_wide[grid](
|
|
293
|
+
q=q,
|
|
294
|
+
k=k,
|
|
295
|
+
v=v,
|
|
296
|
+
v_new=v_new,
|
|
297
|
+
g=g,
|
|
298
|
+
beta=beta,
|
|
299
|
+
A=A,
|
|
300
|
+
h=h,
|
|
301
|
+
do=do,
|
|
302
|
+
dh=dh,
|
|
303
|
+
dq=dq,
|
|
304
|
+
dk=dk,
|
|
305
|
+
dv=dv,
|
|
306
|
+
dv2=dv2,
|
|
307
|
+
dg=dg,
|
|
308
|
+
db=db,
|
|
309
|
+
dA=dA,
|
|
310
|
+
scale=scale,
|
|
311
|
+
T=T,
|
|
312
|
+
H=H,
|
|
313
|
+
HV=HV,
|
|
314
|
+
K=K,
|
|
315
|
+
V=V,
|
|
316
|
+
BT=BT,
|
|
317
|
+
STATE_V_FIRST=state_v_first,
|
|
318
|
+
PROBE_SKIP=0, # explicit: the attribution knob is never on in the shipping path
|
|
319
|
+
)
|
|
320
|
+
return dq, dk, dv2, db, dg, dA
|
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
"""Phase 2 B2b, transposed — the wy_dqkg stage as two Triton kernels (ladder idea 005).
|
|
2
|
+
|
|
3
|
+
Same math as bwd_wy.py (002's kernel_wy2: fla's fused wy_dqkg at full K), re-arranged
|
|
4
|
+
around the two occupancy walls the 2026-09-01 probes found in wy2 (the ledger is in
|
|
5
|
+
kernels/kda/ideas/005-wy-transposed/NOTES.md; the ncu profile there reads 24.6%/36.2% warps
|
|
6
|
+
active against wy2's 12.4%, stage 4.98 -> 4.54ms at prod8192 B=16):
|
|
7
|
+
|
|
8
|
+
* wy2's loop accumulates dq/dk/dw as [BT=64, K=128] tiles: M=64 tcgen05 MMAs, operands
|
|
9
|
+
through registers, 255 regs -> 1 CTA/SM. Here the accumulators are TRANSPOSED —
|
|
10
|
+
dq^T = h^T @ do^T, dw^T = h^T @ dv^T, dk^T = dh^T @ v_new^T, all M=K=128 — and the
|
|
11
|
+
loop alone streams at the DRAM roofline (1.7ms for its 13GB at prod8192, 78 regs).
|
|
12
|
+
* The epilogue's live set ([64,128] fp32 x ~8) is what pins wy2 at 255 regs. Here the
|
|
13
|
+
raw accumulators are parked TRANSPOSED in the fp32 outputs at loop exit and the
|
|
14
|
+
elementwise epilogue re-reads them from L2 in K-strips of KS rows, so the kernel
|
|
15
|
+
compiles to 128 regs / 0 spills and two CTAs share an SM. Two is also the ceiling:
|
|
16
|
+
Triton allocates 256 tmem columns for these accumulators, and tmem is 512/SM.
|
|
17
|
+
* The v-side of the loop (dA = dv @ v^T, dvb = A @ dv -> dv2, db), wy2's `dA += dw @ kg^T`,
|
|
18
|
+
and the dA chain (mask/beta, two [64,64] dots) are a separate kernel: in the fused
|
|
19
|
+
kernel they cost more (1.3ms + 0.95ms, both latency chains at 2 CTAs/SM) than the
|
|
20
|
+
~0.9ms the side kernel takes at 4 CTAs/SM. The main kernel parks -dw^T (bf16, exactly
|
|
21
|
+
wy2's b_dw) and kg = k*exp2(g) (bf16, from its strips) in two [B,T,HV,K] buffers for it,
|
|
22
|
+
so the side kernel never touches g or exp2 (a first cut that recomputed kg there held
|
|
23
|
+
the fp32 g tile at 248 regs and cost 0.5ms); the side kernel finalizes db IN PLACE on
|
|
24
|
+
the main kernel's partial and writes dA, so it must run after it and is never autotuned.
|
|
25
|
+
|
|
26
|
+
Numerics vs wy2: dq/dk/dv2 bit-identical, dg/db ~1e-6 (reassociated rowsums). dA is
|
|
27
|
+
accumulated the way wy2 does it — dv @ v^T then dw @ kg^T into ONE tmem accumulator —
|
|
28
|
+
because the first cut (dw @ kg^T in the main kernel, IEEE-added to the side's partial)
|
|
29
|
+
flipped bf16 roundings in the chain and put prod-NT dk 30x over dbg_bwd's budget.
|
|
30
|
+
Varlen is not plumbed; off the supported shape (K=128, V%64, BT=64) or below the CTA floor
|
|
31
|
+
the dispatcher falls back to bwd_wy's kernel, which is why the chain can always call this.
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
from __future__ import annotations
|
|
35
|
+
|
|
36
|
+
import torch
|
|
37
|
+
import triton
|
|
38
|
+
import triton.language as tl
|
|
39
|
+
|
|
40
|
+
from fla.ops.utils.op import exp2
|
|
41
|
+
|
|
42
|
+
from ..._common.support import MIN_CTAS
|
|
43
|
+
from .bwd_wy import chunk_kda_bwd_wy_dqkg_wide
|
|
44
|
+
|
|
45
|
+
# Pinned configs (probed on b300 at prod8192, the ladder's NOTES.md): BV=32/3 stages/8
|
|
46
|
+
# warps/maxnreg=128 for the main kernel (84-92KB smem, 128 regs, 0 spills -> 2 CTAs/SM);
|
|
47
|
+
# KS=32 strips (64 spills at 128 regs, 16 no faster). The side kernel is occupancy-bound
|
|
48
|
+
# (tmem 128 -> at most 4 CTAs/SM): 8 warps at maxnreg=80 (10 spills, 3 CTAs/SM) 1.12ms beat
|
|
49
|
+
# 4 warps uncapped (255 regs, 2 CTAs) 1.27 and 8 warps at 64 (18 spills, 4 CTAs) 1.17; BV=32
|
|
50
|
+
# and KD=32 lost. The ladder reads these from KDA005_* env knobs; here they are constants.
|
|
51
|
+
MAIN_BV = 32
|
|
52
|
+
MAIN_KS = 32
|
|
53
|
+
MAIN_STAGES = 3
|
|
54
|
+
SIDE_BV = 64
|
|
55
|
+
SIDE_WARPS = 8
|
|
56
|
+
SIDE_MAXNREG = 80
|
|
57
|
+
SIDE_KD = 64 # K-slice of the side kernel's dA dot
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
@triton.jit(do_not_specialize=['T'])
|
|
61
|
+
def chunk_kda_bwd_kernel_wy_t_main(
|
|
62
|
+
q, k, v_new, g, beta, A, h, do, dh, dq, dk, dv, dg, db, dw_buf, kg_buf, dgk_buf, scale, T,
|
|
63
|
+
H: tl.constexpr, HV: tl.constexpr, K: tl.constexpr, V: tl.constexpr,
|
|
64
|
+
BT: tl.constexpr, BV: tl.constexpr, KS: tl.constexpr, STATE_V_FIRST: tl.constexpr,
|
|
65
|
+
):
|
|
66
|
+
i_t, i_bh = tl.program_id(0).to(tl.int64), tl.program_id(1)
|
|
67
|
+
i_b, i_hv = i_bh // HV, i_bh % HV
|
|
68
|
+
i_h = i_hv // (HV // H)
|
|
69
|
+
NT = tl.cdiv(T, BT)
|
|
70
|
+
i_tg = (i_b * NT + i_t).to(tl.int64)
|
|
71
|
+
bos = (i_b * T).to(tl.int64)
|
|
72
|
+
o_t = i_t * BT + tl.arange(0, BT)
|
|
73
|
+
m_t = o_t < T
|
|
74
|
+
i_last = (min(T, i_t * BT + BT) - 1).to(tl.int64)
|
|
75
|
+
m_last = o_t == i_last
|
|
76
|
+
|
|
77
|
+
q += (bos * H + i_h) * K
|
|
78
|
+
k += (bos * H + i_h) * K
|
|
79
|
+
v_new += (bos * HV + i_hv) * V
|
|
80
|
+
g += (bos * HV + i_hv) * K
|
|
81
|
+
beta += bos * HV + i_hv
|
|
82
|
+
A += (bos * HV + i_hv) * BT
|
|
83
|
+
h += (i_tg * HV + i_hv) * K*V
|
|
84
|
+
do += (bos * HV + i_hv) * V
|
|
85
|
+
dh += (i_tg * HV + i_hv) * K*V
|
|
86
|
+
dq += (bos * HV + i_hv) * K
|
|
87
|
+
dk += (bos * HV + i_hv) * K
|
|
88
|
+
dv += (bos * HV + i_hv) * V
|
|
89
|
+
dg += (bos * HV + i_hv) * K
|
|
90
|
+
db += bos * HV + i_hv
|
|
91
|
+
dw_buf += (bos * HV + i_hv) * K
|
|
92
|
+
kg_buf += (bos * HV + i_hv) * K
|
|
93
|
+
dgk_buf += (i_tg * HV + i_hv) * K
|
|
94
|
+
|
|
95
|
+
p_beta = beta + o_t * HV
|
|
96
|
+
b_beta = tl.load(p_beta, mask=m_t, other=0.0)
|
|
97
|
+
o_A = tl.arange(0, BT)
|
|
98
|
+
# b_At[t, a] = A_mem[t, a]: the B operand of dkgb^T = dw^T @ A^T (wy2's dot(A, dw)
|
|
99
|
+
# transposed; wy2's b_A is A_mem^T).
|
|
100
|
+
p_At = A + o_t[:, None] * (HV * BT) + o_A[None, :]
|
|
101
|
+
b_At = tl.load(p_At, mask=m_t[:, None] & (o_A[None, :] < BT), other=0.0)
|
|
102
|
+
|
|
103
|
+
o_k = tl.arange(0, K)
|
|
104
|
+
m_kt = (o_k < K)[:, None] & m_t[None, :]
|
|
105
|
+
|
|
106
|
+
# [do; dv] stacked along rows so one M=K dot yields [dq^T | dw^T]
|
|
107
|
+
o_n = tl.arange(0, 2 * BT)
|
|
108
|
+
o_tn = i_t * BT + (o_n % BT)
|
|
109
|
+
m_tn = o_tn < T
|
|
110
|
+
acc_qw = tl.zeros([K, 2 * BT], dtype=tl.float32)
|
|
111
|
+
acc_k = tl.zeros([K, BT], dtype=tl.float32)
|
|
112
|
+
b_db = tl.zeros([BT], dtype=tl.float32) # strips' dkgb.kg colsum; the side kernel adds dvb.v
|
|
113
|
+
dgk = tl.zeros([K], dtype=tl.float32)
|
|
114
|
+
for i_v in range(tl.cdiv(V, BV)):
|
|
115
|
+
o_v = i_v * BV + tl.arange(0, BV)
|
|
116
|
+
m_v = o_v < V
|
|
117
|
+
m_tv = m_t[:, None] & m_v[None, :]
|
|
118
|
+
if STATE_V_FIRST:
|
|
119
|
+
p_hT = h + o_k[:, None] + o_v[None, :] * K
|
|
120
|
+
p_dhT = dh + o_k[:, None] + o_v[None, :] * K
|
|
121
|
+
else:
|
|
122
|
+
p_hT = h + o_k[:, None] * V + o_v[None, :]
|
|
123
|
+
p_dhT = dh + o_k[:, None] * V + o_v[None, :]
|
|
124
|
+
b_hT = tl.load(p_hT, mask=m_v[None, :], other=0.0)
|
|
125
|
+
b_dhT = tl.load(p_dhT, mask=m_v[None, :], other=0.0)
|
|
126
|
+
p_dodv = tl.where((o_n < BT)[:, None], do + o_tn[:, None] * (HV*V) + o_v[None, :],
|
|
127
|
+
dv + o_tn[:, None] * (HV*V) + o_v[None, :])
|
|
128
|
+
b_dodv = tl.load(p_dodv, mask=m_tn[:, None] & m_v[None, :], other=0.0)
|
|
129
|
+
p_vn = v_new + o_t[:, None] * (HV*V) + o_v[None, :]
|
|
130
|
+
b_vn = tl.load(p_vn, mask=m_tv, other=0.0)
|
|
131
|
+
|
|
132
|
+
dgk += tl.sum(b_hT * b_dhT, axis=1)
|
|
133
|
+
acc_qw += tl.dot(b_hT, tl.trans(b_dodv))
|
|
134
|
+
acc_k += tl.dot(b_dhT, tl.trans(b_vn))
|
|
135
|
+
|
|
136
|
+
# Park dq^T/dk^T raw (fp32) in dq/dk, -dw^T (bf16, = wy2's b_dw) in dw_buf for the side
|
|
137
|
+
# kernel's dA dot, and dkgb^T = dw^T @ A^T (the one dot here, M=128) raw in dg.
|
|
138
|
+
acc_qw3 = tl.permute(tl.reshape(acc_qw, [K, 2, BT]), (0, 2, 1))
|
|
139
|
+
dqT_raw, dwT_raw = tl.split(acc_qw3)
|
|
140
|
+
tl.store(dq + o_t[None, :] * (HV*K) + o_k[:, None], dqT_raw, mask=m_kt)
|
|
141
|
+
tl.store(dk + o_t[None, :] * (HV*K) + o_k[:, None], acc_k, mask=m_kt)
|
|
142
|
+
tl.store(dgk_buf + o_k, dgk)
|
|
143
|
+
b_dwT = -dwT_raw.to(b_At.dtype)
|
|
144
|
+
tl.store(dw_buf + o_t[None, :] * (HV*K) + o_k[:, None], b_dwT, mask=m_kt)
|
|
145
|
+
b_dkgbT = tl.dot(b_dwT, b_At)
|
|
146
|
+
tl.store(dg + o_t[None, :] * (HV*K) + o_k[:, None], b_dkgbT, mask=m_kt)
|
|
147
|
+
tl.debug_barrier()
|
|
148
|
+
|
|
149
|
+
# K-strip epilogue over the parked raw tiles ([KS, BT], k contiguous). Dot-free: every
|
|
150
|
+
# strip is loads -> elementwise -> stores, so the live set stays under 128 regs.
|
|
151
|
+
for s in tl.range(0, K // KS, num_stages=1):
|
|
152
|
+
o_ks = s * KS + tl.arange(0, KS)
|
|
153
|
+
m_kst = (o_ks < K)[:, None] & m_t[None, :]
|
|
154
|
+
off_tk = o_t[None, :] * (HV*K) + o_ks[:, None]
|
|
155
|
+
b_gn = tl.load(g + i_last * HV*K + o_ks).to(tl.float32)
|
|
156
|
+
b_gT = tl.load(g + off_tk, mask=m_kst, other=0.0).to(tl.float32)
|
|
157
|
+
b_kT = tl.load(k + o_t[None, :] * (H*K) + o_ks[:, None], mask=m_kst, other=0.0)
|
|
158
|
+
b_qT = tl.load(q + o_t[None, :] * (H*K) + o_ks[:, None], mask=m_kst, other=0.0)
|
|
159
|
+
b_dqT = tl.load(dq + off_tk, mask=m_kst, other=0.0)
|
|
160
|
+
b_dkT = tl.load(dk + off_tk, mask=m_kst, other=0.0)
|
|
161
|
+
b_dkgb_s = tl.load(dg + off_tk, mask=m_kst, other=0.0)
|
|
162
|
+
b_dgk = tl.load(dgk_buf + o_ks)
|
|
163
|
+
|
|
164
|
+
# wy2's epilogue, transposed ([k, t] tiles), same one-sided exp2 forms
|
|
165
|
+
b_e = exp2(b_gT)
|
|
166
|
+
b_dqT = b_dqT * b_e * scale
|
|
167
|
+
b_dkT = b_dkT * tl.where(m_t[None, :], exp2(b_gn[:, None] - b_gT), 0)
|
|
168
|
+
b_kg_s = b_kT * b_e
|
|
169
|
+
tl.store(kg_buf + off_tk, b_kg_s.to(kg_buf.dtype.element_ty), mask=m_kst) # side's dA operand
|
|
170
|
+
b_db += tl.sum(b_dkgb_s * b_kg_s, 0)
|
|
171
|
+
b_kdkT = b_kT * b_dkT
|
|
172
|
+
b_dgk = b_dgk * exp2(b_gn) + tl.sum(b_kdkT, axis=1)
|
|
173
|
+
b_dgT = (b_qT * b_dqT - b_kdkT + m_last[None, :] * b_dgk[:, None]
|
|
174
|
+
+ b_kg_s * b_dkgb_s * b_beta[None, :])
|
|
175
|
+
b_dkT = b_dkT + b_dkgb_s * (b_e * b_beta[None, :])
|
|
176
|
+
tl.store(dq + off_tk, b_dqT.to(dq.dtype.element_ty), mask=m_kst)
|
|
177
|
+
tl.store(dk + off_tk, b_dkT.to(dk.dtype.element_ty), mask=m_kst)
|
|
178
|
+
tl.store(dg + off_tk, b_dgT.to(dg.dtype.element_ty), mask=m_kst)
|
|
179
|
+
|
|
180
|
+
tl.store(db + o_t * HV, b_db.to(db.dtype.element_ty), mask=m_t) # partial; side adds
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
@triton.jit(do_not_specialize=['T'])
|
|
184
|
+
def chunk_kda_bwd_kernel_wy_t_side(
|
|
185
|
+
v, beta, A, dv, dw_buf, kg_buf, dv2, db, dA, T,
|
|
186
|
+
HV: tl.constexpr, K: tl.constexpr, V: tl.constexpr, BT: tl.constexpr, BV: tl.constexpr,
|
|
187
|
+
KD: tl.constexpr,
|
|
188
|
+
):
|
|
189
|
+
"""The v-side of wy2's loop plus its dA path: dv2 = (A @ dv)*beta,
|
|
190
|
+
db += rowsum((A @ dv) * v), dA = chain(dv @ v^T + dw @ kg^T) with dw and kg read from
|
|
191
|
+
the main kernel's buffers and the two dots accumulated in wy2's order into one
|
|
192
|
+
accumulator. db is finalized IN PLACE on the main kernel's partial: launch after it,
|
|
193
|
+
never autotune it."""
|
|
194
|
+
i_t, i_bh = tl.program_id(0).to(tl.int64), tl.program_id(1)
|
|
195
|
+
i_b, i_hv = i_bh // HV, i_bh % HV
|
|
196
|
+
bos = (i_b * T).to(tl.int64)
|
|
197
|
+
o_t = i_t * BT + tl.arange(0, BT)
|
|
198
|
+
m_t = o_t < T
|
|
199
|
+
o_A = tl.arange(0, BT)
|
|
200
|
+
dw_buf += (bos * HV + i_hv) * K
|
|
201
|
+
kg_buf += (bos * HV + i_hv) * K
|
|
202
|
+
v += (bos * HV + i_hv) * V
|
|
203
|
+
dv += (bos * HV + i_hv) * V
|
|
204
|
+
dv2 += (bos * HV + i_hv) * V
|
|
205
|
+
beta += bos * HV + i_hv
|
|
206
|
+
A += (bos * HV + i_hv) * BT
|
|
207
|
+
dA += (bos * HV + i_hv) * BT
|
|
208
|
+
db += bos * HV + i_hv
|
|
209
|
+
b_beta = tl.load(beta + o_t * HV, mask=m_t, other=0.0)
|
|
210
|
+
p_A = A + o_A[:, None] + o_t[None, :] * (HV * BT) # wy2's b_A = A_mem^T
|
|
211
|
+
b_A = tl.load(p_A, mask=(o_A[:, None] < BT) & m_t[None, :], other=0.0)
|
|
212
|
+
b_dA = tl.zeros([BT, BT], dtype=tl.float32)
|
|
213
|
+
b_db = tl.zeros([BT], dtype=tl.float32)
|
|
214
|
+
for i_v in range(tl.cdiv(V, BV)):
|
|
215
|
+
o_v = i_v * BV + tl.arange(0, BV)
|
|
216
|
+
m_tv = m_t[:, None] & (o_v[None, :] < V)
|
|
217
|
+
b_v = tl.load(v + o_t[:, None] * (HV*V) + o_v[None, :], mask=m_tv, other=0.0)
|
|
218
|
+
b_dv = tl.load(dv + o_t[:, None] * (HV*V) + o_v[None, :], mask=m_tv, other=0.0)
|
|
219
|
+
b_dA += tl.dot(b_dv, tl.trans(b_v))
|
|
220
|
+
b_dvb = tl.dot(b_A, b_dv)
|
|
221
|
+
b_db += tl.sum(b_dvb * b_v, 1)
|
|
222
|
+
p_dv2 = dv2 + o_t[:, None] * (HV*V) + o_v[None, :]
|
|
223
|
+
tl.store(p_dv2, (b_dvb * b_beta[:, None]).to(p_dv2.dtype.element_ty), mask=m_tv)
|
|
224
|
+
# wy2's epilogue dot, into the same accumulator: b_dw / b_kg are wy2's -dw.bf16 and
|
|
225
|
+
# kg.bf16 bit-for-bit (the transposed loop's dw matched wy2's), so dA's chain sees
|
|
226
|
+
# wy2's fp32 sum. In K-slices of KD so the operands never sit in registers whole
|
|
227
|
+
# (hoisting the full [64,128] pair above the loop cost 2 CTAs/SM).
|
|
228
|
+
for s in tl.static_range(K // KD):
|
|
229
|
+
o_k = s * KD + tl.arange(0, KD)
|
|
230
|
+
m_tk = m_t[:, None] & (o_k < K)[None, :]
|
|
231
|
+
b_dw = tl.load(dw_buf + o_t[:, None] * (HV*K) + o_k[None, :], mask=m_tk, other=0.0)
|
|
232
|
+
b_kg = tl.load(kg_buf + o_t[:, None] * (HV*K) + o_k[None, :], mask=m_tk, other=0.0)
|
|
233
|
+
b_dA += tl.dot(b_dw, tl.trans(b_kg))
|
|
234
|
+
m_dA = m_t[:, None] & (o_A[None, :] < BT)
|
|
235
|
+
p_dA = dA + o_t[:, None] * (HV * BT) + o_A[None, :]
|
|
236
|
+
m_A = (o_t[:, None] > o_t[None, :]) & (m_t[:, None] & m_t)
|
|
237
|
+
b_dA = tl.where(m_A, b_dA * b_beta[None, :], 0)
|
|
238
|
+
b_dA = tl.dot(b_dA.to(b_A.dtype), b_A)
|
|
239
|
+
b_dA = tl.dot(b_A, b_dA.to(b_A.dtype))
|
|
240
|
+
b_dA = tl.where(m_A, -b_dA, 0)
|
|
241
|
+
tl.store(p_dA, b_dA.to(p_dA.dtype.element_ty), mask=m_dA)
|
|
242
|
+
p_db = db + o_t * HV
|
|
243
|
+
b_db += tl.load(p_db, mask=m_t, other=0.0)
|
|
244
|
+
tl.store(p_db, b_db.to(p_db.dtype.element_ty), mask=m_t)
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
def _supported(K: int, V: int, BT: int, ctas: int) -> bool:
|
|
248
|
+
"""M=K=128 tcgen05 tiles need K=128; V must tile by both BVs; below the CTA floor a
|
|
249
|
+
grid-starved launch loses to wy2's autotuned kernel, so those shapes stay on it."""
|
|
250
|
+
if K != 128 or V % max(MAIN_BV, SIDE_BV) != 0 or BT != 64:
|
|
251
|
+
return False
|
|
252
|
+
return ctas >= MIN_CTAS
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
def chunk_kda_bwd_wy_dqkg_t(
|
|
256
|
+
q: torch.Tensor,
|
|
257
|
+
k: torch.Tensor,
|
|
258
|
+
v: torch.Tensor,
|
|
259
|
+
v_new: torch.Tensor,
|
|
260
|
+
g: torch.Tensor,
|
|
261
|
+
beta: torch.Tensor,
|
|
262
|
+
A: torch.Tensor,
|
|
263
|
+
h: torch.Tensor,
|
|
264
|
+
do: torch.Tensor,
|
|
265
|
+
dh: torch.Tensor,
|
|
266
|
+
dv: torch.Tensor,
|
|
267
|
+
scale: float | None = None,
|
|
268
|
+
state_v_first: bool = False,
|
|
269
|
+
chunk_size: int = 64,
|
|
270
|
+
):
|
|
271
|
+
"""Drop-in for bwd_wy.chunk_kda_bwd_wy_dqkg_wide: returns (dq, dk, dv2, db, dg, dA).
|
|
272
|
+
|
|
273
|
+
Off the supported shape or below the CTA floor it IS that function."""
|
|
274
|
+
B, T, H, K, HV, V = *k.shape, v.shape[2], v.shape[-1]
|
|
275
|
+
BT = chunk_size
|
|
276
|
+
NT = triton.cdiv(T, BT)
|
|
277
|
+
if not _supported(K, V, BT, NT * B * HV):
|
|
278
|
+
return chunk_kda_bwd_wy_dqkg_wide(
|
|
279
|
+
q=q, k=k, v=v, v_new=v_new, g=g, beta=beta, A=A, h=h, do=do, dh=dh, dv=dv,
|
|
280
|
+
scale=scale, state_v_first=state_v_first, chunk_size=chunk_size,
|
|
281
|
+
)
|
|
282
|
+
if scale is None:
|
|
283
|
+
scale = K ** -0.5
|
|
284
|
+
|
|
285
|
+
# dq, dk are allocated at HV; the caller reduces to H if GVA. dq/dk/dg double as the
|
|
286
|
+
# parking space for the raw transposed accumulators (fp32, overwritten in place).
|
|
287
|
+
dq = g.new_empty(B, T, HV, K, dtype=torch.float)
|
|
288
|
+
dk = g.new_empty(B, T, HV, K, dtype=torch.float)
|
|
289
|
+
dg = g.new_empty(B, T, HV, K, dtype=torch.float)
|
|
290
|
+
dv2 = torch.empty_like(v)
|
|
291
|
+
db = torch.empty(B, T, HV, device=g.device, dtype=torch.float)
|
|
292
|
+
dA = torch.empty_like(A, dtype=torch.float)
|
|
293
|
+
dgk_buf = torch.empty(B, NT, HV, K, device=g.device, dtype=torch.float)
|
|
294
|
+
# main -> side hand-off: -dw.bf16 and kg.bf16, the dA dot's operands
|
|
295
|
+
dw_buf = torch.empty(B, T, HV, K, device=g.device, dtype=A.dtype)
|
|
296
|
+
kg_buf = torch.empty(B, T, HV, K, device=g.device, dtype=A.dtype)
|
|
297
|
+
|
|
298
|
+
grid = (NT, B * HV)
|
|
299
|
+
chunk_kda_bwd_kernel_wy_t_main[grid](
|
|
300
|
+
q, k, v_new, g, beta, A, h, do, dh, dq, dk, dv, dg, db, dw_buf, kg_buf, dgk_buf, scale, T,
|
|
301
|
+
H=H, HV=HV, K=K, V=V, BT=BT, BV=MAIN_BV, KS=MAIN_KS, STATE_V_FIRST=state_v_first,
|
|
302
|
+
num_warps=8, num_stages=MAIN_STAGES, maxnreg=128,
|
|
303
|
+
)
|
|
304
|
+
chunk_kda_bwd_kernel_wy_t_side[grid](
|
|
305
|
+
v, beta, A, dv, dw_buf, kg_buf, dv2, db, dA, T,
|
|
306
|
+
HV=HV, K=K, V=V, BT=BT, BV=SIDE_BV, KD=SIDE_KD,
|
|
307
|
+
num_warps=SIDE_WARPS, num_stages=3, maxnreg=SIDE_MAXNREG,
|
|
308
|
+
)
|
|
309
|
+
return dq, dk, dv2, db, dg, dA
|