flashmap 0.1.0__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.
- flashmap/__init__.py +14 -0
- flashmap/configuration_flashmap.py +105 -0
- flashmap/modeling_flashmap.py +556 -0
- flashmap/sglang/__init__.py +78 -0
- flashmap/sglang/jump_bank.py +203 -0
- flashmap/sglang/kernels.py +457 -0
- flashmap/sglang/model.py +1076 -0
- flashmap/sglang/sampler.py +188 -0
- flashmap/sglang/tree.py +78 -0
- flashmap/sglang/tree_worker.py +396 -0
- flashmap/sglang/verify_attention.py +474 -0
- flashmap/vllm/__init__.py +268 -0
- flashmap/vllm/fold.py +128 -0
- flashmap/vllm/kernels.py +443 -0
- flashmap/vllm/model.py +881 -0
- flashmap/vllm/speculator.py +235 -0
- flashmap-0.1.0.dist-info/METADATA +152 -0
- flashmap-0.1.0.dist-info/RECORD +22 -0
- flashmap-0.1.0.dist-info/WHEEL +5 -0
- flashmap-0.1.0.dist-info/entry_points.txt +5 -0
- flashmap-0.1.0.dist-info/licenses/LICENSE +21 -0
- flashmap-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,457 @@
|
|
|
1
|
+
# coding=utf-8
|
|
2
|
+
"""Triton kernels of the FlashMap sglang draft (see :mod:`.model`).
|
|
3
|
+
|
|
4
|
+
They reproduce the rounding of the reference modules they replace: the RMSNorm
|
|
5
|
+
multiplies the bf16 input by its bf16-rounded scale ``weight * rsqrt(mean(x^2) +
|
|
6
|
+
eps)``, and the interleaved-pair RoPE rotates in float32 and rounds back to
|
|
7
|
+
bf16. The ops are registered as custom ops so torch.compile keeps them whole
|
|
8
|
+
inside the draft step it compiles.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import torch
|
|
14
|
+
import triton
|
|
15
|
+
import triton.language as tl
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@triton.jit
|
|
19
|
+
def _block_self_attention_kernel(
|
|
20
|
+
qkv_ptr,
|
|
21
|
+
q_norm_ptr,
|
|
22
|
+
k_norm_ptr,
|
|
23
|
+
freqs_ptr,
|
|
24
|
+
out_ptr,
|
|
25
|
+
eps,
|
|
26
|
+
sm_scale,
|
|
27
|
+
T: tl.constexpr,
|
|
28
|
+
H: tl.constexpr,
|
|
29
|
+
D: tl.constexpr,
|
|
30
|
+
):
|
|
31
|
+
# One program per (block, head): the block's T queries attend to its T keys.
|
|
32
|
+
b = tl.program_id(0) // H
|
|
33
|
+
h = tl.program_id(0) % H
|
|
34
|
+
HALF: tl.constexpr = D // 2
|
|
35
|
+
t = tl.arange(0, T)[:, None]
|
|
36
|
+
i = tl.arange(0, HALF)[None, :]
|
|
37
|
+
d = tl.arange(0, D)[None, :]
|
|
38
|
+
# qkv is (blocks, T, 3, H, D); RoPE pairs dims (2i, 2i + 1).
|
|
39
|
+
row = qkv_ptr + (b * T + t) * (3 * H * D) + h * D
|
|
40
|
+
freqs = freqs_ptr + (b * T + t) * D + 2 * i # (blocks, T, D / 2, [cos, sin])
|
|
41
|
+
cos = tl.load(freqs)
|
|
42
|
+
sin = tl.load(freqs + 1)
|
|
43
|
+
|
|
44
|
+
q_even = tl.load(row + 2 * i).to(tl.float32)
|
|
45
|
+
q_odd = tl.load(row + 2 * i + 1).to(tl.float32)
|
|
46
|
+
k_even = tl.load(row + H * D + 2 * i).to(tl.float32)
|
|
47
|
+
k_odd = tl.load(row + H * D + 2 * i + 1).to(tl.float32)
|
|
48
|
+
|
|
49
|
+
# RMSNorm: x * bf16(weight * rsqrt(mean(x^2) + eps)), in bf16.
|
|
50
|
+
q_rms = tl.rsqrt((tl.sum(q_even * q_even, 1) + tl.sum(q_odd * q_odd, 1)) / D + eps)[
|
|
51
|
+
:, None
|
|
52
|
+
]
|
|
53
|
+
k_rms = tl.rsqrt((tl.sum(k_even * k_even, 1) + tl.sum(k_odd * k_odd, 1)) / D + eps)[
|
|
54
|
+
:, None
|
|
55
|
+
]
|
|
56
|
+
q_even = _scale(q_even, tl.load(q_norm_ptr + 2 * i) * q_rms)
|
|
57
|
+
q_odd = _scale(q_odd, tl.load(q_norm_ptr + 2 * i + 1) * q_rms)
|
|
58
|
+
k_even = _scale(k_even, tl.load(k_norm_ptr + 2 * i) * k_rms)
|
|
59
|
+
k_odd = _scale(k_odd, tl.load(k_norm_ptr + 2 * i + 1) * k_rms)
|
|
60
|
+
|
|
61
|
+
# RoPE, rounded to bf16 as the attention receives it.
|
|
62
|
+
q_rot_even = (q_even * cos - q_odd * sin).to(tl.bfloat16)
|
|
63
|
+
q_rot_odd = (q_odd * cos + q_even * sin).to(tl.bfloat16)
|
|
64
|
+
k_rot_even = (k_even * cos - k_odd * sin).to(tl.bfloat16)
|
|
65
|
+
k_rot_odd = (k_odd * cos + k_even * sin).to(tl.bfloat16)
|
|
66
|
+
|
|
67
|
+
scores = tl.dot(q_rot_even, tl.trans(k_rot_even)) + tl.dot(
|
|
68
|
+
q_rot_odd, tl.trans(k_rot_odd)
|
|
69
|
+
)
|
|
70
|
+
scores = scores * sm_scale
|
|
71
|
+
probs = tl.exp(scores - tl.max(scores, 1)[:, None])
|
|
72
|
+
denom = tl.sum(probs, 1)[:, None]
|
|
73
|
+
values = tl.load(row + 2 * H * D + d)
|
|
74
|
+
out = tl.dot(probs.to(tl.bfloat16), values) / denom
|
|
75
|
+
tl.store(out_ptr + (b * T + t) * (H * D) + h * D + d, out.to(tl.bfloat16))
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
@triton.jit
|
|
79
|
+
def _scale(x, scale):
|
|
80
|
+
"""``x`` (bf16 values in float32) times the bf16-rounded ``scale``, in bf16."""
|
|
81
|
+
return (x * scale.to(tl.bfloat16).to(tl.float32)).to(tl.bfloat16).to(tl.float32)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
@torch.library.custom_op("flashmap_sglang::block_self_attention", mutates_args=())
|
|
85
|
+
def block_self_attention(
|
|
86
|
+
qkv: torch.Tensor,
|
|
87
|
+
q_norm: torch.Tensor,
|
|
88
|
+
k_norm: torch.Tensor,
|
|
89
|
+
freqs: torch.Tensor,
|
|
90
|
+
eps: float,
|
|
91
|
+
) -> torch.Tensor:
|
|
92
|
+
"""Self-attention within each draft block, from its projected ``qkv
|
|
93
|
+
(blocks, T, 3, heads, head_dim)``: q/k RMSNorms with float32 weights, RoPE
|
|
94
|
+
by ``freqs (blocks, T, head_dim / 2, [cos, sin])``, then unmasked softmax
|
|
95
|
+
attention. Returns ``(blocks, T, heads, head_dim)``."""
|
|
96
|
+
blocks, block_size, _, heads, head_dim = qkv.shape
|
|
97
|
+
assert qkv.is_contiguous() and freqs.is_contiguous()
|
|
98
|
+
assert freqs.dtype == torch.float32
|
|
99
|
+
out = torch.empty(
|
|
100
|
+
(blocks, block_size, heads, head_dim), dtype=qkv.dtype, device=qkv.device
|
|
101
|
+
)
|
|
102
|
+
_block_self_attention_kernel[(blocks * heads,)](
|
|
103
|
+
qkv,
|
|
104
|
+
q_norm,
|
|
105
|
+
k_norm,
|
|
106
|
+
freqs,
|
|
107
|
+
out,
|
|
108
|
+
eps,
|
|
109
|
+
head_dim**-0.5,
|
|
110
|
+
T=block_size,
|
|
111
|
+
H=heads,
|
|
112
|
+
D=head_dim,
|
|
113
|
+
)
|
|
114
|
+
return out
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
@block_self_attention.register_fake
|
|
118
|
+
def _(qkv, q_norm, k_norm, freqs, eps):
|
|
119
|
+
blocks, block_size, _, heads, head_dim = qkv.shape
|
|
120
|
+
return qkv.new_empty((blocks, block_size, heads, head_dim))
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
@triton.jit
|
|
124
|
+
def _rms_norm_kernel(x_ptr, weight_ptr, out_ptr, n_cols, eps, BLOCK: tl.constexpr):
|
|
125
|
+
row = tl.program_id(0).to(tl.int64)
|
|
126
|
+
cols = tl.arange(0, BLOCK)
|
|
127
|
+
mask = cols < n_cols
|
|
128
|
+
x = tl.load(x_ptr + row * n_cols + cols, mask=mask, other=0.0).to(tl.float32)
|
|
129
|
+
rms = tl.rsqrt(tl.sum(x * x, 0) / n_cols + eps)
|
|
130
|
+
scale = tl.load(weight_ptr + cols, mask=mask, other=0.0) * rms
|
|
131
|
+
scale = scale.to(tl.bfloat16).to(tl.float32)
|
|
132
|
+
tl.store(out_ptr + row * n_cols + cols, (x * scale).to(tl.bfloat16), mask=mask)
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def rms_norm(x: torch.Tensor, weight: torch.Tensor, eps: float) -> torch.Tensor:
|
|
136
|
+
"""RMSNorm of a contiguous bf16 ``x (..., C)`` with a float32 ``weight``,
|
|
137
|
+
in one launch."""
|
|
138
|
+
assert x.dtype == torch.bfloat16 and x.is_contiguous()
|
|
139
|
+
assert weight.dtype == torch.float32
|
|
140
|
+
out = torch.empty_like(x)
|
|
141
|
+
n_cols = x.shape[-1]
|
|
142
|
+
if x.numel():
|
|
143
|
+
_rms_norm_kernel[(x.numel() // n_cols,)](
|
|
144
|
+
x, weight, out, n_cols, eps, BLOCK=triton.next_power_of_2(n_cols)
|
|
145
|
+
)
|
|
146
|
+
return out
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
@triton.jit
|
|
150
|
+
def _rms_norm_rope_kernel(
|
|
151
|
+
x_ptr, weight_ptr, freqs_ptr, out_ptr, eps, H: tl.constexpr, D: tl.constexpr
|
|
152
|
+
):
|
|
153
|
+
# One program per (token, head) of a contiguous (tokens, H, D) input.
|
|
154
|
+
token = tl.program_id(0) // H
|
|
155
|
+
head = tl.program_id(0) % H
|
|
156
|
+
HALF: tl.constexpr = D // 2
|
|
157
|
+
i = tl.arange(0, HALF)
|
|
158
|
+
row = x_ptr + (token * H + head) * D
|
|
159
|
+
even = tl.load(row + 2 * i).to(tl.float32)
|
|
160
|
+
odd = tl.load(row + 2 * i + 1).to(tl.float32)
|
|
161
|
+
rms = tl.rsqrt((tl.sum(even * even, 0) + tl.sum(odd * odd, 0)) / D + eps)
|
|
162
|
+
even = _scale(even, tl.load(weight_ptr + 2 * i) * rms)
|
|
163
|
+
odd = _scale(odd, tl.load(weight_ptr + 2 * i + 1) * rms)
|
|
164
|
+
cos = tl.load(freqs_ptr + token * D + 2 * i)
|
|
165
|
+
sin = tl.load(freqs_ptr + token * D + 2 * i + 1)
|
|
166
|
+
out = out_ptr + (token * H + head) * D
|
|
167
|
+
tl.store(out + 2 * i, (even * cos - odd * sin).to(tl.bfloat16))
|
|
168
|
+
tl.store(out + 2 * i + 1, (odd * cos + even * sin).to(tl.bfloat16))
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
@torch.library.custom_op("flashmap_sglang::rms_norm_rope", mutates_args=())
|
|
172
|
+
def rms_norm_rope(
|
|
173
|
+
x: torch.Tensor, weight: torch.Tensor, freqs: torch.Tensor, eps: float
|
|
174
|
+
) -> torch.Tensor:
|
|
175
|
+
"""RoPE of RMSNorm(x) for ``x (..., T, heads, head_dim)`` and ``freqs (...,
|
|
176
|
+
T, head_dim / 2, [cos, sin])``, in one launch."""
|
|
177
|
+
*_, heads, head_dim = x.shape
|
|
178
|
+
assert x.is_contiguous() and freqs.is_contiguous()
|
|
179
|
+
assert freqs.dtype == torch.float32
|
|
180
|
+
out = torch.empty_like(x)
|
|
181
|
+
_rms_norm_rope_kernel[(x.numel() // head_dim,)](
|
|
182
|
+
x, weight, freqs, out, eps, H=heads, D=head_dim
|
|
183
|
+
)
|
|
184
|
+
return out
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
@rms_norm_rope.register_fake
|
|
188
|
+
def _(x, weight, freqs, eps):
|
|
189
|
+
return torch.empty_like(x)
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
__all__ = ["block_self_attention", "rms_norm", "rms_norm_rope"]
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
@triton.jit
|
|
196
|
+
def _sample_tiles_kernel(
|
|
197
|
+
logits_ptr,
|
|
198
|
+
temperatures_ptr,
|
|
199
|
+
greedy_ptr,
|
|
200
|
+
seed_ptr,
|
|
201
|
+
best_ptr,
|
|
202
|
+
best_index_ptr,
|
|
203
|
+
V,
|
|
204
|
+
n_tiles,
|
|
205
|
+
BLOCK_V: tl.constexpr,
|
|
206
|
+
):
|
|
207
|
+
# One program per (row, tile of the vocabulary): the tile's best key and
|
|
208
|
+
# its first index. A greedy row's key is its logit; a sampled row's is its
|
|
209
|
+
# logit over the temperature plus Gumbel noise from in-kernel Philox
|
|
210
|
+
# (Gumbel-max: the argmax is a sample of softmax(logits / T)).
|
|
211
|
+
row = tl.program_id(0)
|
|
212
|
+
tile = tl.program_id(1)
|
|
213
|
+
offs = tile * BLOCK_V + tl.arange(0, BLOCK_V)
|
|
214
|
+
mask = offs < V
|
|
215
|
+
key = tl.load(logits_ptr + row * V + offs, mask=mask, other=float("-inf"))
|
|
216
|
+
key = key.to(tl.float32)
|
|
217
|
+
if tl.load(greedy_ptr + row) == 0:
|
|
218
|
+
uniform = tl.rand(tl.load(seed_ptr), row * V + offs)
|
|
219
|
+
key = key / tl.load(temperatures_ptr + row) - tl.log(-tl.log(uniform))
|
|
220
|
+
key = tl.where(mask, key, float("-inf"))
|
|
221
|
+
best = tl.max(key, axis=0)
|
|
222
|
+
index = tl.min(tl.where(key == best, offs, V), axis=0)
|
|
223
|
+
tl.store(best_ptr + row * n_tiles + tile, best)
|
|
224
|
+
tl.store(best_index_ptr + row * n_tiles + tile, index)
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
def sample_rows(
|
|
228
|
+
logits: torch.Tensor,
|
|
229
|
+
temperatures: torch.Tensor,
|
|
230
|
+
greedy: torch.Tensor,
|
|
231
|
+
seed: torch.Tensor,
|
|
232
|
+
block_v: int = 4096,
|
|
233
|
+
) -> torch.Tensor:
|
|
234
|
+
"""A token per row of contiguous ``logits (rows, V)``: the argmax for
|
|
235
|
+
``greedy`` rows, else a sample of ``softmax(logits / temperature)``, with
|
|
236
|
+
Philox noise seeded by the int64 scalar tensor ``seed`` (advance it between
|
|
237
|
+
calls, in-graph, for fresh noise). Reads the logits once; no noise buffer."""
|
|
238
|
+
rows, vocab = logits.shape
|
|
239
|
+
assert logits.is_contiguous()
|
|
240
|
+
n_tiles = triton.cdiv(vocab, block_v)
|
|
241
|
+
best = torch.empty((rows, n_tiles), dtype=torch.float32, device=logits.device)
|
|
242
|
+
best_index = torch.empty((rows, n_tiles), dtype=torch.int64, device=logits.device)
|
|
243
|
+
_sample_tiles_kernel[(rows, n_tiles)](
|
|
244
|
+
logits, temperatures, greedy, seed, best, best_index, vocab, n_tiles,
|
|
245
|
+
BLOCK_V=block_v,
|
|
246
|
+
) # fmt: skip
|
|
247
|
+
return best_index.gather(1, best.argmax(dim=1, keepdim=True)).squeeze(1)
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
__all__ += ["sample_rows"]
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
# The largest e4m3 magnitude.
|
|
254
|
+
_FP8_MAX = 448.0
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
def quantize_weight_fp8(weight: torch.Tensor):
|
|
258
|
+
"""``weight (N, K)`` -> ``(e4m3 weight (N, K), float32 scale (1, N))``, one
|
|
259
|
+
scale per output channel."""
|
|
260
|
+
scale = weight.float().abs().amax(dim=1, keepdim=True).clamp_min(1e-12) / _FP8_MAX
|
|
261
|
+
quantized = (weight.float() / scale).to(torch.float8_e4m3fn)
|
|
262
|
+
return quantized, scale.t().contiguous()
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
def fp8_linear(x, weight, scale, bias=None) -> torch.Tensor:
|
|
266
|
+
"""``x @ W^T + b`` with ``W`` from :func:`quantize_weight_fp8` and ``x``
|
|
267
|
+
quantized per row on the fly (dynamic per-token scales): one FP8 GEMM with
|
|
268
|
+
row-wise scales (Hopper), bf16 out. Elementwise parts fuse under compile."""
|
|
269
|
+
rows = x.reshape(-1, x.shape[-1])
|
|
270
|
+
x_scale = rows.float().abs().amax(dim=1, keepdim=True).clamp_min(1e-12) / _FP8_MAX
|
|
271
|
+
quantized = (rows.float() / x_scale).to(torch.float8_e4m3fn)
|
|
272
|
+
out = torch._scaled_mm(
|
|
273
|
+
quantized, weight.t(), scale_a=x_scale, scale_b=scale, out_dtype=torch.bfloat16
|
|
274
|
+
)
|
|
275
|
+
if bias is not None:
|
|
276
|
+
out = out + bias
|
|
277
|
+
return out.view(*x.shape[:-1], -1)
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
__all__ += ["fp8_linear", "quantize_weight_fp8"]
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
@triton.jit
|
|
284
|
+
def _kv_norm_rope_write_fp8_kernel(
|
|
285
|
+
kv_ptr,
|
|
286
|
+
meta_ptr,
|
|
287
|
+
knw_ptr,
|
|
288
|
+
cos_sin_ptr,
|
|
289
|
+
pos_ptr,
|
|
290
|
+
loc_ptr,
|
|
291
|
+
commit_lens_ptr,
|
|
292
|
+
locs_row_width,
|
|
293
|
+
k_scale,
|
|
294
|
+
v_scale,
|
|
295
|
+
KV: tl.constexpr,
|
|
296
|
+
D: tl.constexpr,
|
|
297
|
+
NH: tl.constexpr,
|
|
298
|
+
L: tl.constexpr,
|
|
299
|
+
EPS: tl.constexpr,
|
|
300
|
+
HAS_COMMIT_LENS: tl.constexpr,
|
|
301
|
+
):
|
|
302
|
+
# sglang's fused_kv_norm_rope_write (interleaved RoPE), storing e4m3 with
|
|
303
|
+
# static per-tensor scales instead of bf16.
|
|
304
|
+
t = tl.program_id(0).to(tl.int64)
|
|
305
|
+
layer = tl.program_id(1).to(tl.int64)
|
|
306
|
+
if HAS_COMMIT_LENS:
|
|
307
|
+
row_b = t // locs_row_width
|
|
308
|
+
if t - row_b * locs_row_width >= tl.load(commit_lens_ptr + row_b).to(tl.int64):
|
|
309
|
+
return
|
|
310
|
+
loc = tl.load(loc_ptr + t).to(tl.int64)
|
|
311
|
+
if loc < 0:
|
|
312
|
+
return
|
|
313
|
+
pos = tl.load(pos_ptr + t).to(tl.int64)
|
|
314
|
+
HALF: tl.constexpr = D // 2
|
|
315
|
+
half = tl.arange(0, HALF)
|
|
316
|
+
d = tl.arange(0, D)
|
|
317
|
+
even, odd = 2 * half, 2 * half + 1
|
|
318
|
+
cos = tl.load(cos_sin_ptr + pos * D + half).to(tl.float32)
|
|
319
|
+
sin = tl.load(cos_sin_ptr + pos * D + HALF + half).to(tl.float32)
|
|
320
|
+
w_even = tl.load(knw_ptr + layer * D + even).to(tl.float32)
|
|
321
|
+
w_odd = tl.load(knw_ptr + layer * D + odd).to(tl.float32)
|
|
322
|
+
k_buf = tl.load(meta_ptr + layer * 4 + 0).to(tl.pointer_type(tl.float8e4nv))
|
|
323
|
+
v_buf = tl.load(meta_ptr + layer * 4 + 1).to(tl.pointer_type(tl.float8e4nv))
|
|
324
|
+
k_stride = tl.load(meta_ptr + layer * 4 + 2)
|
|
325
|
+
v_stride = tl.load(meta_ptr + layer * 4 + 3)
|
|
326
|
+
row = kv_ptr + t * (L * 2 * KV) + layer * (2 * KV)
|
|
327
|
+
for h in tl.static_range(NH):
|
|
328
|
+
k = tl.load(row + h * D + d).to(tl.float32)
|
|
329
|
+
inv = 1.0 / tl.sqrt(tl.sum(k * k, 0) / D + EPS)
|
|
330
|
+
k1 = (tl.load(row + h * D + even).to(tl.float32) * inv * w_even).to(tl.bfloat16)
|
|
331
|
+
k2 = (tl.load(row + h * D + odd).to(tl.float32) * inv * w_odd).to(tl.bfloat16)
|
|
332
|
+
k1, k2 = k1.to(tl.float32), k2.to(tl.float32)
|
|
333
|
+
o1 = (k1 * cos - k2 * sin) / k_scale
|
|
334
|
+
o2 = (k2 * cos + k1 * sin) / k_scale
|
|
335
|
+
tl.store(k_buf + loc * k_stride + h * D + even, o1.to(tl.float8e4nv))
|
|
336
|
+
tl.store(k_buf + loc * k_stride + h * D + odd, o2.to(tl.float8e4nv))
|
|
337
|
+
v = tl.load(row + KV + h * D + d).to(tl.float32) / v_scale
|
|
338
|
+
tl.store(v_buf + loc * v_stride + h * D + d, v.to(tl.float8e4nv))
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
def kv_norm_rope_write_fp8(
|
|
342
|
+
kv, meta, k_norm_weights, cos_sin, positions, locs, num_layers, kv_size,
|
|
343
|
+
head_dim, eps, *, k_scale=1.0, v_scale=1.0, commit_lens=None, locs_row_width=None,
|
|
344
|
+
): # fmt: skip
|
|
345
|
+
"""``fused_kv_norm_rope_write`` (same arguments; interleaved RoPE) for an
|
|
346
|
+
e4m3 pool: keys k-normed and rotated, both divided by their static scale."""
|
|
347
|
+
if kv.shape[0] == 0:
|
|
348
|
+
return
|
|
349
|
+
has_commit = commit_lens is not None
|
|
350
|
+
_kv_norm_rope_write_fp8_kernel[(kv.shape[0], num_layers)](
|
|
351
|
+
kv, meta, k_norm_weights, cos_sin,
|
|
352
|
+
positions.to(torch.int64).contiguous(), locs.to(torch.int64).contiguous(),
|
|
353
|
+
commit_lens.contiguous() if has_commit else locs, locs_row_width or 1,
|
|
354
|
+
k_scale, v_scale,
|
|
355
|
+
KV=kv_size, D=head_dim, NH=kv_size // head_dim, L=num_layers, EPS=eps,
|
|
356
|
+
HAS_COMMIT_LENS=has_commit,
|
|
357
|
+
) # fmt: skip
|
|
358
|
+
|
|
359
|
+
|
|
360
|
+
__all__ += ["kv_norm_rope_write_fp8"]
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
@triton.jit
|
|
364
|
+
def _tree_commit_kernel(
|
|
365
|
+
accept_ptr, # (bs, D1) int32: global node ids of the accepted path, root first; -1 past it
|
|
366
|
+
num_correct_ptr, # (bs,) int32: accepted drafts
|
|
367
|
+
window_ptr, # (bs, W) int64: the verify window's KV slots, node order
|
|
368
|
+
predict_ptr, # (bs * W,) int32: the target's token after each node on the path
|
|
369
|
+
req_pool_ptr, # (bs,) int64
|
|
370
|
+
prefix_ptr, # (bs,) int64: committed length before this step
|
|
371
|
+
req_to_token_ptr, # (rows, row_stride) int32
|
|
372
|
+
row_stride,
|
|
373
|
+
out_tokens_ptr, # (bs, D1) int64
|
|
374
|
+
bonus_ptr, # (bs,) int64
|
|
375
|
+
commit_ptr, # (bs,) int32
|
|
376
|
+
new_seq_lens_ptr, # (bs,) int64
|
|
377
|
+
inject_loc_ptr, # (bs * W,) int64
|
|
378
|
+
W: tl.constexpr,
|
|
379
|
+
D1: tl.constexpr,
|
|
380
|
+
BLOCK: tl.constexpr,
|
|
381
|
+
):
|
|
382
|
+
# One program per request: the tree path's commit in one launch.
|
|
383
|
+
b = tl.program_id(0)
|
|
384
|
+
n = tl.load(num_correct_ptr + b)
|
|
385
|
+
j = tl.arange(0, BLOCK) # path positions
|
|
386
|
+
i = tl.arange(0, BLOCK) # tree nodes
|
|
387
|
+
on_path_j = j <= n
|
|
388
|
+
node = tl.load(accept_ptr + b * D1 + j, mask=(j < D1) & on_path_j, other=0) - b * W
|
|
389
|
+
node = tl.where(on_path_j & (j < D1), node, -1)
|
|
390
|
+
# Which nodes the path takes (node i at path position j).
|
|
391
|
+
hit = (node[:, None] == i[None, :]) & (node[:, None] >= 0)
|
|
392
|
+
on_path = tl.sum(hit.to(tl.int32), axis=0) > 0
|
|
393
|
+
in_tree = i < W
|
|
394
|
+
window = tl.load(window_ptr + b * W + i, mask=in_tree, other=0)
|
|
395
|
+
# Reorder the window's req_to_token entries: the path first (where its KV
|
|
396
|
+
# is), then every other node in index order.
|
|
397
|
+
off_path = (on_path == 0) & in_tree
|
|
398
|
+
rank = tl.cumsum(off_path.to(tl.int32), axis=0) - 1
|
|
399
|
+
row = tl.load(req_pool_ptr + b)
|
|
400
|
+
prefix = tl.load(prefix_ptr + b)
|
|
401
|
+
base = req_to_token_ptr + row * row_stride + prefix
|
|
402
|
+
path_slot = tl.load(window_ptr + b * W + node, mask=node >= 0, other=0)
|
|
403
|
+
tl.store(base + j, path_slot.to(tl.int32), mask=node >= 0)
|
|
404
|
+
tl.store(base + n + 1 + rank, window.to(tl.int32), mask=off_path)
|
|
405
|
+
# The path's tokens (the drafts, then the target's next one) and the bonus.
|
|
406
|
+
token = tl.load(predict_ptr + b * W + node, mask=node >= 0, other=0).to(tl.int64)
|
|
407
|
+
tl.store(out_tokens_ptr + b * D1 + j, token, mask=j < D1)
|
|
408
|
+
tl.store(bonus_ptr + b, tl.sum(tl.where(j == n, token, 0), axis=0))
|
|
409
|
+
tl.store(commit_ptr + b, n + 1)
|
|
410
|
+
tl.store(new_seq_lens_ptr + b, prefix + n + 1)
|
|
411
|
+
# Draft context goes where the path's KV is; other rows to dummy slot 0.
|
|
412
|
+
tl.store(inject_loc_ptr + b * W + i, tl.where(on_path, window, 0), mask=in_tree)
|
|
413
|
+
|
|
414
|
+
|
|
415
|
+
def tree_commit(
|
|
416
|
+
accept_index,
|
|
417
|
+
num_correct,
|
|
418
|
+
window,
|
|
419
|
+
predict,
|
|
420
|
+
req_pool_indices,
|
|
421
|
+
prefix_lens,
|
|
422
|
+
req_to_token,
|
|
423
|
+
):
|
|
424
|
+
"""The tree path's commit after verify_tree_greedy, one launch for the batch.
|
|
425
|
+
Reorders each request's verify window in req_to_token so the accepted path
|
|
426
|
+
comes first (no KV moves) and returns (out_tokens (bs, D1) int64, bonus,
|
|
427
|
+
commit_lens int32, new_seq_lens, inject_locs (bs * W) int64)."""
|
|
428
|
+
bs, d1 = accept_index.shape
|
|
429
|
+
width = window.shape[1]
|
|
430
|
+
device = accept_index.device
|
|
431
|
+
out_tokens = torch.empty((bs, d1), dtype=torch.int64, device=device)
|
|
432
|
+
bonus = torch.empty((bs,), dtype=torch.int64, device=device)
|
|
433
|
+
commit = torch.empty((bs,), dtype=torch.int32, device=device)
|
|
434
|
+
new_seq_lens = torch.empty((bs,), dtype=prefix_lens.dtype, device=device)
|
|
435
|
+
inject_locs = torch.empty((bs * width,), dtype=torch.int64, device=device)
|
|
436
|
+
_tree_commit_kernel[(bs,)](
|
|
437
|
+
accept_index,
|
|
438
|
+
num_correct,
|
|
439
|
+
window,
|
|
440
|
+
predict,
|
|
441
|
+
req_pool_indices,
|
|
442
|
+
prefix_lens,
|
|
443
|
+
req_to_token,
|
|
444
|
+
req_to_token.stride(0),
|
|
445
|
+
out_tokens,
|
|
446
|
+
bonus,
|
|
447
|
+
commit,
|
|
448
|
+
new_seq_lens,
|
|
449
|
+
inject_locs,
|
|
450
|
+
W=width,
|
|
451
|
+
D1=d1,
|
|
452
|
+
BLOCK=triton.next_power_of_2(max(width, d1)),
|
|
453
|
+
)
|
|
454
|
+
return out_tokens, bonus, commit, new_seq_lens, inject_locs
|
|
455
|
+
|
|
456
|
+
|
|
457
|
+
__all__ += ["tree_commit"]
|