-
-
Notifications
You must be signed in to change notification settings - Fork 207
Expand file tree
/
Copy pathgpu_model_runner.py
More file actions
7157 lines (6356 loc) · 328 KB
/
Copy pathgpu_model_runner.py
File metadata and controls
7157 lines (6356 loc) · 328 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
import functools
import gc
import itertools
import threading
import time
from collections import defaultdict
from collections.abc import Callable, Iterable, Iterator, Sequence
from contextlib import contextmanager
from copy import copy, deepcopy
from dataclasses import dataclass, replace
from functools import reduce
from typing import TYPE_CHECKING, Any, NamedTuple, TypeAlias, cast
import numpy as np
import torch
import torch.distributed
import torch.nn as nn
import aphrodite.envs as envs
from aphrodite.compilation.breakable_cudagraph import (
BreakableCUDAGraphWrapper,
is_breakable_cudagraph_enabled,
)
from aphrodite.compilation.counter import compilation_counter
from aphrodite.compilation.cuda_graph import CUDAGraphStat, CUDAGraphWrapper
from aphrodite.compilation.monitor import set_cudagraph_capturing_enabled
from aphrodite.config import (
AphroditeConfig,
CompilationMode,
CUDAGraphMode,
get_layers_from_aphrodite_config,
set_current_aphrodite_config,
update_config,
)
from aphrodite.config.cache import CacheConfig
from aphrodite.distributed.ec_transfer import get_ec_transfer, has_ec_transfer
from aphrodite.distributed.eplb.eplb_state import EplbState
from aphrodite.distributed.kv_transfer import get_kv_transfer_group, has_kv_transfer_group
from aphrodite.distributed.kv_transfer.kv_connector.utils import copy_kv_blocks
from aphrodite.distributed.parallel_state import (
get_dcp_group,
get_pp_group,
get_tp_group,
graph_capture,
is_global_first_rank,
prepare_communication_buffer_for_model,
)
from aphrodite.forward_context import (
BatchDescriptor,
set_forward_context,
)
from aphrodite.logger import init_logger
from aphrodite.lora.layers import LoRAMapping, LoRAMappingType
from aphrodite.model_executor.layers.attention import Attention, MLAAttention
from aphrodite.model_executor.layers.attention_layer_base import AttentionLayerBase
from aphrodite.model_executor.layers.fused_moe.all2all_utils import get_ep_all2all_manager
from aphrodite.model_executor.layers.fused_moe.routed_experts_capturer import (
RoutedExpertsCapturer,
)
from aphrodite.model_executor.layers.mamba.ops.ssu_dispatch import (
initialize_mamba_ssu_backend,
)
from aphrodite.model_executor.layers.rotary_embedding import (
MRotaryEmbedding,
XDRotaryEmbedding,
)
from aphrodite.model_executor.model_loader import get_model_loader
from aphrodite.model_executor.model_loader.reload import (
finalize_layerwise_reload,
initialize_layerwise_reload,
)
from aphrodite.model_executor.models.interfaces import (
MixtureOfExperts,
MultiModalEmbeddings,
SupportsMRoPE,
SupportsMultiModal,
SupportsXDRoPE,
is_mixture_of_experts,
supports_eagle3,
supports_mrope,
supports_multimodal_pruning,
supports_realtime,
supports_transcription,
supports_xdrope,
)
from aphrodite.model_executor.models.interfaces_base import (
AphroditeModelForPooling,
is_pooling_model,
is_text_generation_model,
)
from aphrodite.model_executor.offloader import (
create_offloader,
get_offloader,
set_offloader,
)
from aphrodite.multimodal import MULTIMODAL_REGISTRY
from aphrodite.multimodal.encoder_budget import MultiModalBudget
from aphrodite.multimodal.inputs import (
BatchedTensorInputs,
MultiModalKwargsItem,
PlaceholderRange,
)
from aphrodite.multimodal.utils import get_mm_features_in_window, group_and_batch_mm_kwargs
from aphrodite.platforms import current_platform
from aphrodite.pooling_params import PoolingParams
from aphrodite.sampling_params import SamplingType
from aphrodite.sequence import IntermediateTensors
from aphrodite.tasks import GenerationTask, PoolingTask, SupportedTask
from aphrodite.tracing import instrument
from aphrodite.utils import length_from_prompt_token_ids_or_embeds
from aphrodite.utils.math_utils import cdiv, round_up
from aphrodite.utils.mem_utils import DeviceMemoryProfiler, format_gib
from aphrodite.utils.nvtx_pytorch_hooks import PytHooks
from aphrodite.utils.platform_utils import num_compute_units
from aphrodite.utils.torch_utils import (
PIN_MEMORY,
async_tensor_h2d,
get_dtype_size,
is_quantized_kv_cache,
kv_cache_dtype_str_to_dtype,
)
from aphrodite.v1.attention.backend import (
AttentionBackend,
AttentionCGSupport,
AttentionMetadata,
AttentionMetadataBuilder,
AttentionType,
CommonAttentionMetadata,
)
from aphrodite.v1.attention.backends.gdn_attn import GDNAttentionMetadataBuilder
from aphrodite.v1.attention.backends.linear_attn import (
BailingLinearAttentionMetadataBuilder,
)
from aphrodite.v1.attention.backends.mamba2_attn import Mamba2AttentionMetadataBuilder
from aphrodite.v1.attention.backends.utils import (
NULL_BLOCK_ID,
create_fast_prefill_custom_backend,
get_dcp_local_seq_lens,
reorder_batch_to_split_decodes_and_prefills,
)
from aphrodite.v1.core.sched.output import NewRequestData
from aphrodite.v1.cudagraph_dispatcher import CudagraphDispatcher
from aphrodite.v1.kv_cache_interface import (
AttentionSpec,
ChunkedLocalAttentionSpec,
CrossAttentionSpec,
EncoderOnlyAttentionSpec,
FullAttentionSpec,
KVCacheConfig,
KVCacheGroupSpec,
KVCacheSpec,
MambaSpec,
SlidingWindowSpec,
UniformTypeKVCacheSpecs,
)
from aphrodite.v1.kv_cache_spec_registry import KVCacheSpecRegistry
from aphrodite.v1.outputs import (
EMPTY_MODEL_RUNNER_OUTPUT,
AsyncModelRunnerOutput,
DraftTokenIds,
ECConnectorOutput,
KVConnectorOutput,
LogprobsLists,
LogprobsTensors,
ModelRunnerOutput,
PoolerOutput,
RoutedExpertsLists,
RoutedExpertsTensors,
SamplerOutput,
make_empty_encoder_model_runner_output,
)
from aphrodite.v1.pool.late_interaction_runner import LateInteractionRunner
from aphrodite.v1.pool.metadata import PoolingMetadata, PoolingStates
from aphrodite.v1.sample.logits_processor import LogitsProcessors, build_logitsprocs
from aphrodite.v1.sample.logits_processor.interface import LogitsProcessor
from aphrodite.v1.sample.metadata import SamplingMetadata
from aphrodite.v1.sample.ops.dry import update_dry_state
from aphrodite.v1.sample.rejection_sampler import RejectionSampler
from aphrodite.v1.sample.sampler import Sampler
from aphrodite.v1.spec_decode.custom_class_proposer import create_custom_proposer
from aphrodite.v1.spec_decode.dflash import DFlashProposer
from aphrodite.v1.spec_decode.draft_model import DraftModelProposer
from aphrodite.v1.spec_decode.eagle import EagleProposer
from aphrodite.v1.spec_decode.extract_hidden_states import ExtractHiddenStatesProposer
from aphrodite.v1.spec_decode.gemma4 import Gemma4Proposer
from aphrodite.v1.spec_decode.medusa import MedusaProposer
from aphrodite.v1.spec_decode.metadata import SpecDecodeMetadata
from aphrodite.v1.spec_decode.ngram_proposer_gpu import (
NgramProposerGPU,
copy_num_valid_draft_tokens,
update_ngram_gpu_tensors_incremental,
update_scheduler_for_invalid_drafts,
)
from aphrodite.v1.spec_decode.step3p5 import Step3p5MTPProposer
from aphrodite.v1.spec_decode.suffix_decoding import SuffixDecodingProposer
from aphrodite.v1.spec_decode.utils import update_num_computed_tokens_for_batch_change
from aphrodite.v1.structured_output.utils import apply_grammar_bitmask
from aphrodite.v1.utils import CpuGpuBuffer, record_function_or_nullcontext
from aphrodite.v1.worker import mamba_utils
from aphrodite.v1.worker.cp_utils import (
check_attention_cp_compatibility,
get_total_cp_world_size,
)
from aphrodite.v1.worker.dp_utils import coordinate_batch_across_dp
from aphrodite.v1.worker.ec_connector_model_runner_mixin import ECConnectorModelRunnerMixin
from aphrodite.v1.worker.gpu.attn_utils import _reshape_attention_kv_cache
from aphrodite.v1.worker.gpu_input_batch import CachedRequestState, InputBatch
from aphrodite.v1.worker.gpu_ubatch_wrapper import UBatchWrapper
from aphrodite.v1.worker.kv_connector_model_runner_mixin import KVConnectorModelRunnerMixin
from aphrodite.v1.worker.lora_model_runner_mixin import LoRAModelRunnerMixin
from aphrodite.v1.worker.ubatch_utils import (
UBatchSlices,
check_ubatch_thresholds,
maybe_create_ubatch_slices,
split_attn_metadata,
)
from aphrodite.v1.worker.utils import is_residual_scattered_for_sp
from aphrodite.v1.worker.workspace import lock_workspace
from .utils import (
AttentionGroup,
KVBlockZeroer,
add_kv_sharing_layers_to_kv_cache_groups,
bind_kv_cache,
copy_kv_cache_blocks_inplace,
prepare_kernel_block_sizes,
sanity_check_mm_encoder_outputs,
)
if TYPE_CHECKING:
from aphrodite.v1.core.sched.output import GrammarOutput, SchedulerOutput
from aphrodite.v1.spec_decode.ngram_proposer import NgramProposer
from aphrodite.v1.worker.encoder_cudagraph import EncoderCudaGraphManager
logger = init_logger(__name__)
AttnMetadataDict: TypeAlias = dict[str, AttentionMetadata]
# list when ubatching is enabled
PerLayerAttnMetadata: TypeAlias = list[AttnMetadataDict] | AttnMetadataDict
# Wrapper for ModelRunnerOutput to support overlapped execution.
class AsyncGPUModelRunnerOutput(AsyncModelRunnerOutput):
def __init__(
self,
model_runner_output: ModelRunnerOutput,
sampled_token_ids: torch.Tensor,
logprobs_tensors: LogprobsTensors | None,
invalid_req_indices: list[int],
async_output_copy_stream: torch.cuda.Stream,
vocab_size: int,
routed_experts: RoutedExpertsTensors | None = None,
check_ep_fault: bool = False,
):
self._model_runner_output = model_runner_output
self._invalid_req_indices = invalid_req_indices
# Event on the copy stream so we can synchronize the non-blocking copy.
# Blocking (sleep) event to avoid busy-polling the CUDA driver lock.
self.async_copy_ready_event = torch.cuda.Event(blocking=True)
# Keep a reference to the device tensor to avoid it being
# deallocated until we finish copying it to the host.
self._sampled_token_ids = sampled_token_ids
self.vocab_size = vocab_size
self._logprobs_tensors = logprobs_tensors
self._routed_experts = routed_experts
self._has_fault: torch.Tensor | None = None
# Initiate the copy on a separate stream, but do not synchronize it.
default_stream = torch.cuda.current_stream()
with torch.cuda.stream(async_output_copy_stream):
async_output_copy_stream.wait_stream(default_stream)
self.sampled_token_ids_cpu = self._sampled_token_ids.to("cpu", non_blocking=True)
self._logprobs_tensors_cpu = self._logprobs_tensors.to_cpu_nonblocking() if self._logprobs_tensors else None
self._routed_experts_cpu = (
self._routed_experts.to_cpu_nonblocking() if self._routed_experts is not None else None
)
if check_ep_fault:
has_fault = get_ep_all2all_manager().query_fault()
self._has_fault = has_fault.to("cpu", non_blocking=True)
self.async_copy_ready_event.record()
def get_output(self) -> ModelRunnerOutput:
"""Copy the device tensors to the host and return a ModelRunnerOutput.
This function blocks until the copy is finished.
"""
max_gen_len = self.sampled_token_ids_cpu.shape[-1]
self.async_copy_ready_event.synchronize()
# Release the device tensors once the copy has completed.
del self._logprobs_tensors
del self._sampled_token_ids
if max_gen_len == 1:
valid_sampled_token_ids = self.sampled_token_ids_cpu.tolist()
for i in self._invalid_req_indices:
valid_sampled_token_ids[i].clear()
logprobs_lists = None
if self._logprobs_tensors_cpu is not None:
logprobs_lists = self._logprobs_tensors_cpu.tolists()
else:
valid_sampled_token_ids, logprobs_lists = RejectionSampler.parse_output(
self.sampled_token_ids_cpu,
self.vocab_size,
self._invalid_req_indices,
logprobs_tensors=self._logprobs_tensors_cpu,
)
output = self._model_runner_output
output.sampled_token_ids = valid_sampled_token_ids
output.logprobs = logprobs_lists
if self._routed_experts_cpu is not None:
output.routed_experts = self._routed_experts_cpu.tolists()
del self._routed_experts
if self._has_fault is not None and self._has_fault.item():
mask = get_ep_all2all_manager().query_active_mask()
raise RuntimeError(
"Fault detected in EP all2all communication: "
"one or more ranks timed out during dispatch/combine. "
f"Mask: {mask.cpu().tolist()}"
)
return output
def _copy_pooler_output_to_cpu(raw_pooler_output: PoolerOutput, finished_mask: list[bool]) -> list[torch.Tensor | None]:
num_reqs = len(finished_mask)
if isinstance(raw_pooler_output, torch.Tensor):
if raw_pooler_output.shape[0] != num_reqs:
raise ValueError(
"Pooler output batch size does not match finished mask size: "
f"{raw_pooler_output.shape[0]} != {num_reqs}."
)
num_finished = sum(finished_mask)
if num_finished == 0:
return [None] * num_reqs
if num_finished == num_reqs:
return list(raw_pooler_output.to("cpu", non_blocking=True))
# partial finished
finished_indices = [i for i, include in enumerate(finished_mask) if include]
index_tensor = torch.tensor(finished_indices, device=raw_pooler_output.device, dtype=torch.long)
finished_outputs = raw_pooler_output.index_select(0, index_tensor).to("cpu", non_blocking=True)
partial_pooler_output: list[torch.Tensor | None] = [None] * num_reqs
for i, out in zip(finished_indices, finished_outputs):
partial_pooler_output[i] = out
return partial_pooler_output
assert isinstance(raw_pooler_output, list)
if len(raw_pooler_output) != num_reqs:
raise ValueError(
f"Pooler output batch size does not match finished mask size: {len(raw_pooler_output)} != {num_reqs}."
)
pooler_output: list[torch.Tensor | None] = [None] * num_reqs
for i, (out, include) in enumerate(zip(raw_pooler_output, finished_mask)):
if include and out is not None:
pooler_output[i] = out.to("cpu", non_blocking=True)
return pooler_output
class AsyncGPUPoolingModelRunnerOutput(AsyncModelRunnerOutput):
def __init__(
self,
model_runner_output: ModelRunnerOutput,
raw_pooler_output: PoolerOutput,
finished_mask: list[bool],
async_output_copy_stream: torch.cuda.Stream,
):
self._model_runner_output = model_runner_output
# Event on the copy stream so we can synchronize the non-blocking copy.
# Blocking (sleep) event to avoid busy-polling the CUDA driver lock.
self.async_copy_ready_event = torch.cuda.Event(blocking=True)
# Keep a reference to the device tensors to avoid them being
# deallocated until we finish copying it to the host.
self._raw_pooler_output = raw_pooler_output
# Initiate the copy on a separate stream, but do not synchronize it.
default_stream = torch.cuda.current_stream()
with torch.cuda.stream(async_output_copy_stream):
async_output_copy_stream.wait_stream(default_stream)
self._model_runner_output.pooler_output = _copy_pooler_output_to_cpu(
raw_pooler_output=self._raw_pooler_output,
finished_mask=finished_mask,
)
self.async_copy_ready_event.record()
def get_output(self) -> ModelRunnerOutput:
"""Copy the device tensors to the host and return a ModelRunnerOutput.
This function blocks until the copy is finished.
"""
self.async_copy_ready_event.synchronize()
# Release the device tensors once the copy has completed.
del self._raw_pooler_output
return self._model_runner_output
class ExecuteModelState(NamedTuple):
"""Ephemeral cached state transferred between execute_model() and
sample_tokens(), after execute_model() returns None."""
scheduler_output: "SchedulerOutput"
logits: torch.Tensor
spec_decode_metadata: SpecDecodeMetadata | None
spec_decode_common_attn_metadata: CommonAttentionMetadata | None
hidden_states: torch.Tensor
sample_hidden_states: torch.Tensor
aux_hidden_states: list[torch.Tensor] | None
ec_connector_output: ECConnectorOutput | None
cudagraph_stats: CUDAGraphStat | None
slot_mappings: dict[str, torch.Tensor] | list[dict[str, torch.Tensor]] | None
class GPUModelRunner(LoRAModelRunnerMixin, KVConnectorModelRunnerMixin, ECConnectorModelRunnerMixin):
def __init__(
self,
aphrodite_config: AphroditeConfig,
device: torch.device,
):
self.aphrodite_config = aphrodite_config
self.model_config = aphrodite_config.model_config
self.cache_config = aphrodite_config.cache_config
self.offload_config = aphrodite_config.offload_config
self.compilation_config = aphrodite_config.compilation_config
self.lora_config = aphrodite_config.lora_config
self.load_config = aphrodite_config.load_config
self.parallel_config = aphrodite_config.parallel_config
self.scheduler_config = aphrodite_config.scheduler_config
self.speculative_config = aphrodite_config.speculative_config
self.observability_config = aphrodite_config.observability_config
model_config = self.model_config
cache_config = self.cache_config
scheduler_config = self.scheduler_config
parallel_config = self.parallel_config
self.device = device
self.dtype = self.model_config.dtype
self.check_ep_fault = False
if parallel_config.data_parallel_size > 1 and self.model_config.is_moe:
self.check_ep_fault = get_ep_all2all_manager().support_fault_tolerance
self.kv_cache_dtype = kv_cache_dtype_str_to_dtype(cache_config.cache_dtype, self.model_config)
self.is_pooling_model = model_config.runner_type == "pooling"
self.enable_prompt_embeds = model_config.enable_prompt_embeds
self.is_multimodal_raw_input_only_model = model_config.is_multimodal_raw_input_only_model
# These will be overridden in load_model()
self.is_multimodal_pruning_enabled = False
self.requires_sequential_video_encoding = False
# Set to True after init_routed_experts_capturer() completes.
# Prevents routed experts code from running during profiling/dummy run.
self.routed_experts_initialized = False
self.max_model_len = model_config.max_model_len
# Always set to false after the first forward pass
self.calculate_kv_scales = self.cache_config.calculate_kv_scales
self.dcp_world_size = self.parallel_config.decode_context_parallel_size
self.dcp_rank = 0 if self.dcp_world_size <= 1 else get_dcp_group().rank_in_group
self.max_num_tokens = scheduler_config.max_num_batched_tokens
self.max_num_reqs = scheduler_config.max_num_seqs
# Broadcast PP output for external_launcher (torchrun)
# to make sure we are synced across pp ranks
# TODO: Support overlapping micro-batches
# https://github.com/vllm-project/vllm/issues/18019
self.broadcast_pp_output = (
self.parallel_config.distributed_executor_backend == "external_launcher" and len(get_pp_group().ranks) > 1
)
# Model-related.
self.num_query_heads = model_config.get_num_attention_heads(parallel_config)
self.inputs_embeds_size = model_config.get_inputs_embeds_size()
# Only relevant for models using ALiBi (e.g, MPT)
self.use_alibi = model_config.uses_alibi
self.cascade_attn_enabled = not self.model_config.disable_cascade_attn
self.is_mm_prefix_lm = self.model_config.is_mm_prefix_lm
# Multi-modal data support
self.mm_registry = MULTIMODAL_REGISTRY
self.uses_mrope = model_config.uses_mrope
self.uses_xdrope_dim = model_config.uses_xdrope_dim
self.supports_mm_inputs = self.mm_registry.supports_multimodal_inputs(model_config)
if self.model_config.is_encoder_decoder:
# Maximum length of the encoder input, only for encoder-decoder
# models.
self.max_encoder_len = scheduler_config.max_num_encoder_input_tokens
else:
self.max_encoder_len = 0
# Async scheduling
self.use_async_scheduling = self.scheduler_config.async_scheduling
# Sampler
self.sampler = Sampler(
logprobs_mode=self.model_config.logprobs_mode,
use_fp64_gumbel=self.model_config.use_fp64_gumbel,
)
self.eplb_state: EplbState | None = None
self._moe_model: MixtureOfExperts | None = None
# NOTE(yongji): flag to temporarily disable EPLB during scaling up/down
self.eep_eplb_suppressed = False
"""
State of the expert parallelism load balancer.
Will be lazily initialized when the model is loaded.
"""
# Lazy initializations
# self.model: nn.Module # Set after load_model
# Initialize in initialize_kv_cache
self.kv_caches: list[torch.Tensor] = []
# Initialize in initialize_kv_cache_tensors
self.cross_layers_kv_cache: torch.Tensor | None = None
self.cross_layers_attn_backend: type[AttentionBackend] | None = None
# indexes: [kv_cache_group_id][attn_group]
self.attn_groups: list[list[AttentionGroup]] = []
# self.kv_cache_config: KVCacheConfig
# mm_hash -> encoder_output
self.encoder_cache: dict[str, torch.Tensor] = {}
self.late_interaction_runner = LateInteractionRunner()
# Encoder CUDA graph manager (initialized after model load if enabled)
self.encoder_cudagraph_manager: EncoderCudaGraphManager | None = None
self.use_aux_hidden_state_outputs = False
# Set up speculative decoding.
# NOTE(Jiayi): currently we put the entire draft model on
# the last PP rank. This is not ideal if there are many
# layers in the draft model.
if self.speculative_config and get_pp_group().is_last_rank:
self.drafter: (
NgramProposer # noqa: F823
| NgramProposerGPU
| SuffixDecodingProposer
| EagleProposer
| DFlashProposer
| DraftModelProposer
| MedusaProposer
| ExtractHiddenStatesProposer
| Gemma4Proposer
| Step3p5MTPProposer
)
if self.speculative_config.method == "custom_class":
self.drafter = create_custom_proposer( # type: ignore[assignment]
self.aphrodite_config
)
elif self.speculative_config.method == "ngram":
from aphrodite.v1.spec_decode.ngram_proposer import NgramProposer
self.drafter = NgramProposer(self.aphrodite_config)
elif self.speculative_config.uses_draft_model():
self.drafter = DraftModelProposer(
aphrodite_config=self.aphrodite_config,
device=self.device,
runner=self,
)
elif self.speculative_config.use_ngram_gpu():
self.drafter = NgramProposerGPU(self.aphrodite_config, self.device, self)
self.num_tokens_no_spec_gpu = torch.zeros(self.max_num_reqs, dtype=torch.int32, device=device)
self.token_ids_gpu_tensor = torch.zeros(
self.max_num_reqs,
self.max_model_len,
dtype=torch.int32,
device=device,
)
self._ngram_pinned_idx_buf = torch.zeros(self.max_num_reqs, dtype=torch.long, pin_memory=True)
self._ngram_pinned_val_buf = torch.zeros(self.max_num_reqs, dtype=torch.int32, pin_memory=True)
elif self.speculative_config.use_gemma4_mtp():
self.drafter = Gemma4Proposer(self.aphrodite_config, self.device, self)
elif self.speculative_config.use_step3p5_mtp():
self.drafter = Step3p5MTPProposer(self.aphrodite_config, self.device, self)
elif self.speculative_config.use_dflash():
self.drafter = DFlashProposer(self.aphrodite_config, self.device, self)
self.use_aux_hidden_state_outputs = True
elif self.speculative_config.method == "suffix":
self.drafter = SuffixDecodingProposer(self.aphrodite_config)
elif self.speculative_config.use_eagle():
self.drafter = EagleProposer(self.aphrodite_config, self.device, self)
if self.speculative_config.method == "eagle3":
self.use_aux_hidden_state_outputs = self.drafter.eagle3_use_aux_hidden_state
elif self.speculative_config.method == "medusa":
self.drafter = MedusaProposer(aphrodite_config=self.aphrodite_config, device=self.device)
elif self.speculative_config.method == "extract_hidden_states":
self.drafter = ExtractHiddenStatesProposer(aphrodite_config=self.aphrodite_config, device=self.device)
self.use_aux_hidden_state_outputs = True
else:
raise ValueError(f"Unknown speculative decoding method: {self.speculative_config.method}")
self.rejection_sampler = RejectionSampler(self.sampler, self.speculative_config, self.device)
self.num_spec_tokens = 0
self.prev_num_spec_tokens = 0
self.valid_sampled_token_count_gpu: torch.Tensor | None = None
if self.speculative_config:
self.num_spec_tokens = self.speculative_config.num_speculative_tokens
self.prev_num_spec_tokens = self.num_spec_tokens
draft_config = self.speculative_config.draft_model_config
if draft_config is not None and draft_config.max_model_len is not None:
self.effective_drafter_max_model_len = draft_config.max_model_len
else:
self.effective_drafter_max_model_len = self.max_model_len
self.use_async_spec_decode = self.use_async_scheduling and self.num_spec_tokens > 0
# Request states.
self.requests: dict[str, CachedRequestState] = {}
# NOTE(rob): num_prompt_logprobs only includes reqs
# that are currently in the prefill phase.
self.num_prompt_logprobs: dict[str, int] = {}
# Input Batch
# NOTE(Chen): Ideally, we should initialize the input batch inside
# `initialize_kv_cache` based on the kv cache config. However, as in
# https://github.com/vllm-project/vllm/pull/18298, due to some unknown
# reasons, we have to initialize the input batch before `load_model`,
# quantization + weight offloading will fail otherwise. As a temporary
# solution, we initialize the input batch here, and re-initialize it
# in `initialize_kv_cache` if the block_sizes here is different from
# the block_sizes in the kv cache config.
logits_processors = model_config.logits_processors
custom_logitsprocs: Sequence[str | type[LogitsProcessor]] = (
tuple(logits_processors) if logits_processors is not None else ()
)
placeholder_block_size = self.cache_config.block_size or CacheConfig.DEFAULT_BLOCK_SIZE
self._init_block_sizes = [placeholder_block_size]
self._init_kernel_block_sizes = [placeholder_block_size]
self.input_batch = InputBatch(
max_num_reqs=self.max_num_reqs,
# We need to use the encoder length for encoder-decoder
# because of KV cache for cross-attention.
max_model_len=max(self.max_model_len, self.max_encoder_len),
max_num_batched_tokens=self.max_num_tokens,
device=self.device,
vocab_size=self.model_config.get_vocab_size(),
block_sizes=[placeholder_block_size],
kernel_block_sizes=[placeholder_block_size],
num_spec_tokens=self.num_spec_tokens,
logitsprocs=build_logitsprocs(
self.aphrodite_config,
self.device,
PIN_MEMORY,
self.is_pooling_model,
custom_logitsprocs,
),
# We currently don't know whether a particular custom logits processor
# uses output token ids so we set this conservatively. Thinking-budget
# tracking is requested dynamically when a budgeted request is in the batch.
logitsprocs_need_output_token_ids=bool(custom_logitsprocs),
is_pooling_model=self.is_pooling_model,
cp_kv_cache_interleave_size=self.parallel_config.cp_kv_cache_interleave_size,
reasoning_config=self.aphrodite_config.reasoning_config,
)
# Separate cuda stream for overlapping transfer of sampled token ids from
# GPU to CPU when async scheduling is enabled.
self.async_output_copy_stream: torch.cuda.Stream | None = None
# cuda event to synchronize use of reused CPU tensors between steps
# when async scheduling is enabled.
self.prepare_inputs_event: torch.Event | None = None
if self.use_async_scheduling:
self.async_output_copy_stream = torch.cuda.Stream()
# Blocking (sleep) event to avoid busy-polling the CUDA driver lock;
# under TP contention that spin can balloon and make the rank a straggler.
self.prepare_inputs_event = torch.cuda.Event(blocking=True)
# self.cudagraph_batch_sizes sorts in ascending order.
if (
self.compilation_config.cudagraph_capture_sizes
and self.compilation_config.cudagraph_mode != CUDAGraphMode.NONE
):
self.cudagraph_batch_sizes = sorted(self.compilation_config.cudagraph_capture_sizes)
else:
self.cudagraph_batch_sizes = []
# Cache the device properties.
self._init_device_properties()
# Encoder timing registry for observability
self.encoder_timing_registry: dict[str, EncoderTimingStats] = {}
self._encoder_timing_lock = threading.Lock()
# Persistent buffers for CUDA graphs.
self.input_ids = self._make_buffer(self.max_num_tokens, dtype=torch.int32)
self.positions = torch.zeros(self.max_num_tokens, dtype=torch.int64, device=self.device)
self.query_start_loc = self._make_buffer(self.max_num_reqs + 1, dtype=torch.int32)
self.seq_lens = torch.zeros(self.max_num_reqs, dtype=torch.int32, device=self.device)
self.optimistic_seq_lens_cpu = torch.zeros(self.max_num_reqs, dtype=torch.int32, pin_memory=PIN_MEMORY)
self.num_computed_tokens = torch.zeros(self.max_num_reqs, dtype=torch.int32, device=self.device)
self.prev_num_draft_tokens = self._make_buffer(self.max_num_reqs, dtype=torch.int32)
self.req_indices = self._make_buffer(self.max_num_tokens, dtype=torch.int64)
# Maps current batch position -> previous batch position (-1 for new reqs)
self.prev_positions = self._make_buffer(self.max_num_reqs, dtype=torch.int64)
self.num_scheduled_tokens = self._make_buffer(self.max_num_reqs, dtype=torch.int32)
self.encoder_seq_lens = self._make_buffer(self.max_num_reqs, dtype=torch.int32)
if self.dcp_world_size > 1:
self.dcp_local_seq_lens = self._make_buffer(self.max_num_reqs, dtype=torch.int32)
# Because inputs_embeds may be bfloat16 and we don't need a numpy
# version of this tensor, avoid a RuntimeError by not creating a
# numpy buffer.
self.inputs_embeds = self._make_buffer(
self.max_num_tokens, self.inputs_embeds_size, dtype=self.dtype, numpy=False
)
self.is_token_ids = self._make_buffer(self.max_num_tokens, dtype=torch.bool)
self.discard_request_mask = self._make_buffer(self.max_num_reqs, dtype=torch.bool)
self.num_decode_draft_tokens = self._make_buffer(self.max_num_reqs, dtype=torch.int32)
self.num_accepted_tokens = self._make_buffer(self.max_num_reqs, dtype=torch.int32)
# Only relevant for models using M-RoPE (e.g, Qwen2-VL)
if self.uses_mrope:
# NOTE: `mrope_positions` is implemented with one additional dummy
# position on purpose to make it non-contiguous so that it can work
# with torch compile.
# See detailed explanation in https://github.com/vllm-project/vllm/pull/12128#discussion_r1926431923
# NOTE: When M-RoPE is enabled, position ids are 3D regardless of
# the modality of inputs. For text-only inputs, each dimension has
# identical position IDs, making M-RoPE functionally equivalent to
# 1D-RoPE.
# See page 5 of https://arxiv.org/abs/2409.12191
self.mrope_positions = self._make_buffer((3, self.max_num_tokens + 1), dtype=torch.int64)
# Only relevant for models using XD-RoPE (e.g, HunYuan-VL)
if self.uses_xdrope_dim > 0:
# Similar to mrope but use assigned dimension number for RoPE, 4 as default.
self.xdrope_positions = self._make_buffer(
(self.uses_xdrope_dim, self.max_num_tokens + 1), dtype=torch.int64
)
# None in the first PP rank. The rest are set after load_model.
self.intermediate_tensors: IntermediateTensors | None = None
# OPTIMIZATION: Cache the arange tensors rather than creating them
# every step. Keep in int64 to avoid overflow with long context.
# - arange_np: immutable [0, 1, 2, ...] used as source for batched computation
# - query_pos: CpuGpuBuffer for the computed batched arange result
arange_size = max(self.max_num_reqs + 1, self.max_num_tokens)
self.arange_np = np.arange(arange_size, dtype=np.int64)
self.query_pos = self._make_buffer(arange_size, dtype=torch.int64)
self._arange_scratch = np.empty(arange_size, dtype=np.int64)
# Layer pairings for cross-layer KV sharing.
# If an Attention layer `layer_name` is in the keys of this dict, it
# means this layer will perform attention using the keys and values
# from the KV cache of `shared_kv_cache_layers[layer_name]`.
self.shared_kv_cache_layers: dict[str, str] = {}
self.kv_sharing_fast_prefill_eligible_layers: set[str] = set()
self.kv_sharing_fast_prefill_logits_indices = None
if self.cache_config.kv_sharing_fast_prefill:
self.kv_sharing_fast_prefill_logits_indices = torch.zeros(
self.max_num_tokens, dtype=torch.int32, device=self.device
)
self.uniform_decode_query_len = 1 + self.num_spec_tokens
# Cudagraph dispatcher for runtime cudagraph dispatching.
self.cudagraph_dispatcher = CudagraphDispatcher(self.aphrodite_config)
self.mm_budget = MultiModalBudget(self.aphrodite_config, self.mm_registry) if self.supports_mm_inputs else None
self.reorder_batch_threshold: int | None = None
# Attention layers that are only in the KVCacheConfig of the runner
# (e.g., KV sharing, encoder-only attention), but not in the
# KVCacheConfig of the scheduler.
self.runner_only_attn_layers: set[str] = set()
# Cached outputs.
self._draft_token_ids: list[list[int]] | torch.Tensor | None = None
self._draft_probs: torch.Tensor | None = None
self._draft_prob_req_ids: list[str] | None = None
# N-gram GPU path: async D2H buffer/event for per-request valid draft counts.
self._num_valid_draft_tokens: torch.Tensor | None = None
self._num_valid_draft_tokens_cpu: torch.Tensor | None = None
self._num_valid_draft_tokens_event: torch.Event | None = None
self._num_valid_draft_tokens_copy_stream: torch.cuda.Stream | None = None
if self.speculative_config is not None and self.speculative_config.use_ngram_gpu():
self._num_valid_draft_tokens_cpu = torch.empty(self.max_num_reqs, dtype=torch.int32, pin_memory=PIN_MEMORY)
self._num_valid_draft_tokens_event = torch.Event()
self._num_valid_draft_tokens_copy_stream = torch.cuda.Stream()
self._draft_token_req_ids: list[str] | None = None
self.transfer_event = torch.Event()
self.sampled_token_ids_pinned_cpu = torch.empty(
(self.max_num_reqs, 1),
dtype=torch.int64,
device="cpu",
pin_memory=PIN_MEMORY,
)
# Pre-allocated tensor for copying valid sampled token counts to CPU,
# with dedicated stream for overlapping and event for coordination.
self.valid_sampled_token_count_event: torch.Event | None = None
self.valid_sampled_token_count_copy_stream: torch.cuda.Stream | None = None
# We also copy the drafted tokens to the CPU asynchronously,
# in case we need them for structured outputs.
self.draft_token_ids_event: torch.Event | None = None
self.draft_token_ids_copy_stream: torch.cuda.Stream | None = None
self.valid_sampled_token_count_cpu: torch.Tensor | None = None
self.draft_token_ids_cpu: torch.Tensor | None = None
self.num_accepted_tokens_event: torch.Event | None = None
if self.num_spec_tokens:
self.draft_token_ids_event = torch.Event()
self.num_accepted_tokens_event = torch.Event()
self.draft_token_ids_copy_stream = torch.cuda.Stream()
self.draft_token_ids_cpu = torch.empty(
(self.max_num_reqs, self.num_spec_tokens),
dtype=torch.int64,
device="cpu",
pin_memory=PIN_MEMORY,
)
if self.use_async_scheduling:
self.valid_sampled_token_count_event = torch.Event()
self.valid_sampled_token_count_copy_stream = torch.cuda.Stream()
self.valid_sampled_token_count_cpu = torch.empty(
self.max_num_reqs,
dtype=torch.int32,
device="cpu",
pin_memory=PIN_MEMORY,
)
# Model weight offloader
# Make sure this is called before any get_offloader call
set_offloader(create_offloader(self.offload_config))
# Ephemeral state transferred between execute_model() and sample_tokens().
self.execute_model_state: ExecuteModelState | None = None
self.kv_connector_output: KVConnectorOutput | None = None
self.mamba_state_idx: dict[str, int] = {}
self._mamba_bufs: mamba_utils.MambaBuffers | None = None
self.mamba_prev_last_scheduled_idx: CpuGpuBuffer | None = None
if self.cache_config.mamba_cache_mode == "all" and self.num_spec_tokens > 0:
self.mamba_prev_last_scheduled_idx = self._make_buffer(self.max_num_reqs, dtype=torch.int32)
self.layerwise_nvtx_hooks_registered = False
def update_max_model_len(self, max_model_len: int) -> None:
self.max_model_len = max_model_len
if self.speculative_config:
draft_config = self.speculative_config.draft_model_config
if draft_config is None or draft_config.max_model_len is None:
self.effective_drafter_max_model_len = self.max_model_len
def reset_mm_cache(self) -> None:
"""
Clear the multi-modal cache that was used during profiling,
but no longer needed during inference.
"""
if self.mm_budget:
self.mm_budget.reset_cache()
self.late_interaction_runner.clear()
def reset_encoder_cache(self) -> None:
"""Clear the GPU-side encoder cache storing vision embeddings.
This should be called when model weights are updated to ensure
stale embeddings computed with old weights are not reused.
"""
self.encoder_cache.clear()
self.late_interaction_runner.clear()
def post_kv_cache_wake_up(self) -> None:
self.init_fp8_kv_scales()
@torch.inference_mode()
def init_fp8_kv_scales(self) -> None:
"""
Re-initialize the KV cache and FP8 scales after waking from sleep.
1. Zero out the KV cache tensors to remove garbage data from re-allocation.
2. Reset Attention layer scaling factors (_k_scale, _v_scale) to 1.0.
If these are left at 0.0 (default after wake_up), all KV cache values
become effectively zero, causing gibberish output.
"""
if not is_quantized_kv_cache(self.cache_config.cache_dtype):
return
kv_caches = getattr(self, "kv_caches", [])
for cache_tensor in kv_caches:
if cache_tensor is not None:
cache_tensor.zero_()
k_attr_names = ("_k_scale", "k_scale")
v_attr_names = ("_v_scale", "v_scale")
attn_layers = self.compilation_config.static_forward_context
for name, module in attn_layers.items():
if isinstance(module, (Attention, MLAAttention)):
# TODO: Generally, scale is 1.0 if user uses on-the-fly fp8
# kvcache quant. However, to get better accuracy, compression
# frameworks like llm-compressors allow users to tune the
# scale. We may need to restore the specific calibrated scales
# here in the future.
k_scale_val, v_scale_val = 1.0, 1.0
# Processing K Scale
for attr in k_attr_names:
if hasattr(module, attr):
param = getattr(module, attr)
if isinstance(param, torch.Tensor):
param.fill_(k_scale_val)
# Processing V Scale
for attr in v_attr_names:
if hasattr(module, attr):
param = getattr(module, attr)
if isinstance(param, torch.Tensor):
param.fill_(v_scale_val)
def _get_positions(self, num_tokens: Any):
if isinstance(num_tokens, int):
if self.uses_mrope:
return self.mrope_positions.gpu[:, :num_tokens]
if self.uses_xdrope_dim > 0:
return self.xdrope_positions.gpu[:, :num_tokens]
return self.positions[:num_tokens]
else:
if self.uses_mrope:
return self.mrope_positions.gpu[:, num_tokens]
if self.uses_xdrope_dim > 0:
return self.xdrope_positions.gpu[:, num_tokens]
return self.positions[num_tokens]
def _make_buffer(self, *size: int | torch.SymInt, dtype: torch.dtype, numpy: bool = True) -> CpuGpuBuffer:
return CpuGpuBuffer(
*size,
dtype=dtype,
device=self.device,
with_numpy=numpy,
)
def _get_mamba_bufs(self) -> mamba_utils.MambaBuffers:
# Only reachable on the ``mamba_cache_mode == "align"`` path.
# The postprocess sub-object is additionally gated on spec
# decode + hybrid model.
assert self.cache_config.mamba_cache_mode == "align"
if self._mamba_bufs is None:
self._mamba_bufs = mamba_utils.MambaBuffers.create(
max_num_reqs=self.max_num_reqs,
kv_cache_config=self.kv_cache_config,
copy_funcs=self.model.get_mamba_state_copy_func(),
make_buffer=self._make_buffer,
device=self.device,
with_postprocess_align=(self.speculative_config is not None and self.model_config.is_hybrid),
)
return self._mamba_bufs
def _init_model_kwargs(self):
model_kwargs = dict[str, Any]()
if not self.is_pooling_model:
return model_kwargs
num_reqs = self.input_batch.num_reqs
pooling_params = self.input_batch.get_pooling_params()
token_type_id_requests = dict[int, Any]()
for i, param in enumerate(pooling_params):
if (
param.extra_kwargs is not None
and (token_types := param.extra_kwargs.get("compressed_token_type_ids")) is not None
):
token_type_id_requests[i] = token_types
if len(token_type_id_requests) == 0:
return model_kwargs
# Build ids on CPU using the CPU-resident upper bound for seq_lens;
# `torch.arange(seq_lens[i])` with a GPU scalar would force a sync.
seq_lens_cpu = self.optimistic_seq_lens_cpu[:num_reqs].tolist()
token_type_ids = []
for i in range(num_reqs):
seq_len_i = seq_lens_cpu[i]
pos = token_type_id_requests.get(i, seq_len_i)
ids = (torch.arange(seq_len_i) >= pos).int()
token_type_ids.append(ids)
token_type_ids_cpu = torch.empty(sum(seq_lens_cpu), dtype=torch.int32, pin_memory=PIN_MEMORY)
torch.cat(token_type_ids, out=token_type_ids_cpu)
model_kwargs["token_type_ids"] = token_type_ids_cpu.to(device=self.device, non_blocking=True)
return model_kwargs
def _may_reorder_batch(self, scheduler_output: "SchedulerOutput") -> None:
"""
Update the order of requests in the batch based on the attention
backend's needs. For example, some attention backends (namely MLA) may