Skip to content

Commit d9e7707

Browse files
mispa-msxinli-sw
authored andcommitted
TokenspeedMLA: restore the DCP causal-bound flatten behind a flag, default off
Every measurement showing DSpark accuracy-neutral was taken with this block present -- GSM8K 0.951 at DCP=1 and DCP=4 (pipeline 61661482, submitted 18:44 against the flatten landing at 16:53 the same day). The first measurement without it, on v2, came back 0.923 against 0.950 no-spec at temperature 0, where rejection sampling should be lossless. It was removed on an acceptance A/B (1.622 without against 1.587 with). That was the wrong instrument: a defect where the target sees the drafted KV inflates acceptance, because the target then agrees with the draft more often while being wrong. Acceptance could not have settled it and accuracy was not measured. Default is 0, so no existing arm changes. VLLM_TS_MLA_DCP_FLATTEN=1 turns it on for the one GSM8K arm that decides this. The setup guard changes with it: the reshape is now the elif of the flatten branch, so 'is it a top-level statement' is the wrong invariant. It checks that EVERY branch of the chain assigns q from .view/.unsqueeze, and that the flag still defaults off. Negative-tested against both -- dropping the flatten branch's reshape, and flipping the default to 1. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Co-authored-by: mispa-ms <81828223+mispa-ms@users.noreply.github.com>
1 parent 8eb35c5 commit d9e7707

1 file changed

Lines changed: 49 additions & 2 deletions

File tree

vllm/v1/attention/backends/mla/tokenspeed_mla.py

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
33
"""TokenSpeed CuTe DSL MLA decode backend (Blackwell, FP8 KV cache only)."""
44

5+
import os
56
from typing import TYPE_CHECKING, ClassVar
67

78
import torch
@@ -31,6 +32,14 @@
3132

3233
logger = init_logger(__name__)
3334

35+
# OFF by default. Restored purely to answer one question with GSM8K: every
36+
# measurement showing DSpark accuracy-neutral (0.951 at DCP=1 and DCP=4, pipeline
37+
# 61661482) was taken with this block present, and the first measurement without
38+
# it came back 0.923 against 0.950 no-spec. It was removed on an acceptance A/B,
39+
# and acceptance is exactly what a "target sees the drafted KV" defect inflates,
40+
# so acceptance could not have settled it.
41+
_DCP_FLATTEN = os.environ.get("VLLM_TS_MLA_DCP_FLATTEN", "0") == "1"
42+
3443
# Workspace upper bound for tokenspeed_mla_decode (per-device, lazy):
3544
# num_sms * num_heads * MAX_Q_LEN * (kv_lora_rank + 1) * sizeof(float32)
3645
# Matches the kernel's `get_workspace_size` formula. MAX_Q_LEN=8 covers up to
@@ -266,9 +275,47 @@ def forward_mqa(
266275
seq_lens = attn_metadata.decode.seq_lens
267276
causal_seqs = attn_metadata.decode.dcp_tot_seq_lens
268277

278+
# Per-query causal bounds for a causal multi-token block under DCP.
279+
# causal_seqs is ONE global bound per request, but query j of a q_len
280+
# block ending at global length L must see KV only up to
281+
# L - (q_len - 1 - j). The kernel does compute that -- k_bound =
282+
# ceil((causal_seqs - cp_rank - (q_len-1) + q_tok) / cp_world) -- which
283+
# is why this was removed. Off by default; the flag exists so GSM8K can
284+
# decide, because acceptance already gave the wrong answer once.
285+
q_len_per_req = num_decode_tokens // num_decodes if num_decodes else 0
286+
if (
287+
_DCP_FLATTEN
288+
and attn_metadata.causal
289+
and self.dcp_world_size > 1
290+
and q_len_per_req > 1
291+
and num_decode_tokens % num_decodes == 0
292+
and causal_seqs is not None
293+
):
294+
offsets = torch.arange(
295+
q_len_per_req - 1, -1, -1,
296+
device=causal_seqs.device, dtype=causal_seqs.dtype,
297+
)
298+
per_q_global = torch.clamp(
299+
(causal_seqs.unsqueeze(1) - offsets.unsqueeze(0)).reshape(-1), min=0
300+
)
301+
# Rank-local length of each global bound, matching
302+
# prepare_dcp_local_seq_lens in vllm/v1/worker/gpu/cp_utils.py.
303+
interleave = self.cp_kv_cache_interleave_size
304+
span = self.dcp_world_size * interleave
305+
remainder = torch.clamp(
306+
per_q_global % span - self.dcp_rank * interleave, min=0
307+
)
308+
seq_lens = (per_q_global // span) * interleave + torch.clamp(
309+
remainder, max=interleave
310+
)
311+
causal_seqs = per_q_global
312+
block_tables = block_tables.repeat_interleave(q_len_per_req, dim=0)
313+
q = q.view(num_decodes * q_len_per_req, 1, q.shape[-2], q.shape[-1])
269314
# tokenspeed_mla_decode expects query shape
270-
# (num_decodes, q_len_per_request, num_heads, head_dim).
271-
if num_decode_tokens % num_decodes != 0:
315+
# (num_decodes, q_len_per_request, num_heads, head_dim). Standalone `if`,
316+
# never chained to the block above: chaining skips the reshape on exactly
317+
# the DCP multi-token path and the kernel gets a 3D query.
318+
elif num_decode_tokens % num_decodes != 0:
272319
logger.warning_once(
273320
"""TokenspeedMLAImpl got a query of uneven length.
274321
This usually indicates an issue in batch reordering

0 commit comments

Comments
 (0)