@tduncan / Nvidia-Optimization-Stack-VLLM Mirror · read-only / commits / d70a16625d
Fork 0

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 14, 05:30 UTC

[Performance] V1 Pooling Models E2E Performance Optimization (#23162)

Signed-off-by: wang.yuqi <noooop@126.com>

wang.yuqi committed Aug 21, 2025 at 21:26 UTC d70a16625dc74d9517641aa82f4ae7367854da96
8 files changed +161 -167
vllm/model_executor/layers/pooler.py
+48 -83
@@ -19,7 +19,8 @@ from vllm.model_executor.pooling_metadata import PoolingTensors
19 from vllm.pooling_params import PoolingParams
20 from vllm.sequence import PoolerOutput, PoolingSequenceGroupOutput
21 from vllm.tasks import PoolingTask
22 -from vllm.utils import resolve_obj_by_qualname
22 +from vllm.utils import current_stream, resolve_obj_by_qualname
23 +from vllm.v1.pool.metadata import PoolingCursor
24 from vllm.v1.pool.metadata import PoolingMetadata as V1PoolingMetadata
25
26 PoolingMetadata = Union[V0PoolingMetadata, V1PoolingMetadata]
@@ -205,6 +206,13 @@ def get_cross_encoder_activation_function(config: PretrainedConfig):
206
207 def build_output(
208 all_data: Union[torch.Tensor, list[torch.Tensor]], ) -> PoolerOutput:
209 + # Pooling models D2H & synchronize occurs here
210 + if isinstance(all_data, list):
211 + all_data = [d.to("cpu", non_blocking=True) for d in all_data]
212 + else:
213 + all_data = all_data.to("cpu", non_blocking=True)
214 + current_stream().synchronize()
215 +
216 all_outputs = [PoolingSequenceGroupOutput(data) for data in all_data]
217 return PoolerOutput(outputs=all_outputs)
218
@@ -231,40 +239,21 @@ class PoolingMethod(nn.Module, ABC):
239 def get_pooling_updates(self, task: PoolingTask) -> PoolingParamsUpdate:
240 return PoolingParamsUpdate()
241
234 - @abstractmethod
235 - def forward_one(
236 - self,
237 - hidden_states: torch.Tensor,
238 - prompt_len: Optional[torch.Tensor] = None,
239 - ) -> torch.Tensor:
240 - """
241 - Note:
242 - `prompt_len=None` means `prompt_len=len(hidden_states)`.
243 - """
244 - raise NotImplementedError
245 -
242 @abstractmethod
243 def forward_all(
244 self,
245 hidden_states: torch.Tensor,
250 - prompt_lens: torch.Tensor,
246 + pooling_cursor: PoolingCursor,
247 ) -> Union[list[torch.Tensor], torch.Tensor]:
248 raise NotImplementedError
249
250 def forward(
251 self,
256 - hidden_states: Union[torch.Tensor, list[torch.Tensor]],
252 + hidden_states: torch.Tensor,
253 pooling_metadata: PoolingMetadata,
254 ) -> Union[list[torch.Tensor], torch.Tensor]:
259 - prompt_lens = get_prompt_lens(hidden_states, pooling_metadata)
260 -
261 - if isinstance(hidden_states, list):
262 - return [
263 - self.forward_one(h, prompt_len)
264 - for h, prompt_len in zip(hidden_states, prompt_lens)
265 - ]
266 -
267 - return self.forward_all(hidden_states, prompt_lens)
255 + pooling_cursor = pooling_metadata.pooling_cursor
256 + return self.forward_all(hidden_states, pooling_cursor)
257
258
259 class CLSPool(PoolingMethod):
@@ -272,24 +261,15 @@ class CLSPool(PoolingMethod):
261 def get_supported_tasks(self) -> Set[PoolingTask]:
262 return {"encode", "embed", "classify", "score"}
263
275 - def forward_one(
276 - self,
277 - hidden_states: torch.Tensor,
278 - prompt_len: Optional[torch.Tensor] = None,
279 - ) -> torch.Tensor:
280 - assert prompt_len is None or prompt_len == hidden_states.shape[0], \
281 - "partial prefill not supported with CLS pooling"
282 -
283 - return hidden_states[0]
284 -
264 def forward_all(
265 self,
266 hidden_states: torch.Tensor,
288 - prompt_lens: torch.Tensor,
267 + pooling_cursor: PoolingCursor,
268 ) -> Union[list[torch.Tensor], torch.Tensor]:
290 - first_token_flat_indices = torch.zeros_like(prompt_lens)
291 - first_token_flat_indices[1:] += torch.cumsum(prompt_lens, dim=0)[:-1]
292 - return hidden_states[first_token_flat_indices]
269 + assert not pooling_cursor.is_partial_prefill(), \
270 + "partial prefill not supported with CLS pooling"
271 +
272 + return hidden_states[pooling_cursor.first_token_indices_gpu]
273
274
275 class LastPool(PoolingMethod):
@@ -297,20 +277,12 @@ class LastPool(PoolingMethod):
277 def get_supported_tasks(self) -> Set[PoolingTask]:
278 return {"encode", "embed", "classify", "score"}
279
300 - def forward_one(
301 - self,
302 - hidden_states: torch.Tensor,
303 - prompt_len: Optional[torch.Tensor] = None,
304 - ) -> torch.Tensor:
305 - return hidden_states[-1]
306 -
280 def forward_all(
281 self,
282 hidden_states: torch.Tensor,
310 - prompt_lens: torch.Tensor,
283 + pooling_cursor: PoolingCursor,
284 ) -> Union[list[torch.Tensor], torch.Tensor]:
312 - last_token_flat_indices = torch.cumsum(prompt_lens, dim=0) - 1
313 - return hidden_states[last_token_flat_indices]
285 + return hidden_states[pooling_cursor.last_token_indices_gpu]
286
287
288 class AllPool(PoolingMethod):
@@ -318,22 +290,19 @@ class AllPool(PoolingMethod):
290 def get_supported_tasks(self) -> Set[PoolingTask]:
291 return {"encode"}
292
321 - def forward_one(
322 - self,
323 - hidden_states: torch.Tensor,
324 - prompt_len: Optional[torch.Tensor] = None,
325 - ) -> torch.Tensor:
326 - assert prompt_len is None or prompt_len == hidden_states.shape[0], \
327 - "partial prefill not supported with ALL pooling"
328 -
329 - return hidden_states
330 -
293 def forward_all(
294 self,
295 hidden_states: torch.Tensor,
334 - prompt_lens: torch.Tensor,
296 + pooling_cursor: PoolingCursor,
297 ) -> Union[list[torch.Tensor], torch.Tensor]:
336 - return list(hidden_states.split_with_sizes(prompt_lens.tolist()))
298 +
299 + assert not pooling_cursor.is_partial_prefill(), \
300 + "partial prefill not supported with ALL pooling"
301 +
302 + hidden_states_lst = list(
303 + hidden_states.split(
304 + pooling_cursor.num_scheduled_tokens_cpu.tolist()))
305 + return [hidden_states_lst[i] for i in pooling_cursor.index]
306
307
308 class MeanPool(PoolingMethod):
@@ -341,31 +310,25 @@ class MeanPool(PoolingMethod):
310 def get_supported_tasks(self) -> Set[PoolingTask]:
311 return {"encode", "embed", "classify", "score"}
312
344 - def forward_one(
313 + def forward_all(
314 self,
315 hidden_states: torch.Tensor,
347 - prompt_len: Optional[torch.Tensor] = None,
348 - ) -> torch.Tensor:
349 - assert prompt_len is None or prompt_len == hidden_states.shape[0], \
316 + pooling_cursor: PoolingCursor,
317 + ) -> Union[list[torch.Tensor], torch.Tensor]:
318 +
319 + assert not pooling_cursor.is_partial_prefill(), \
320 "partial prefill not supported with MEAN pooling"
321
352 - return hidden_states.mean(dim=0, dtype=torch.float32)
322 + prompt_lens = pooling_cursor.prompt_lens_cpu.to(hidden_states.device,
323 + non_blocking=True)
324
354 - def forward_all(
355 - self,
356 - hidden_states: torch.Tensor,
357 - prompt_lens: torch.Tensor,
358 - ) -> Union[list[torch.Tensor], torch.Tensor]:
325 # Use float32 for torch.cumsum in MeanPool,
326 # otherwise precision will be lost significantly.
327 cumsum = torch.cumsum(hidden_states, dim=0, dtype=torch.float32)
328
363 - start_indices = torch.cat([
364 - torch.tensor([0], device=hidden_states.device),
365 - torch.cumsum(prompt_lens[:-1], dim=0)
366 - ])
367 - end_indices = torch.cumsum(prompt_lens, dim=0)
368 - return (cumsum[end_indices - 1] - cumsum[start_indices] +
329 + start_indices = pooling_cursor.first_token_indices_gpu
330 + end_indices = pooling_cursor.last_token_indices_gpu
331 + return (cumsum[end_indices] - cumsum[start_indices] +
332 hidden_states[start_indices]) / prompt_lens.unsqueeze(1)
333
334
@@ -477,6 +440,10 @@ class EmbeddingPoolerHead(PoolerHead):
440
441 pooling_params = get_pooling_params(pooling_metadata)
442
443 + if isinstance(pooled_data, list):
444 + pooled_data = torch.stack(pooled_data)
445 + # pooled_data shape: [batchsize, embedding_dimension]
446 +
447 # for matryoshka representation
448 dimensions_list = [
449 pooling_param.dimensions for pooling_param in pooling_params
@@ -667,6 +634,10 @@ class ClassifierPooler(Pooler):
634 ) -> PoolerOutput:
635 pooled_data = self.pooling(hidden_states, pooling_metadata)
636
637 + if isinstance(pooled_data, list):
638 + pooled_data = torch.stack(pooled_data)
639 + # pooled_data shape: [batchsize, hidden_size]
640 +
641 if self.classifier is not None:
642 # apply classifier once on the full batch if possible
643 if isinstance(pooled_data, torch.Tensor):
@@ -717,12 +688,6 @@ class DispatchPooler(Pooler):
688 ) -> PoolerOutput:
689 poolers_by_task = self.poolers_by_task
690
720 - if isinstance(hidden_states, list):
721 - hidden_states_lst = hidden_states
722 - else:
723 - prompt_lens = get_prompt_lens(hidden_states, pooling_metadata)
724 - hidden_states_lst = list(hidden_states.split(prompt_lens.tolist()))
725 -
691 outputs = list[PoolingSequenceGroupOutput]()
692 offset = 0
693 for task, group in groupby(get_tasks(pooling_metadata)):
@@ -733,7 +698,7 @@ class DispatchPooler(Pooler):
698
699 num_items = len(list(group))
700 group_output: PoolerOutput = pooler(
736 - hidden_states_lst[offset:offset + num_items],
701 + hidden_states,
702 pooling_metadata[offset:offset + num_items],
703 )
704
vllm/model_executor/models/bert.py
+3 -3
@@ -528,9 +528,9 @@ def _encode_token_type_ids(input_ids: torch.Tensor,
528
529 def _decode_token_type_ids(input_ids: torch.Tensor) -> torch.Tensor:
530
531 - ids_mask = torch.ones(input_ids.shape,
532 - dtype=torch.int32,
533 - device=input_ids.device) << TOKEN_TYPE_SHIFT
531 + ids_mask = torch.ones_like(input_ids,
532 + dtype=torch.int32,
533 + device=input_ids.device) << TOKEN_TYPE_SHIFT
534 tokens_mask = ids_mask.bitwise_not()
535
536 token_type_ids = input_ids.bitwise_and(ids_mask) >> TOKEN_TYPE_SHIFT
vllm/model_executor/models/roberta.py
+10 -55
@@ -9,7 +9,6 @@ from torch import nn
9 from transformers import RobertaConfig
10
11 from vllm.config import VllmConfig
12 -from vllm.forward_context import get_forward_context
12 from vllm.model_executor.layers.pooler import (ClassifierPooler, CLSPool,
13 DispatchPooler, Pooler)
14 from vllm.model_executor.layers.vocab_parallel_embedding import (
@@ -100,7 +99,7 @@ class RobertaEmbeddingModel(BertEmbeddingModel):
99
100 def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""):
101 super().__init__(vllm_config=vllm_config, prefix=prefix)
103 - self.padding_idx = vllm_config.model_config.hf_config.pad_token_id
102 + self.padding_idx: int = vllm_config.model_config.hf_config.pad_token_id
103
104 def forward(
105 self,
@@ -178,7 +177,7 @@ class RobertaForSequenceClassification(nn.Module, SupportsCrossEncoding):
177 def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""):
178 super().__init__()
179 config = vllm_config.model_config.hf_config
181 - self.padding_idx = vllm_config.model_config.hf_config.pad_token_id
180 + self.padding_idx: int = vllm_config.model_config.hf_config.pad_token_id
181
182 self.num_labels = config.num_labels
183 self.roberta = BertModel(vllm_config=vllm_config,
@@ -233,58 +232,14 @@ class RobertaForSequenceClassification(nn.Module, SupportsCrossEncoding):
232 intermediate_tensors=intermediate_tensors)
233
234
236 -# Adapted from transformers
237 -def create_position_ids_from_input_ids(input_ids,
238 - padding_idx,
239 - past_key_values_length=0):
240 - """
241 - Replace non-padding symbols with their position numbers.
242 - Position numbers begin at padding_idx+1. Padding symbols
243 - are ignored. This is modified from fairseq's `utils.make_positions`.
244 -
245 - Args:
246 - x: torch.Tensor x:
247 -
248 - Returns: torch.Tensor
249 - """
250 - # The series of casts and type-conversions here are carefully
251 - # balanced to both work with ONNX export and XLA.
252 - mask = input_ids.ne(padding_idx).int()
253 -
254 - incremental_indices = (torch.cumsum(mask, dim=0).type_as(mask) +
255 - past_key_values_length) * mask
256 -
257 - return incremental_indices.long() + padding_idx
258 -
259 -
235 def replace_roberta_positions(input_ids: torch.Tensor,
236 position_ids: torch.Tensor,
237 padding_idx: int) -> None:
263 -
264 - seq_lens: Optional[torch.Tensor] = None
265 - attn_metadata = get_forward_context().attn_metadata
266 - if attn_metadata is not None: # can be None during warmup
267 - if isinstance(attn_metadata, dict):
268 - attn_metadata = next(iter(attn_metadata.values()))
269 - # TODO: remove "seq_lens_tensor" after V0 is removed
270 - seq_lens = getattr(attn_metadata, "seq_lens_tensor",
271 - getattr(attn_metadata, "seq_lens", None))
272 -
273 - if seq_lens is not None:
274 - assert isinstance(seq_lens, torch.Tensor)
275 -
276 - # Replace position ids because in RoBERTa models
277 - # they have to start at padding_idx + 1 and ignore
278 - # existing padding tokens
279 - # References:
280 - # - https://github.com/huggingface/transformers/blob/a3d69a8994d673899608a7c17fbf4f953f50474e/src/transformers/models/roberta/modeling_roberta.py#L133
281 - # - https://github.com/huggingface/transformers/blob/a3d69a8994d673899608a7c17fbf4f953f50474e/src/transformers/models/roberta/modeling_roberta.py#L1669
282 - token_list = torch.split(input_ids[:torch.sum(seq_lens)],
283 - seq_lens.tolist())
284 -
285 - offset = 0
286 - for tokens in token_list:
287 - length = tokens.shape[0]
288 - position_ids[offset:offset+length] = \
289 - create_position_ids_from_input_ids(tokens, padding_idx)
290 - offset = offset + length
238 + # Replace position ids because in RoBERTa models
239 + # they have to start at padding_idx + 1 and ignore
240 + # existing padding tokens
241 + # References:
242 + # - https://github.com/huggingface/transformers/blob/a3d69a8994d673899608a7c17fbf4f953f50474e/src/transformers/models/roberta/modeling_roberta.py#L133
243 + # - https://github.com/huggingface/transformers/blob/a3d69a8994d673899608a7c17fbf4f953f50474e/src/transformers/models/roberta/modeling_roberta.py#L1669
244 + # vllm does not use padding tokens, let's make things simpler
245 + position_ids += padding_idx + 1
vllm/model_executor/pooling_metadata.py
+17 -6
@@ -2,12 +2,13 @@
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
4 from dataclasses import dataclass
5 -from typing import Any
5 +from typing import Any, Optional
6
7 import torch
8
9 from vllm.pooling_params import PoolingParams
10 from vllm.utils import is_pin_memory_available
11 +from vllm.v1.pool.metadata import PoolingCursor, build_pooling_cursor
12
13
14 class PoolingMetadata:
@@ -23,14 +24,15 @@ class PoolingMetadata:
24 """
25
26 def __init__(
26 - self,
27 - seq_groups: list[tuple[list[int], PoolingParams]],
28 - seq_data: dict[int, Any], # Specific data related to sequences
29 - prompt_lens: list[int],
30 - ) -> None:
27 + self,
28 + seq_groups: list[tuple[list[int], PoolingParams]],
29 + seq_data: dict[int, Any], # Specific data related to sequences
30 + prompt_lens: list[int],
31 + pooling_cursor: Optional[PoolingCursor] = None) -> None:
32 self.seq_groups = seq_groups
33 self.seq_data = seq_data
34 self.prompt_lens = prompt_lens
35 + self.pooling_cursor: Optional[PoolingCursor] = pooling_cursor
36
37 def __repr__(self) -> str:
38 return ("PoolingMetadata("
@@ -43,8 +45,17 @@ class PoolingMetadata:
45 seq_groups=self.seq_groups[indices],
46 seq_data=dict(list(self.seq_data.items())[indices]),
47 prompt_lens=self.prompt_lens[indices],
48 + pooling_cursor=None
49 + if self.pooling_cursor is None else self.pooling_cursor[indices],
50 )
51
52 + def build_pooling_cursor(self, num_scheduled_tokens: list[int],
53 + device: torch.device):
54 + prompt_lens = torch.tensor(self.prompt_lens, device="cpu")
55 + self.pooling_cursor = build_pooling_cursor(num_scheduled_tokens,
56 + prompt_lens,
57 + device=device)
58 +
59
60 @dataclass
61 class PoolingTensors:
vllm/v1/pool/metadata.py
+54 -2
@@ -6,15 +6,40 @@ from typing import Optional
6 import torch
7
8 from vllm.pooling_params import PoolingParams
9 +from vllm.utils import is_pin_memory_available
10 +
11 +pin_memory = is_pin_memory_available()
12 +
13 +
14 +@dataclass
15 +class PoolingCursor:
16 + index: list[int]
17 + first_token_indices_gpu: torch.Tensor
18 + last_token_indices_gpu: torch.Tensor
19 + prompt_lens_cpu: torch.Tensor
20 + num_scheduled_tokens_cpu: torch.Tensor
21 +
22 + def __getitem__(self, indices: slice):
23 + return PoolingCursor(
24 + index=self.index[indices],
25 + first_token_indices_gpu=self.first_token_indices_gpu[indices],
26 + last_token_indices_gpu=self.last_token_indices_gpu[indices],
27 + prompt_lens_cpu=self.prompt_lens_cpu[indices],
28 + num_scheduled_tokens_cpu=self.num_scheduled_tokens_cpu[indices],
29 + )
30 +
31 + def is_partial_prefill(self):
32 + return not torch.all(
33 + self.prompt_lens_cpu == self.num_scheduled_tokens_cpu)
34
35
36 @dataclass
37 class PoolingMetadata:
38 """Tensors for pooling."""
14 -
15 - prompt_lens: torch.Tensor
39 + prompt_lens: torch.Tensor # CPU Tensor
40 prompt_token_ids: Optional[torch.Tensor]
41 pooling_params: list[PoolingParams]
42 + pooling_cursor: Optional[PoolingCursor] = None
43
44 def __getitem__(self, indices: slice):
45 return PoolingMetadata(
@@ -22,4 +47,31 @@ class PoolingMetadata:
47 prompt_token_ids=None if self.prompt_token_ids is None else
48 self.prompt_token_ids[indices],
49 pooling_params=self.pooling_params[indices],
50 + pooling_cursor=None
51 + if self.pooling_cursor is None else self.pooling_cursor[indices],
52 )
53 +
54 + def build_pooling_cursor(self, num_scheduled_tokens: list[int],
55 + device: torch.device):
56 + self.pooling_cursor = build_pooling_cursor(num_scheduled_tokens,
57 + self.prompt_lens, device)
58 +
59 +
60 +def build_pooling_cursor(num_scheduled_tokens: list[int],
61 + prompt_lens: torch.Tensor, device: torch.device):
62 + assert len(prompt_lens) == len(num_scheduled_tokens)
63 +
64 + n_seq = len(num_scheduled_tokens)
65 + index = list(range(n_seq))
66 + num_scheduled_tokens = torch.tensor(num_scheduled_tokens, device="cpu")
67 + cumsum = torch.zeros(n_seq + 1,
68 + dtype=torch.int64,
69 + pin_memory=pin_memory,
70 + device="cpu")
71 + torch.cumsum(num_scheduled_tokens, dim=0, out=cumsum[1:])
72 + cumsum = cumsum.to(device, non_blocking=True)
73 + return PoolingCursor(index=index,
74 + first_token_indices_gpu=cumsum[:n_seq],
75 + last_token_indices_gpu=cumsum[1:] - 1,
76 + prompt_lens_cpu=prompt_lens,
77 + num_scheduled_tokens_cpu=num_scheduled_tokens)
vllm/v1/worker/gpu_input_batch.py
+1 -1
@@ -713,7 +713,7 @@ class InputBatch:
713
714 return PoolingMetadata(
715 prompt_lens=torch.from_numpy(
716 - self.num_prompt_tokens[:self.num_reqs]).to(self.device),
716 + self.num_prompt_tokens[:self.num_reqs]),
717 prompt_token_ids=self.sampling_metadata.prompt_token_ids,
718 pooling_params=pooling_params,
719 )
vllm/v1/worker/gpu_model_runner.py
+20 -16
@@ -1476,23 +1476,22 @@ class GPUModelRunner(LoRAModelRunnerMixin, KVConnectorModelRunnerMixin):
1476 "Either all or none of the requests in" \
1477 " a batch must be pooling request"
1478
1479 - extracted_hidden_states = list(
1480 - torch.split(hidden_states[:num_scheduled_tokens],
1481 - num_scheduled_tokens_np.tolist()))
1482 -
1479 + hidden_states = hidden_states[:num_scheduled_tokens]
1480 pooling_metadata = self.input_batch.pooling_metadata
1481 + pooling_metadata.build_pooling_cursor(num_scheduled_tokens_np.tolist(),
1482 + device=hidden_states.device)
1483 + seq_lens_cpu = self.seq_lens_cpu[:self.input_batch.num_reqs]
1484
1485 + # Pooling models D2H & synchronize occurs in pooler.py:build_output
1486 raw_pooler_output = self.model.pooler(
1486 - hidden_states=extracted_hidden_states,
1487 - pooling_metadata=pooling_metadata)
1487 + hidden_states=hidden_states, pooling_metadata=pooling_metadata)
1488
1489 pooler_output: list[Optional[torch.Tensor]] = []
1490 - seq_lens = self.seq_lens[:self.input_batch.num_reqs]
1490 for raw_output, seq_len, prompt_len in zip(
1492 - raw_pooler_output, seq_lens, pooling_metadata.prompt_lens):
1491 + raw_pooler_output, seq_lens_cpu, pooling_metadata.prompt_lens):
1492
1493 if seq_len == prompt_len:
1495 - pooler_output.append(raw_output.data.cpu())
1494 + pooler_output.append(raw_output.data)
1495 else:
1496 pooler_output.append(None)
1497
@@ -2524,13 +2523,11 @@ class GPUModelRunner(LoRAModelRunnerMixin, KVConnectorModelRunnerMixin):
2523 assert sum(num_scheduled_tokens_list) == num_tokens
2524 assert len(num_scheduled_tokens_list) == num_reqs
2525
2527 - hidden_states_list = list(
2528 - torch.split(hidden_states, num_scheduled_tokens_list))
2526 req_num_tokens = num_tokens // num_reqs
2527
2528 dummy_prompt_lens = torch.tensor(
2532 - [h.shape[0] for h in hidden_states_list],
2533 - device=self.device,
2529 + num_scheduled_tokens_list,
2530 + device="cpu",
2531 )
2532 dummy_token_ids = torch.zeros((num_reqs, req_num_tokens),
2533 dtype=torch.int32,
@@ -2547,8 +2544,11 @@ class GPUModelRunner(LoRAModelRunnerMixin, KVConnectorModelRunnerMixin):
2544 pooling_params=[dummy_pooling_params] * num_reqs,
2545 )
2546
2547 + dummy_metadata.build_pooling_cursor(num_scheduled_tokens_list,
2548 + device=hidden_states.device)
2549 +
2550 try:
2551 - return model.pooler(hidden_states=hidden_states_list,
2551 + return model.pooler(hidden_states=hidden_states,
2552 pooling_metadata=dummy_metadata)
2553 except RuntimeError as e:
2554 if 'out of memory' in str(e):
@@ -3316,10 +3316,14 @@ class GPUModelRunner(LoRAModelRunnerMixin, KVConnectorModelRunnerMixin):
3316
3317 dummy_block_table = torch.zeros((num_reqs, 1),
3318 dtype=torch.int32,
3319 - device=self.device)
3319 + pin_memory=self.pin_memory,
3320 + device="cpu").to(self.device,
3321 + non_blocking=True)
3322 dummy_slot_mapping = torch.zeros((total_num_scheduled_tokens, ),
3323 dtype=torch.int32,
3322 - device=self.device)
3324 + pin_memory=self.pin_memory,
3325 + device="cpu").to(self.device,
3326 + non_blocking=True)
3327
3328 group_metadata = dict[str, tuple[CommonAttentionMetadata, Any]]()
3329
vllm/worker/pooling_model_runner.py
+8 -1
@@ -149,9 +149,16 @@ class PoolingModelRunner(
149 if not self.is_driver_worker:
150 return []
151
152 + pooling_metadata = model_input.pooling_metadata
153 + assert pooling_metadata is not None
154 +
155 + pooling_metadata.build_pooling_cursor(
156 + num_scheduled_tokens=pooling_metadata.prompt_lens,
157 + device=hidden_or_intermediate_states.device)
158 +
159 return [
160 self.model.pooler(hidden_states=hidden_or_intermediate_states,
154 - pooling_metadata=model_input.pooling_metadata)
161 + pooling_metadata=pooling_metadata)
162 ]
163
164 def make_model_input_from_broadcasted_tensor_dict(