From b53c507bc91f87e28b03e9b54bbff7c76e97d58b Mon Sep 17 00:00:00 2001 From: wi-adam Date: Tue, 5 May 2026 22:31:14 -0700 Subject: [PATCH] [Bugfix] Skip PP sampled-token receive on last rank during async scheduling (#40749) Signed-off-by: Adam Winstanley --- tests/v1/worker/test_gpu_model_runner.py | 53 ++++++++++++++++++++++++ vllm/v1/worker/gpu_model_runner.py | 2 +- 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/tests/v1/worker/test_gpu_model_runner.py b/tests/v1/worker/test_gpu_model_runner.py index 0de443858c9..ceea6cacf0a 100644 --- a/tests/v1/worker/test_gpu_model_runner.py +++ b/tests/v1/worker/test_gpu_model_runner.py @@ -7,6 +7,7 @@ import numpy as np import pytest import torch +import vllm.v1.worker.gpu_model_runner as gpu_model_runner_module from vllm.config import ( AttentionConfig, CacheConfig, @@ -219,6 +220,58 @@ def test_select_common_block_size_uses_largest_shared_int(): assert selected_size == 64 +@pytest.mark.skip_global_cleanup +@pytest.mark.parametrize( + ("world_size", "is_last_rank", "expected_calls"), + [(1, True, 0), (2, True, 0), (2, False, 1)], +) +def test_sample_tokens_receives_pp_sampled_ids_only_on_non_last_rank( + monkeypatch: pytest.MonkeyPatch, + world_size: int, + is_last_rank: bool, + expected_calls: int, +): + runner = GPUModelRunner.__new__(GPUModelRunner) + runner.execute_model_state = None + runner.kv_connector_output = None + runner.use_async_scheduling = True + receive_calls = 0 + + def receive_prev_sampled_token_ids(): + nonlocal receive_calls + receive_calls += 1 + + runner._pp_receive_prev_sampled_token_ids_to_input_batch = ( + receive_prev_sampled_token_ids + ) + monkeypatch.setattr( + gpu_model_runner_module, + "get_pp_group", + lambda: SimpleNamespace(world_size=world_size, is_last_rank=is_last_rank), + ) + + assert GPUModelRunner.sample_tokens(runner, None) is None + assert receive_calls == expected_calls + + +@pytest.mark.skip_global_cleanup +def test_sample_tokens_skips_pp_group_lookup_without_async_scheduling( + monkeypatch: pytest.MonkeyPatch, +): + runner = GPUModelRunner.__new__(GPUModelRunner) + runner.execute_model_state = None + runner.kv_connector_output = None + runner.use_async_scheduling = False + + monkeypatch.setattr( + gpu_model_runner_module, + "get_pp_group", + pytest.fail, + ) + + assert GPUModelRunner.sample_tokens(runner, None) is None + + def test_select_common_block_size_no_valid_option(): backend_a = _make_mock_backend_for_kernel_block_size([64]) backend_b = _make_mock_backend_for_kernel_block_size([MultipleOf(16)]) diff --git a/vllm/v1/worker/gpu_model_runner.py b/vllm/v1/worker/gpu_model_runner.py index 0ca530c15ba..4975077c056 100644 --- a/vllm/v1/worker/gpu_model_runner.py +++ b/vllm/v1/worker/gpu_model_runner.py @@ -4182,7 +4182,7 @@ class GPUModelRunner( kv_connector_output = self.kv_connector_output self.kv_connector_output = None # receive sampled token ids from the last PP rank. - if self.use_async_scheduling and get_pp_group().world_size > 1: + if self.use_async_scheduling and not get_pp_group().is_last_rank: self._pp_receive_prev_sampled_token_ids_to_input_batch() if not kv_connector_output: return None # type: ignore[return-value]