vllm.v1.attention.ops.triton_decode_attention
¶
Memory-efficient attention for decoding. It supports page size >= 1.
Functions:
-
decode_attention_fwd_verify–Causal MLA verify over a per-request block table.
-
select_verify_config–Launch config from MI300X (gfx942) sweeps, bf16, page 16.
-
verify_num_kv_splits–Static upper bound on KV splits (safe to bake into a cudagraph).
-
warmup_decode_attention_fwd_verify–Compile every launch variant serving can reach for these tensors.
_storage_view(t, shape)
¶
Contiguous shape over t's storage, or a new tensor if too small.
Source code in vllm/v1/attention/ops/triton_decode_attention.py
decode_attention_fwd_verify(q, k_buffer, v_buffer, o, lse, req_to_token, seq_lens, real_seq_lens, qlen, attn_logits, num_kv_splits, sm_scale, page_size, k_scale=None)
¶
Causal MLA verify over a per-request block table.
Parameters:
-
(q¶Tensor) –[B * qlen, H, Lk] queries, verify tokens of a request contiguous.
-
(k_buffer¶Tensor) –paged latent cache, [num_pages, page_size, 1, Lk] (or 3D [num_pages * page_size, 1, Lk]).
-
(v_buffer¶Tensor) –k_buffer[..., :Lv] (V is read from K; only its width is used).
-
(o¶Tensor) –[B * qlen, H, Lv] output.
-
(lse¶Tensor) –[B * qlen, H] fp32 natural-log LSE (-inf for rows with no key).
-
(req_to_token¶Tensor) –[B, max_pages] per-request block table (not repeated).
-
(seq_lens¶Tensor) –[B] int32 kernel KV lengths L.
-
(real_seq_lens¶Tensor | None) –[B] int32 or None; row limit is max(min(L - qlen + 1 + t, L_real), 0).
-
(qlen¶int) –verify tokens per request.
-
(attn_logits¶Tensor | None) –fp32 workspace with >= B * qlen * H * num_kv_splits * (Lv + 1) elements, laid out [B * qlen, H, num_kv_splits, Lv + 1]. Unused, and may be None, when num_kv_splits is 1.
-
(num_kv_splits¶int) –static cap on KV splits per request (see verify_num_kv_splits); shorter requests use fewer, longer splits.
-
(sm_scale¶float) –softmax scale.
-
(page_size¶int) –tokens per KV cache page.
-
(k_scale¶Tensor | None, default:None) –scalar tensor dequantizing an FP8 cache; ignored for bf16.
Source code in vllm/v1/attention/ops/triton_decode_attention.py
1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 | |
select_verify_config(batch, qlen, num_heads)
¶
Launch config from MI300X (gfx942) sweeps, bf16, page 16.
Few rows (B * qlen * H <= 640): 32-row tiles with more KV splits fill the GPU. Otherwise 64-row tiles (Q split in two 256-wide halves, next K/V tile prefetched) halve the KV re-reads per request.
Source code in vllm/v1/attention/ops/triton_decode_attention.py
verify_num_kv_splits(max_seq_len, batch, qlen, num_heads, sm_count, block_h=VERIFY_DEFAULT_CONFIG['BLOCK_H'], occupancy=1.0, min_work_per_split=64, max_splits=8)
¶
Static upper bound on KV splits (safe to bake into a cudagraph).
The kernel splits each request's own length into
max(cdiv(L, splits), MIN_SPLIT_LEN) chunks, so this only caps the split
count. occupancy is the number of resident stage-1 programs per CU.
If one split of every request fits in a single dispatch round, use as many
splits as fit in that round; otherwise pick n <= max_splits minimising
rounds(n) / n (the idle tail of the last round), preferring fewer splits
within 5% of the best.
Source code in vllm/v1/attention/ops/triton_decode_attention.py
warmup_decode_attention_fwd_verify(q, k_buffer, o, lse, req_to_token, has_real, qlens, attn_logits, sm_scale, page_size, k_scale=None)
¶
Compile every launch variant serving can reach for these tensors.
A launch specializes on qlen, the row-count config and a single split, but graph capture and profiling only run the full verify length. Each variant runs once on scratch tensors with the strides of the real ones; the split scratch reuses attn_logits' storage, whose size the launch specializes on.