Signed-off-by: MrlixiangWE <mrdanaer@gmail.com> Co-authored-by: zijianc2 <157244773+zijianc2@users.noreply.github.com> Co-authored-by: Yifan Tian <221685243+Sendoh-code@users.noreply.github.com> Co-authored-by: amy-why-3459 <wuhaiyan17@huawei.com>
This commit is contained in:
8 files changed
+968
-55
No files matched your search
@@ -26,6 +26,7 @@ from vllm.model_executor.models.interfaces import (
|
||||
SupportsPP,
|
||||
SupportsQuant,
|
||||
SupportsRealtime,
|
||||
supports_encoder_cudagraph,
|
||||
)
|
||||
from vllm.model_executor.models.qwen3_asr_realtime import Qwen3ASRRealtimeBuffer
|
||||
from vllm.model_executor.models.qwen3_omni_moe_thinker import (
|
||||
@@ -90,6 +91,24 @@ TALKER_CODEC_THINK_EOS_ID = 4205 # Think mode end
|
||||
|
||||
logger = init_logger(__name__)
|
||||
|
||||
# The encoder CUDA graph protocol, forwarded from the thinker to the stage
|
||||
# wrapper that the runner inspects.
|
||||
_THINKER_ENCODER_CUDAGRAPH_MEMBERS = (
|
||||
"supports_encoder_cudagraph",
|
||||
"encoder_cudagraph_single_replay",
|
||||
"get_encoder_cudagraph_config",
|
||||
"get_input_modality",
|
||||
"get_max_frames_per_video",
|
||||
"get_encoder_cudagraph_budget_range",
|
||||
"get_encoder_cudagraph_item_specs",
|
||||
"select_encoder_cudagraph_items",
|
||||
"prepare_encoder_cudagraph_capture_inputs",
|
||||
"prepare_encoder_cudagraph_replay_buffers",
|
||||
"encoder_cudagraph_forward",
|
||||
"encoder_eager_forward",
|
||||
"postprocess_encoder_output",
|
||||
)
|
||||
|
||||
|
||||
def _cpu_long(values: Any) -> torch.Tensor:
|
||||
"""Token ids as a host tensor (payload ids arrive as lists or CPU tensors)."""
|
||||
@@ -221,6 +240,12 @@ class Qwen3OmniMoeForConditionalGeneration(
|
||||
self.model = self.thinker
|
||||
self.talker = None
|
||||
self.code2wav = None
|
||||
if supports_encoder_cudagraph(self.thinker):
|
||||
# Expose the protocol on this stage instance only. Defining
|
||||
# these methods on the shared class also advertises an encoder
|
||||
# for talker/code2wav under runtime structural protocol checks.
|
||||
for name in _THINKER_ENCODER_CUDAGRAPH_MEMBERS:
|
||||
setattr(self, name, getattr(self.thinker, name))
|
||||
self.tts_tokens = torch.tensor(
|
||||
[[self.config.tts_bos_token_id, self.config.tts_eos_token_id, self.config.tts_pad_token_id]],
|
||||
device=self._module_device(self.thinker),
|
||||
|
||||
@@ -117,6 +117,7 @@ from vllm.multimodal.processing.processor import (
|
||||
from vllm.multimodal.utils import set_mm_embedding_modality
|
||||
from vllm.sequence import IntermediateTensors
|
||||
from vllm.transformers_utils.processor import cached_processor_from_config
|
||||
from vllm.utils.torch_utils import async_tensor_h2d
|
||||
|
||||
from vllm_omni.data_entry_keys import OmniPayload
|
||||
from vllm_omni.model_executor.models.qwen2_5_omni.qwen2_5_omni_thinker import (
|
||||
@@ -129,6 +130,7 @@ from vllm_omni.model_executor.models.qwen2_5_omni.qwen2_5_omni_thinker import (
|
||||
from vllm_omni.model_executor.models.qwen3_omni.quantization import (
|
||||
Qwen3OmniNestedSupportsQuant,
|
||||
)
|
||||
from vllm_omni.model_executor.models.qwen3_omni.vision_encoder_cudagraph import Qwen3OmniVisionEncoderCudaGraphMixin
|
||||
from vllm_omni.quantization.component_config import (
|
||||
PRE_QUANTIZED_METHODS,
|
||||
ComponentQuantizationConfig,
|
||||
@@ -167,79 +169,74 @@ class Qwen3Omni_VisionTransformer(_Qwen3Omni_VisionTransformer):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.tp_size = get_tensor_model_parallel_world_size()
|
||||
|
||||
def forward(
|
||||
self,
|
||||
x: torch.Tensor,
|
||||
grid_thw,
|
||||
) -> torch.Tensor:
|
||||
hidden_states = x.to(device=self.device, dtype=self.dtype)
|
||||
hidden_states = self.patch_embed(hidden_states)
|
||||
|
||||
def prepare_encoder_metadata(self, grid_thw) -> dict[str, torch.Tensor]:
|
||||
"""Prepare positions and attention metadata outside CUDA graph capture."""
|
||||
if not isinstance(grid_thw, torch.Tensor):
|
||||
grid_thw = torch.as_tensor(grid_thw, dtype=torch.int32)
|
||||
metadata = {}
|
||||
if self.apply_vit_abs_pos_embed:
|
||||
pos_embeds = self.fast_pos_embed_interpolate(grid_thw)
|
||||
hidden_states = hidden_states + pos_embeds
|
||||
metadata["pos_embeds"] = self.fast_pos_embed_interpolate(grid_thw)
|
||||
rotary_pos_emb_cos, rotary_pos_emb_sin = self.rot_pos_emb(grid_thw)
|
||||
|
||||
if isinstance(grid_thw, torch.Tensor):
|
||||
grid_thw_tensor = grid_thw.to(self.device)
|
||||
else:
|
||||
grid_thw_tensor = torch.as_tensor(grid_thw, dtype=torch.int32, device=self.device)
|
||||
rotary_pos_emb_cos = rotary_pos_emb_cos.to(self.device)
|
||||
rotary_pos_emb_sin = rotary_pos_emb_sin.to(self.device)
|
||||
|
||||
try:
|
||||
cu_seqlens = torch.repeat_interleave(
|
||||
grid_thw_tensor[:, 1] * grid_thw_tensor[:, 2],
|
||||
grid_thw_tensor[:, 0],
|
||||
).cumsum(
|
||||
dim=0,
|
||||
dtype=grid_thw_tensor.dtype if torch.jit.is_tracing() else torch.int32,
|
||||
)
|
||||
cu_seqlens = F.pad(cu_seqlens, (1, 0), value=0)
|
||||
except RuntimeError:
|
||||
logger.warning(
|
||||
"torch.repeat_interleave not executable, switching to vectorized searchsorted implementation."
|
||||
)
|
||||
repeat_counts = grid_thw_tensor[:, 0]
|
||||
values = grid_thw_tensor[:, 1] * grid_thw_tensor[:, 2]
|
||||
repeat_cumsum = repeat_counts.cumsum(0)
|
||||
total_items = repeat_cumsum[-1].item()
|
||||
indices = torch.searchsorted(
|
||||
repeat_cumsum,
|
||||
torch.arange(total_items, device=grid_thw_tensor.device),
|
||||
right=True,
|
||||
)
|
||||
cu_seqlens = values[indices].cumsum(
|
||||
dim=0,
|
||||
dtype=grid_thw_tensor.dtype if torch.jit.is_tracing() else torch.int32,
|
||||
)
|
||||
cu_seqlens = F.pad(cu_seqlens, (1, 0), value=0)
|
||||
|
||||
hidden_states = hidden_states.unsqueeze(1)
|
||||
rotary_pos_emb_cos = rotary_pos_emb_cos.to(hidden_states.device)
|
||||
rotary_pos_emb_sin = rotary_pos_emb_sin.to(hidden_states.device)
|
||||
max_seqlen = self.compute_attn_mask_seqlen(cu_seqlens)
|
||||
|
||||
grid_thw_np = grid_thw_tensor.cpu().numpy().astype(np.int32)
|
||||
# Sequence boundaries come from the host grid, so neither the eager
|
||||
# path nor a replay reads a device value back.
|
||||
grid_thw_np = grid_thw.cpu().numpy().astype(np.int32)
|
||||
cu_seqlens_np = np.repeat(grid_thw_np[:, 1] * grid_thw_np[:, 2], grid_thw_np[:, 0]).cumsum(
|
||||
axis=0, dtype=np.int32
|
||||
)
|
||||
cu_seqlens_np = np.concatenate([np.zeros(1, dtype=np.int32), cu_seqlens_np])
|
||||
cu_seqlens_host = torch.from_numpy(cu_seqlens_np)
|
||||
if self.device.type == "cpu":
|
||||
cu_seqlens = cu_seqlens_host
|
||||
else:
|
||||
cu_seqlens = async_tensor_h2d(cu_seqlens_host, device=self.device)
|
||||
# Attention reads this scalar on the host. Replay keeps the capture
|
||||
# budget's bound instead.
|
||||
max_seqlen = self.compute_attn_mask_seqlen(cu_seqlens_host)
|
||||
sequence_lengths = MMEncoderAttention.maybe_compute_seq_lens(
|
||||
self.attn_backend,
|
||||
cu_seqlens_np,
|
||||
self.device,
|
||||
)
|
||||
|
||||
metadata.update(
|
||||
rotary_pos_emb_cos=rotary_pos_emb_cos,
|
||||
rotary_pos_emb_sin=rotary_pos_emb_sin,
|
||||
cu_seqlens=cu_seqlens,
|
||||
max_seqlen=max_seqlen,
|
||||
)
|
||||
if sequence_lengths is not None:
|
||||
metadata["sequence_lengths"] = sequence_lengths
|
||||
return metadata
|
||||
|
||||
def forward(
|
||||
self, x: torch.Tensor, grid_thw, *, encoder_metadata: dict[str, torch.Tensor] | None = None
|
||||
) -> torch.Tensor:
|
||||
if encoder_metadata is None:
|
||||
encoder_metadata = self.prepare_encoder_metadata(grid_thw)
|
||||
return self.forward_with_encoder_metadata(x, encoder_metadata)
|
||||
|
||||
def forward_with_encoder_metadata(self, x: torch.Tensor, metadata: dict[str, torch.Tensor]) -> torch.Tensor:
|
||||
"""Run patch embedding, vision blocks and all DeepStack mergers."""
|
||||
hidden_states = self.patch_embed(x.to(device=self.device, dtype=self.dtype))
|
||||
if self.apply_vit_abs_pos_embed:
|
||||
hidden_states = hidden_states + metadata["pos_embeds"]
|
||||
hidden_states = hidden_states.unsqueeze(1)
|
||||
|
||||
hidden_states_list = []
|
||||
deepstack_visual_indexes = self.deepstack_visual_indexes
|
||||
|
||||
for layer_num, blk in enumerate(self.blocks):
|
||||
hidden_states = blk(
|
||||
hidden_states,
|
||||
cu_seqlens=cu_seqlens,
|
||||
rotary_pos_emb_cos=rotary_pos_emb_cos,
|
||||
rotary_pos_emb_sin=rotary_pos_emb_sin,
|
||||
max_seqlen=max_seqlen,
|
||||
sequence_lengths=sequence_lengths,
|
||||
cu_seqlens=metadata["cu_seqlens"],
|
||||
rotary_pos_emb_cos=metadata["rotary_pos_emb_cos"],
|
||||
rotary_pos_emb_sin=metadata["rotary_pos_emb_sin"],
|
||||
max_seqlen=metadata["max_seqlen"],
|
||||
sequence_lengths=metadata.get("sequence_lengths"),
|
||||
)
|
||||
|
||||
if deepstack_visual_indexes is not None and layer_num in deepstack_visual_indexes:
|
||||
@@ -440,8 +437,6 @@ class Qwen3OmniMoeAudioEncoder(_Qwen3OmniMoeAudioEncoder):
|
||||
feature_lens: torch.Tensor,
|
||||
aftercnn_lens: torch.Tensor,
|
||||
):
|
||||
import torch.nn.functional as F
|
||||
|
||||
chunk_num = torch.ceil(feature_lens / (self.n_window * 2)).long()
|
||||
|
||||
chunk_lengths = torch.tensor(
|
||||
@@ -1024,6 +1019,7 @@ class Qwen3OmniMoeConditionalGenerationMixin(Qwen2_5OmniConditionalGenerationMix
|
||||
)
|
||||
class Qwen3OmniMoeThinkerForConditionalGeneration(
|
||||
nn.Module,
|
||||
Qwen3OmniVisionEncoderCudaGraphMixin,
|
||||
SupportsMultiModal,
|
||||
SupportsPP,
|
||||
SupportsLoRA,
|
||||
@@ -1159,6 +1155,8 @@ class Qwen3OmniMoeThinkerForConditionalGeneration(
|
||||
]
|
||||
self.deepstack_input_embeds_num_tokens = 0
|
||||
|
||||
self._enable_image_encoder_cudagraph()
|
||||
|
||||
with self._mark_language_model(vllm_config):
|
||||
lm_vllm_config = vllm_config.with_hf_config(
|
||||
thinker_config.text_config,
|
||||
|
||||
@@ -0,0 +1,182 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-FileCopyrightText: Copyright contributors to the vLLM-Omni project
|
||||
"""Qwen3-Omni image encoder contract for the upstream CUDA graph manager."""
|
||||
|
||||
from collections.abc import Hashable
|
||||
from typing import Any
|
||||
|
||||
import torch
|
||||
from vllm.distributed import get_pp_group
|
||||
from vllm.model_executor.models.utils import scatter_output_slices
|
||||
from vllm.v1.attention.backends.registry import AttentionBackendEnum
|
||||
from vllm.v1.worker.encoder_cudagraph_defs import (
|
||||
EncoderCudaGraphCaptureInputs,
|
||||
EncoderCudaGraphConfig,
|
||||
EncoderCudaGraphReplayBuffers,
|
||||
EncoderItemSpec,
|
||||
)
|
||||
|
||||
|
||||
def _pad_cu_seqlens(dst: torch.Tensor, src: torch.Tensor) -> None:
|
||||
# Unused attention sequences must be empty, not run backwards to zero.
|
||||
dst.copy_(src[-1].expand_as(dst))
|
||||
dst[: src.shape[0]].copy_(src)
|
||||
|
||||
|
||||
class Qwen3OmniVisionEncoderCudaGraphMixin:
|
||||
"""Image-only protocol methods; video and audio retain embed_multimodal.
|
||||
|
||||
The capability attribute is set on eligible model instances after tower
|
||||
construction. It is deliberately absent from this mixin's class: the
|
||||
runtime protocol checks attribute presence, not the value of a false flag.
|
||||
"""
|
||||
|
||||
# The eager tower is launch-bound, so a group that needs several replays
|
||||
# or per-image eager calls runs as one eager call instead; see
|
||||
# SingleReplayEncoderCudaGraphManager.
|
||||
encoder_cudagraph_single_replay = True
|
||||
|
||||
def _enable_image_encoder_cudagraph(self) -> None:
|
||||
# Only the first pipeline rank runs the encoder. Dynamic-scale FP8 ViT
|
||||
# attention advances a host-side amax slot per call, which a graph
|
||||
# would freeze. The pipeline-group query comes last so a thinker built
|
||||
# without a vision tower or distributed state never reaches it.
|
||||
if (
|
||||
not self.multimodal_config.enable_mm_embeds
|
||||
and self.multimodal_config.mm_encoder_attn_dtype is None
|
||||
and self.multimodal_config.get_limit_per_prompt("image") > 0
|
||||
and getattr(self.visual, "attn_backend", None)
|
||||
in {
|
||||
AttentionBackendEnum.FLASH_ATTN,
|
||||
AttentionBackendEnum.ROCM_AITER_FA,
|
||||
AttentionBackendEnum.TRITON_ATTN,
|
||||
}
|
||||
and get_pp_group().is_first_rank
|
||||
):
|
||||
self.supports_encoder_cudagraph = True
|
||||
|
||||
def get_encoder_cudagraph_config(self) -> EncoderCudaGraphConfig:
|
||||
keys = ["pixel_values", "rotary_pos_emb_cos", "rotary_pos_emb_sin", "cu_seqlens", "max_seqlen"]
|
||||
if self.visual.apply_vit_abs_pos_embed:
|
||||
keys.append("pos_embeds")
|
||||
return EncoderCudaGraphConfig(
|
||||
modalities=["image"],
|
||||
buffer_keys=keys,
|
||||
out_hidden_size=self.visual_dim + self.multiscale_dim,
|
||||
padding_logics={"cu_seqlens": _pad_cu_seqlens},
|
||||
)
|
||||
|
||||
def get_input_modality(self, mm_kwargs: dict[str, Any]) -> str:
|
||||
return "image"
|
||||
|
||||
def get_max_frames_per_video(self) -> int:
|
||||
return 1
|
||||
|
||||
def get_encoder_cudagraph_budget_range(self, vllm_config) -> tuple[int, int]:
|
||||
# Replay pays off while the eager tower is launch-bound: on an A800 a
|
||||
# 196-token image replays 32% faster, while 280-400 tokens replay
|
||||
# 4-10% slower and 560-784 tokens padded to the 1024 budget 12-51%
|
||||
# slower. Every budget also adds captured graph memory. Auto-inferred
|
||||
# budgets therefore stop at 256 tokens; larger images run eagerly
|
||||
# unless encoder_cudagraph_token_budgets asks for more.
|
||||
maximum = min(vllm_config.scheduler_config.max_num_batched_tokens, vllm_config.model_config.max_model_len, 256)
|
||||
return min(64, maximum), maximum
|
||||
|
||||
def _image_grid(self, mm_kwargs: dict[str, Any]) -> list[list[int]]:
|
||||
grid = mm_kwargs["image_grid_thw"]
|
||||
return grid if isinstance(grid, list) else grid.tolist()
|
||||
|
||||
def get_encoder_cudagraph_item_specs(self, mm_kwargs: dict[str, Any]) -> list[EncoderItemSpec]:
|
||||
merge = self.visual.spatial_merge_size
|
||||
result = []
|
||||
for t, h, w in self._image_grid(mm_kwargs):
|
||||
if t != 1 or h <= 0 or w <= 0 or h % merge or w % merge:
|
||||
raise ValueError(f"Invalid Qwen3-Omni image grid: {(t, h, w)}")
|
||||
result.append(EncoderItemSpec(input_size=h * w, output_tokens=(h // merge) * (w // merge)))
|
||||
return result
|
||||
|
||||
def select_encoder_cudagraph_items(self, mm_kwargs: dict[str, Any], indices: list[int]) -> dict[str, Any]:
|
||||
grid = self._image_grid(mm_kwargs)
|
||||
pixels = mm_kwargs["pixel_values"]
|
||||
offsets = [0]
|
||||
for spec in self.get_encoder_cudagraph_item_specs(mm_kwargs):
|
||||
offsets.append(offsets[-1] + spec.input_size)
|
||||
return {
|
||||
"pixel_values": torch.cat([pixels[offsets[i] : offsets[i + 1]] for i in indices])
|
||||
if indices
|
||||
else pixels[:0],
|
||||
"image_grid_thw": [grid[i] for i in indices],
|
||||
}
|
||||
|
||||
def prepare_encoder_cudagraph_capture_inputs(
|
||||
self,
|
||||
token_budget: int,
|
||||
max_batch_size: int,
|
||||
max_frames_per_batch: int,
|
||||
device: torch.device,
|
||||
dtype: torch.dtype,
|
||||
path: str = "default",
|
||||
axis_keys: tuple[Hashable, ...] | None = None,
|
||||
) -> EncoderCudaGraphCaptureInputs:
|
||||
if path != "default" or axis_keys:
|
||||
raise ValueError("Qwen3-Omni images use one encoder path without capture axes")
|
||||
visual = self.visual
|
||||
merge = visual.spatial_merge_size
|
||||
patches = token_budget * merge**2
|
||||
# Infer the RoPE layout from one real minimal grid. No large dummy
|
||||
# aspect ratio is needed, and no dummy grid indexes past the RoPE cache.
|
||||
small = visual.prepare_encoder_metadata([[1, merge, merge]])
|
||||
values = {
|
||||
"rotary_pos_emb_cos": torch.ones(
|
||||
(patches, *small["rotary_pos_emb_cos"].shape[1:]),
|
||||
device=device,
|
||||
dtype=small["rotary_pos_emb_cos"].dtype,
|
||||
),
|
||||
"rotary_pos_emb_sin": torch.zeros(
|
||||
(patches, *small["rotary_pos_emb_sin"].shape[1:]),
|
||||
device=device,
|
||||
dtype=small["rotary_pos_emb_sin"].dtype,
|
||||
),
|
||||
"cu_seqlens": torch.full((max_batch_size + 1,), patches, device=device, dtype=torch.int32),
|
||||
# This host scalar is baked into the captured attention launch.
|
||||
# Any single image in this graph has at most `patches` patches.
|
||||
"max_seqlen": torch.tensor(patches, dtype=torch.int32),
|
||||
}
|
||||
values["cu_seqlens"][0] = 0
|
||||
patch_embed = visual.patch_embed
|
||||
patch_width = patch_embed.proj.in_channels * patch_embed.temporal_patch_size * patch_embed.patch_size**2
|
||||
values["pixel_values"] = torch.zeros((patches, patch_width), device=device, dtype=dtype)
|
||||
if visual.apply_vit_abs_pos_embed:
|
||||
values["pos_embeds"] = torch.zeros((patches, visual.hidden_size), device=device, dtype=visual.dtype)
|
||||
return EncoderCudaGraphCaptureInputs(values=values)
|
||||
|
||||
def prepare_encoder_cudagraph_replay_buffers(
|
||||
self,
|
||||
mm_kwargs: dict[str, Any],
|
||||
max_batch_size: int,
|
||||
max_frames_per_batch: int,
|
||||
path: str = "default",
|
||||
) -> EncoderCudaGraphReplayBuffers:
|
||||
values = self.visual.prepare_encoder_metadata(self._image_grid(mm_kwargs))
|
||||
# Replaying a smaller request must not lower the scalar bound retained
|
||||
# by the capture. Tensor positions and sequence boundaries do change.
|
||||
values.pop("max_seqlen")
|
||||
values["pixel_values"] = mm_kwargs["pixel_values"]
|
||||
return EncoderCudaGraphReplayBuffers(values=values)
|
||||
|
||||
def encoder_cudagraph_forward(self, inputs: dict[str, torch.Tensor], path: str = "default") -> torch.Tensor:
|
||||
return self.visual(inputs["pixel_values"], None, encoder_metadata=inputs)
|
||||
|
||||
def encoder_eager_forward(self, mm_kwargs: dict[str, Any], path: str = "default") -> torch.Tensor:
|
||||
return self.visual(mm_kwargs["pixel_values"], self._image_grid(mm_kwargs))
|
||||
|
||||
def postprocess_encoder_output(
|
||||
self,
|
||||
outputs: dict[str, torch.Tensor],
|
||||
indices: list[int],
|
||||
per_item_out_tokens: list[int],
|
||||
dest: dict[int, torch.Tensor] | list[torch.Tensor | None],
|
||||
clone: bool = False,
|
||||
batch_mm_kwargs: dict[str, Any] | None = None,
|
||||
) -> None:
|
||||
scatter_output_slices(outputs["default"], indices, per_item_out_tokens, dest, clone)
|
||||
@@ -0,0 +1,32 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-FileCopyrightText: Copyright contributors to the vLLM-Omni project
|
||||
"""Encoder CUDA graph manager that replays a group only when one graph holds it."""
|
||||
|
||||
from typing import Any
|
||||
|
||||
import torch
|
||||
from vllm.v1.worker.encoder_cudagraph import EncoderCudaGraphManager
|
||||
|
||||
|
||||
class SingleReplayEncoderCudaGraphManager(EncoderCudaGraphManager):
|
||||
"""Replay an encoder group only when one captured graph holds all of it.
|
||||
|
||||
The upstream manager splits a group across several replays and gives each
|
||||
item above the largest budget its own eager call. For a launch-bound vision
|
||||
tower that is usually slower than one eager call over the whole group, so a
|
||||
group that does not fit one graph goes back to the runner, which then
|
||||
encodes it with a single ``embed_multimodal`` call. Models opt in by
|
||||
setting ``encoder_cudagraph_single_replay``.
|
||||
"""
|
||||
|
||||
def execute(self, mm_kwargs: dict[str, Any]) -> list[torch.Tensor] | None: # type: ignore[override]
|
||||
if not self.use_dp:
|
||||
specs = self._get_item_specs(mm_kwargs)
|
||||
fits = len(specs) <= self.max_batch_size and all(
|
||||
sum(spec.get_path_output_tokens(path) for spec in specs) <= max(budgets)
|
||||
for path, budgets in self.path_token_budgets.items()
|
||||
)
|
||||
if not fits:
|
||||
self.graph_misses += len(specs)
|
||||
return None
|
||||
return super().execute(mm_kwargs)
|
||||
@@ -283,6 +283,25 @@ class OmniGPUModelRunner(PrefixCacheRunnerMixin, GPUModelRunner):
|
||||
capture_sizes=capture_sizes,
|
||||
)
|
||||
|
||||
@torch.inference_mode()
|
||||
def _create_encoder_cudagraph_manager(self):
|
||||
raw_model = self.get_model()
|
||||
if not getattr(raw_model, "encoder_cudagraph_single_replay", False):
|
||||
return super()._create_encoder_cudagraph_manager()
|
||||
from vllm.model_executor.models.interfaces import supports_encoder_cudagraph
|
||||
|
||||
from vllm_omni.worker.encoder_cudagraph import SingleReplayEncoderCudaGraphManager
|
||||
|
||||
if not (
|
||||
self.compilation_config.cudagraph_mm_encoder
|
||||
and self.supports_mm_inputs
|
||||
and supports_encoder_cudagraph(raw_model)
|
||||
):
|
||||
return None
|
||||
return SingleReplayEncoderCudaGraphManager(
|
||||
vllm_config=self.vllm_config, device=self.device, dtype=self.dtype, model=raw_model
|
||||
)
|
||||
|
||||
def _maybe_attach_attention_metadata_extensions(
|
||||
self,
|
||||
*,
|
||||
|
||||
Reference in new issue
Block a user