Kernels
3v324v23 Claude Opus 4.6 (1M context) commited on
Commit
7e86d2e
·
1 Parent(s): 35a25ee

cleanup: drop k_pe RoPE custom kernel (caller uses PyTorch native)

Browse files

Remove _rope_fwd_kernel, _rope_bwd_kernel, their custom_op wrappers,
FusedRoPE autograd class, and the fused_apply_rope user-facing wrapper
(plus the activation.__init__ export).

Rationale: k_pe in MLA is head-shared (H=1), so the kernel handles a
trivially small problem (B*S*1*rope_dim ≈ 8 MB at B=8, S=4096).
At that size the kernel is launch-bound, peak HBM utilization sits
around 5%, and a standalone Triton launch costs more than letting
inductor inline the work into the surrounding graph. The caller now
uses the same view_as_complex + reorder_headdim_elements_rope path
as the upstream PyTorch implementation.

Verified at B=8, S=4096, n_layers=4 on B200: step-10 loss matches
(0.0001), aggregate RoPE-path savings vs vanilla unchanged at -68%.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

torch-ext/activation/__init__.py CHANGED
@@ -3,7 +3,6 @@ import torch
3
  from . import layers, parallel_style
4
  from ._ops import ops
5
  from .fused_rope import (
6
- fused_apply_rope,
7
  fused_kv_split_rope_cat,
8
  fused_q_rope_inplace,
9
  )
@@ -54,7 +53,6 @@ __all__ = [
54
  "fused_mul_grouped_poly_norm",
55
  "rms_norm",
56
  "fused_add_rms_norm",
57
- "fused_apply_rope",
58
  "fused_q_rope_inplace",
59
  "fused_kv_split_rope_cat",
60
  "layers",
 
3
  from . import layers, parallel_style
4
  from ._ops import ops
5
  from .fused_rope import (
 
6
  fused_kv_split_rope_cat,
7
  fused_q_rope_inplace,
8
  )
 
53
  "fused_mul_grouped_poly_norm",
54
  "rms_norm",
55
  "fused_add_rms_norm",
 
56
  "fused_q_rope_inplace",
57
  "fused_kv_split_rope_cat",
58
  "layers",
torch-ext/activation/fused_rope.py CHANGED
@@ -27,207 +27,12 @@ import triton.language as tl
27
  # ---------------------------------------------------------------------------
28
 
29
  # BLOCK_H, num_warps, num_stages per kernel. H values denote head_num
30
- # (head-shared k_pe is H=1; per-head q/kv is H=80 in motif3_seq).
31
- _CFG_ROPE_FWD = dict(BLOCK_H=1, num_warps=1, num_stages=3) # k_pe fwd, H=1
32
- _CFG_ROPE_BWD = dict(BLOCK_H=2, num_warps=1, num_stages=1) # k_pe bwd, H=1
33
  _CFG_KV_ROPE_FWD = dict(BLOCK_H=32, num_warps=8, num_stages=2) # kv fused, H=80
 
34
  _CFG_Q_ROPE_INPLACE_FWD = dict(BLOCK_H=8, num_warps=1, num_stages=2) # q in-place, H=80
35
  _CFG_Q_ROPE_BWD = dict(BLOCK_H=4, num_warps=2, num_stages=2) # q bwd, H=80
36
- _CFG_KV_ROPE_BWD = dict(BLOCK_H=16, num_warps=4, num_stages=2) # kv bwd; BLOCK_H must cover H_kv=16 (single program per token)
37
-
38
 
39
- # ---------------------------------------------------------------------------
40
- # Triton kernels
41
- # ---------------------------------------------------------------------------
42
-
43
- @triton.jit
44
- def _rope_fwd_kernel(
45
- X, # [B*S, H, rope_dim] interleaved
46
- O, # [B*S, H, rope_dim] contiguous
47
- COS, # [S, rope_dim // 2]
48
- SIN, # [S, rope_dim // 2]
49
- rope_dim: tl.constexpr,
50
- head_num: tl.constexpr,
51
- seq_len,
52
- stride_x_token,
53
- stride_x_head,
54
- stride_o_token,
55
- stride_o_head,
56
- BLOCK_H: tl.constexpr,
57
- ):
58
- HALF: tl.constexpr = rope_dim // 2
59
-
60
- pid_token = tl.program_id(0)
61
- pid_hblock = tl.program_id(1)
62
-
63
- pos = pid_token % seq_len
64
- cos = tl.load(COS + pos * HALF + tl.arange(0, HALF))
65
- sin = tl.load(SIN + pos * HALF + tl.arange(0, HALF))
66
- cos = cos.expand_dims(0).broadcast_to(BLOCK_H, HALF)
67
- sin = sin.expand_dims(0).broadcast_to(BLOCK_H, HALF)
68
-
69
- X_ptr = X + pid_token * stride_x_token + pid_hblock * BLOCK_H * stride_x_head
70
- O_ptr = O + pid_token * stride_o_token + pid_hblock * BLOCK_H * stride_o_head
71
-
72
- h_off = tl.arange(0, BLOCK_H)[:, None]
73
- mask = (pid_hblock * BLOCK_H + h_off) < head_num
74
-
75
- x1_off = h_off * stride_x_head + tl.arange(0, HALF)[None, :] * 2
76
- x2_off = x1_off + 1
77
- x1 = tl.load(X_ptr + x1_off, mask=mask).to(tl.float32)
78
- x2 = tl.load(X_ptr + x2_off, mask=mask).to(tl.float32)
79
-
80
- out_real = x1 * cos - x2 * sin
81
- out_imag = x1 * sin + x2 * cos
82
-
83
- real_off = h_off * stride_o_head + tl.arange(0, HALF)[None, :]
84
- imag_off = real_off + HALF
85
- tl.store(O_ptr + real_off, out_real, mask=mask)
86
- tl.store(O_ptr + imag_off, out_imag, mask=mask)
87
-
88
-
89
- @triton.jit
90
- def _rope_bwd_kernel(
91
- DO, # [B*S, H, rope_dim] contiguous
92
- DX, # [B*S, H, rope_dim] interleaved
93
- COS, # [S, rope_dim // 2]
94
- SIN, # [S, rope_dim // 2]
95
- rope_dim: tl.constexpr,
96
- head_num: tl.constexpr,
97
- seq_len,
98
- stride_do_token,
99
- stride_do_head,
100
- stride_dx_token,
101
- stride_dx_head,
102
- BLOCK_H: tl.constexpr,
103
- ):
104
- HALF: tl.constexpr = rope_dim // 2
105
-
106
- pid_token = tl.program_id(0)
107
- pid_hblock = tl.program_id(1)
108
-
109
- pos = pid_token % seq_len
110
- cos = tl.load(COS + pos * HALF + tl.arange(0, HALF))
111
- sin = tl.load(SIN + pos * HALF + tl.arange(0, HALF))
112
- cos = cos.expand_dims(0).broadcast_to(BLOCK_H, HALF)
113
- sin = sin.expand_dims(0).broadcast_to(BLOCK_H, HALF)
114
-
115
- DO_ptr = DO + pid_token * stride_do_token + pid_hblock * BLOCK_H * stride_do_head
116
- DX_ptr = DX + pid_token * stride_dx_token + pid_hblock * BLOCK_H * stride_dx_head
117
-
118
- h_off = tl.arange(0, BLOCK_H)[:, None]
119
- mask = (pid_hblock * BLOCK_H + h_off) < head_num
120
-
121
- real_off = h_off * stride_do_head + tl.arange(0, HALF)[None, :]
122
- imag_off = real_off + HALF
123
- d_real = tl.load(DO_ptr + real_off, mask=mask).to(tl.float32)
124
- d_imag = tl.load(DO_ptr + imag_off, mask=mask).to(tl.float32)
125
-
126
- dx1 = d_real * cos + d_imag * sin
127
- dx2 = -d_real * sin + d_imag * cos
128
-
129
- x1_off = h_off * stride_dx_head + tl.arange(0, HALF)[None, :] * 2
130
- x2_off = x1_off + 1
131
- tl.store(DX_ptr + x1_off, dx1, mask=mask)
132
- tl.store(DX_ptr + x2_off, dx2, mask=mask)
133
-
134
-
135
- # ---------------------------------------------------------------------------
136
- # custom_op registration (no graph break under torch.compile)
137
- # ---------------------------------------------------------------------------
138
-
139
- @torch.library.custom_op("motif::rope_fwd", mutates_args=())
140
- def _rope_fwd(
141
- x: torch.Tensor,
142
- cos: torch.Tensor,
143
- sin: torch.Tensor,
144
- seq_len: int,
145
- ) -> torch.Tensor:
146
- assert x.stride(-1) == 1, "fused_rope kernel requires last-dim unit stride"
147
- B_S, H, D = x.shape
148
- out = torch.empty_like(x)
149
- grid = lambda META: (B_S, triton.cdiv(H, META["BLOCK_H"]))
150
- _rope_fwd_kernel[grid](
151
- x, out, cos, sin,
152
- D, H, seq_len,
153
- x.stride(0), x.stride(1),
154
- out.stride(0), out.stride(1),
155
- **_CFG_ROPE_FWD,
156
- )
157
- return out
158
-
159
-
160
- @_rope_fwd.register_fake
161
- def _rope_fwd_fake(x, cos, sin, seq_len):
162
- return torch.empty_like(x)
163
-
164
-
165
- @torch.library.custom_op("motif::rope_bwd", mutates_args=())
166
- def _rope_bwd(
167
- grad_out: torch.Tensor,
168
- cos: torch.Tensor,
169
- sin: torch.Tensor,
170
- seq_len: int,
171
- ) -> torch.Tensor:
172
- assert grad_out.stride(-1) == 1, "fused_rope kernel requires last-dim unit stride"
173
- B_S, H, D = grad_out.shape
174
- grad_in = torch.empty_like(grad_out)
175
- grid = lambda META: (B_S, triton.cdiv(H, META["BLOCK_H"]))
176
- _rope_bwd_kernel[grid](
177
- grad_out, grad_in, cos, sin,
178
- D, H, seq_len,
179
- grad_out.stride(0), grad_out.stride(1),
180
- grad_in.stride(0), grad_in.stride(1),
181
- **_CFG_ROPE_BWD,
182
- )
183
- return grad_in
184
-
185
-
186
- @_rope_bwd.register_fake
187
- def _rope_bwd_fake(grad_out, cos, sin, seq_len):
188
- return torch.empty_like(grad_out)
189
-
190
-
191
- # ---------------------------------------------------------------------------
192
- # Autograd wrapper
193
- # ---------------------------------------------------------------------------
194
-
195
- class FusedRoPE(torch.autograd.Function):
196
- @staticmethod
197
- def forward(ctx, x, cos, sin, seq_len):
198
- # x: [B, S, H, rope_dim] interleaved
199
- B, S, H, D = x.shape
200
- out_3d = _rope_fwd(x.reshape(B * S, H, D), cos, sin, seq_len)
201
- ctx.save_for_backward(cos, sin)
202
- ctx.seq_len = seq_len
203
- ctx.shape = (B, S, H, D)
204
- return out_3d.view(B, S, H, D)
205
-
206
- @staticmethod
207
- def backward(ctx, grad_out):
208
- cos, sin = ctx.saved_tensors
209
- B, S, H, D = ctx.shape
210
- grad_in_3d = _rope_bwd(
211
- grad_out.contiguous().reshape(B * S, H, D),
212
- cos, sin, ctx.seq_len,
213
- )
214
- return grad_in_3d.view(B, S, H, D), None, None, None
215
-
216
-
217
- def fused_apply_rope(x, freqs_cis):
218
- """Apply fused RoPE, outputting contiguous format. No graph break.
219
-
220
- Args:
221
- x: [B, S, H, rope_dim] interleaved format
222
- freqs_cis: [max_seq_len, rope_dim//2] complex64
223
-
224
- Returns:
225
- [B, S, H, rope_dim] contiguous format [real..., imag...]
226
- """
227
- S = x.shape[1]
228
- cos = freqs_cis[:S].real.contiguous()
229
- sin = freqs_cis[:S].imag.contiguous()
230
- return FusedRoPE.apply(x, cos, sin, S)
231
 
232
 
233
  # ---------------------------------------------------------------------------
 
27
  # ---------------------------------------------------------------------------
28
 
29
  # BLOCK_H, num_warps, num_stages per kernel. H values denote head_num
30
+ # (q is per-head H=80, kv fused expands head-shared k_pe to H_kv=16 in motif3_seq).
 
 
31
  _CFG_KV_ROPE_FWD = dict(BLOCK_H=32, num_warps=8, num_stages=2) # kv fused, H=80
32
+ _CFG_KV_ROPE_BWD = dict(BLOCK_H=16, num_warps=4, num_stages=2) # kv bwd; BLOCK_H must cover H_kv=16 (single program per token)
33
  _CFG_Q_ROPE_INPLACE_FWD = dict(BLOCK_H=8, num_warps=1, num_stages=2) # q in-place, H=80
34
  _CFG_Q_ROPE_BWD = dict(BLOCK_H=4, num_warps=2, num_stages=2) # q bwd, H=80
 
 
35
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
36
 
37
 
38
  # ---------------------------------------------------------------------------