A high-throughput and memory-efficient inference and serving engine for LLMs
Mirrored from https://github.com/tduncan-lum/Nvidia-Optimization-Stack---VLLM.git
Synced Nov 8, 19:00 UTC
[Perf] Disable clean_logits in deepgemm fp8_mqa_logits kernel (#33568)
Xin Yang committed
Feb 5, 2026 at 17:34 UTC
79028d438859162841d35bbf2a91eaa8236733fa
4 files changed
+61
-27
tests/kernels/attention/test_deepgemm_attention.py
+13
-7
@@ -95,7 +95,8 @@ def _ref_fp8_mqa_logits(
95
@pytest.mark.skipif(
96
not current_platform.has_device_capability(90), reason="SM90 and SM100 only"
97
)
98
-def test_deepgemm_fp8_mqa_logits():
98
+@pytest.mark.parametrize("clean_logits", [True, False])
99
+def test_deepgemm_fp8_mqa_logits(clean_logits: bool):
100
torch.manual_seed(0)
101
random.seed(0)
102
num_heads, head_dim = 32, 128
@@ -126,7 +127,9 @@ def test_deepgemm_fp8_mqa_logits():
127
128
q_fp8 = q.to(torch.float8_e4m3fn)
129
kv_fp8 = per_custom_dims_cast_to_fp8(kv, (0,), False)
129
- logits = fp8_mqa_logits(q_fp8, kv_fp8, weights, ks, ke)
130
+ logits = fp8_mqa_logits(
131
+ q_fp8, kv_fp8, weights, ks, ke, clean_logits=clean_logits
132
+ )
133
134
ref_logits = _ref_fp8_mqa_logits(
135
q=q,
@@ -135,13 +138,14 @@ def test_deepgemm_fp8_mqa_logits():
138
cu_seqlen_ks=ks,
139
cu_seqlen_ke=ke,
140
)
138
-
141
ref_neginf_mask = ref_logits == float("-inf")
140
- neginf_mask = logits == float("-inf")
141
- assert torch.equal(neginf_mask, ref_neginf_mask)
142
+
143
+ if clean_logits:
144
+ neginf_mask = logits == float("-inf")
145
+ assert torch.equal(neginf_mask, ref_neginf_mask)
146
147
ref_logits = ref_logits.masked_fill(ref_neginf_mask, 0)
144
- logits = logits.masked_fill(neginf_mask, 0)
148
+ logits = logits.masked_fill(ref_neginf_mask, 0)
149
diff = calc_diff(logits, ref_logits)
150
assert diff < 1e-3, f"{diff=}"
151
@@ -201,7 +205,8 @@ def _ref_fp8_paged_mqa_logits(
205
@pytest.mark.skipif(
206
not current_platform.has_device_capability(90), reason="SM90 and SM100 only"
207
)
204
-def test_deepgemm_fp8_paged_mqa_logits():
208
+@pytest.mark.parametrize("clean_logits", [True, False])
209
+def test_deepgemm_fp8_paged_mqa_logits(clean_logits: bool):
210
torch.manual_seed(0)
211
random.seed(0)
212
@@ -264,6 +269,7 @@ def test_deepgemm_fp8_paged_mqa_logits():
269
block_tables,
270
schedule_metadata,
271
max_model_len,
272
+ clean_logits=clean_logits,
273
)
274
275
ref_logits = _ref_fp8_paged_mqa_logits(
tests/kernels/test_top_k_per_row.py
+38
-18
@@ -6,6 +6,7 @@ import pytest
6
import torch
7
8
from vllm.platforms import current_platform
9
+from vllm.utils.torch_utils import set_random_seed
10
11
# Test parameters
12
NUM_ROWS = [1, 32, 2050]
@@ -20,6 +21,7 @@ def create_random_logits(
21
row_ends: torch.Tensor,
22
dtype: torch.dtype,
23
seed: int,
24
+ clean_logits: bool,
25
data_generation: str,
26
) -> torch.Tensor:
27
"""Create random logits tensor for testing."""
@@ -48,8 +50,9 @@ def create_random_logits(
50
)
51
logits = logits_bits.view(dtype)
52
51
- for i, end in enumerate(row_ends):
52
- logits[i, end:] = float("-inf")
53
+ if clean_logits:
54
+ for i, end in enumerate(row_ends):
55
+ logits[i, end:] = float("-inf")
56
return logits
57
58
@@ -121,21 +124,26 @@ def compare_top_k_results(
124
125
@pytest.mark.parametrize("num_rows", NUM_ROWS)
126
@pytest.mark.parametrize("top_k", TOP_K_VALUES)
127
+@pytest.mark.parametrize("clean_logits", [True, False])
128
@pytest.mark.skipif(not current_platform.is_cuda(), reason="This test requires CUDA")
129
@torch.inference_mode()
130
def test_top_k_per_row(
131
num_rows: int,
132
top_k: int,
133
+ clean_logits: bool,
134
) -> None:
135
"""
136
Test top_k_per_row.
137
"""
138
+ set_random_seed(0)
139
torch.set_default_device("cuda:0")
140
141
# Create test data
142
vocab_size = 20000
143
row_starts, row_ends = create_row_boundaries(num_rows, vocab_size)
138
- logits = create_random_logits(row_starts, row_ends, torch.float32, 42, "random")
144
+ logits = create_random_logits(
145
+ row_starts, row_ends, torch.float32, 42, clean_logits, "random"
146
+ )
147
148
# Create output tensors
149
indices = torch.empty((num_rows, top_k), dtype=torch.int32, device="cuda")
@@ -153,11 +161,12 @@ def test_top_k_per_row(
161
)
162
163
# Run reference implementation
156
- torch_indices = logits.topk(min(top_k, max(row_ends)), dim=-1)[1]
157
- mask_lo = torch_indices >= 0
158
- mask_hi = (torch_indices - (row_ends - row_starts)[:, None]) < 0
159
- mask = mask_lo & mask_hi
160
- torch_indices = torch_indices.masked_fill(~mask, -1)
164
+ torch_indices = torch.empty((num_rows, top_k), dtype=torch.int32, device="cuda")
165
+ for i in range(num_rows):
166
+ row_end = int(row_ends[i])
167
+ k_i = min(top_k, row_end)
168
+ idx = logits[i, :row_end].topk(k_i, dim=-1)[1]
169
+ torch_indices[i, :k_i] = idx
170
171
# Compare results
172
assert compare_top_k_results(
@@ -170,6 +179,7 @@ def _run_top_k_per_row_decode_test(
179
batch_size: int,
180
next_n: int,
181
vocab_size: int,
182
+ clean_logits: bool,
183
data_generation: str,
184
) -> None:
185
"""
@@ -180,14 +190,18 @@ def _run_top_k_per_row_decode_test(
190
# Create test data
191
num_rows = batch_size * next_n
192
seq_lens = torch.randint(
183
- vocab_size, (batch_size,), dtype=torch.int32, device="cuda"
193
+ low=next_n,
194
+ high=vocab_size,
195
+ size=(batch_size,),
196
+ dtype=torch.int32,
197
+ device="cuda",
198
)
199
row_starts = torch.zeros(num_rows, dtype=torch.int32, device="cuda")
200
row_indices = torch.arange(num_rows, device="cuda") // next_n
201
next_n_offset = torch.arange(num_rows, device="cuda") % next_n
202
row_ends = seq_lens[row_indices] - next_n + next_n_offset + 1
203
logits = create_random_logits(
190
- row_starts, row_ends, torch.float32, 42, data_generation
204
+ row_starts, row_ends, torch.float32, 42, clean_logits, data_generation
205
)
206
207
# Create output tensors
@@ -208,11 +222,12 @@ def _run_top_k_per_row_decode_test(
222
torch.cuda.synchronize()
223
224
# Run reference implementation
211
- torch_indices = logits.topk(min(top_k, max(row_ends)), dim=-1)[1]
212
- mask_lo = torch_indices >= 0
213
- mask_hi = (torch_indices - (row_ends - row_starts)[:, None]) < 0
214
- mask = mask_lo & mask_hi
215
- torch_indices = torch_indices.masked_fill(~mask, -1)
225
+ torch_indices = torch.empty((num_rows, top_k), dtype=torch.int32, device="cuda")
226
+ for i in range(num_rows):
227
+ row_end = int(row_ends[i])
228
+ k_i = min(top_k, row_end)
229
+ idx = logits[i, :row_end].topk(k_i, dim=-1)[1]
230
+ torch_indices[i, :k_i] = idx
231
232
# Compare results
233
assert compare_top_k_results(
@@ -223,6 +238,7 @@ def _run_top_k_per_row_decode_test(
238
@pytest.mark.parametrize("top_k", TOP_K_VALUES)
239
@pytest.mark.parametrize("batch_size", BATCH_SIZE)
240
@pytest.mark.parametrize("next_n", NEXT_N)
241
+@pytest.mark.parametrize("clean_logits", [True, False])
242
@pytest.mark.parametrize("data_generation", DATA_GENERATION)
243
@pytest.mark.skipif(not current_platform.is_cuda(), reason="This test requires CUDA")
244
@torch.inference_mode()
@@ -230,28 +246,32 @@ def test_top_k_per_row_decode(
246
top_k: int,
247
batch_size: int,
248
next_n: int,
249
+ clean_logits: bool,
250
data_generation: str,
251
) -> None:
252
"""
253
Test top_k_per_row with seq_lens tensor.
254
"""
255
+ set_random_seed(0)
256
vocab_size = 20000
257
_run_top_k_per_row_decode_test(
240
- top_k, batch_size, next_n, vocab_size, data_generation
258
+ top_k, batch_size, next_n, vocab_size, clean_logits, data_generation
259
)
260
261
262
@pytest.mark.skipif(not current_platform.is_cuda(), reason="This test requires CUDA")
263
+@pytest.mark.parametrize("clean_logits", [True, False])
264
@torch.inference_mode()
246
-def test_top_k_per_row_decode_large_vocab_size() -> None:
265
+def test_top_k_per_row_decode_large_vocab_size(clean_logits: bool) -> None:
266
"""
267
Test top_k_per_row_decode with large vocabulary size.
268
"""
269
+ set_random_seed(0)
270
top_k = 2048
271
batch_size = 2
272
next_n = 2
273
vocab_size = 300000
274
data_generation = "random"
275
_run_top_k_per_row_decode_test(
256
- top_k, batch_size, next_n, vocab_size, data_generation
276
+ top_k, batch_size, next_n, vocab_size, clean_logits, data_generation
277
)
vllm/model_executor/layers/sparse_attn_indexer.py
+2
@@ -108,6 +108,7 @@ def sparse_attn_indexer(
108
weights[chunk.token_start : chunk.token_end],
109
chunk.cu_seqlen_ks,
110
chunk.cu_seqlen_ke,
111
+ clean_logits=False,
112
)
113
num_rows = logits.shape[0]
114
@@ -157,6 +158,7 @@ def sparse_attn_indexer(
158
decode_metadata.block_table,
159
decode_metadata.schedule_metadata,
160
max_model_len=max_model_len,
161
+ clean_logits=False,
162
)
163
164
num_rows = logits.shape[0]
vllm/utils/deep_gemm.py
+8
-2
@@ -242,6 +242,7 @@ def fp8_mqa_logits(
242
weights: torch.Tensor,
243
cu_seqlen_ks: torch.Tensor,
244
cu_seqlen_ke: torch.Tensor,
245
+ clean_logits: bool,
246
) -> torch.Tensor:
247
"""Compute FP8 MQA logits for a single sequence without KV paging.
248
@@ -256,6 +257,7 @@ def fp8_mqa_logits(
257
shape [M], dtype int32.
258
cu_seqlen_ke: End indices (exclusive) for valid K per query position,
259
shape [M], dtype int32.
260
+ clean_logits: Whether to clean the unfilled logits into `-inf`.
261
262
Returns:
263
Logits tensor of shape [M, N], dtype `torch.float32`.
@@ -263,7 +265,9 @@ def fp8_mqa_logits(
265
_lazy_init()
266
if _fp8_mqa_logits_impl is None:
267
return _missing()
266
- return _fp8_mqa_logits_impl(q, kv, weights, cu_seqlen_ks, cu_seqlen_ke)
268
+ return _fp8_mqa_logits_impl(
269
+ q, kv, weights, cu_seqlen_ks, cu_seqlen_ke, clean_logits=clean_logits
270
+ )
271
272
273
def get_paged_mqa_logits_metadata(
@@ -295,6 +299,7 @@ def fp8_paged_mqa_logits(
299
block_tables: torch.Tensor,
300
schedule_metadata: torch.Tensor,
301
max_model_len: int,
302
+ clean_logits: bool,
303
) -> torch.Tensor:
304
"""Compute FP8 MQA logits using paged KV-cache.
305
@@ -312,6 +317,7 @@ def fp8_paged_mqa_logits(
317
schedule_metadata: Returned by `get_paged_mqa_logits_metadata`;
318
used to distribute work across SMs.
319
max_model_len: Maximum sequence length used to size the logits output.
320
+ clean_logits: Whether to clean the unfilled logits into `-inf`.
321
322
Returns:
323
Logits tensor of shape [B * next_n, max_model_len], dtype
@@ -328,7 +334,7 @@ def fp8_paged_mqa_logits(
334
block_tables,
335
schedule_metadata,
336
max_model_len,
331
- clean_logits=True,
337
+ clean_logits=clean_logits,
338
)
339
340