Skip to content

Commit 20db643

Browse files
test(v1): exercise _sync_num_accepted_tokens directly in TestSyncNumAcceptedTokens
Signed-off-by: Manikanta Bandham <bandhammanikanta@gmail.com>
1 parent f14a730 commit 20db643

1 file changed

Lines changed: 62 additions & 55 deletions

File tree

tests/v1/worker/test_gpu_model_runner.py

Lines changed: 62 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
33

44
from types import SimpleNamespace
5-
from unittest.mock import MagicMock, Mock
5+
from unittest.mock import Mock
66

77
import numpy as np
88
import pytest
@@ -1756,58 +1756,65 @@ def test_mixed_none_tensor_and_list(self):
17561756
assert all((t == 0).all() for t in list_entry)
17571757

17581758

1759-
def test_async_mamba_align_accepted_counts_race():
1760-
"""Verify async MTP align mode reads accepted counts
1761-
from unmutated CPU buffer (#51571).
1759+
class TestSyncNumAcceptedTokens:
1760+
"""Test GPUModelRunner._sync_num_accepted_tokens() behavior
1761+
for async and non-async modes."""
17621762

1763-
Ensures that when InputBatch.condense() shifts row indices during async scheduling,
1764-
the runner reads historical accepted counts from its unmutated CPU buffer
1765-
snapshot rather than reading from mutated InputBatch rows.
1766-
"""
1767-
runner = MagicMock()
1768-
runner.use_async_scheduling = True
1769-
runner.num_accepted_tokens_event = MagicMock()
1770-
1771-
num_reqs = 3
1772-
1773-
# 2. Step N: Runner D2H accepted counts buffer holds historical Step N output
1774-
# Req A (Row 0) accepted 4, Req B (Row 1) accepted 3, Req C (Row 2) accepted 2
1775-
runner.num_accepted_tokens.np = np.array([4, 3, 2, 0, 0], dtype=np.int32)
1776-
runner.num_accepted_tokens.cpu = torch.from_numpy(runner.num_accepted_tokens.np)
1777-
1778-
# 3. Step N+1: InputBatch was condensed by _update_states() BEFORE event sync!
1779-
# Req B (Row 1) finished. Req C moved from Row 2 -> Row 1. New Req D added at Row 2.
1780-
# input_batch.num_accepted_tokens_cpu became [4, 2, 1] after condense.
1781-
runner.input_batch.num_accepted_tokens_cpu = np.array(
1782-
[4, 2, 1, 0, 0], dtype=np.int32
1783-
)
1784-
1785-
# 4. prev_positions mapping for Step N+1:
1786-
# Row 0 came from prev Row 0 (Req A)
1787-
# Row 1 came from prev Row 2 (Req C)
1788-
# Row 2 is a new request (-1) (Req D)
1789-
runner.prev_positions.np = np.array([0, 2, -1], dtype=np.int32)
1790-
1791-
# 5. Gather logic
1792-
prev_idx = runner.prev_positions.np[:num_reqs]
1793-
new_mask = prev_idx < 0
1794-
src_accepted_counts = (
1795-
runner.num_accepted_tokens.np
1796-
if runner.use_async_scheduling
1797-
else runner.input_batch.num_accepted_tokens_cpu
1798-
)
1799-
runner.num_accepted_tokens.np[:num_reqs] = src_accepted_counts[
1800-
np.where(new_mask, 0, prev_idx)
1801-
]
1802-
runner.num_accepted_tokens.np[:num_reqs][new_mask] = 1
1803-
1804-
# 6. Assertions
1805-
assert runner.num_accepted_tokens.np[0] == 4, "Expected 4, got {}".format(
1806-
runner.num_accepted_tokens.np[0]
1807-
) # Req A
1808-
assert runner.num_accepted_tokens.np[1] == 2, "Expected 2, got {}".format(
1809-
runner.num_accepted_tokens.np[1]
1810-
) # Req C
1811-
assert runner.num_accepted_tokens.np[2] == 1, "Expected 1, got {}".format(
1812-
runner.num_accepted_tokens.np[2]
1813-
) # Req D (New)
1763+
def test_async_mamba_align_accepted_counts_race(self):
1764+
num_reqs = 3
1765+
runner = Mock(spec=GPUModelRunner)
1766+
runner.use_async_scheduling = True
1767+
runner.num_accepted_tokens = SimpleNamespace(
1768+
np=np.array([4, 3, 2, 0, 0], dtype=np.int32),
1769+
)
1770+
runner.input_batch = SimpleNamespace(
1771+
num_accepted_tokens_cpu=np.array([4, 2, 1, 0, 0], dtype=np.int32)
1772+
)
1773+
runner.prev_positions = SimpleNamespace(np=np.array([0, 2, -1], dtype=np.int32))
1774+
1775+
GPUModelRunner._sync_num_accepted_tokens(
1776+
runner, num_reqs, {"req_a": 0, "req_c": 2}
1777+
)
1778+
expected = np.array([4, 2, 1], dtype=np.int32)
1779+
np.testing.assert_array_equal(
1780+
runner.num_accepted_tokens.np[:num_reqs], expected
1781+
)
1782+
np.testing.assert_array_equal(
1783+
runner.input_batch.num_accepted_tokens_cpu[:num_reqs], expected
1784+
)
1785+
1786+
def test_async_initial_step_empty_prev_index(self):
1787+
num_reqs = 2
1788+
runner = Mock(spec=GPUModelRunner)
1789+
runner.use_async_scheduling = True
1790+
runner.num_accepted_tokens = SimpleNamespace(np=np.zeros(5, dtype=np.int32))
1791+
runner.input_batch = SimpleNamespace(
1792+
num_accepted_tokens_cpu=np.zeros(5, dtype=np.int32)
1793+
)
1794+
1795+
GPUModelRunner._sync_num_accepted_tokens(runner, num_reqs, {})
1796+
expected = np.array([1, 1], dtype=np.int32)
1797+
np.testing.assert_array_equal(
1798+
runner.num_accepted_tokens.np[:num_reqs], expected
1799+
)
1800+
np.testing.assert_array_equal(
1801+
runner.input_batch.num_accepted_tokens_cpu[:num_reqs], expected
1802+
)
1803+
1804+
def test_non_async_mode_direct_copy(self):
1805+
num_reqs = 2
1806+
runner = Mock(spec=GPUModelRunner)
1807+
runner.use_async_scheduling = False
1808+
runner.num_accepted_tokens = SimpleNamespace(np=np.zeros(5, dtype=np.int32))
1809+
runner.input_batch = SimpleNamespace(
1810+
num_accepted_tokens_cpu=np.array([5, 4, 0, 0, 0], dtype=np.int32)
1811+
)
1812+
1813+
GPUModelRunner._sync_num_accepted_tokens(runner, num_reqs, None)
1814+
expected = np.array([5, 4], dtype=np.int32)
1815+
np.testing.assert_array_equal(
1816+
runner.num_accepted_tokens.np[:num_reqs], expected
1817+
)
1818+
np.testing.assert_array_equal(
1819+
runner.input_batch.num_accepted_tokens_cpu[:num_reqs], expected
1820+
)

0 commit comments

Comments
 (0)