A high-throughput and memory-efficient inference and serving engine for LLMs
Mirrored from https://github.com/tduncan-lum/Nvidia-Optimization-Stack---VLLM.git
Synced Nov 15, 23:30 UTC
[V1] Optimize handling of sampling metadata and req_ids list (#13244)
Signed-off-by: Nick Hill <nhill@redhat.com>
Nick Hill committed
Feb 18, 2025 at 12:15 UTC
30172b4947c52890b808c6da3a6c7580f55cbb74
15 files changed
+255
-298
tests/v1/sample/test_rejection_sampler.py
+2
-7
@@ -26,17 +26,13 @@ def create_logits_tensor(token_ids: List[int],
26
def create_sampling_metadata(spec_tokens: List[List[int]]) -> SamplingMetadata:
27
batch_size = len(spec_tokens)
28
return SamplingMetadata(
29
- temperature=0.0,
29
+ temperature=torch.tensor([]),
30
all_greedy=True,
31
all_random=False,
32
- rejection_sampling=True,
32
spec_token_ids=spec_tokens,
33
top_p=None,
34
top_k=None,
36
- no_top_p=False,
37
- no_top_k=False,
35
min_p=torch.empty(batch_size, ),
39
- no_min_p=True,
36
generators={},
37
max_num_logprobs=0,
38
no_penalties=False,
@@ -45,8 +41,7 @@ def create_sampling_metadata(spec_tokens: List[List[int]]) -> SamplingMetadata:
41
presence_penalties=torch.tensor([]),
42
repetition_penalties=torch.tensor([]),
43
output_token_ids=[],
48
- min_tokens=[],
49
- stop_token_ids=[],
44
+ min_tokens={},
45
logit_bias=[None] * batch_size,
46
)
47
tests/v1/sample/test_sampler.py
+18
-26
@@ -77,25 +77,20 @@ def _create_default_sampling_metadata(
77
temperature=torch.full((batch_size, ), 0.0),
78
all_greedy=True,
79
all_random=False,
80
- rejection_sampling=False,
81
- top_p=torch.empty(batch_size, ),
82
- top_k=torch.empty(batch_size, ),
83
- no_top_p=True,
84
- no_top_k=True,
85
- min_p=torch.empty(batch_size, ),
86
- no_min_p=True,
80
+ top_p=None,
81
+ top_k=None,
82
+ min_p=None,
83
generators={},
84
max_num_logprobs=0,
85
prompt_token_ids=_create_prompt_tokens_tensor(prompt_token_ids,
86
vocab_size, device),
87
output_token_ids=output_token_ids,
92
- spec_token_ids=[],
88
+ spec_token_ids=None,
89
frequency_penalties=_create_penalty_tensor(batch_size, 0.0, device),
90
presence_penalties=_create_penalty_tensor(batch_size, 0.0, device),
91
repetition_penalties=_create_penalty_tensor(batch_size, 1.0, device),
92
no_penalties=True,
97
- min_tokens=[],
98
- stop_token_ids=[],
93
+ min_tokens={},
94
logit_bias=[None] * batch_size,
95
)
96
return fake_sampling_metadata
@@ -104,10 +99,10 @@ def _create_default_sampling_metadata(
99
def _generate_min_token_penalties_and_stop_tokens(
100
num_output_tokens: int, batch_size: int, vocab_size: int,
101
batch_indices_for_min_token_penalty: List[int]
107
-) -> Tuple[List[int], List[Set[int]]]:
102
+) -> Dict[int, Tuple[int, Set[int]]]:
103
"""
109
- Generates and returns a list of minimum token penalties (`min_tokens`)
110
- and a corresponding list of stop token IDs (`stop_token_ids`) for each
104
+ Generates and returns a dict of minimum token penalties and
105
+ corresponding stop token IDs (`min_tokens`, `stop_token_ids`) for each
106
batch.
107
108
If a batch index is included in `batch_indices_for_min_token_penalty`,
@@ -115,22 +110,19 @@ def _generate_min_token_penalties_and_stop_tokens(
110
and a random set of stop token IDs is created. Otherwise, a lower
111
`min_tokens` value is assigned, and the stop token IDs set is empty.
112
"""
118
- stop_token_ids: List[Set[int]] = []
119
- min_tokens: List[int] = []
113
+ min_tokens: Dict[int, Tuple[int, Set[int]]] = {}
114
for index in range(batch_size):
115
if index in batch_indices_for_min_token_penalty:
122
- min_tokens.append(
116
+ min_tokens[index] = (
117
np.random.randint(num_output_tokens + 1,
124
- 2 * num_output_tokens))
125
- stop_token_ids.append(
118
+ 2 * num_output_tokens),
119
set(
120
np.random.randint(0, vocab_size - 1)
121
for _ in range(np.random.randint(0, vocab_size))))
129
-
122
else:
131
- min_tokens.append(np.random.randint(0, num_output_tokens))
132
- stop_token_ids.append(set())
133
- return (min_tokens, stop_token_ids)
123
+ min_tokens[index] = (np.random.randint(0,
124
+ num_output_tokens), set())
125
+ return min_tokens
126
127
128
def _create_weighted_output_token_list(
@@ -165,7 +157,7 @@ def _create_weighted_output_token_list(
157
output_token_ids_for_batch.extend(
158
[token_id for _ in range(index + 1)])
159
output_token_ids.append(output_token_ids_for_batch)
168
- return (output_token_ids, sorted_token_ids_in_output)
160
+ return output_token_ids, sorted_token_ids_in_output
161
162
163
@pytest.mark.parametrize("device", CUDA_DEVICES)
@@ -182,17 +174,17 @@ def test_sampler_min_tokens_penalty(device: str, batch_size: int):
174
NUM_OUTPUT_TOKENS, batch_size, VOCAB_SIZE, torch.device(device))
175
batch_indices_for_min_token_penalty = np.random.randint(
176
0, batch_size - 1, size=np.random.randint(0, batch_size)).tolist()
185
- min_tokens, stop_token_ids = _generate_min_token_penalties_and_stop_tokens(
177
+ min_tokens = _generate_min_token_penalties_and_stop_tokens(
178
NUM_OUTPUT_TOKENS, batch_size, VOCAB_SIZE,
179
batch_indices_for_min_token_penalty)
180
sampling_metadata.min_tokens = min_tokens
189
- sampling_metadata.stop_token_ids = stop_token_ids
181
sampler = Sampler()
182
logits = sampler.apply_penalties(fake_logits, sampling_metadata)
183
logits = logits.cpu()
184
for batch_idx in range(batch_size):
185
for token_id in range(VOCAB_SIZE):
195
- if token_id in stop_token_ids[batch_idx]:
186
+ _, stop_token_ids = min_tokens.get(batch_idx, (0, set()))
187
+ if token_id in stop_token_ids:
188
assert logits[batch_idx][token_id] == -float("inf")
189
else:
190
assert logits[batch_idx][token_id] != -float("inf")
tests/v1/worker/test_gpu_input_batch.py
+21
-26
@@ -1,6 +1,6 @@
1
# SPDX-License-Identifier: Apache-2.0
2
3
-from typing import Dict, List, Set, Tuple
3
+from typing import Dict, List, Optional, Set, Tuple
4
5
import numpy as np
6
import pytest
@@ -41,7 +41,7 @@ def _remove_requests(
41
for index in req_indices_to_remove:
42
input_batch.remove_request(reqs[index].req_id)
43
req_ids_to_remove.add(reqs[index].req_id)
44
- return (req_ids_to_remove, req_indices_to_remove_list)
44
+ return req_ids_to_remove, req_indices_to_remove_list
45
46
47
def _construct_expected_sampling_metadata(
@@ -64,8 +64,7 @@ def _construct_expected_sampling_metadata(
64
top_p = [0.0 for _ in range(num_reqs)]
65
min_p = [0.0 for _ in range(num_reqs)]
66
temperature = [0.0 for _ in range(num_reqs)]
67
- stop_token_ids: List[Set[int]] = [set() for _ in range(num_reqs)]
68
- min_tokens = [0 for _ in range(num_reqs)]
67
+ min_tokens = {}
68
logit_bias = [None] * num_reqs
69
for req in reqs:
70
if req.req_id not in req_ids_retained:
@@ -83,22 +82,21 @@ def _construct_expected_sampling_metadata(
82
top_p[index_in_input_batch] = req.sampling_params.top_p
83
min_p[index_in_input_batch] = req.sampling_params.min_p
84
temperature[index_in_input_batch] = req.sampling_params.temperature
86
- stop_token_ids[
87
- index_in_input_batch] = req.sampling_params.all_stop_token_ids
88
- min_tokens[index_in_input_batch] = req.sampling_params.min_tokens
85
+ min_tokens[index_in_input_batch] = (
86
+ req.sampling_params.min_tokens,
87
+ req.sampling_params.all_stop_token_ids)
88
logit_bias[index_in_input_batch] = req.sampling_params.logit_bias
89
return SamplingMetadata(
90
temperature=torch.tensor(temperature, dtype=torch.float,
91
device=device),
92
all_greedy=False,
93
all_random=True,
95
- rejection_sampling=False,
96
- top_p=torch.tensor(top_p, dtype=torch.float, device=device),
97
- top_k=torch.tensor(top_k, dtype=torch.int, device=device),
98
- no_top_p=all(x == 1.0 for x in top_p),
99
- no_top_k=all(x == 0 for x in top_k),
100
- min_p=torch.tensor(min_p, dtype=torch.float, device=device),
101
- no_min_p=all(x == 0.0 for x in min_p),
94
+ top_p=None if all(x == 1.0 for x in top_p) else torch.tensor(
95
+ top_p, dtype=torch.float, device=device),
96
+ top_k=None if all(x == 0 for x in top_k) else torch.tensor(
97
+ top_k, dtype=torch.int, device=device),
98
+ min_p=None if all(x == 0.0 for x in min_p) else torch.tensor(
99
+ min_p, dtype=torch.float, device=device),
100
generators={},
101
max_num_logprobs=0,
102
prompt_token_ids=make_tensor_with_pad(
@@ -117,9 +115,8 @@ def _construct_expected_sampling_metadata(
115
dtype=torch.float,
116
device=device),
117
output_token_ids=output_token_ids,
120
- spec_token_ids=[],
118
+ spec_token_ids=None,
119
min_tokens=min_tokens,
122
- stop_token_ids=stop_token_ids,
120
no_penalties=(all(x == 0 for x in presence_penalties)
121
and all(x == 0 for x in frequency_penalties)
122
and all(x == 1 for x in repetition_penalties)),
@@ -206,8 +203,7 @@ def test_sampling_metadata_in_input_batch(device: str, batch_size: int):
203
input_batch.condense(req_indices_to_remove)
204
205
# Generate the sampling metadata
209
- sampling_metadata = input_batch.make_sampling_metadata(
210
- req_id_output_token_ids, req_id_to_spec_token_ids={}, skip_copy=False)
206
+ sampling_metadata = input_batch._make_sampling_metadata()
207
208
# Create expected output.
209
expected_sampling_metadata = _construct_expected_sampling_metadata(
@@ -216,13 +212,16 @@ def test_sampling_metadata_in_input_batch(device: str, batch_size: int):
212
input_batch.req_id_to_index,
213
device=torch.device(device))
214
215
+ def same(t1: Optional[torch.Tensor], t2: Optional[torch.Tensor]) -> bool:
216
+ return (t1 is None
217
+ and t2 is None) or (t1 is not None and t2 is not None
218
+ and torch.allclose(t1, t2))
219
+
220
# Assert the actual and expected output.
221
assert torch.allclose(expected_sampling_metadata.temperature,
222
sampling_metadata.temperature)
222
- assert torch.allclose(expected_sampling_metadata.top_p,
223
- sampling_metadata.top_p)
224
- assert torch.allclose(expected_sampling_metadata.top_k,
225
- sampling_metadata.top_k)
223
+ assert same(expected_sampling_metadata.top_p, sampling_metadata.top_p)
224
+ assert same(expected_sampling_metadata.top_k, sampling_metadata.top_k)
225
assert torch.allclose(
226
expected_sampling_metadata.frequency_penalties,
227
sampling_metadata.frequency_penalties,
@@ -240,10 +239,6 @@ def test_sampling_metadata_in_input_batch(device: str, batch_size: int):
239
assert (expected_sampling_metadata.output_token_ids ==
240
sampling_metadata.output_token_ids)
241
assert expected_sampling_metadata.min_tokens == sampling_metadata.min_tokens
243
- assert expected_sampling_metadata.stop_token_ids == \
244
- sampling_metadata.stop_token_ids
242
assert expected_sampling_metadata.no_penalties == \
243
sampling_metadata.no_penalties
247
- assert expected_sampling_metadata.no_top_p == sampling_metadata.no_top_p
248
- assert expected_sampling_metadata.no_top_k == sampling_metadata.no_top_k
244
assert expected_sampling_metadata.logit_bias == sampling_metadata.logit_bias
tests/v1/worker/test_gpu_model_runner.py
+22
-11
@@ -5,6 +5,7 @@ from vllm.config import CacheConfig, ModelConfig, SchedulerConfig, VllmConfig
5
from vllm.sampling_params import SamplingParams
6
from vllm.v1.core.scheduler_output import (CachedRequestData, NewRequestData,
7
SchedulerOutput)
8
+from vllm.v1.sample.metadata import SamplingMetadata
9
from vllm.v1.worker.gpu_model_runner import GPUModelRunner
10
11
@@ -82,14 +83,21 @@ def _is_req_added(model_runner, req_id: str) -> bool:
83
return req_id in model_runner.requests
84
85
86
+def _is_sampling_metadata_changed(model_runner,
87
+ sampling_metadata_before: SamplingMetadata):
88
+ return model_runner.input_batch.sampling_metadata is not (
89
+ sampling_metadata_before)
90
+
91
+
92
def test_update_states_new_request(model_runner):
93
req_id = "req_0"
94
95
# new req
96
scheduler_output = _schedule_new_request(req_id)
97
91
- batch_changed = model_runner._update_states(scheduler_output)
92
- assert batch_changed is True
98
+ metadata_before = model_runner.input_batch.sampling_metadata
99
+ model_runner._update_states(scheduler_output)
100
+ assert _is_sampling_metadata_changed(model_runner, metadata_before)
101
assert _is_req_added(model_runner, req_id)
102
assert _is_req_scheduled(model_runner, req_id)
103
@@ -117,8 +125,9 @@ def test_update_states_request_finished(model_runner):
125
free_encoder_input_ids=[],
126
)
127
120
- batch_changed = model_runner._update_states(scheduler_output)
121
- assert batch_changed is True
128
+ metadata_before = model_runner.input_batch.sampling_metadata
129
+ model_runner._update_states(scheduler_output)
130
+ assert _is_sampling_metadata_changed(model_runner, metadata_before)
131
assert not _is_req_added(model_runner, req_id)
132
assert not _is_req_scheduled(model_runner, req_id)
133
@@ -142,7 +151,7 @@ def test_update_states_request_resumed(model_runner):
151
scheduled_spec_decode_tokens={},
152
scheduled_encoder_inputs={},
153
num_common_prefix_blocks=0,
145
- finished_req_ids={},
154
+ finished_req_ids=set(),
155
free_encoder_input_ids=[],
156
)
157
@@ -171,8 +180,9 @@ def test_update_states_request_resumed(model_runner):
180
free_encoder_input_ids=[],
181
)
182
174
- batch_changed = model_runner._update_states(scheduler_output)
175
- assert batch_changed is True
183
+ metadata_before = model_runner.input_batch.sampling_metadata
184
+ model_runner._update_states(scheduler_output)
185
+ assert _is_sampling_metadata_changed(model_runner, metadata_before)
186
assert _is_req_added(model_runner, req_id)
187
assert _is_req_scheduled(model_runner, req_id)
188
@@ -200,8 +210,9 @@ def test_update_states_no_changes(model_runner):
210
free_encoder_input_ids=[],
211
)
212
203
- batch_changed = model_runner._update_states(scheduler_output)
204
- assert batch_changed is False
213
+ metadata_before = model_runner.input_batch.sampling_metadata
214
+ model_runner._update_states(scheduler_output)
215
+ assert not _is_sampling_metadata_changed(model_runner, metadata_before)
216
assert _is_req_added(model_runner, req_id)
217
assert _is_req_scheduled(model_runner, req_id)
218
@@ -233,8 +244,8 @@ def test_update_states_request_unscheduled(model_runner):
244
free_encoder_input_ids=[],
245
)
246
236
- batch_changed = model_runner._update_states(scheduler_output)
237
- assert batch_changed is True
247
+ metadata_before = model_runner._update_states(scheduler_output)
248
+ assert _is_sampling_metadata_changed(model_runner, metadata_before)
249
250
assert _is_req_added(model_runner, req_ids[0])
251
assert _is_req_scheduled(model_runner, req_ids[0])
vllm/model_executor/layers/utils.py
+3
-3
@@ -45,7 +45,7 @@ def apply_penalties(logits: torch.Tensor, prompt_tokens_tensor: torch.Tensor,
45
vocab_size, num_seqs)
46
output_bin_counts, output_mask = get_token_bin_counts_and_mask(
47
output_tokens_tensor, vocab_size, num_seqs)
48
- repetition_penalties = repetition_penalties.unsqueeze_(dim=1).repeat(
48
+ repetition_penalties = repetition_penalties.unsqueeze(dim=1).repeat(
49
1, vocab_size)
50
logits[logits > 0] /= torch.where(prompt_mask | output_mask,
51
repetition_penalties, 1.0)[logits > 0]
@@ -53,6 +53,6 @@ def apply_penalties(logits: torch.Tensor, prompt_tokens_tensor: torch.Tensor,
53
repetition_penalties, 1.0)[logits <= 0]
54
# We follow the definition in OpenAI API.
55
# Refer to https://platform.openai.com/docs/api-reference/parameter-details
56
- logits -= frequency_penalties.unsqueeze_(dim=1) * output_bin_counts
57
- logits -= presence_penalties.unsqueeze_(dim=1) * output_mask
56
+ logits -= frequency_penalties.unsqueeze(dim=1) * output_bin_counts
57
+ logits -= presence_penalties.unsqueeze(dim=1) * output_mask
58
return logits
vllm/v1/core/scheduler.py
+4
-2
@@ -195,8 +195,10 @@ class Scheduler:
195
request.num_computed_tokens -
196
request.num_tokens)
197
if num_scheduled_spec_tokens > 0:
198
+ # Trim spec_token_ids list to num_scheduled_spec_tokens.
199
+ del request.spec_token_ids[num_scheduled_spec_tokens:]
200
scheduled_spec_decode_tokens[request.request_id] = (
199
- request.spec_token_ids[:num_scheduled_spec_tokens])
201
+ request.spec_token_ids)
202
203
# Encoder-related.
204
if encoder_inputs_to_schedule:
@@ -567,7 +569,7 @@ class Scheduler:
569
outputs.append(
570
EngineCoreOutput(
571
request_id=req_id,
570
- new_token_ids=new_token_ids or [],
572
+ new_token_ids=new_token_ids,
573
finish_reason=request.get_finished_reason(),
574
new_logprobs=new_logprobs,
575
new_prompt_logprobs_tensors=prompt_logprobs_tensors,
vllm/v1/sample/metadata.py
+10
-11
@@ -1,7 +1,7 @@
1
# SPDX-License-Identifier: Apache-2.0
2
3
from dataclasses import dataclass
4
-from typing import Dict, List, Optional, Set
4
+from typing import Dict, List, Optional, Set, Tuple
5
6
import torch
7
@@ -12,15 +12,13 @@ class SamplingMetadata:
12
temperature: torch.Tensor
13
all_greedy: bool
14
all_random: bool
15
- rejection_sampling: bool
16
- spec_token_ids: List[List[int]]
15
18
- top_p: torch.Tensor
19
- top_k: torch.Tensor
20
- no_top_p: bool
21
- no_top_k: bool
22
- min_p: torch.Tensor
23
- no_min_p: bool
16
+ # None when there are no speculated tokens.
17
+ spec_token_ids: Optional[List[List[int]]]
18
+
19
+ top_p: Optional[torch.Tensor]
20
+ top_k: Optional[torch.Tensor]
21
+ min_p: Optional[torch.Tensor]
22
23
generators: Dict[int, torch.Generator]
24
@@ -34,7 +32,8 @@ class SamplingMetadata:
32
repetition_penalties: torch.Tensor
33
34
output_token_ids: List[List[int]]
37
- min_tokens: List[int]
38
- stop_token_ids: List[Set[int]]
35
+
36
+ # req_index -> (min_tokens, stop_token_ids)
37
+ min_tokens: Dict[int, Tuple[int, Set[int]]]
38
39
logit_bias: List[Optional[Dict[int, float]]]
vllm/v1/sample/ops/penalties.py
+6
-7
@@ -1,6 +1,6 @@
1
# SPDX-License-Identifier: Apache-2.0
2
3
-from typing import List, Set, Tuple
3
+from typing import Dict, List, Set, Tuple
4
5
import torch
6
@@ -8,18 +8,17 @@ from vllm.model_executor.layers.utils import apply_penalties
8
from vllm.utils import is_pin_memory_available, make_tensor_with_pad
9
10
11
-def apply_min_token_penalties(logits: torch.Tensor,
12
- output_token_ids: List[List[int]],
13
- stop_token_ids: List[Set[int]],
14
- min_tokens: List[int]) -> None:
11
+def apply_min_token_penalties(
12
+ logits: torch.Tensor, output_token_ids: List[List[int]],
13
+ min_tokens: Dict[int, Tuple[int, Set[int]]]) -> None:
14
"""
15
Applies minimum token penalty by setting the logits of the stop tokens
16
to -inf.
17
"""
18
min_tokens_logits_to_penalize: List[Tuple[int, int]] = []
20
- for index, min_token in enumerate(min_tokens):
19
+ for index, (min_token, stop_token_ids) in min_tokens.items():
20
if len(output_token_ids[index]) < min_token:
22
- for stop_token_id in stop_token_ids[index]:
21
+ for stop_token_id in stop_token_ids:
22
min_tokens_logits_to_penalize.append((index, stop_token_id))
23
if min_tokens_logits_to_penalize:
24
logits[tuple(zip(*min_tokens_logits_to_penalize))] = -float("inf")
vllm/v1/sample/ops/topk_topp_sampler.py
+20
-28
@@ -1,6 +1,6 @@
1
# SPDX-License-Identifier: Apache-2.0
2
3
-from typing import Dict
3
+from typing import Dict, Optional
4
5
import torch
6
import torch.nn as nn
@@ -55,13 +55,11 @@ class TopKTopPSampler(nn.Module):
55
self,
56
logits: torch.Tensor,
57
generators: Dict[int, torch.Generator],
58
- no_top_k: bool,
59
- k: torch.Tensor,
60
- no_top_p: bool,
61
- p: torch.Tensor,
58
+ k: Optional[torch.Tensor],
59
+ p: Optional[torch.Tensor],
60
) -> torch.Tensor:
61
"""PyTorch-native implementation of top-k and top-p sampling."""
64
- logits = apply_top_k_top_p(logits, no_top_k, k, no_top_p, p)
62
+ logits = apply_top_k_top_p(logits, k, p)
63
probs = logits.softmax(dim=-1, dtype=torch.float32)
64
return random_sample(probs, generators)
65
@@ -69,37 +67,33 @@ class TopKTopPSampler(nn.Module):
67
self,
68
logits: torch.Tensor,
69
generators: Dict[int, torch.Generator],
72
- no_top_k: bool,
73
- k: torch.Tensor,
74
- no_top_p: bool,
75
- p: torch.Tensor,
70
+ k: Optional[torch.Tensor],
71
+ p: Optional[torch.Tensor],
72
) -> torch.Tensor:
73
"""More optimized implementation for top-k and top-p sampling."""
74
probs = logits.softmax(dim=-1, dtype=torch.float32)
79
- if no_top_k and no_top_p:
75
+ if k is None and p is None:
76
# We prefer `random_sample` over `flashinfer_sample` when sorting is
77
# not needed. This is because `random_sample` does not require
78
# CPU-GPU synchronization while `flashinfer_sample` does.
79
return random_sample(probs, generators)
84
- return flashinfer_sample(probs, no_top_k, k, no_top_p, p, generators)
80
+ return flashinfer_sample(probs, k, p, generators)
81
82
83
def apply_top_k_top_p(
84
logits: torch.Tensor,
89
- no_top_k: bool,
90
- k: torch.Tensor,
91
- no_top_p: bool,
92
- p: torch.Tensor,
85
+ k: Optional[torch.Tensor],
86
+ p: Optional[torch.Tensor],
87
) -> torch.Tensor:
88
"""Apply top-k and top-p masks to the logits.
89
90
This function sorts the logits tensor, which can be slow for large batches.
91
"""
98
- if no_top_k and no_top_p:
92
+ if k is None and p is None:
93
return logits
94
logits_sort, logits_idx = logits.sort(dim=-1, descending=False)
95
102
- if not no_top_k:
96
+ if k is not None:
97
# Apply top-k.
98
top_k_mask = logits_sort.size(1) - k.to(torch.long)
99
# Get all the top_k values.
@@ -107,7 +101,7 @@ def apply_top_k_top_p(
101
top_k_mask = logits_sort < top_k_mask
102
logits_sort.masked_fill_(top_k_mask, -float("inf"))
103
110
- if not no_top_p:
104
+ if p is not None:
105
# Apply top-p.
106
probs_sort = logits_sort.softmax(dim=-1)
107
probs_sum = probs_sort.cumsum(dim=-1)
@@ -147,10 +141,8 @@ def random_sample(
141
142
def flashinfer_sample(
143
probs: torch.Tensor,
150
- no_top_k: bool,
151
- k: torch.Tensor,
152
- no_top_p: bool,
153
- p: torch.Tensor,
144
+ k: Optional[torch.Tensor],
145
+ p: Optional[torch.Tensor],
146
generators: Dict[int, torch.Generator],
147
) -> torch.Tensor:
148
"""Sample from the probabilities using FlashInfer.
@@ -167,7 +159,7 @@ def flashinfer_sample(
159
does not. Call this function at the end of the forward pass to minimize
160
the synchronization overhead.
161
"""
170
- assert not (no_top_k and no_top_p)
162
+ assert not (k is None and p is None)
163
max_top_k_round = 32
164
batch_size = probs.shape[0]
165
uniform_samples = torch.empty((max_top_k_round, batch_size),
@@ -178,11 +170,11 @@ def flashinfer_sample(
170
for i, generator in generators.items():
171
uniform_samples[:, i].uniform_(generator=generator)
172
181
- if no_top_k:
173
+ if k is None:
174
# Top-p only.
175
next_token_ids, success = flashinfer.sampling.top_p_sampling_from_probs(
176
probs, uniform_samples, p, deterministic=True)
185
- elif no_top_p:
177
+ elif p is None:
178
# Top-k only.
179
next_token_ids, success = flashinfer.sampling.top_k_sampling_from_probs(
180
probs, uniform_samples, k, deterministic=True)
@@ -194,9 +186,9 @@ def flashinfer_sample(
186
187
# NOTE: CPU-GPU synchronization happens here.
188
if not success.all():
197
- if not no_top_k:
189
+ if k is not None:
190
probs = flashinfer.sampling.top_k_renorm_prob(probs, k)
199
- if not no_top_p:
191
+ if p is not None:
192
probs = flashinfer.sampling.top_p_renorm_prob(probs, p)
193
next_token_ids = flashinfer.sampling.sampling_from_probs(
194
probs, uniform_samples[0], deterministic=True)
vllm/v1/sample/rejection_sampler.py
+2
@@ -68,6 +68,7 @@ class RejectionSampler(nn.Module):
68
# NOTE: The following input preparationg can be moved
69
# to the model runner with a persistent manner for better
70
# performance.
71
+ assert sampling_metadata.spec_token_ids is not None
72
spec_token_ids = sampling_metadata.spec_token_ids
73
max_spec_len = max(len(s) for s in spec_token_ids)
74
batch_size = len(spec_token_ids)
@@ -119,6 +120,7 @@ class RejectionSampler(nn.Module):
120
logits: torch.Tensor,
121
sampling_metadata: SamplingMetadata,
122
) -> SamplerOutput:
123
+ assert sampling_metadata.spec_token_ids is not None
124
spec_lens = [len(x) for x in sampling_metadata.spec_token_ids]
125
# Add 1 to include the 'bonus' token.
126
sample_lens = [x + 1 for x in spec_lens]
vllm/v1/sample/sampler.py
+6
-7
@@ -26,7 +26,7 @@ class Sampler(nn.Module):
26
logits: torch.Tensor,
27
sampling_metadata: SamplingMetadata,
28
) -> SamplerOutput:
29
- if sampling_metadata.rejection_sampling:
29
+ if sampling_metadata.spec_token_ids:
30
if sampling_metadata.max_num_logprobs:
31
raise NotImplementedError(
32
"Rejection sampling does not support logprobs.")
@@ -104,16 +104,14 @@ class Sampler(nn.Module):
104
logits = self.apply_temperature(logits, sampling_metadata.temperature)
105
106
# Apply min_p.
107
- if not sampling_metadata.no_min_p:
107
+ if sampling_metadata.min_p is not None:
108
logits = self.apply_min_p(logits, sampling_metadata.min_p)
109
110
# Apply top_k and/or top_p.
111
random_sampled = self.topk_topp_sampler(
112
logits,
113
sampling_metadata.generators,
114
- sampling_metadata.no_top_k,
114
sampling_metadata.top_k,
116
- sampling_metadata.no_top_p,
115
sampling_metadata.top_p,
116
)
117
@@ -179,9 +177,10 @@ class Sampler(nn.Module):
177
logits: torch.Tensor,
178
sampling_metadata: SamplingMetadata,
179
) -> torch.Tensor:
182
- apply_min_token_penalties(logits, sampling_metadata.output_token_ids,
183
- sampling_metadata.stop_token_ids,
184
- sampling_metadata.min_tokens)
180
+ if sampling_metadata.min_tokens:
181
+ apply_min_token_penalties(logits,
182
+ sampling_metadata.output_token_ids,
183
+ sampling_metadata.min_tokens)
184
if not sampling_metadata.no_penalties:
185
assert sampling_metadata.prompt_token_ids is not None
186
logits = apply_all_penalties(
vllm/v1/utils.py
+11
@@ -188,3 +188,14 @@ def bind_kv_cache(
188
for layer_name, kv_cache in kv_caches.items():
189
# NOTE: Use list because of v0 PP virtual engine.
190
forward_context[layer_name].kv_cache = [kv_cache]
191
+
192
+
193
+def copy_slice(from_tensor: torch.Tensor, to_tensor: torch.Tensor,
194
+ length: int) -> None:
195
+ """
196
+ Copy the first length elements of a tensor into another tensor in a
197
+ non-blocking manner.
198
+
199
+ Used to copy pinned CPU tensor data to pre-allocated GPU tensors.
200
+ """
201
+ to_tensor[:length].copy_(from_tensor[:length], non_blocking=True)
vllm/v1/worker/gpu_input_batch.py
+102
-111
@@ -1,9 +1,8 @@
1
# SPDX-License-Identifier: Apache-2.0
2
-
2
# Datastructures defining an input batch
3
4
from dataclasses import dataclass
6
-from typing import TYPE_CHECKING, Dict, List, Optional, Set, Tuple
5
+from typing import TYPE_CHECKING, Dict, List, Optional, Set, Tuple, cast
6
7
import numpy as np
8
import torch
@@ -12,6 +11,7 @@ from vllm.lora.request import LoRARequest
11
from vllm.multimodal import MultiModalKwargs
12
from vllm.sampling_params import SamplingParams, SamplingType
13
from vllm.v1.sample.metadata import SamplingMetadata
14
+from vllm.v1.utils import copy_slice
15
from vllm.v1.worker.block_table import BlockTable
16
17
_SAMPLING_EPS = 1e-5
@@ -63,7 +63,7 @@ class InputBatch:
63
self.pin_memory = pin_memory
64
self.vocab_size = vocab_size
65
66
- self.req_ids: List[Optional[str]] = [None] * max_num_reqs
66
+ self._req_ids: List[Optional[str]] = []
67
self.req_id_to_index: Dict[str, int] = {}
68
69
# TODO(woosuk): This buffer could be too large if max_model_len is big.
@@ -171,11 +171,8 @@ class InputBatch:
171
self.repetition_penalties_cpu_tensor.numpy()
172
self.repetition_penalties_reqs: Set[str] = set()
173
174
- self.min_tokens: List[int] = [0] * max_num_reqs
175
- self.stop_token_ids: List[Set[int]] = [
176
- set() for _ in range(max_num_reqs)
177
- ]
178
- self.prompt_token_ids: Optional[torch.Tensor] = None
174
+ # req_index -> (min_tokens, stop_token_ids)
175
+ self.min_tokens: Dict[int, Tuple[int, Set[int]]] = {}
176
177
# lora related
178
self.request_lora_mapping = np.zeros((self.max_num_reqs, ),
@@ -196,6 +193,17 @@ class InputBatch:
193
self.logit_bias: List[Optional[Dict[int,
194
float]]] = [None] * max_num_reqs
195
196
+ self.req_output_token_ids: List[Optional[List[int]]] = []
197
+
198
+ # This is updated each time the batch constituents change.
199
+ self.sampling_metadata = self._make_sampling_metadata()
200
+
201
+ @property
202
+ def req_ids(self) -> List[str]:
203
+ # None elements should only be present transiently
204
+ # while performing state updates to the batch.
205
+ return cast(List[str], self._req_ids)
206
+
207
def add_request(
208
self,
209
request: "CachedRequestState",
@@ -206,7 +214,13 @@ class InputBatch:
214
assert req_index < self.max_num_reqs
215
216
req_id = request.req_id
209
- self.req_ids[req_index] = req_id
217
+ if req_index == len(self._req_ids):
218
+ self._req_ids.append(req_id)
219
+ self.req_output_token_ids.append(request.output_token_ids)
220
+ else:
221
+ self._req_ids[req_index] = req_id
222
+ self.req_output_token_ids[req_index] = request.output_token_ids
223
+
224
self.req_id_to_index[req_id] = req_index
225
226
# Copy the prompt token ids and output token ids.
@@ -255,8 +269,9 @@ class InputBatch:
269
req_index] = sampling_params.repetition_penalty
270
if sampling_params.repetition_penalty != 1.0:
271
self.repetition_penalties_reqs.add(req_id)
258
- self.min_tokens[req_index] = sampling_params.min_tokens
259
- self.stop_token_ids[req_index] = sampling_params.all_stop_token_ids
272
+ if sampling_params.min_tokens:
273
+ self.min_tokens[req_index] = (sampling_params.min_tokens,
274
+ sampling_params.all_stop_token_ids)
275
276
# NOTE(woosuk): self.generators should not include the requests that
277
# do not have their own generator.
@@ -284,16 +299,20 @@ class InputBatch:
299
self.request_lora_mapping[req_index] = 0
300
301
def remove_request(self, req_id: str) -> Optional[int]:
302
+ """This method must always be followed by a call to condense()."""
303
+
304
req_index = self.req_id_to_index.pop(req_id, None)
305
if req_index is None:
306
return None
290
- self.req_ids[req_index] = None
307
+ self._req_ids[req_index] = None
308
+ self.req_output_token_ids[req_index] = None
309
310
self.greedy_reqs.discard(req_id)
311
self.random_reqs.discard(req_id)
312
self.top_p_reqs.discard(req_id)
313
self.top_k_reqs.discard(req_id)
314
self.min_p_reqs.discard(req_id)
315
+ self.min_tokens.pop(req_index, None)
316
self.frequency_penalties_reqs.discard(req_id)
317
self.presence_penalties_reqs.discard(req_id)
318
self.repetition_penalties_reqs.discard(req_id)
@@ -313,33 +332,17 @@ class InputBatch:
332
self.logit_bias[req_index] = None
333
return req_index
334
316
- def clear(self) -> None:
317
- self.req_ids = [None] * self.max_num_reqs
318
- self.req_id_to_index.clear()
319
- self.greedy_reqs.clear()
320
- self.random_reqs.clear()
321
- self.top_p_reqs.clear()
322
- self.top_k_reqs.clear()
323
- self.min_p_reqs.clear()
324
- self.frequency_penalties_reqs.clear()
325
- self.presence_penalties_reqs.clear()
326
- self.repetition_penalties_reqs.clear()
327
- self.generators.clear()
328
- self.num_logprobs.clear()
329
- self.num_prompt_logprobs.clear()
330
- self.request_lora_mapping.fill(0)
331
- self.lora_id_to_lora_request.clear()
332
- self.lora_id_to_request_ids.clear()
333
- self.logit_bias = [None] * self.max_num_reqs
334
-
335
def condense(self, empty_req_indices: List[int]) -> None:
336
- if self.num_reqs == 0:
336
+ num_reqs = self.num_reqs
337
+ if num_reqs == 0:
338
# The batched states are empty.
339
+ self._req_ids.clear()
340
+ self.req_output_token_ids.clear()
341
return
342
343
# NOTE(woosuk): This function assumes that the empty_req_indices
344
# is sorted in descending order.
342
- last_req_index = self.num_reqs + len(empty_req_indices) - 1
345
+ last_req_index = num_reqs + len(empty_req_indices) - 1
346
while empty_req_indices:
347
# Find the largest non-empty index.
348
while last_req_index in empty_req_indices:
@@ -351,10 +354,13 @@ class InputBatch:
354
break
355
356
# Swap the states.
354
- req_id = self.req_ids[last_req_index]
357
+ req_id = self._req_ids[last_req_index]
358
+ output_token_ids = self.req_output_token_ids[last_req_index]
359
assert req_id is not None
356
- self.req_ids[empty_index] = req_id
357
- self.req_ids[last_req_index] = None
360
+ self._req_ids[empty_index] = req_id
361
+ self._req_ids[last_req_index] = None
362
+ self.req_output_token_ids[empty_index] = output_token_ids
363
+ self.req_output_token_ids[last_req_index] = None
364
self.req_id_to_index[req_id] = empty_index
365
366
num_tokens = self.num_tokens[last_req_index]
@@ -379,13 +385,14 @@ class InputBatch:
385
self.repetition_penalties_cpu[
386
empty_index] = self.repetition_penalties_cpu[last_req_index]
387
self.min_p_cpu[empty_index] = self.min_p_cpu[last_req_index]
382
- self.min_tokens[empty_index] = self.min_tokens[last_req_index]
383
- self.stop_token_ids[empty_index] = self.stop_token_ids[
384
- last_req_index]
388
generator = self.generators.pop(last_req_index, None)
389
if generator is not None:
390
self.generators[empty_index] = generator
391
392
+ min_token = self.min_tokens.pop(last_req_index, None)
393
+ if min_token is not None:
394
+ self.min_tokens[empty_index] = min_token
395
+
396
self.request_lora_mapping[empty_index] = self.request_lora_mapping[
397
last_req_index]
398
@@ -394,87 +401,71 @@ class InputBatch:
401
# Decrement last_req_index since it is now empty.
402
last_req_index -= 1
403
397
- def make_sampling_metadata(
398
- self,
399
- req_id_output_token_ids: Dict[str, List[int]],
400
- req_id_to_spec_token_ids: Dict[str, List[int]],
401
- skip_copy: bool = False,
402
- ) -> SamplingMetadata:
403
- if not skip_copy:
404
- self.temperature[:self.num_reqs].copy_(
405
- self.temperature_cpu_tensor[:self.num_reqs], non_blocking=True)
406
- self.top_p[:self.num_reqs].copy_(
407
- self.top_p_cpu_tensor[:self.num_reqs], non_blocking=True)
408
- self.top_k[:self.num_reqs].copy_(
409
- self.top_k_cpu_tensor[:self.num_reqs], non_blocking=True)
410
- self.min_p[:self.num_reqs].copy_(
411
- self.min_p_cpu_tensor[:self.num_reqs], non_blocking=True)
412
- if not self.no_penalties:
413
- # Since syncing these tensors is expensive only copy them
414
- # if necessary i.e. if there are requests which require
415
- # penalties to be applied during sampling.
416
- self.frequency_penalties[:self.num_reqs].copy_(
417
- self.frequency_penalties_cpu_tensor[:self.num_reqs],
418
- non_blocking=True,
419
- )
420
- self.presence_penalties[:self.num_reqs].copy_(
421
- self.presence_penalties_cpu_tensor[:self.num_reqs],
422
- non_blocking=True,
423
- )
424
- self.repetition_penalties[:self.num_reqs].copy_(
425
- self.repetition_penalties_cpu_tensor[:self.num_reqs],
426
- non_blocking=True,
427
- )
428
- # The prompt tokens are used only for applying penalties during
429
- # the sampling process. Hence copy these tensors only when
430
- # there are requests which need penalties to be applied.
431
- self.prompt_token_ids = self._make_prompt_token_ids_tensor()
432
-
433
- output_token_ids: List[List[int]] = []
434
- spec_token_ids: List[List[int]] = []
435
- rejection_sampling = False
436
- for req_id in self.req_ids[:self.num_reqs]:
437
- assert req_id is not None
438
- # Currently we create a tensor for output_token_ids from scratch
439
- # at each step. However, for the penalties computation what we
440
- # need is stats about the token ids present in the output. This
441
- # stats can be maintained incrementally instead of computing it
442
- # from scratch at each step.
443
- # TODO - Replace this with incremental update to output token
444
- # statistics.
445
- output_token_ids.append(req_id_output_token_ids[req_id])
446
- req_spec_token_ids = req_id_to_spec_token_ids.get(req_id, [])
447
- spec_token_ids.append(req_spec_token_ids)
448
- if req_spec_token_ids:
449
- # If any of the requests require speculative decoding, set the
450
- # flag to True.
451
- rejection_sampling = True
404
+ # Trim lists to the batch size.
405
+ del self._req_ids[self.num_reqs:]
406
+ del self.req_output_token_ids[self.num_reqs:]
407
+
408
+ def refresh_sampling_metadata(self):
409
+ self.sampling_metadata = self._make_sampling_metadata()
410
+
411
+ def _make_sampling_metadata(self) -> SamplingMetadata:
412
+ num_reqs = self.num_reqs
413
+ copy_slice(self.temperature_cpu_tensor, self.temperature, num_reqs)
414
+ if not self.no_top_p:
415
+ copy_slice(self.top_p_cpu_tensor, self.top_p, num_reqs)
416
+ if not self.no_top_k:
417
+ copy_slice(self.top_k_cpu_tensor, self.top_k, num_reqs)
418
+ if not self.no_min_p:
419
+ copy_slice(self.min_p_cpu_tensor, self.min_p, num_reqs)
420
+
421
+ if not self.no_penalties:
422
+ # Since syncing these tensors is expensive only copy them
423
+ # if necessary i.e. if there are requests which require
424
+ # penalties to be applied during sampling.
425
+ copy_slice(self.frequency_penalties_cpu_tensor,
426
+ self.frequency_penalties, num_reqs)
427
+ copy_slice(self.presence_penalties_cpu_tensor,
428
+ self.presence_penalties, num_reqs)
429
+ copy_slice(self.repetition_penalties_cpu_tensor,
430
+ self.repetition_penalties, num_reqs)
431
+
432
+ # The prompt tokens are used only for applying penalties during
433
+ # the sampling process. Hence copy these tensors only when
434
+ # there are requests which need penalties to be applied.
435
+ prompt_token_ids = self._make_prompt_token_ids_tensor()
436
+ else:
437
+ prompt_token_ids = None
438
439
return SamplingMetadata(
454
- temperature=self.temperature[:self.num_reqs],
440
+ temperature=self.temperature[:num_reqs],
441
all_greedy=self.all_greedy,
442
all_random=self.all_random,
457
- rejection_sampling=rejection_sampling,
458
- top_p=self.top_p[:self.num_reqs],
459
- top_k=self.top_k[:self.num_reqs],
460
- min_p=self.min_p[:self.num_reqs],
461
- no_min_p=self.no_min_p,
462
- no_top_p=self.no_top_p,
463
- no_top_k=self.no_top_k,
443
+ top_p=None if self.no_top_p else self.top_p[:num_reqs],
444
+ top_k=None if self.no_top_k else self.top_k[:num_reqs],
445
+ min_p=None if self.no_min_p else self.min_p[:num_reqs],
446
generators=self.generators,
447
max_num_logprobs=self.max_num_logprobs,
466
- prompt_token_ids=self.prompt_token_ids,
467
- frequency_penalties=self.frequency_penalties[:self.num_reqs],
468
- presence_penalties=self.presence_penalties[:self.num_reqs],
469
- repetition_penalties=self.repetition_penalties[:self.num_reqs],
470
- output_token_ids=output_token_ids,
471
- spec_token_ids=spec_token_ids,
472
- min_tokens=self.min_tokens[:self.num_reqs],
473
- stop_token_ids=self.stop_token_ids[:self.num_reqs],
448
+ prompt_token_ids=prompt_token_ids,
449
+ frequency_penalties=self.frequency_penalties[:num_reqs],
450
+ presence_penalties=self.presence_penalties[:num_reqs],
451
+ repetition_penalties=self.repetition_penalties[:num_reqs],
452
+ output_token_ids=cast(List[List[int]], self.req_output_token_ids),
453
+ spec_token_ids=None,
454
+ min_tokens=self.min_tokens,
455
no_penalties=self.no_penalties,
475
- logit_bias=self.logit_bias[:self.num_reqs],
456
+ logit_bias=self.logit_bias[:num_reqs],
457
)
458
459
+ def get_sampling_metadata(
460
+ self,
461
+ req_id_to_spec_token_ids: Dict[str, List[int]],
462
+ ) -> SamplingMetadata:
463
+ # Set the new spec token ids in the cached sampling metadata.
464
+ self.sampling_metadata.spec_token_ids = [
465
+ req_id_to_spec_token_ids.get(req_id, []) for req_id in self.req_ids
466
+ ] if req_id_to_spec_token_ids else None
467
+ return self.sampling_metadata
468
+
469
def _make_prompt_token_ids_tensor(self) -> torch.Tensor:
470
max_prompt_len = self.num_prompt_tokens[:self.num_reqs].max()
471
prompt_token_ids_cpu_tensor = torch.empty(
vllm/v1/worker/gpu_model_runner.py
+28
-57
@@ -31,7 +31,6 @@ from vllm.v1.engine.mm_input_cache import MMInputCacheClient
31
from vllm.v1.kv_cache_interface import (FullAttentionSpec, KVCacheConfig,
32
KVCacheSpec)
33
from vllm.v1.outputs import LogprobsTensors, ModelRunnerOutput
34
-from vllm.v1.sample.metadata import SamplingMetadata
34
from vllm.v1.sample.rejection_sampler import INVALID_TOKEN_ID
35
from vllm.v1.spec_decode.ngram_proposer import NgramProposer
36
from vllm.v1.utils import bind_kv_cache
@@ -224,16 +223,15 @@ class GPUModelRunner(LoRAModelRunnerMixin):
223
pin_memory=self.pin_memory)
224
self.seq_lens_np = self.seq_lens_cpu.numpy()
225
227
- def _update_states(self, scheduler_output: "SchedulerOutput") -> bool:
226
+ def _update_states(self, scheduler_output: "SchedulerOutput") -> None:
227
"""Update the cached states and the persistent batch with the scheduler
228
output.
229
230
The updated states are used by the `_prepare_inputs` function to create
231
the input GPU tensors for the model.
232
234
- Returns:
235
- True if there is a new/resumed/paused/finished request in the batch.
236
- If False, we can skip copying SamplingMetadata to the GPU.
233
+ The SamplingMetadata is updated and copied to the GPU if there is a
234
+ new/resumed/paused/finished request in the batch.
235
"""
236
# Remove finished requests from the cached states.
237
for req_id in scheduler_output.finished_req_ids:
@@ -344,9 +342,12 @@ class GPUModelRunner(LoRAModelRunnerMixin):
342
num_new_tokens = (num_computed_tokens +
343
len(req_data.new_token_ids) -
344
req_state.num_tokens)
347
- new_token_ids = (req_data.new_token_ids[-num_new_tokens:]
348
- if num_new_tokens > 0 else [])
349
- req_state.output_token_ids.extend(new_token_ids)
345
+ if num_new_tokens == 1:
346
+ # Avoid slicing list in most common case.
347
+ req_state.output_token_ids.append(req_data.new_token_ids[-1])
348
+ elif num_new_tokens > 0:
349
+ req_state.output_token_ids.extend(
350
+ req_data.new_token_ids[-num_new_tokens:])
351
# Update the block IDs.
352
if not req_data.resumed_from_preemption:
353
# Append the new blocks to the existing block IDs.
@@ -380,7 +381,7 @@ class GPUModelRunner(LoRAModelRunnerMixin):
381
self.input_batch.num_tokens_no_spec[req_index] = end_token_index
382
# Add spec_token_ids to token_ids_cpu.
383
spec_token_ids = scheduler_output.scheduled_spec_decode_tokens.get(
383
- req_id, [])
384
+ req_id, ())
385
if spec_token_ids:
386
start_index = end_token_index
387
end_token_index += len(spec_token_ids)
@@ -410,7 +411,8 @@ class GPUModelRunner(LoRAModelRunnerMixin):
411
if removed_req_indices:
412
self.input_batch.condense(removed_req_indices)
413
413
- return batch_changed
414
+ if batch_changed:
415
+ self.input_batch.refresh_sampling_metadata()
416
417
def _prepare_inputs(
418
self,
@@ -429,8 +431,7 @@ class GPUModelRunner(LoRAModelRunnerMixin):
431
# TODO: The Python loop can be slow. Optimize.
432
num_scheduled_tokens = np.empty(num_reqs, dtype=np.int32)
433
max_num_scheduled_tokens = 0
432
- for i, req_id in zip(range(num_reqs), self.input_batch.req_ids):
433
- assert req_id is not None
434
+ for i, req_id in enumerate(self.input_batch.req_ids):
435
num_tokens = scheduler_output.num_scheduled_tokens[req_id]
436
num_scheduled_tokens[i] = num_tokens
437
max_num_scheduled_tokens = max(max_num_scheduled_tokens,
@@ -669,10 +670,7 @@ class GPUModelRunner(LoRAModelRunnerMixin):
670
671
def _calc_mrope_positions(self, scheduler_output: "SchedulerOutput"):
672
mrope_pos_ptr = 0
672
- num_reqs = self.input_batch.num_reqs
673
- for index, req_id in enumerate(self.input_batch.req_ids[:num_reqs]):
674
- assert req_id is not None
675
-
673
+ for index, req_id in enumerate(self.input_batch.req_ids):
674
req = self.requests[req_id]
675
assert req.mrope_positions is not None
676
@@ -726,12 +724,11 @@ class GPUModelRunner(LoRAModelRunnerMixin):
724
self,
725
scheduler_output: "SchedulerOutput",
726
cu_num_tokens: np.ndarray,
729
- ) -> Tuple[torch.Tensor, torch.Tensor]:
727
+ ) -> torch.Tensor:
728
# Get the number of spec decode tokens for each request.
729
num_reqs = self.input_batch.num_reqs
730
num_spec_decode_tokens = np.empty(num_reqs, dtype=np.int32)
733
- for i, req_id in zip(range(num_reqs), self.input_batch.req_ids):
734
- assert req_id is not None
731
+ for i, req_id in enumerate(self.input_batch.req_ids):
732
num_spec_decode_tokens[i] = len(
733
scheduler_output.scheduled_spec_decode_tokens.get(req_id, ()))
734
@@ -769,22 +766,6 @@ class GPUModelRunner(LoRAModelRunnerMixin):
766
return torch.from_numpy(spec_decode_logits_indices).to(
767
self.device, non_blocking=True)
768
772
- def _prepare_sampling(
773
- self,
774
- batch_changed: bool,
775
- req_to_spec_token_ids: Dict[str, List[int]],
776
- ) -> SamplingMetadata:
777
- # Create the sampling metadata.
778
- req_id_output_token_ids: Dict[str, List[int]] = \
779
- {req_id: req.output_token_ids \
780
- for req_id, req in self.requests.items()}
781
-
782
- sampling_metadata = self.input_batch.make_sampling_metadata(
783
- req_id_output_token_ids,
784
- req_to_spec_token_ids,
785
- skip_copy=not batch_changed)
786
- return sampling_metadata
787
-
769
def _execute_encoder(self, scheduler_output: "SchedulerOutput"):
770
scheduled_encoder_inputs = scheduler_output.scheduled_encoder_inputs
771
if not scheduled_encoder_inputs:
@@ -838,9 +819,7 @@ class GPUModelRunner(LoRAModelRunnerMixin):
819
scheduler_output: "SchedulerOutput",
820
) -> List[torch.Tensor]:
821
encoder_outputs: List[torch.Tensor] = []
841
- num_reqs = self.input_batch.num_reqs
842
- for req_id in self.input_batch.req_ids[:num_reqs]:
843
- assert req_id is not None
822
+ for req_id in self.input_batch.req_ids:
823
num_scheduled_tokens = scheduler_output.num_scheduled_tokens[
824
req_id]
825
req_state = self.requests[req_id]
@@ -882,7 +861,7 @@ class GPUModelRunner(LoRAModelRunnerMixin):
861
scheduler_output: "SchedulerOutput",
862
intermediate_tensors: Optional[IntermediateTensors] = None,
863
) -> Union[ModelRunnerOutput, torch.Tensor]:
885
- batch_changed = self._update_states(scheduler_output)
864
+ self._update_states(scheduler_output)
865
866
if self.is_multimodal_model:
867
# Run the multimodal encoder if any.
@@ -964,8 +943,8 @@ class GPUModelRunner(LoRAModelRunnerMixin):
943
logits = self.model.compute_logits(sample_hidden_states, None)
944
945
# Sample the next token and get logprobs if needed.
967
- sampling_metadata = self._prepare_sampling(
968
- batch_changed, scheduler_output.scheduled_spec_decode_tokens)
946
+ sampling_metadata = self.input_batch.get_sampling_metadata(
947
+ scheduler_output.scheduled_spec_decode_tokens)
948
sampler_output = self.model.sample(
949
logits=logits,
950
sampling_metadata=sampling_metadata,
@@ -973,14 +952,7 @@ class GPUModelRunner(LoRAModelRunnerMixin):
952
953
# TODO(woosuk): The following loop can be slow since it iterates over
954
# the requests one by one. Optimize.
976
- num_reqs = self.input_batch.num_reqs
977
- req_ids: List[str] = []
978
- # Because `input_batch.req_ids` is a list of length `max_num_reqs`,
979
- # we need to stop at `num_reqs`.
980
- # FIXME(woosuk): This is hacky. Refactor.
981
- for i, req_id in zip(range(num_reqs), self.input_batch.req_ids):
982
- assert req_id is not None
983
- req_ids.append(req_id)
955
+ for i, req_id in enumerate(self.input_batch.req_ids):
956
req_state = self.requests[req_id]
957
seq_len = (req_state.num_computed_tokens +
958
scheduler_output.num_scheduled_tokens[req_id])
@@ -1027,7 +999,7 @@ class GPUModelRunner(LoRAModelRunnerMixin):
999
valid_sampled_token_ids)
1000
1001
model_runner_output = ModelRunnerOutput(
1030
- req_ids=req_ids,
1002
+ req_ids=self.input_batch.req_ids,
1003
req_id_to_index=self.input_batch.req_id_to_index,
1004
sampled_token_ids=valid_sampled_token_ids,
1005
spec_token_ids=spec_token_ids,
@@ -1041,19 +1013,18 @@ class GPUModelRunner(LoRAModelRunnerMixin):
1013
sampled_token_ids: List[List[int]],
1014
) -> List[List[int]]:
1015
# TODO(woosuk): Optimize.
1044
- num_reqs = len(sampled_token_ids)
1016
draft_token_ids: List[List[int]] = []
1046
- for i in range(num_reqs):
1047
- if len(sampled_token_ids[i]) == 0:
1017
+ for i, sampled_ids in enumerate(sampled_token_ids):
1018
+ num_sampled_ids = len(sampled_ids)
1019
+ if not num_sampled_ids:
1020
# Skip speculative decoding.
1021
draft_token_ids.append([])
1022
continue
1023
1024
# Add sampled_token_ids to token_ids_cpu.
1025
start_idx = self.input_batch.num_tokens_no_spec[i]
1054
- end_idx = start_idx + len(sampled_token_ids[i])
1055
- self.input_batch.token_ids_cpu[
1056
- i, start_idx:end_idx] = sampled_token_ids[i]
1026
+ end_idx = start_idx + num_sampled_ids
1027
+ self.input_batch.token_ids_cpu[i, start_idx:end_idx] = sampled_ids
1028
drafter_output = self.drafter.propose(
1029
self.input_batch.token_ids_cpu[i, :end_idx],
1030
self.speculative_config.ngram_prompt_lookup_min,
@@ -1204,7 +1175,7 @@ class GPUModelRunner(LoRAModelRunnerMixin):
1175
# multiplying the list, to avoid Dynamo from treating them as
1176
# tensor aliasing.
1177
dummy_kv_caches = [
1207
- torch.tensor([], dtype=torch.float32, device=self.device)
1178
+ torch.tensor((), dtype=torch.float32, device=self.device)
1179
for _ in range(self.num_attn_layers)
1180
]
1181
vllm/v1/worker/tpu_model_runner.py
-2
@@ -1048,8 +1048,6 @@ def swap_positions(b: InputBatch, id_1, id_2):
1048
1049
b.min_tokens[id_1], b.min_tokens[id_2] = b.min_tokens[id_2], b.min_tokens[
1050
id_1]
1051
- b.stop_token_ids[id_1], b.stop_token_ids[id_2] = b.stop_token_ids[
1052
- id_2], b.stop_token_ids[id_1]
1051
1052
gen_1 = b.generators.pop(id_1, None)
1053
gen_2 = b.generators.pop(id_2, None)