[MRV2] Miscellaneous code cleanup (#57980)

Nick Hill committed Sep 23, 2026 at 01:51 UTC afec265bca16d02350a2da1855cbb90dd4c7123b
24 files changed +163 -326
tests/v1/spec_decode/test_eagle_draft_attn_metadata.py
+1 -3
@@ -166,9 +166,7 @@ def test_build_draft_attn_metadata_recomputes_dcp_local_seq_lens():
166 out[num_reqs:].zero_()
167 return out
168
169 - with patch.object(
170 - base_speculator, "maybe_prepare_dcp_local_seq_lens", fake_prepare
171 - ):
169 + with patch.object(base_speculator, "prepare_dcp_local_seq_lens", fake_prepare):
170 captured = _run_build(
171 fake,
172 num_reqs=3,
tests/v1/worker/test_gpu_input_batch_v2.py
+4 -31
@@ -81,7 +81,7 @@ def test_make_dummy_padding_controls_moe_routing(monkeypatch, is_padding: bool):
81 assert bool((topk_ids >= 0).all()) is not is_padding
82
83
84 -def test_maybe_prepare_dcp_local_seq_lens_uses_shared_buffer(monkeypatch):
84 +def test_prepare_dcp_local_seq_lens_uses_shared_buffer(monkeypatch):
85 """The batch must view the caller-owned buffer, sliced to padded length.
86
87 Runtime (and capture) paths all funnel through this helper; attention
@@ -115,7 +115,7 @@ def test_maybe_prepare_dcp_local_seq_lens_uses_shared_buffer(monkeypatch):
115 return fake_kernel
116
117 monkeypatch.setattr(cp_utils, "_dcp_local_seq_lens_kernel", FakeKernel())
118 - batch.dcp_local_seq_lens = cp_utils.maybe_prepare_dcp_local_seq_lens(
118 + batch.dcp_local_seq_lens = cp_utils.prepare_dcp_local_seq_lens(
119 buffers.dcp_local_seq_lens,
120 batch.seq_lens,
121 batch.num_reqs,
@@ -130,37 +130,10 @@ def test_maybe_prepare_dcp_local_seq_lens_uses_shared_buffer(monkeypatch):
130 assert batch.dcp_local_seq_lens.tolist() == [1, 2, 0, 0]
131
132
133 -def test_maybe_prepare_dcp_local_seq_lens_clears_stale_metadata(monkeypatch):
134 - """dcp_size == 1 resets the field to None instead of leaving a leftover.
135 -
136 - DCP is toggled per deployment, and batches are recycled; a value from an
137 - earlier DCP batch would otherwise be consumed as if it were current.
138 - """
139 - buffers = InputBuffers(max_num_reqs=2, max_num_tokens=2, device=torch.device("cpu"))
140 - batch = InputBatch.make_dummy(1, 1, buffers)
141 - batch.dcp_local_seq_lens = buffers.dcp_local_seq_lens[:1]
142 -
143 - def fail_if_called(*args, **kwargs):
144 - raise AssertionError("kernel must not run with dcp_size == 1")
145 -
146 - monkeypatch.setattr(cp_utils, "_dcp_local_seq_lens_kernel", fail_if_called)
147 - batch.dcp_local_seq_lens = cp_utils.maybe_prepare_dcp_local_seq_lens(
148 - buffers.dcp_local_seq_lens,
149 - batch.seq_lens,
150 - batch.num_reqs,
151 - 1,
152 - 0,
153 - 1,
154 - num_reqs_padded=batch.num_reqs_after_padding,
155 - )
156 -
157 - assert batch.dcp_local_seq_lens is None
158 -
159 -
133 @pytest.mark.skipif(not torch.cuda.is_available(), reason="triton kernel needs CUDA")
134 @pytest.mark.parametrize("dcp_size", [2, 4])
135 @pytest.mark.parametrize("cp_interleave", [1, 16])
163 -def test_maybe_prepare_dcp_local_seq_lens_matches_reference(
136 +def test_prepare_dcp_local_seq_lens_matches_reference(
137 dcp_size: int, cp_interleave: int
138 ):
139 """Every rank's local lengths must equal the reference torch formula."""
@@ -172,7 +145,7 @@ def test_maybe_prepare_dcp_local_seq_lens_matches_reference(
145
146 for dcp_rank in range(dcp_size):
147 buffers.dcp_local_seq_lens.fill_(-1)
175 - batch.dcp_local_seq_lens = cp_utils.maybe_prepare_dcp_local_seq_lens(
148 + batch.dcp_local_seq_lens = cp_utils.prepare_dcp_local_seq_lens(
149 buffers.dcp_local_seq_lens,
150 batch.seq_lens,
151 batch.num_reqs,
tests/v1/worker/test_gpu_pcp_manager.py
+31 -23
@@ -2,7 +2,6 @@
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3 from dataclasses import replace
4 from types import SimpleNamespace
5 -from unittest.mock import MagicMock
5
6 import numpy as np
7 import pytest
@@ -170,39 +169,45 @@ def test_graph_padding_cannot_be_smaller_than_largest_pcp_rank(monkeypatch):
169 )
170
171
172 +@pytest.mark.skipif(not torch.cuda.is_available(), reason="needs GPU kernels")
173 @pytest.mark.parametrize(
174 ("cg_mode", "num_reqs", "expected_tokens", "expected_reqs"),
175 [
176 - (CUDAGraphMode.NONE, 4, None, None),
177 - (CUDAGraphMode.PIECEWISE, None, 8, None),
176 + (CUDAGraphMode.NONE, 4, 2, 2),
177 + (CUDAGraphMode.PIECEWISE, None, 8, 2),
178 (CUDAGraphMode.FULL, 4, 8, 4),
179 ],
180 )
181 def test_partition_padding_is_derived_from_batch_descriptor(
182 cg_mode, num_reqs, expected_tokens, expected_reqs
183 ):
184 - manager = MagicMock()
185 - input_batch = MagicMock()
186 - manager.partition_batch.return_value = input_batch
187 - batch_desc = BatchExecutionDescriptor(
188 - cg_mode=cg_mode,
189 - num_tokens=8,
190 - num_reqs=num_reqs,
184 + """partition_batch derives graph padding from the batch descriptor: no
185 + padding without graphs, token padding only for PIECEWISE, and request
186 + padding on top for FULL."""
187 + device = torch.device("cuda:0")
188 + manager = PCPManager(
189 + pcp_world_size=2,
190 + pcp_rank=0,
191 + device=device,
192 + max_num_reqs=4,
193 + max_num_tokens=8,
194 )
192 -
193 - result = pcp_manager_module.maybe_partition_pcp_batch(
194 - manager,
195 - input_batch,
196 - batch_desc,
195 + global_batch = _make_global_decode_batch(
196 + [16, 24], InputBuffers(4, 8, device), device
197 )
198
199 - assert result is input_batch
200 - manager.partition_batch.assert_called_once_with(
201 - input_batch,
202 - padded_num_tokens=expected_tokens,
203 - padded_num_reqs=expected_reqs,
199 + local_batch = manager.partition_batch(
200 + global_batch,
201 + BatchExecutionDescriptor(
202 + cg_mode=cg_mode,
203 + num_tokens=8,
204 + num_reqs=num_reqs,
205 + ),
206 )
207
208 + assert local_batch.num_tokens_after_padding == expected_tokens
209 + assert local_batch.num_reqs_after_padding == expected_reqs
210 +
211
212 def test_capture_uses_pcp_persistent_inputs():
213 manager, _ = _make_capture_manager(torch.ones((4, 2), dtype=torch.int32))
@@ -472,8 +477,11 @@ def test_partition_defers_dcp_metadata_to_post_partition_batch():
477
478 local_batch = manager.partition_batch(
479 global_batch,
475 - padded_num_tokens=4,
476 - padded_num_reqs=4,
480 + BatchExecutionDescriptor(
481 + cg_mode=CUDAGraphMode.FULL,
482 + num_tokens=4,
483 + num_reqs=4,
484 + ),
485 )
486
487 assert local_batch.dcp_local_seq_lens is None
@@ -489,7 +497,7 @@ def test_partition_defers_dcp_metadata_to_post_partition_batch():
497
498 # What execute_model does next: derive DCP metadata from the final batch
499 # on the PCP-owned buffers.
492 - local_batch.dcp_local_seq_lens = gpu_cp_utils.maybe_prepare_dcp_local_seq_lens(
500 + local_batch.dcp_local_seq_lens = gpu_cp_utils.prepare_dcp_local_seq_lens(
501 manager.input_buffers.dcp_local_seq_lens,
502 local_batch.seq_lens,
503 local_batch.num_reqs,
tests/v1/worker/test_gpu_ubatch_slicing.py
+1 -1
@@ -756,7 +756,7 @@ def test_microbatches_recompute_dcp_lens_from_truncated_seq_lens(dcp_rank: int):
756 [1, 1, 10, 1, 1], [64, 96, 512, 32, 48]
757 )
758 # execute_model has populated the merged batch's DCP metadata already.
759 - input_batch.dcp_local_seq_lens = gpu_cp_utils.maybe_prepare_dcp_local_seq_lens(
759 + input_batch.dcp_local_seq_lens = gpu_cp_utils.prepare_dcp_local_seq_lens(
760 buffers.dcp_local_seq_lens,
761 input_batch.seq_lens,
762 input_batch.num_reqs,
vllm/v1/worker/gpu/block_table.py
+1 -2
@@ -59,8 +59,7 @@ class BlockTables:
59 self.block_tables.append(block_table)
60
61 self.num_blocks = UvaBackedTensor(
62 - (self.num_kv_cache_groups, self.max_num_reqs),
63 - dtype=torch.int32,
62 + (self.num_kv_cache_groups, self.max_num_reqs), dtype=torch.int32
63 )
64 self.fused_writer: FusedStagedWriter | None = None
65 if self.num_kv_cache_groups > 1:
vllm/v1/worker/gpu/cp_utils.py
+2 -3
@@ -5,7 +5,7 @@ import torch
5 from vllm.triton_utils import tl, triton
6
7
8 -def maybe_prepare_dcp_local_seq_lens(
8 +def prepare_dcp_local_seq_lens(
9 dcp_local_seq_lens: torch.Tensor,
10 seq_lens: torch.Tensor,
11 num_reqs: int,
@@ -16,8 +16,7 @@ def maybe_prepare_dcp_local_seq_lens(
16 num_reqs_padded: int | None = None,
17 ) -> torch.Tensor | None:
18 """Populate caller-owned storage and return its padded view, or None without DCP."""
19 - if dcp_size == 1:
20 - return None
19 + assert dcp_size > 1
20
21 max_num_reqs = dcp_local_seq_lens.shape[0]
22 BLOCK_SIZE = 128
vllm/v1/worker/gpu/cudagraph_utils.py
+12 -12
@@ -40,7 +40,7 @@ from vllm.v1.kv_cache_interface import KVCacheConfig
40 from vllm.v1.spec_decode.dynamic.utils import build_dynamic_sd_schedule_lookup
41 from vllm.v1.worker.gpu.attn_utils import build_slot_mappings_by_layer
42 from vllm.v1.worker.gpu.block_table import BlockTables
43 -from vllm.v1.worker.gpu.cp_utils import maybe_prepare_dcp_local_seq_lens
43 +from vllm.v1.worker.gpu.cp_utils import prepare_dcp_local_seq_lens
44 from vllm.v1.worker.gpu.input_batch import InputBatch, InputBuffers
45 from vllm.v1.worker.gpu.model_states.interface import ModelState
46 from vllm.v1.worker.ubatch_utils import check_ubatch_thresholds, get_num_ubatches
@@ -643,8 +643,7 @@ class ModelCudaGraphManager(CudaGraphManager):
643 self.intermediate_tensors[k][:num_tokens] = v
644
645 def create_forward_fn(
646 - desc: BatchExecutionDescriptor,
647 - warmup: bool,
646 + desc: BatchExecutionDescriptor, warmup: bool
647 ) -> Callable[[CUDAGraphMode], None]:
648 num_tokens = desc.num_tokens
649 num_reqs = desc.num_reqs or min(num_tokens, self.max_num_reqs)
@@ -789,15 +788,16 @@ def prepare_inputs_to_capture(
788 slot_mappings, kv_cache_config
789 )
790
792 - input_batch.dcp_local_seq_lens = maybe_prepare_dcp_local_seq_lens(
793 - input_buffers.dcp_local_seq_lens,
794 - input_batch.seq_lens,
795 - input_batch.num_reqs,
796 - block_tables.cp_size,
797 - block_tables.cp_rank,
798 - block_tables.cp_interleave,
799 - num_reqs_padded=input_batch.num_reqs_after_padding,
800 - )
791 + if block_tables.cp_size > 1:
792 + input_batch.dcp_local_seq_lens = prepare_dcp_local_seq_lens(
793 + input_buffers.dcp_local_seq_lens,
794 + input_batch.seq_lens,
795 + input_batch.num_reqs,
796 + block_tables.cp_size,
797 + block_tables.cp_rank,
798 + block_tables.cp_interleave,
799 + num_reqs_padded=input_batch.num_reqs_after_padding,
800 + )
801
802 # NOTE(woosuk): Attention metadata is required not just by standard attention
803 # kernels, but also by specialized attention-like operations (e.g., Inkling's sconv,
vllm/v1/worker/gpu/ec_connector.py
+3 -10
@@ -29,10 +29,7 @@ class ECConnector:
29 ) -> Generator[ECConnectorOutput | None, None, None]:
30 yield None
31
32 - def no_forward(
33 - self,
34 - scheduler_output: "SchedulerOutput",
35 - ) -> ModelRunnerOutput:
32 + def no_forward(self, scheduler_output: "SchedulerOutput") -> ModelRunnerOutput:
33 return EMPTY_MODEL_RUNNER_OUTPUT
34
35
@@ -84,10 +81,7 @@ class ActiveECConnector(ECConnector):
81 output.ec_connector_worker_meta = ec_connector.build_connector_worker_meta()
82 ec_connector.clear_connector_metadata()
83
87 - def no_forward(
88 - self,
89 - scheduler_output: "SchedulerOutput",
90 - ) -> ModelRunnerOutput:
84 + def no_forward(self, scheduler_output: "SchedulerOutput") -> ModelRunnerOutput:
85 # EC send/recv even if no work to do.
86 with self.maybe_get_output(scheduler_output) as ec_connector_output:
87 pass
@@ -99,8 +93,7 @@ NO_OP_EC_CONNECTOR = ECConnector()
93
94
95 def get_ec_connector(
102 - vllm_config: VllmConfig,
103 - encoder_cache: "EncoderCache | None",
96 + vllm_config: VllmConfig, encoder_cache: "EncoderCache | None"
97 ) -> ECConnector:
98 if (
99 not has_ec_transfer()
vllm/v1/worker/gpu/eplb_utils.py
+5 -18
@@ -77,19 +77,13 @@ class EPLBController:
77 assert speculative_config is not None
78 assert speculative_config.draft_model_config is not None
79 assert self.state is not None
80 - self.state.add_model(
81 - draft_moe_model,
82 - speculative_config.draft_model_config,
83 - )
80 + self.state.add_model(draft_moe_model, speculative_config.draft_model_config)
81 speculator.set_eplb_state(self.state)
82 self._has_registered_models = True
83 return True
84
85 def maybe_register_model(
89 - self,
90 - model: nn.Module,
91 - model_config: Any,
92 - load_dummy_weights: bool,
86 + self, model: nn.Module, model_config: Any, load_dummy_weights: bool
87 ) -> bool:
88 if not self.parallel_config.enable_eplb or load_dummy_weights:
89 return False
@@ -110,11 +104,7 @@ class EPLBController:
104 if eplb_models_added and self.state is not None and self.state.is_async:
105 self.state.start_async_loop()
106
113 - def step(
114 - self,
115 - is_dummy: bool = False,
116 - is_profile: bool = False,
117 - ) -> None:
107 + def step(self, is_dummy: bool = False, is_profile: bool = False) -> None:
108 if (
109 not self.parallel_config.enable_eplb
110 or self.suppressed
@@ -123,11 +113,8 @@ class EPLBController:
113 ):
114 return
115
126 - self.state.step(
127 - is_dummy,
128 - is_profile,
129 - log_stats=self.parallel_config.eplb_config.log_balancedness,
130 - )
116 + log_stats = self.parallel_config.eplb_config.log_balancedness
117 + self.state.step(is_dummy, is_profile, log_stats=log_stats)
118
119 def prepare_forward(
120 self,
vllm/v1/worker/gpu/input_batch.py
+3 -14
@@ -17,12 +17,7 @@ if TYPE_CHECKING:
17
18
19 class InputBuffers:
20 - def __init__(
21 - self,
22 - max_num_reqs: int,
23 - max_num_tokens: int,
24 - device: torch.device,
25 - ):
20 + def __init__(self, max_num_reqs: int, max_num_tokens: int, device: torch.device):
21 self.max_num_reqs = max_num_reqs
22 self.max_num_tokens = max_num_tokens
23 self.device = device
@@ -474,11 +469,7 @@ def combine_sampled_and_draft_tokens(
469 num_reqs = idx_mapping.shape[0]
470 num_speculative_steps = draft_tokens.shape[-1]
471
477 - logits_indices = torch.empty(
478 - num_logits,
479 - dtype=torch.int64,
480 - device=input_ids.device,
481 - )
472 + logits_indices = torch.empty(num_logits, dtype=torch.int64, device=input_ids.device)
473 _combine_sampled_and_draft_tokens_kernel[(num_reqs,)](
474 input_ids,
475 idx_mapping,
@@ -711,9 +702,7 @@ def expand_idx_mapping(
702 ) -> tuple[torch.Tensor, torch.Tensor]:
703 num_reqs = idx_mapping.shape[0]
704 expanded_idx_mapping = idx_mapping.new_empty(total_num_logits)
714 - expanded_local_pos = torch.empty(
715 - total_num_logits, dtype=torch.int32, device=idx_mapping.device
716 - )
705 + expanded_local_pos = idx_mapping.new_empty(total_num_logits)
706 _expand_idx_mapping_kernel[(num_reqs,)](
707 idx_mapping,
708 expanded_idx_mapping,
vllm/v1/worker/gpu/model_runner.py
+44 -71
@@ -36,10 +36,7 @@ from vllm.distributed.aux_output_connector.worker import (
36 AuxOutputWorkerConnector,
37 get_aux_output_connector,
38 )
39 -from vllm.distributed.parallel_state import (
40 - get_dcp_group,
41 - get_pp_group,
42 -)
39 +from vllm.distributed.parallel_state import get_dcp_group, get_pp_group
40 from vllm.forward_context import BatchDescriptor, set_forward_context
41 from vllm.logger import init_logger
42 from vllm.model_executor.layers.fused_moe.all2all_utils import get_ep_all2all_manager
@@ -97,7 +94,7 @@ from vllm.v1.worker.gpu.attn_utils import (
94 )
95 from vllm.v1.worker.gpu.block_table import BlockTables
96 from vllm.v1.worker.gpu.buffer_utils import set_default_max_concurrency
100 -from vllm.v1.worker.gpu.cp_utils import maybe_prepare_dcp_local_seq_lens
97 +from vllm.v1.worker.gpu.cp_utils import prepare_dcp_local_seq_lens
98 from vllm.v1.worker.gpu.cudagraph_utils import (
99 BatchExecutionDescriptor,
100 ModelCudaGraphManager,
@@ -494,8 +491,7 @@ class GPUModelRunner(LoRAModelRunnerMixin):
491 ),
492 )
493 self.prompt_logprobs_worker = PromptLogprobsWorker(
497 - self.max_num_reqs,
498 - logprobs_mode=self.model_config.logprobs_mode,
494 + self.max_num_reqs, logprobs_mode=self.model_config.logprobs_mode
495 )
496 self.structured_outputs_worker = StructuredOutputsWorker(
497 max_num_logits=self.max_num_reqs * self.decode_query_len,
@@ -508,9 +504,7 @@ class GPUModelRunner(LoRAModelRunnerMixin):
504 if self.is_pooling_model and self.is_last_pp_rank:
505 self.pooling_runner = PoolingRunner(self.model, self.vllm_config)
506 eplb_models_added |= self.eplb.maybe_register_model(
511 - self.model,
512 - self.model_config,
513 - load_dummy_weights,
507 + self.model, self.model_config, load_dummy_weights
508 )
509 self.eplb.maybe_start_async_loop(eplb_models_added)
510
@@ -869,14 +863,10 @@ class GPUModelRunner(LoRAModelRunnerMixin):
863 self.step_timing.drafter_start()
864 mm_inputs: tuple[list[torch.Tensor], torch.Tensor] | None = None
865 if self.speculator.supports_mm_inputs:
872 - mm_inputs = (
873 - [],
874 - torch.zeros(
875 - input_batch.num_tokens,
876 - dtype=torch.bool,
877 - device="cpu",
878 - ),
866 + all_false = torch.zeros(
867 + input_batch.num_tokens, dtype=torch.bool, device="cpu"
868 )
869 + mm_inputs = [], all_false
870
871 # Let the target override the hidden state fed to the drafter
872 # (e.g. DeepSeek V4 MTP needs the pre-hc_head residual). The
@@ -945,9 +935,7 @@ class GPUModelRunner(LoRAModelRunnerMixin):
935 mm_config = self.model_config.multimodal_config
936 if mm_config is not None and not mm_config.skip_mm_profiling:
937 mm_budget = MultiModalBudget(
948 - self.vllm_config,
949 - self.mm_registry,
950 - enable_cache=False,
938 + self.vllm_config, self.mm_registry, enable_cache=False
939 )
940 dummy_mm_inputs = mm_budget.get_dummy_encoder_profile_inputs()
941 self.model_state.encoder_runner.profile_encoder_cache(
@@ -1294,7 +1282,9 @@ class GPUModelRunner(LoRAModelRunnerMixin):
1282 req_ids = batch_req_state.req_ids
1283 num_scheduled_tokens_np = batch_req_state.num_scheduled_tokens
1284 idx_mapping_np = batch_req_state.idx_mapping_np
1297 - idx_mapping = async_tensor_h2d(idx_mapping_np, device=self.device)
1285 + idx_mapping = async_tensor_h2d(
1286 + idx_mapping_np, device=self.device, dtype=torch.int32
1287 + )
1288 num_reqs = len(req_ids)
1289
1290 # Get the number of draft tokens for each request.
@@ -1471,11 +1461,8 @@ class GPUModelRunner(LoRAModelRunnerMixin):
1461 else None
1462 ),
1463 )
1474 - input_batch = pcp.maybe_partition_pcp_batch(
1475 - self.pcp_manager,
1476 - input_batch,
1477 - batch_desc,
1478 - )
1464 + if self.pcp_manager is not None:
1465 + input_batch = self.pcp_manager.partition_batch(input_batch, batch_desc)
1466 return input_batch
1467
1468 def prepare_attn(
@@ -1486,8 +1473,7 @@ class GPUModelRunner(LoRAModelRunnerMixin):
1473
1474 # Block tables: num_kv_cache_groups x [num_reqs_padded, max_num_blocks].
1475 block_tables = self.block_tables.gather_block_tables(
1489 - input_batch.idx_mapping,
1490 - num_reqs_padded=input_batch.num_reqs_after_padding,
1476 + input_batch.idx_mapping, num_reqs_padded=input_batch.num_reqs_after_padding
1477 )
1478 # Slot mappings: [num_kv_cache_groups, num_tokens_padded].
1479 # Kernel pads beyond num_tokens with PAD_SLOT_ID.
@@ -1506,15 +1492,12 @@ class GPUModelRunner(LoRAModelRunnerMixin):
1492 block_tables = block_table_provider.get_dummy_block_tables(input_batch.num_reqs)
1493 if valid_state_slots:
1494 state_slots = torch.arange(
1509 - 1,
1510 - input_batch.num_reqs + 1,
1511 - dtype=torch.int32,
1512 - device=self.device,
1495 + 1, input_batch.num_reqs + 1, dtype=torch.int32, device=self.device
1496 )
1497 for block_table in block_tables:
1498 block_table[:, 0].copy_(state_slots)
1516 - slot_mappings = pcp.maybe_get_pcp_dummy_slot_mappings(
1517 - self.pcp_manager, self.block_tables, input_batch.num_tokens
1499 + slot_mappings = block_table_provider.get_dummy_slot_mappings(
1500 + input_batch.num_tokens
1501 )
1502 return block_tables, slot_mappings
1503
@@ -1607,11 +1590,10 @@ class GPUModelRunner(LoRAModelRunnerMixin):
1590 query_start_loc: torch.Tensor | None = None,
1591 ) -> None:
1592 # Update the number of computed tokens.
1593 + output_bin_counts = None
1594 if self.is_last_pp_rank:
1595 assert self.sampler is not None
1596 output_bin_counts = self.sampler.penalties_state.output_bin_counts
1613 - else:
1614 - output_bin_counts = None
1597 post_update(
1598 idx_mapping,
1599 self.req_states.num_computed_tokens.gpu,
@@ -1769,32 +1751,27 @@ class GPUModelRunner(LoRAModelRunnerMixin):
1751 "Attention metadata must be prepared for dummy runs when using "
1752 "FULL cudagraph mode."
1753 )
1772 - block_tables = None
1773 - slot_mappings = None
1754 + block_tables, slot_mappings = None, None
1755 else:
1756 block_tables, slot_mappings = self.prepare_dummy_attn(
1757 input_batch, valid_dummy_state_slots
1758 )
1778 - if not skip_attn_for_dummy_run and context_len:
1779 - assert block_tables is not None
1780 - set_dummy_context(
1781 - input_batch,
1782 - self.block_tables,
1783 - context_len,
1784 - self.kv_cache_config.num_blocks,
1785 - self.max_model_len,
1786 - input_block_tables=block_tables,
1787 - )
1759 + if context_len:
1760 + set_dummy_context(
1761 + input_batch,
1762 + self.block_tables,
1763 + context_len,
1764 + self.kv_cache_config.num_blocks,
1765 + self.max_model_len,
1766 + input_block_tables=block_tables,
1767 + )
1768
1789 - attn_metadata = None
1790 - slot_mappings_by_layer = None
1791 - if not (dummy_run and skip_attn_for_dummy_run):
1792 - input_batch.dcp_local_seq_lens = maybe_prepare_dcp_local_seq_lens(
1793 - (
1794 - self.pcp_manager.input_buffers
1795 - if self.pcp_manager is not None
1796 - else self.input_buffers
1797 - ).dcp_local_seq_lens,
1769 + if self.dcp_size > 1 and not (dummy_run and skip_attn_for_dummy_run):
1770 + input_buffers = self.input_buffers
1771 + if self.pcp_manager is not None:
1772 + input_buffers = self.pcp_manager.input_buffers
1773 + input_batch.dcp_local_seq_lens = prepare_dcp_local_seq_lens(
1774 + input_buffers.dcp_local_seq_lens,
1775 input_batch.seq_lens,
1776 input_batch.num_reqs,
1777 self.dcp_size,
@@ -1802,6 +1779,9 @@ class GPUModelRunner(LoRAModelRunnerMixin):
1779 self.cp_interleave,
1780 num_reqs_padded=input_batch.num_reqs_after_padding,
1781 )
1782 +
1783 + attn_metadata = None
1784 + slot_mappings_by_layer = None
1785 ubatch_state: UBatchState | None = None
1786 if batch_desc.num_ubatches > 1:
1787 assert self.ubatch_runner is not None
@@ -2046,16 +2026,12 @@ class GPUModelRunner(LoRAModelRunnerMixin):
2026 return ModelRunnerOutput.with_ec_conn_output(output, ec_connector_output)
2027
2028 # Last rank: sample tokens
2029 + assert hidden_states is not None
2030 draft_hidden_states = hidden_states
2050 - assert draft_hidden_states is not None
2051 - hidden_states, input_batch = pcp.maybe_restore_pcp_for_sampling(
2052 - self.pcp_manager, hidden_states, input_batch
2053 - )
2054 - if self.pcp_manager is not None and aux_hidden_states is not None:
2055 - aux_hidden_states = [
2056 - self.pcp_manager.restore_hidden_states(states)
2057 - for states in aux_hidden_states
2058 - ]
2031 + if self.pcp_manager is not None:
2032 + hidden_states, aux_hidden_states, input_batch = (
2033 + self.pcp_manager.restore_for_sampling(hidden_states, aux_hidden_states)
2034 + )
2035
2036 sampler_output, num_sampled, num_rejected = self.sample(
2037 hidden_states, input_batch, grammar_output
@@ -2305,13 +2281,10 @@ class GPUModelRunner(LoRAModelRunnerMixin):
2281 self.eplb.suppressed = suppressed
2282
2283 def setup_eplb_from_mapping(
2308 - self,
2309 - expanded_physical_to_logical: torch.Tensor,
2284 + self, expanded_physical_to_logical: torch.Tensor
2285 ) -> None:
2286 self.eplb.setup_from_mapping(
2312 - self.model,
2313 - self.model_config,
2314 - expanded_physical_to_logical,
2287 + self.model, self.model_config, expanded_physical_to_logical
2288 )
2289
2290 ########### EPLB methods end ###########
vllm/v1/worker/gpu/model_states/encoder_decoder.py
+1 -3
@@ -27,9 +27,7 @@ class EncoderDecoderAttnMetadata(ModelSpecificAttnMetadata):
27 encoder_seq_lens: dict[int, tuple[torch.Tensor, np.ndarray]]
28
29 def get_extra_common_attn_kwargs(
30 - self,
31 - kv_cache_group_id: int,
32 - num_reqs: int,
30 + self, kv_cache_group_id: int, num_reqs: int
31 ) -> dict[str, Any]:
32 encoder_seq_lens = self.encoder_seq_lens.get(kv_cache_group_id)
33 if encoder_seq_lens is None:
vllm/v1/worker/gpu/model_states/encoder_only.py
+1 -4
@@ -188,10 +188,7 @@ class EncoderOnlyModelState(DefaultModelState):
188 return attn_metadata
189
190 def _build_encoder_attn_metadata(
191 - self,
192 - input_batch: InputBatch,
193 - cudagraph_mode: CUDAGraphMode,
194 - for_capture: bool,
191 + self, input_batch: InputBatch, cudagraph_mode: CUDAGraphMode, for_capture: bool
192 ) -> dict[str, Any]:
193 if cudagraph_mode == CUDAGraphMode.FULL:
194 num_reqs = input_batch.num_reqs_after_padding
vllm/v1/worker/gpu/model_states/interface.py
+2 -6
@@ -28,16 +28,12 @@ class ModelSpecificAttnMetadata:
28 """Base class for model-specific attention metadata."""
29
30 def get_extra_common_attn_kwargs(
31 - self,
32 - kv_cache_group_id: int,
33 - num_reqs: int,
31 + self, kv_cache_group_id: int, num_reqs: int
32 ) -> dict[str, Any]:
33 return {}
34
35 def get_extra_attn_kwargs(
38 - self,
39 - attn_metadata_builder: Any,
40 - num_reqs: int,
36 + self, attn_metadata_builder: Any, num_reqs: int
37 ) -> dict[str, Any]:
38 return {}
39
vllm/v1/worker/gpu/model_states/mamba_hybrid.py
+3 -9
@@ -43,16 +43,12 @@ class MambaHybridAttnMetadata(ModelSpecificAttnMetadata):
43 num_decode_draft_tokens_cpu: torch.Tensor | None = None
44
45 def get_extra_common_attn_kwargs(
46 - self,
47 - kv_cache_group_id: int,
48 - num_reqs: int,
46 + self, kv_cache_group_id: int, num_reqs: int
47 ) -> dict[str, Any]:
48 return {"is_prefilling": self.is_prefilling[:num_reqs]}
49
50 def get_extra_attn_kwargs(
53 - self,
54 - attn_metadata_builder: Any,
55 - num_reqs: int,
51 + self, attn_metadata_builder: Any, num_reqs: int
52 ) -> dict[str, Any]:
53 if not isinstance(
54 attn_metadata_builder,
@@ -338,9 +334,7 @@ class MambaHybridModelState(DefaultModelState):
334 )
335 if self.recoverssm is not None:
336 self.recoverssm.record_step(
341 - attn_metadata,
342 - attn_groups,
343 - for_capture=for_capture,
337 + attn_metadata, attn_groups, for_capture=for_capture
338 )
339 return attn_metadata
340
vllm/v1/worker/gpu/pcp_manager.py
+21 -64
@@ -192,8 +192,7 @@ class PCPManager:
192
193 @staticmethod
194 def _reorder_segments(
195 - segments: list[RankSegment],
196 - is_prefilling: np.ndarray,
195 + segments: list[RankSegment], is_prefilling: np.ndarray
196 ) -> list[RankSegment]:
197 """Order this rank's rows decodes-first, then prefills, canonically."""
198
@@ -218,9 +217,7 @@ class PCPManager:
217 return segments
218
219 def replicated_requests(
221 - self,
222 - num_scheduled_tokens: np.ndarray,
223 - is_prefilling: np.ndarray,
220 + self, num_scheduled_tokens: np.ndarray, is_prefilling: np.ndarray
221 ) -> np.ndarray:
222 """Per global request, whether every PCP rank gets the whole query."""
223 num_chunks = 2 * self.pcp_world_size
@@ -407,10 +404,7 @@ class PCPManager:
404 return self._input_buffers
405
406 def partition_batch(
410 - self,
411 - input_batch: InputBatch,
412 - padded_num_tokens: int | None = None,
413 - padded_num_reqs: int | None = None,
407 + self, input_batch: InputBatch, batch_desc: "BatchExecutionDescriptor"
408 ) -> InputBatch:
409 assert self._input_buffers is not None
410 input_buffers = self._input_buffers
@@ -422,6 +416,13 @@ class PCPManager:
416 num_computed_tokens = global_batch.num_computed_tokens_np
417 is_prefilling = global_batch.is_prefilling_np
418
419 + padded_num_tokens = None
420 + padded_num_reqs = None
421 + if batch_desc.cg_mode != CUDAGraphMode.NONE:
422 + padded_num_tokens = batch_desc.num_tokens
423 + if batch_desc.cg_mode == CUDAGraphMode.FULL:
424 + padded_num_reqs = batch_desc.num_reqs
425 +
426 segments_by_rank, per_rank_num_tokens = self._build_batch_layout(
427 num_scheduled_tokens,
428 num_computed_tokens,
@@ -442,9 +443,7 @@ class PCPManager:
443
444 num_local_reqs = len(local_segments)
445 num_reqs_after_padding = self._resolve_num_reqs_after_padding(
445 - global_batch,
446 - padded_num_reqs,
447 - num_local_reqs,
446 + global_batch, padded_num_reqs, num_local_reqs
447 )
448 if num_reqs_after_padding > input_buffers.max_num_reqs:
449 raise RuntimeError(
@@ -714,8 +713,7 @@ class PCPManager:
713 return self._gathered_kv_slot_mappings[:, : num_tokens * self.pcp_world_size]
714
715 def _convert_to_gathered_slot_mappings(
717 - self,
718 - global_batch_slot_mappings: torch.Tensor,
716 + self, global_batch_slot_mappings: torch.Tensor
717 ) -> torch.Tensor:
718 assert self._padded_gather_idx is not None
719 assert self._gathered_kv_write_mask is not None
@@ -754,9 +752,7 @@ class PCPManager:
752 return self.draft_prefill_batch or input_buffers
753
754 def prepare_draft_prefill(
757 - self,
758 - input_batch: InputBatch,
759 - input_ids: torch.Tensor,
755 + self, input_batch: InputBatch, input_ids: torch.Tensor
756 ) -> None:
757 if input_batch is not self._global_batch or self._local_batch is None:
758 return
@@ -789,54 +785,15 @@ class PCPManager:
785 return last_hidden_states, hidden_states
786
787 def restore_for_sampling(
792 - self,
793 - hidden_states: torch.Tensor,
794 - ) -> tuple[torch.Tensor, InputBatch]:
788 + self, hidden_states: torch.Tensor, aux_hidden_states: list[torch.Tensor] | None
789 + ) -> tuple[torch.Tensor, list[torch.Tensor] | None, InputBatch]:
790 assert self._global_batch is not None
796 - return self.restore_hidden_states(hidden_states), self._global_batch
797 -
798 -
799 -def maybe_partition_pcp_batch(
800 - manager: PCPManager | None,
801 - input_batch: InputBatch,
802 - batch_desc: "BatchExecutionDescriptor",
803 -) -> InputBatch:
804 - if manager is None:
805 - return input_batch
806 -
807 - padded_num_tokens = None
808 - padded_num_reqs = None
809 - if batch_desc.cg_mode != CUDAGraphMode.NONE:
810 - padded_num_tokens = batch_desc.num_tokens
811 - if batch_desc.cg_mode == CUDAGraphMode.FULL:
812 - padded_num_reqs = batch_desc.num_reqs
813 -
814 - return manager.partition_batch(
815 - input_batch,
816 - padded_num_tokens=padded_num_tokens,
817 - padded_num_reqs=padded_num_reqs,
818 - )
819 -
820 -
821 -def maybe_get_pcp_dummy_slot_mappings(
822 - manager: PCPManager | None,
823 - block_tables: BlockTables,
824 - num_tokens: int,
825 -) -> torch.Tensor:
826 - if manager is None:
827 - return block_tables.get_dummy_slot_mappings(num_tokens)
828 - return manager.get_dummy_slot_mappings(num_tokens)
829 -
830 -
831 -def maybe_restore_pcp_for_sampling(
832 - manager: PCPManager | None,
833 - hidden_states: torch.Tensor | None,
834 - input_batch: InputBatch,
835 -) -> tuple[torch.Tensor, InputBatch]:
836 - assert hidden_states is not None
837 - if manager is None:
838 - return hidden_states, input_batch
839 - return manager.restore_for_sampling(hidden_states)
791 + hidden_states = self.restore_hidden_states(hidden_states)
792 + if aux_hidden_states is not None:
793 + aux_hidden_states = [
794 + self.restore_hidden_states(states) for states in aux_hidden_states
795 + ]
796 + return hidden_states, aux_hidden_states, self._global_batch
797
798
799 def maybe_build_pcp_manager(
vllm/v1/worker/gpu/spec_decode/adaptive_verification.py
+4 -7
@@ -255,9 +255,7 @@ class AdaptiveVerificationManager:
255 logger.debug("DSpark cost tables: %s", self.cost_tables)
256
257 def record_confidences(
258 - self,
259 - confidence_probs: torch.Tensor,
260 - input_batch: "InputBatch",
258 + self, confidence_probs: torch.Tensor, input_batch: "InputBatch"
259 ) -> None:
260 """Publish this step's raw confidences for the ranking kernel and start
261 copying them to the CPU, where a later step's budget reads them."""
@@ -305,9 +303,9 @@ class AdaptiveVerificationManager:
303 )
304 num_non_draft_tokens = scheduled_tokens - scheduled_drafts
305 slots = np.fromiter(
308 - (self.req_states.req_id_to_index[req_id] for req_id in req_ids),
306 + map(self.req_states.req_id_to_index.__getitem__, req_ids),
307 dtype=np.int32,
310 - count=len(req_ids),
308 + count=num_reqs,
309 )
310 stale_confidences = self._stale_confidences[self._stale_idx].np[slots]
311 survival_probability = np.cumprod(stale_confidences.astype(np.float64), axis=1)
@@ -469,8 +467,7 @@ def maybe_create_adaptive_verification_manager(
467 # The selector rejects unsupported backends, but models that
468 # hard-wire theirs (e.g. DeepSeek-V4) never go through it.
469 backend = get_query_lens_mismatch_unsupported_backend(
472 - attn_groups,
473 - checked_layer_names=target_layer_names,
470 + attn_groups, checked_layer_names=target_layer_names
471 )
472 if backend is not None:
473 raise ValueError(
vllm/v1/worker/gpu/spec_decode/autoregressive/speculator.py
+3 -10
@@ -53,10 +53,7 @@ class AutoRegressiveSpeculator(DraftModelSpeculator):
53 return
54
55 self.inputs_embeds = torch.zeros(
56 - self.max_num_tokens,
57 - self.hidden_size,
58 - dtype=self.dtype,
59 - device=self.device,
56 + self.max_num_tokens, self.hidden_size, dtype=self.dtype, device=self.device
57 )
58
59 # Lifecycle hooks for model-specific optimizations. Subclasses override
@@ -140,10 +137,7 @@ class AutoRegressiveSpeculator(DraftModelSpeculator):
137
138 # Initialize cudagraph manager for draft decodes (draft positions > 0).
139 self.decode_cudagraph_manager = SpeculatorCudaGraphManager(
143 - self.vllm_config,
144 - self.device,
145 - cudagraph_mode,
146 - decode_query_len=1,
140 + self.vllm_config, self.device, cudagraph_mode, decode_query_len=1
141 )
142
143 def capture(self) -> None:
@@ -272,8 +266,7 @@ class AutoRegressiveSpeculator(DraftModelSpeculator):
266
267 if self.pcp_manager is not None:
268 self.pcp_manager.prepare_draft_prefill(
275 - input_batch,
276 - self.input_buffers.input_ids[:num_tokens_padded],
269 + input_batch, self.input_buffers.input_ids[:num_tokens_padded]
270 )
271 prefill = self.pcp_manager.draft_prefill_batch
272 if prefill is not None:
vllm/v1/worker/gpu/spec_decode/dflash/cudagraph.py
+11 -10
@@ -11,7 +11,7 @@ from vllm.v1.worker.gpu.attn_utils import (
11 build_slot_mappings_by_layer,
12 )
13 from vllm.v1.worker.gpu.block_table import BlockTables
14 -from vllm.v1.worker.gpu.cp_utils import maybe_prepare_dcp_local_seq_lens
14 +from vllm.v1.worker.gpu.cp_utils import prepare_dcp_local_seq_lens
15 from vllm.v1.worker.gpu.cudagraph_utils import (
16 AttentionState,
17 BatchExecutionDescriptor,
@@ -42,15 +42,16 @@ def _prepare_dflash_inputs_to_capture(
42 attn_metadata = None
43 if not skip_attn:
44 query_start_loc_cpu = torch.from_numpy(input_batch.query_start_loc_np)
45 - input_batch.dcp_local_seq_lens = maybe_prepare_dcp_local_seq_lens(
46 - input_buffers.dcp_local_seq_lens,
47 - input_batch.seq_lens,
48 - input_batch.num_reqs,
49 - block_tables.cp_size,
50 - block_tables.cp_rank,
51 - block_tables.cp_interleave,
52 - num_reqs_padded=input_batch.num_reqs_after_padding,
53 - )
45 + if block_tables.cp_size > 1:
46 + input_batch.dcp_local_seq_lens = prepare_dcp_local_seq_lens(
47 + input_buffers.dcp_local_seq_lens,
48 + input_batch.seq_lens,
49 + input_batch.num_reqs,
50 + block_tables.cp_size,
51 + block_tables.cp_rank,
52 + block_tables.cp_interleave,
53 + num_reqs_padded=input_batch.num_reqs_after_padding,
54 + )
55 attn_metadata = build_attn_metadata(
56 attn_groups=attn_groups,
57 num_reqs=num_reqs,
vllm/v1/worker/gpu/spec_decode/dspark/speculator.py
+1 -4
@@ -149,10 +149,7 @@ class DSparkSpeculator(DFlashSpeculator):
149 )
150 if self.draft_watermarker is not None:
151 sampled = self.draft_watermarker.sample(
152 - logits,
153 - sampled,
154 - idx_map,
155 - self.temperature,
152 + logits, sampled, idx_map, self.temperature
153 )
154 return sampled
155
vllm/v1/worker/gpu/spec_decode/rejection_sampler.py
+1 -2
@@ -134,8 +134,7 @@ class RejectionSampler:
134 if expanded_logits:
135 if self.enable_adaptive_verification:
136 # Adaptive verification keeps the true per-request boundaries
137 - # on device only; cu_num_logits_np holds the pre-compacted
138 - # layout.
137 + # on device only; cu_num_logits_np holds the pre-compacted layout.
138 cu_num_generated_tokens = cu_num_logits.clone()
139 else:
140 cu_num_generated_tokens = cu_num_logits_np.tolist()
vllm/v1/worker/gpu/spec_decode/speculator.py
+5 -13
@@ -26,7 +26,7 @@ from vllm.v1.worker.gpu.attn_utils import (
26 init_attn_backend,
27 )
28 from vllm.v1.worker.gpu.block_table import BlockTables
29 -from vllm.v1.worker.gpu.cp_utils import maybe_prepare_dcp_local_seq_lens
29 +from vllm.v1.worker.gpu.cp_utils import prepare_dcp_local_seq_lens
30 from vllm.v1.worker.gpu.cudagraph_utils import BatchExecutionDescriptor
31 from vllm.v1.worker.gpu.dp_utils import DPSyncState
32 from vllm.v1.worker.gpu.input_batch import InputBatch, InputBuffers
@@ -202,9 +202,7 @@ class DraftModelSpeculator(BaseSpeculator):
202
203 @abstractmethod
204 def load_draft_model(
205 - self,
206 - target_model: nn.Module,
207 - target_attn_layer_names: set[str],
205 + self, target_model: nn.Module, target_attn_layer_names: set[str]
206 ) -> nn.Module:
207 pass
208
@@ -337,7 +335,7 @@ class DraftModelSpeculator(BaseSpeculator):
335 if dcp_local_seq_lens is None and self.block_tables.cp_size > 1:
336 # Draft steps advance and rewind their own global sequence lengths,
337 # so the target model's DCP-local lengths may already be stale.
340 - dcp_local_seq_lens = maybe_prepare_dcp_local_seq_lens(
338 + dcp_local_seq_lens = prepare_dcp_local_seq_lens(
339 self.input_buffers.dcp_local_seq_lens,
340 self.input_buffers.seq_lens,
341 num_reqs,
@@ -422,10 +420,7 @@ class DraftModelSpeculator(BaseSpeculator):
420 )
421 if self.draft_watermarker is not None:
422 sampled = self.draft_watermarker.sample(
425 - logits,
426 - sampled,
427 - idx_mapping,
428 - temperature,
423 + logits, sampled, idx_mapping, temperature
424 )
425 elif self.use_local_argmax_reduction:
426 return self.model.get_top_tokens(hidden_states)
@@ -436,10 +431,7 @@ class DraftModelSpeculator(BaseSpeculator):
431 return sampled
432
433 def _maybe_predict_acceptance(
439 - self,
440 - logits: torch.Tensor,
441 - idx_mapping: torch.Tensor,
442 - draft_step: torch.Tensor,
434 + self, logits: torch.Tensor, idx_mapping: torch.Tensor, draft_step: torch.Tensor
435 ) -> None:
436 if self.acceptance_estimator is not None:
437 self.acceptance_estimator.predict(
vllm/v1/worker/gpu/states.py
+1 -4
@@ -78,10 +78,7 @@ class RequestState:
78 )
79
80 self.next_prefill_tokens = torch.zeros(
81 - num_prefill_lookahead,
82 - self.max_num_reqs,
83 - dtype=torch.int32,
84 - device=device,
81 + num_prefill_lookahead, self.max_num_reqs, dtype=torch.int32, device=device
82 )
83
84 @property
vllm/v1/worker/gpu/ubatch_utils.py
+2 -2
@@ -24,7 +24,7 @@ from vllm.sequence import IntermediateTensors
24 from vllm.utils.torch_utils import current_stream
25 from vllm.v1.kv_cache_interface import KVCacheConfig
26 from vllm.v1.worker.gpu.attn_utils import build_slot_mappings_by_layer
27 -from vllm.v1.worker.gpu.cp_utils import maybe_prepare_dcp_local_seq_lens
27 +from vllm.v1.worker.gpu.cp_utils import prepare_dcp_local_seq_lens
28 from vllm.v1.worker.gpu.input_batch import InputBatch
29 from vllm.v1.worker.gpu.model_states.interface import ModelState
30 from vllm.v1.worker.ubatch_utils import (
@@ -150,7 +150,7 @@ def _slice_input_batch(
150 dcp_local_seq_lens = None
151 if dcp_size > 1:
152 assert dcp_local_seq_lens_buf is not None
153 - dcp_local_seq_lens = maybe_prepare_dcp_local_seq_lens(
153 + dcp_local_seq_lens = prepare_dcp_local_seq_lens(
154 dcp_local_seq_lens_buf,
155 seq_lens,
156 num_reqs,