[NPU] Upgrade to v0.20.0 & align with GPU model runner (#3325)
Signed-off-by: gcanlin <canlinguosdu@gmail.com>
This commit is contained in:
1 parent
c708aecb18
commit
c007d40b14
7 files changed
+519
-160
No files matched your search
@@ -71,3 +71,9 @@ stages:
|
||||
max_tokens: 65536
|
||||
seed: 42
|
||||
repetition_penalty: 1.0
|
||||
|
||||
platforms:
|
||||
npu:
|
||||
stages:
|
||||
- stage_id: 0
|
||||
enforce_eager: true
|
||||
@@ -12,7 +12,7 @@ Analogous to ``Qwen3TTSTalkerForConditionalGeneration`` in qwen3_tts.
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import dataclasses
|
||||
import copy
|
||||
import math
|
||||
from collections.abc import Iterable
|
||||
from typing import Any
|
||||
@@ -224,10 +224,18 @@ class FishSpeechSlowARForConditionalGeneration(nn.Module):
|
||||
self.text_config.hidden_size,
|
||||
)
|
||||
|
||||
# Fast AR (residual codebook predictor).
|
||||
predictor_compilation = dataclasses.replace(vllm_config.compilation_config)
|
||||
# Fast AR (residual codebook predictor). Use copy.copy rather than
|
||||
# dataclasses.replace: CompilationConfig / VllmConfig are pydantic
|
||||
# dataclasses, so `replace` re-runs __init__→pydantic validators +
|
||||
# __post_init__. If a backend has already rebound
|
||||
# compilation_config.backend to a non-stock value, the piecewise-backend
|
||||
# validator in vllm/config/compilation.py rejects it and the clone
|
||||
# raises. copy.copy goes through __reduce_ex__, skips validation, and
|
||||
# leaves the parent's already-initialized state intact.
|
||||
predictor_compilation = copy.copy(vllm_config.compilation_config)
|
||||
predictor_compilation.static_forward_context = {}
|
||||
self._fast_ar_vllm_config = dataclasses.replace(vllm_config, compilation_config=predictor_compilation)
|
||||
self._fast_ar_vllm_config = copy.copy(vllm_config)
|
||||
self._fast_ar_vllm_config.compilation_config = predictor_compilation
|
||||
from vllm.config.vllm import set_current_vllm_config as _set_cfg
|
||||
|
||||
with _set_cfg(self._fast_ar_vllm_config):
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import base64
|
||||
import dataclasses
|
||||
import copy
|
||||
import io
|
||||
import os
|
||||
from collections.abc import Callable, Iterable, Mapping
|
||||
@@ -384,9 +384,17 @@ class Qwen3TTSTalkerForConditionalGeneration(nn.Module):
|
||||
# Code predictor uses an isolated vLLM config so its KV cache doesn't
|
||||
# pollute the main engine's static_forward_context (shallow-copy shares
|
||||
# the dict by reference — must assign a fresh one).
|
||||
predictor_compilation = dataclasses.replace(vllm_config.compilation_config)
|
||||
# Use copy.copy rather than dataclasses.replace: CompilationConfig /
|
||||
# VllmConfig are pydantic dataclasses, so `replace` re-runs
|
||||
# __init__→pydantic validators + __post_init__. If a backend has
|
||||
# already rebound compilation_config.backend to a non-stock value, the
|
||||
# piecewise-backend validator in vllm/config/compilation.py rejects it
|
||||
# and the clone raises. copy.copy goes through __reduce_ex__, skips
|
||||
# validation, and leaves the parent's already-initialized state intact.
|
||||
predictor_compilation = copy.copy(vllm_config.compilation_config)
|
||||
predictor_compilation.static_forward_context = {}
|
||||
self._code_predictor_vllm_config = dataclasses.replace(vllm_config, compilation_config=predictor_compilation)
|
||||
self._code_predictor_vllm_config = copy.copy(vllm_config)
|
||||
self._code_predictor_vllm_config.compilation_config = predictor_compilation
|
||||
from vllm.config.vllm import set_current_vllm_config as _set_cfg
|
||||
|
||||
with _set_cfg(self._code_predictor_vllm_config):
|
||||
|
||||
@@ -12,7 +12,7 @@ from vllm.distributed.parallel_state import (
|
||||
init_model_parallel_group as vllm_init_model_parallel_group,
|
||||
)
|
||||
from vllm_ascend.ascend_forward_context import MoECommType
|
||||
from vllm_ascend.ops.fused_moe.fused_moe import AscendSharedFusedMoE
|
||||
from vllm_ascend.ops.fused_moe.fused_moe import AscendFusedMoE
|
||||
from vllm_ascend.ops.fused_moe.moe_comm_method import _MoECommMethods
|
||||
from vllm_ascend.utils import AscendDeviceType, get_ascend_device_type
|
||||
|
||||
@@ -103,10 +103,11 @@ def prepare_hunyuan_fused_moe_runtime() -> None:
|
||||
_ensure_forward_context_attr("flash_comm_v1_enabled", bool, False)
|
||||
|
||||
|
||||
# NOTE: As of v0.20,0 vLLM has folded SharedFusedMoE -> FusedMoE and removed the class.
|
||||
# Since AscendSharedFusedMoE is a subclass of SharedFusedMoE, we should be careful
|
||||
# to ensure that this is updated correctly.
|
||||
class AscendHunyuanFusedMoE(AscendSharedFusedMoE):
|
||||
# NOTE: vLLM v0.20.0 folded SharedFusedMoE into FusedMoE, and vllm-ascend in turn
|
||||
# removed AscendSharedFusedMoE — the shared-experts / gate / multistream-overlap
|
||||
# paths now live directly on AscendFusedMoE and are activated by passing
|
||||
# shared_experts= as a kwarg.
|
||||
class AscendHunyuanFusedMoE(AscendFusedMoE):
|
||||
def __init__(self, *, prefix: str = "", **kwargs: Any) -> None:
|
||||
super().__init__(prefix=prefix, **kwargs)
|
||||
self._prefix = prefix
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import time
|
||||
from copy import copy, deepcopy
|
||||
from typing import Any, NamedTuple
|
||||
|
||||
@@ -13,6 +14,7 @@ from vllm.config import CUDAGraphMode
|
||||
from vllm.distributed.ec_transfer import get_ec_transfer, has_ec_transfer
|
||||
from vllm.distributed.kv_transfer import get_kv_transfer_group, has_kv_transfer_group
|
||||
from vllm.distributed.parallel_state import get_pp_group, get_tp_group
|
||||
from vllm.forward_context import BatchDescriptor
|
||||
from vllm.logger import logger
|
||||
from vllm.model_executor.layers.fused_moe.routed_experts_capturer import RoutedExpertsCapturer
|
||||
from vllm.sequence import IntermediateTensors
|
||||
@@ -26,20 +28,24 @@ from vllm.v1.outputs import (
|
||||
from vllm.v1.spec_decode.metadata import SpecDecodeMetadata
|
||||
from vllm.v1.structured_output.utils import apply_grammar_bitmask
|
||||
from vllm.v1.utils import record_function_or_nullcontext
|
||||
from vllm.v1.worker import mamba_utils
|
||||
from vllm.v1.worker.gpu_model_runner import AsyncGPUModelRunnerOutput, PerLayerAttnMetadata
|
||||
from vllm.v1.worker.mamba_utils import preprocess_mamba
|
||||
from vllm.v1.worker.ubatch_utils import maybe_create_ubatch_slices
|
||||
from vllm_ascend.ascend_forward_context import set_ascend_forward_context
|
||||
from vllm_ascend.attention.utils import AscendCommonAttentionMetadata
|
||||
from vllm_ascend.compilation.acl_graph import ACLGraphWrapper
|
||||
|
||||
# yapf conflicts with isort for this block
|
||||
# yapf: disable
|
||||
from vllm_ascend.ops.rotary_embedding import update_cos_sin
|
||||
from vllm_ascend.utils import enable_sp, global_stream
|
||||
from vllm_ascend.worker.model_runner_v1 import graph_capture
|
||||
|
||||
from vllm_omni.data_entry_keys import flatten_payload
|
||||
from vllm_omni.distributed.omni_connectors.kv_transfer_manager import OmniKVTransferManager
|
||||
from vllm_omni.outputs import OmniModelRunnerOutput
|
||||
from vllm_omni.platforms.npu.worker.npu_model_runner import OmniNPUModelRunner
|
||||
from vllm_omni.utils.mm_outputs import build_mm_cpu, to_payload_element
|
||||
|
||||
|
||||
class ExecuteModelState(NamedTuple):
|
||||
@@ -57,6 +63,7 @@ class ExecuteModelState(NamedTuple):
|
||||
positions: torch.Tensor
|
||||
ec_connector_output: ECConnectorOutput | None
|
||||
cudagraph_stats: CUDAGraphStat | None
|
||||
batch_desc: BatchDescriptor
|
||||
multimodal_outputs: Any # Omni-Specific
|
||||
|
||||
class NPUARModelRunner(OmniNPUModelRunner):
|
||||
@@ -70,6 +77,7 @@ class NPUARModelRunner(OmniNPUModelRunner):
|
||||
self.inputs_embeds = self._make_buffer(self.max_num_tokens, self.hidden_size, dtype=self.dtype, numpy=False)
|
||||
# Initialize KV cache manager (preserve vllm_config fallback behavior)
|
||||
self.kv_transfer_manager = OmniKVTransferManager.from_vllm_config(self.vllm_config, self.model_config)
|
||||
self._downstream_payload_cache: dict[str, bool] = {}
|
||||
|
||||
def _make_buffer(self, *size, dtype, numpy=True):
|
||||
# Prevent ray from pinning the buffer due to large size
|
||||
@@ -84,6 +92,153 @@ class NPUARModelRunner(OmniNPUModelRunner):
|
||||
with maybe_disable_pin_memory_for_ray(self, total_bytes):
|
||||
return super()._make_buffer(*size, dtype=dtype, numpy=numpy)
|
||||
|
||||
# -------------------------------------- Omni-new -------------------------------------------------
|
||||
def capture_model(self) -> int:
|
||||
npugraph_memory_bytes = super().capture_model()
|
||||
self._capture_talker_mtp_graphs()
|
||||
return npugraph_memory_bytes
|
||||
|
||||
def _capture_talker_mtp_graphs(self) -> None:
|
||||
if not self.has_talker_mtp or not isinstance(self.talker_mtp, ACLGraphWrapper):
|
||||
return
|
||||
|
||||
from vllm.compilation.monitor import set_cudagraph_capturing_enabled
|
||||
|
||||
capture_sizes = sorted(self.compilation_config.cudagraph_capture_sizes, reverse=True)
|
||||
num_warmups = self.compilation_config.cudagraph_num_of_warmups
|
||||
logger.info("Capturing talker_mtp graphs for sizes %s", capture_sizes)
|
||||
|
||||
set_cudagraph_capturing_enabled(True)
|
||||
try:
|
||||
with torch.inference_mode(), graph_capture(device=self.device):
|
||||
for bsz in capture_sizes:
|
||||
_, batch_desc, _, _, _ = self._determine_batch_execution_and_padding(
|
||||
num_tokens=bsz,
|
||||
num_reqs=bsz,
|
||||
num_scheduled_tokens_np=np.ones(bsz, dtype=np.int32),
|
||||
max_num_scheduled_tokens=1,
|
||||
use_cascade_attn=False,
|
||||
)
|
||||
n = batch_desc.num_tokens
|
||||
ids = self.talker_mtp_input_ids.gpu[:n]
|
||||
emb = self.talker_mtp_inputs_embeds.gpu[:n]
|
||||
hid = self.last_talker_hidden.gpu[:n]
|
||||
ts = self.text_step.gpu[:n]
|
||||
|
||||
for _ in range(num_warmups):
|
||||
with set_ascend_forward_context(
|
||||
None,
|
||||
self.vllm_config,
|
||||
aclgraph_runtime_mode=CUDAGraphMode.NONE,
|
||||
batch_descriptor=batch_desc,
|
||||
):
|
||||
self.talker_mtp(ids, emb, hid, ts)
|
||||
|
||||
with set_ascend_forward_context(
|
||||
None,
|
||||
self.vllm_config,
|
||||
aclgraph_runtime_mode=CUDAGraphMode.FULL,
|
||||
batch_descriptor=batch_desc,
|
||||
):
|
||||
self.talker_mtp(ids, emb, hid, ts)
|
||||
torch.npu.synchronize()
|
||||
|
||||
logger.info("Captured talker_mtp graphs for %d sizes", len(capture_sizes))
|
||||
except RuntimeError as e:
|
||||
raise RuntimeError(
|
||||
f"talker_mtp graph capture failed for a model that declared talker_mtp_graph_safe=True: {e}"
|
||||
) from e
|
||||
finally:
|
||||
set_cudagraph_capturing_enabled(False)
|
||||
|
||||
def _maybe_update_prefix_cache(
|
||||
self,
|
||||
hidden_states: torch.Tensor,
|
||||
multimodal_outputs: dict,
|
||||
num_tokens_unpadded: int,
|
||||
num_tokens_padded: int,
|
||||
):
|
||||
if self.omni_prefix_cache is not None and get_pp_group().is_last_rank:
|
||||
if multimodal_outputs is not None and not isinstance(multimodal_outputs, dict):
|
||||
logger.warning_once(
|
||||
"prefix caching expects mm outputs to be a dict, but got %s",
|
||||
type(multimodal_outputs),
|
||||
)
|
||||
|
||||
self.omni_prefix_cache.update_omni_tensor_prefix_cache(
|
||||
hidden_states=hidden_states,
|
||||
multimodal_outputs=multimodal_outputs,
|
||||
num_tokens_unpadded=num_tokens_unpadded,
|
||||
slot_mapping=self.input_batch.block_table[0].slot_mapping.cpu,
|
||||
num_tokens_padded=num_tokens_padded,
|
||||
)
|
||||
|
||||
def _maybe_get_combined_prefix_cache_tensors(
|
||||
self,
|
||||
hidden_states: torch.Tensor,
|
||||
multimodal_outputs: dict,
|
||||
num_scheduled_tokens: dict[str, int],
|
||||
) -> tuple[dict[str, torch.Tensor] | None, dict | None]:
|
||||
combined_hidden_states, combined_multimodal_outputs = None, None
|
||||
if self.omni_prefix_cache is not None:
|
||||
combined_hidden_states = self.omni_prefix_cache.get_merged_hidden_states(
|
||||
query_start_loc=self.query_start_loc.cpu,
|
||||
input_batch=self.input_batch,
|
||||
hidden_states=hidden_states,
|
||||
num_scheduled_tokens=num_scheduled_tokens,
|
||||
)
|
||||
combined_multimodal_outputs = self.omni_prefix_cache.get_merged_multimodal_states(
|
||||
query_start_loc=self.query_start_loc.cpu,
|
||||
input_batch=self.input_batch,
|
||||
multimodal_outputs=multimodal_outputs,
|
||||
num_scheduled_tokens=num_scheduled_tokens,
|
||||
)
|
||||
return combined_hidden_states, combined_multimodal_outputs
|
||||
|
||||
@staticmethod
|
||||
def _resolve_req_hidden_states(
|
||||
hidden_states_cpu: torch.Tensor,
|
||||
combined_hidden_states: dict[str, torch.Tensor] | None,
|
||||
rid: str,
|
||||
start: int,
|
||||
end: int,
|
||||
):
|
||||
if combined_hidden_states is not None:
|
||||
if rid not in combined_hidden_states:
|
||||
raise RuntimeError("Request IDs in the batch are missing from the merged states!")
|
||||
return combined_hidden_states[rid]
|
||||
return hidden_states_cpu[start:end]
|
||||
|
||||
def _request_final_stage_id(self, req_id: str) -> int | None:
|
||||
info = self.model_intermediate_buffer.get(req_id)
|
||||
if not isinstance(info, dict):
|
||||
req_state = self.requests.get(req_id)
|
||||
info = getattr(req_state, "additional_information_cpu", None)
|
||||
if not isinstance(info, dict):
|
||||
return None
|
||||
val = info.get("omni_final_stage_id")
|
||||
try:
|
||||
return int(val)
|
||||
except (TypeError, ValueError):
|
||||
return None
|
||||
|
||||
def _request_needs_downstream_stage_payload(self, req_id: str) -> bool:
|
||||
cached = self._downstream_payload_cache.get(req_id)
|
||||
if cached is not None:
|
||||
return cached
|
||||
final_stage_id = self._request_final_stage_id(req_id)
|
||||
needs_payload = final_stage_id is None or final_stage_id > 0
|
||||
self._downstream_payload_cache[req_id] = needs_payload
|
||||
return needs_payload
|
||||
|
||||
def _resolve_pooler_payload_req_ids(self, req_ids_output_copy: list[str]) -> tuple[str, list[str]]:
|
||||
downstream_req_ids = [rid for rid in req_ids_output_copy if self._request_needs_downstream_stage_payload(rid)]
|
||||
engine_output_type = (self.vllm_config.model_config.engine_output_type or "").lower()
|
||||
if engine_output_type == "audio" and not downstream_req_ids:
|
||||
downstream_req_ids = req_ids_output_copy
|
||||
return engine_output_type, downstream_req_ids
|
||||
# -------------------------------------- Omni-new -------------------------------------------------
|
||||
|
||||
@torch.inference_mode()
|
||||
def execute_model(
|
||||
self,
|
||||
@@ -96,6 +251,9 @@ class NPUARModelRunner(OmniNPUModelRunner):
|
||||
capturer.clear_buffer()
|
||||
else:
|
||||
logger.warning("RoutedExpertsCapturer is not initialized.")
|
||||
if self.ascend_config.profiling_chunk_config.enabled:
|
||||
self._sync_device()
|
||||
self._execution_start_time = time.perf_counter()
|
||||
if self.execute_model_state is not None:
|
||||
raise RuntimeError("State error: sample_tokens() must be called after execute_model() returns None.")
|
||||
|
||||
@@ -111,7 +269,14 @@ class NPUARModelRunner(OmniNPUModelRunner):
|
||||
if finished_reqs and hasattr(self.model, "get_kv_transfer_metadata"):
|
||||
for req_id, data in finished_reqs.items():
|
||||
try:
|
||||
model_meta = self.model.get_kv_transfer_metadata(req_id)
|
||||
req_idx = self.input_batch.req_id_to_index.get(req_id)
|
||||
num_computed = (
|
||||
int(self.input_batch.num_computed_tokens_cpu[req_idx]) if req_idx is not None else None
|
||||
)
|
||||
model_meta = self.model.get_kv_transfer_metadata(
|
||||
req_id,
|
||||
num_computed_tokens=num_computed,
|
||||
)
|
||||
if model_meta:
|
||||
existing = data.get("custom_metadata") or {}
|
||||
existing.update(model_meta)
|
||||
@@ -133,15 +298,37 @@ class NPUARModelRunner(OmniNPUModelRunner):
|
||||
# scheduler_output in engine core process.
|
||||
# TODO(Ronald1995): deepcopy is expensive when there is a large
|
||||
# number of requests, optimize it later.
|
||||
if (
|
||||
if ((
|
||||
self.use_async_scheduling and self.num_spec_tokens and self._draft_token_ids is None # type: ignore[has-type]
|
||||
):
|
||||
) or (
|
||||
# NOTE: This branch specifically triggers a deepcopy during the prefill phase
|
||||
# only for PCP (Parallel Context Processing) + Multi-Modal (MM) scenarios.
|
||||
# It does not affect other use cases. This is a temporary workaround and
|
||||
# will be removed once upstream vLLM provides native support for PCP + MM.
|
||||
self.pcp_size > 1
|
||||
and self.supports_mm_inputs
|
||||
and get_pp_group().is_first_rank
|
||||
and not self.model_config.is_encoder_decoder
|
||||
)):
|
||||
scheduler_output = deepcopy(scheduler_output)
|
||||
|
||||
# -------------------------------------- Omni-new -------------------------------------------------
|
||||
if has_kv_transfer_group():
|
||||
kv_connector_metadata = scheduler_output.kv_connector_metadata
|
||||
if kv_connector_metadata is not None:
|
||||
get_kv_transfer_group().handle_preemptions(kv_connector_metadata)
|
||||
# -------------------------------------- Omni-new -------------------------------------------------
|
||||
|
||||
num_scheduled_tokens = scheduler_output.total_num_scheduled_tokens
|
||||
with record_function_or_nullcontext("prepare input"):
|
||||
with self.synchronize_input_prep():
|
||||
# Update persistent batch states.
|
||||
self._update_states(scheduler_output)
|
||||
deferred_state_corrections_fn = self._update_states(scheduler_output)
|
||||
|
||||
# -------------------------------------- Omni-new -------------------------------------------------
|
||||
if scheduler_output.finished_req_ids and hasattr(self.model, "on_requests_finished"):
|
||||
self.model.on_requests_finished(scheduler_output.finished_req_ids)
|
||||
# -------------------------------------- Omni-new -------------------------------------------------
|
||||
|
||||
if has_ec_transfer() and get_ec_transfer().is_producer:
|
||||
with self.maybe_get_ec_connector_output(
|
||||
@@ -232,6 +419,7 @@ class NPUARModelRunner(OmniNPUModelRunner):
|
||||
num_scheduled_tokens_np=num_scheduled_tokens_np,
|
||||
max_num_scheduled_tokens=max_num_scheduled_tokens,
|
||||
use_cascade_attn=cascade_attn_prefix_lens is not None,
|
||||
force_eager=self.model_config.enforce_eager,
|
||||
num_encoder_reqs=len(scheduler_output.scheduled_encoder_inputs),
|
||||
)
|
||||
|
||||
@@ -261,7 +449,13 @@ class NPUARModelRunner(OmniNPUModelRunner):
|
||||
# '_update_states_after_model_execute', which is not overridden in vLLM-Ascend.
|
||||
# We simply utilize the implementation in vLLM.
|
||||
if self.cache_config.mamba_cache_mode == "align":
|
||||
preprocess_mamba(
|
||||
# preprocess_mamba reads req_state.num_computed_tokens (CPU)
|
||||
# to decide copy operations, so we must apply deferred
|
||||
# corrections before it runs.
|
||||
if deferred_state_corrections_fn:
|
||||
deferred_state_corrections_fn()
|
||||
deferred_state_corrections_fn = None
|
||||
mamba_utils.preprocess_mamba(
|
||||
scheduler_output,
|
||||
self.kv_cache_config,
|
||||
self.cache_config,
|
||||
@@ -272,6 +466,12 @@ class NPUARModelRunner(OmniNPUModelRunner):
|
||||
self.model.get_mamba_state_copy_func(),
|
||||
self._get_mamba_copy_bufs(),
|
||||
)
|
||||
# preprocess_mamba resets num_accepted_tokens_cpu to 1
|
||||
# for requests whose state was copied to a new block.
|
||||
# Re-sync to GPU so the mamba kernel reads from the
|
||||
# correct initial state slot (init_token_idx = 0).
|
||||
self.num_accepted_tokens.np[:num_reqs] = self.input_batch.num_accepted_tokens_cpu[:num_reqs]
|
||||
self.num_accepted_tokens.copy_to_gpu(num_reqs)
|
||||
|
||||
use_spec_decode = len(scheduler_output.scheduled_spec_decode_tokens) > 0
|
||||
ubatch_slices_attn = ubatch_slices_padded if pad_attn else ubatch_slices
|
||||
@@ -279,19 +479,14 @@ class NPUARModelRunner(OmniNPUModelRunner):
|
||||
if (
|
||||
cudagraph_mode == CUDAGraphMode.FULL
|
||||
or (enable_sp() and not self.model_config.use_mla)
|
||||
and self.pcp_size == 1 # TODO(lxs): fix this
|
||||
and self.pcp_size * self.dcp_size == 1
|
||||
):
|
||||
# Currently, Graph Mode and SP will both pad num_tokens,
|
||||
# Another possible condition is num_tokens_padded != num_tokens_unpadded
|
||||
# but this scope is way too big and the consequences are unpredictable
|
||||
old_num_reqs_padded = num_reqs_padded
|
||||
num_reqs_padded = self._pad_query_start_loc_for_fia(
|
||||
num_tokens_padded, num_reqs_padded, num_reqs, cudagraph_mode, batch_desc.num_reqs
|
||||
)
|
||||
if enable_sp() and num_tokens_padded == num_tokens_unpadded:
|
||||
if num_reqs_padded > old_num_reqs_padded:
|
||||
num_reqs_padded = old_num_reqs_padded
|
||||
self.query_start_loc.np[num_reqs_padded + 1] = 0
|
||||
|
||||
(attn_metadata, spec_decode_common_attn_metadata) = self._build_attention_metadata(
|
||||
num_tokens=num_tokens_unpadded
|
||||
@@ -324,6 +519,19 @@ class NPUARModelRunner(OmniNPUModelRunner):
|
||||
intermediate_tensors,
|
||||
)
|
||||
|
||||
# -------------------------------------- Omni-new -------------------------------------------------
|
||||
if hasattr(self.model, "prepare_runner_inputs"):
|
||||
input_ids, positions = self.model.prepare_runner_inputs(
|
||||
input_ids=input_ids,
|
||||
positions=positions,
|
||||
inputs_embeds=inputs_embeds,
|
||||
req_ids=req_ids[:num_reqs],
|
||||
num_computed_tokens=[int(self.input_batch.num_computed_tokens_cpu[i]) for i in range(num_reqs)],
|
||||
num_scheduled_tokens=[int(num_scheduled_tokens_np[i]) for i in range(num_reqs)],
|
||||
input_ids_buffer=self.input_ids.gpu[:num_tokens_padded],
|
||||
)
|
||||
# -------------------------------------- Omni-new -------------------------------------------------
|
||||
|
||||
# update global cos, sin
|
||||
update_cos_sin(positions)
|
||||
|
||||
@@ -375,7 +583,10 @@ class NPUARModelRunner(OmniNPUModelRunner):
|
||||
skip_compiled=has_encoder_input,
|
||||
),
|
||||
self.maybe_get_kv_connector_output(
|
||||
scheduler_output, defer_finalize=not clear_kv_metadata
|
||||
scheduler_output,
|
||||
**(
|
||||
{"defer_finalize": not clear_kv_metadata}
|
||||
),
|
||||
) as kv_connector_output,
|
||||
):
|
||||
hidden_states = self._model_forward(
|
||||
@@ -412,6 +623,15 @@ class NPUARModelRunner(OmniNPUModelRunner):
|
||||
for aux_hidden_states_pcp in aux_hidden_states
|
||||
]
|
||||
|
||||
# -------------------------------------- Omni-new -------------------------------------------------
|
||||
self._maybe_update_prefix_cache(
|
||||
hidden_states=hidden_states,
|
||||
multimodal_outputs=multimodal_outputs,
|
||||
num_tokens_unpadded=num_tokens_unpadded,
|
||||
num_tokens_padded=num_tokens_padded,
|
||||
)
|
||||
# -------------------------------------- Omni-new -------------------------------------------------
|
||||
|
||||
if not self.broadcast_pp_output:
|
||||
# Common case.
|
||||
if not get_pp_group().is_last_rank:
|
||||
@@ -486,9 +706,15 @@ class NPUARModelRunner(OmniNPUModelRunner):
|
||||
positions,
|
||||
ec_connector_output,
|
||||
cudagraph_stats,
|
||||
batch_desc,
|
||||
multimodal_outputs, # Omni-specific
|
||||
)
|
||||
self.kv_connector_output = kv_connector_output
|
||||
|
||||
# Now the batch has been launched we can wait for corrections from the
|
||||
# previous model forward without breaking async scheduling.
|
||||
if deferred_state_corrections_fn:
|
||||
deferred_state_corrections_fn()
|
||||
return None
|
||||
|
||||
@torch.inference_mode()
|
||||
@@ -501,6 +727,9 @@ class NPUARModelRunner(OmniNPUModelRunner):
|
||||
# -------------------------------------- Omni-new -------------------------------------------------
|
||||
kv_extracted_req_ids = getattr(self, "kv_extracted_req_ids", None)
|
||||
self.kv_extracted_req_ids = None
|
||||
combined_hidden_states = None
|
||||
combined_multimodal_outputs = None
|
||||
mm_cpu = {}
|
||||
# -------------------------------------- Omni-new -------------------------------------------------
|
||||
|
||||
|
||||
@@ -535,10 +764,12 @@ class NPUARModelRunner(OmniNPUModelRunner):
|
||||
positions,
|
||||
ec_connector_output,
|
||||
cudagraph_stats,
|
||||
batch_desc,
|
||||
multimodal_outputs, # Omni-Specific
|
||||
) = self.execute_model_state
|
||||
# Clear ephemeral state.
|
||||
self.execute_model_state = None
|
||||
seq_len = hidden_states.shape[0]
|
||||
|
||||
# Apply structured output bitmasks if present.
|
||||
if grammar_output is not None:
|
||||
@@ -570,6 +801,8 @@ class NPUARModelRunner(OmniNPUModelRunner):
|
||||
assert self.sampling_done_event is not None
|
||||
self.sampling_done_event.record()
|
||||
|
||||
self.valid_sampled_token_count_gpu: torch.Tensor | None = None # type: ignore[no-redef]
|
||||
|
||||
def propose_draft_token_ids(sampled_token_ids):
|
||||
assert spec_decode_common_attn_metadata is not None
|
||||
self._draft_token_ids = self.propose_draft_token_ids(
|
||||
@@ -583,11 +816,11 @@ class NPUARModelRunner(OmniNPUModelRunner):
|
||||
hidden_states,
|
||||
aux_hidden_states,
|
||||
sample_hidden_states,
|
||||
batch_desc,
|
||||
)
|
||||
self._copy_draft_token_ids_to_cpu(scheduler_output)
|
||||
|
||||
(
|
||||
_num_nans_in_logits,
|
||||
logprobs_lists,
|
||||
valid_sampled_token_ids,
|
||||
prompt_logprobs_dict,
|
||||
@@ -600,6 +833,7 @@ class NPUARModelRunner(OmniNPUModelRunner):
|
||||
logits,
|
||||
hidden_states,
|
||||
scheduler_output.total_num_scheduled_tokens,
|
||||
spec_decode_metadata,
|
||||
)
|
||||
|
||||
with record_function_or_nullcontext("draft_token"):
|
||||
@@ -618,8 +852,11 @@ class NPUARModelRunner(OmniNPUModelRunner):
|
||||
# tokens on the CPU, so they are run after bookkeeping.
|
||||
propose_draft_token_ids(valid_sampled_token_ids)
|
||||
|
||||
if has_kv_transfer_group():
|
||||
get_kv_transfer_group().clear_connector_metadata()
|
||||
# vLLM v0.18 defers KV connector finalization during target-model
|
||||
# forward when speculative decoding is enabled. Finalize here after
|
||||
# draft model runs so KV pool save/put can complete.
|
||||
if self.speculative_config is not None:
|
||||
self.finalize_kv_connector()
|
||||
|
||||
if self.model_config.enable_return_routed_experts:
|
||||
capturer = RoutedExpertsCapturer.get_instance()
|
||||
@@ -629,7 +866,20 @@ class NPUARModelRunner(OmniNPUModelRunner):
|
||||
logger.warning("RoutedExpertsCapturer is not initialized.")
|
||||
|
||||
# -------------------------------------- Omni-new -------------------------------------------------
|
||||
hidden_states_cpu = hidden_states.detach().to("cpu").contiguous()
|
||||
engine_output_type, downstream_req_ids = self._resolve_pooler_payload_req_ids(req_ids_output_copy)
|
||||
needs_pooler_payload = len(downstream_req_ids) > 0
|
||||
downstream_req_id_set = set(downstream_req_ids)
|
||||
hidden_states_cpu = None
|
||||
req_hidden_states_cpu: dict[str, torch.Tensor] | None = None
|
||||
if needs_pooler_payload:
|
||||
num_valid_tokens = min(
|
||||
int(scheduler_output.total_num_scheduled_tokens),
|
||||
int(hidden_states.shape[0]),
|
||||
)
|
||||
if len(downstream_req_ids) == len(req_ids_output_copy):
|
||||
hidden_states_cpu = hidden_states[:num_valid_tokens].detach().to("cpu").contiguous()
|
||||
else:
|
||||
req_hidden_states_cpu = {}
|
||||
num_scheduled_tokens_np = getattr(self, "_omni_num_scheduled_tokens_np", None)
|
||||
if num_scheduled_tokens_np is None:
|
||||
req_ids = self.input_batch.req_ids
|
||||
@@ -637,68 +887,81 @@ class NPUARModelRunner(OmniNPUModelRunner):
|
||||
[scheduler_output.num_scheduled_tokens[rid] for rid in req_ids],
|
||||
dtype=np.int32,
|
||||
)
|
||||
query_start_loc_cpu = self.query_start_loc.cpu
|
||||
|
||||
self._process_additional_information_updates(
|
||||
hidden_states, multimodal_outputs, num_scheduled_tokens_np, scheduler_output
|
||||
)
|
||||
pooler_output: list[dict[str, object]] | None = None
|
||||
if needs_pooler_payload:
|
||||
if self.omni_prefix_cache is not None:
|
||||
(
|
||||
combined_hidden_states,
|
||||
combined_multimodal_outputs,
|
||||
) = self._maybe_get_combined_prefix_cache_tensors(
|
||||
hidden_states,
|
||||
multimodal_outputs,
|
||||
scheduler_output.num_scheduled_tokens,
|
||||
)
|
||||
else:
|
||||
mm_cpu = build_mm_cpu(flatten_payload(multimodal_outputs))
|
||||
|
||||
# Pre-copy multimodal tensors to CPU once (not per-request) to avoid
|
||||
# redundant D2H transfers when gpu_resident_buffer_keys keeps them on GPU.
|
||||
mm_cpu: dict[str, object] = {}
|
||||
if isinstance(multimodal_outputs, dict) and multimodal_outputs:
|
||||
for k, v in multimodal_outputs.items():
|
||||
try:
|
||||
if isinstance(v, torch.Tensor) and v.shape[0] == hidden_states_cpu.shape[0]:
|
||||
mm_cpu[k] = v.detach().to("cpu").contiguous()
|
||||
elif isinstance(v, dict):
|
||||
sub_dict: dict[str, torch.Tensor] = {}
|
||||
for sk, sv in v.items():
|
||||
if isinstance(sv, torch.Tensor) and sv.shape[0] == hidden_states_cpu.shape[0]:
|
||||
sub_dict[str(sk)] = sv.detach().to("cpu").contiguous()
|
||||
if sub_dict:
|
||||
mm_cpu[k] = sub_dict
|
||||
elif isinstance(v, list):
|
||||
if len(v) == 0:
|
||||
continue
|
||||
cpu_list = []
|
||||
for elem in v:
|
||||
if isinstance(elem, torch.Tensor):
|
||||
cpu_list.append(elem.detach().to("cpu").contiguous())
|
||||
else:
|
||||
cpu_list.append(elem)
|
||||
mm_cpu[k] = cpu_list
|
||||
except Exception as e:
|
||||
logger.error(f"Error in merge multimodal outputs: {e}")
|
||||
self._process_additional_information_updates(
|
||||
hidden_states,
|
||||
multimodal_outputs,
|
||||
num_scheduled_tokens_np,
|
||||
scheduler_output,
|
||||
combined_hidden_states,
|
||||
combined_multimodal_outputs,
|
||||
req_ids_filter=downstream_req_id_set,
|
||||
)
|
||||
|
||||
pooler_output: list[dict[str, object]] = []
|
||||
for rid in req_ids_output_copy:
|
||||
idx = req_id_to_index_output_copy[rid]
|
||||
start = int(self.query_start_loc.cpu[idx])
|
||||
sched = int(num_scheduled_tokens_np[idx])
|
||||
end = start + sched
|
||||
hidden_slice = hidden_states_cpu[start:end]
|
||||
payload: dict[str, object] = {"hidden": hidden_slice}
|
||||
if mm_cpu:
|
||||
if req_hidden_states_cpu is not None and combined_hidden_states is None:
|
||||
for rid in downstream_req_ids:
|
||||
idx = req_id_to_index_output_copy[rid]
|
||||
start = int(query_start_loc_cpu[idx])
|
||||
sched = int(num_scheduled_tokens_np[idx])
|
||||
end = start + sched
|
||||
req_hidden_states_cpu[rid] = hidden_states[start:end].detach().to("cpu").contiguous()
|
||||
|
||||
pooler_output = []
|
||||
for rid in req_ids_output_copy:
|
||||
if rid not in downstream_req_id_set:
|
||||
pooler_output.append({})
|
||||
continue
|
||||
idx = req_id_to_index_output_copy[rid]
|
||||
start = int(query_start_loc_cpu[idx])
|
||||
sched = int(num_scheduled_tokens_np[idx])
|
||||
end = start + sched
|
||||
if req_hidden_states_cpu is not None and combined_hidden_states is None:
|
||||
req_hidden_states = req_hidden_states_cpu[rid]
|
||||
else:
|
||||
req_hidden_states = self._resolve_req_hidden_states(
|
||||
hidden_states_cpu,
|
||||
combined_hidden_states,
|
||||
rid,
|
||||
start,
|
||||
end,
|
||||
)
|
||||
payload: dict[str, object] = {"hidden": req_hidden_states}
|
||||
mm_payload: dict[str, object] = {}
|
||||
for k, v in mm_cpu.items():
|
||||
if isinstance(v, torch.Tensor) and v.shape[0] == hidden_states_cpu.shape[0]:
|
||||
mm_payload[k] = v[start:end].contiguous()
|
||||
elif isinstance(v, dict):
|
||||
mm_payload[k] = {sk: sv[start:end].contiguous() for sk, sv in v.items()}
|
||||
elif isinstance(v, list):
|
||||
element = v[idx] if idx < len(v) else v[0]
|
||||
# Clone tensors to avoid cross-request aliasing
|
||||
if isinstance(element, torch.Tensor):
|
||||
element = element.clone()
|
||||
mm_payload[k] = element
|
||||
elif isinstance(v, torch.Tensor):
|
||||
# List-derived tensor payloads are request-invariant; clone to
|
||||
# avoid accidental cross-request aliasing on downstream mutation.
|
||||
mm_payload[k] = v.clone()
|
||||
if combined_multimodal_outputs or mm_cpu:
|
||||
if combined_multimodal_outputs:
|
||||
for mm_key in combined_multimodal_outputs.keys():
|
||||
value = combined_multimodal_outputs[mm_key][rid]
|
||||
if isinstance(value, list):
|
||||
mm_payload[mm_key] = value[idx] if idx < len(value) else value[0]
|
||||
else:
|
||||
mm_payload[mm_key] = value
|
||||
else:
|
||||
mm_payload[k] = v
|
||||
payload.update(mm_payload)
|
||||
pooler_output.append(payload)
|
||||
for mm_key, mm_val in mm_cpu.items():
|
||||
mm_payload[mm_key] = to_payload_element(
|
||||
element=mm_val,
|
||||
idx=idx,
|
||||
start=start,
|
||||
end=end,
|
||||
pass_lists_through=False,
|
||||
seq_len=seq_len,
|
||||
)
|
||||
payload.update(mm_payload)
|
||||
pooler_output.append(flatten_payload(payload))
|
||||
|
||||
model_runner_output = OmniModelRunnerOutput(
|
||||
req_ids=req_ids_output_copy,
|
||||
@@ -706,12 +969,18 @@ class NPUARModelRunner(OmniNPUModelRunner):
|
||||
sampled_token_ids=valid_sampled_token_ids,
|
||||
logprobs=logprobs_lists,
|
||||
prompt_logprobs_dict=prompt_logprobs_dict,
|
||||
pooler_output=(pooler_output if self.vllm_config.model_config.engine_output_type != "text" else None),
|
||||
pooler_output=(pooler_output if engine_output_type != "text" and needs_pooler_payload else None),
|
||||
kv_connector_output=kv_connector_output,
|
||||
ec_connector_output=ec_connector_output if self.supports_mm_inputs else None,
|
||||
cudagraph_stats=cudagraph_stats,
|
||||
)
|
||||
model_runner_output.kv_extracted_req_ids = kv_extracted_req_ids
|
||||
# -------------------------------------- Omni-new -------------------------------------------------
|
||||
|
||||
if self.ascend_config.profiling_chunk_config.enabled and hasattr(self, "_execution_start_time"):
|
||||
self._sync_device()
|
||||
model_runner_output.execution_time_ms = (time.perf_counter() - self._execution_start_time) * 1000.0
|
||||
|
||||
if self.dynamic_eplb:
|
||||
with record_function_or_nullcontext("EPLB update"):
|
||||
self.eplb_updator.forward_end()
|
||||
@@ -739,7 +1008,7 @@ class NPUARModelRunner(OmniNPUModelRunner):
|
||||
|
||||
if not self.use_async_scheduling:
|
||||
return model_runner_output
|
||||
return AsyncGPUModelRunnerOutput(
|
||||
async_output = AsyncGPUModelRunnerOutput(
|
||||
model_runner_output=model_runner_output,
|
||||
sampled_token_ids=sampler_output.sampled_token_ids,
|
||||
logprobs_tensors=sampler_output.logprobs_tensors,
|
||||
@@ -747,7 +1016,13 @@ class NPUARModelRunner(OmniNPUModelRunner):
|
||||
async_output_copy_stream=self.async_output_copy_stream,
|
||||
vocab_size=self.input_batch.vocab_size,
|
||||
)
|
||||
self.input_batch.set_async_sampled_token_ids(
|
||||
async_output.sampled_token_ids_cpu,
|
||||
async_output.async_copy_ready_event,
|
||||
)
|
||||
return async_output
|
||||
|
||||
# -------------------------------------- Omni-new -------------------------------------------------
|
||||
def _resolve_global_request_id(self, req_id: str) -> str:
|
||||
"""Resolve global request ID from request state."""
|
||||
req_state = self.requests.get(req_id)
|
||||
@@ -763,3 +1038,4 @@ class NPUARModelRunner(OmniNPUModelRunner):
|
||||
return global_id.decode("utf-8")
|
||||
return str(global_id)
|
||||
return req_id
|
||||
# -------------------------------------- Omni-new -------------------------------------------------
|
||||
@@ -4,6 +4,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import gc
|
||||
import time
|
||||
from copy import copy, deepcopy
|
||||
|
||||
import numpy as np
|
||||
@@ -11,7 +12,7 @@ import torch
|
||||
from vllm.config import CUDAGraphMode
|
||||
from vllm.distributed import get_tensor_model_parallel_world_size
|
||||
from vllm.distributed.ec_transfer import get_ec_transfer, has_ec_transfer
|
||||
from vllm.distributed.kv_transfer import has_kv_transfer_group
|
||||
from vllm.distributed.kv_transfer import get_kv_transfer_group, has_kv_transfer_group
|
||||
from vllm.distributed.parallel_state import get_pp_group
|
||||
from vllm.logger import logger
|
||||
from vllm.model_executor.layers.fused_moe.routed_experts_capturer import RoutedExpertsCapturer
|
||||
@@ -78,6 +79,9 @@ class NPUGenerationModelRunner(OmniNPUModelRunner):
|
||||
capturer.clear_buffer()
|
||||
else:
|
||||
logger.warning("RoutedExpertsCapturer is not initialized.")
|
||||
if self.ascend_config.profiling_chunk_config.enabled:
|
||||
self._sync_device()
|
||||
self._execution_start_time = time.perf_counter()
|
||||
if self.execute_model_state is not None:
|
||||
raise RuntimeError("State error: sample_tokens() must be called after execute_model() returns None.")
|
||||
# self._draft_token_ids is None when `input_fits_in_drafter=False`
|
||||
@@ -89,8 +93,23 @@ class NPUGenerationModelRunner(OmniNPUModelRunner):
|
||||
# number of requests, optimize it later.
|
||||
if (
|
||||
self.use_async_scheduling and self.num_spec_tokens and self._draft_token_ids is None # type: ignore[has-type]
|
||||
) or (
|
||||
# NOTE: This branch specifically triggers a deepcopy during the prefill phase
|
||||
# only for PCP (Parallel Context Processing) + Multi-Modal (MM) scenarios.
|
||||
# It does not affect other use cases. This is a temporary workaround and
|
||||
# will be removed once upstream vLLM provides native support for PCP + MM.
|
||||
self.pcp_size > 1
|
||||
and self.supports_mm_inputs
|
||||
and get_pp_group().is_first_rank
|
||||
and not self.model_config.is_encoder_decoder
|
||||
):
|
||||
scheduler_output = deepcopy(scheduler_output)
|
||||
|
||||
if has_kv_transfer_group():
|
||||
kv_connector_metadata = scheduler_output.kv_connector_metadata
|
||||
if kv_connector_metadata is not None:
|
||||
get_kv_transfer_group().handle_preemptions(kv_connector_metadata)
|
||||
|
||||
num_scheduled_tokens = scheduler_output.total_num_scheduled_tokens
|
||||
with record_function_or_nullcontext("prepare input"):
|
||||
# -------------------------------------- Omni-new -------------------------------------------------
|
||||
@@ -99,7 +118,7 @@ class NPUGenerationModelRunner(OmniNPUModelRunner):
|
||||
# -------------------------------------- Omni-new -------------------------------------------------
|
||||
with self.synchronize_input_prep():
|
||||
# Update persistent batch states.
|
||||
self._update_states(scheduler_output)
|
||||
deferred_state_corrections_fn = self._update_states(scheduler_output)
|
||||
|
||||
if has_ec_transfer() and get_ec_transfer().is_producer:
|
||||
with self.maybe_get_ec_connector_output(
|
||||
@@ -172,6 +191,7 @@ class NPUGenerationModelRunner(OmniNPUModelRunner):
|
||||
num_scheduled_tokens_np=num_scheduled_tokens_np,
|
||||
max_num_scheduled_tokens=max_num_scheduled_tokens,
|
||||
use_cascade_attn=cascade_attn_prefix_lens is not None,
|
||||
force_eager=self.model_config.enforce_eager,
|
||||
num_encoder_reqs=len(scheduler_output.scheduled_encoder_inputs),
|
||||
)
|
||||
|
||||
@@ -201,6 +221,12 @@ class NPUGenerationModelRunner(OmniNPUModelRunner):
|
||||
# '_update_states_after_model_execute', which is not overridden in vLLM-Ascend.
|
||||
# We simply utilize the implementation in vLLM.
|
||||
if self.cache_config.mamba_cache_mode == "align":
|
||||
# preprocess_mamba reads req_state.num_computed_tokens (CPU)
|
||||
# to decide copy operations, so we must apply deferred
|
||||
# corrections before it runs.
|
||||
if deferred_state_corrections_fn:
|
||||
deferred_state_corrections_fn()
|
||||
deferred_state_corrections_fn = None
|
||||
mamba_utils.preprocess_mamba(
|
||||
scheduler_output,
|
||||
self.kv_cache_config,
|
||||
@@ -212,6 +238,12 @@ class NPUGenerationModelRunner(OmniNPUModelRunner):
|
||||
self.model.get_mamba_state_copy_func(),
|
||||
self._get_mamba_copy_bufs(),
|
||||
)
|
||||
# preprocess_mamba resets num_accepted_tokens_cpu to 1
|
||||
# for requests whose state was copied to a new block.
|
||||
# Re-sync to GPU so the mamba kernel reads from the
|
||||
# correct initial state slot (init_token_idx = 0).
|
||||
self.num_accepted_tokens.np[:num_reqs] = self.input_batch.num_accepted_tokens_cpu[:num_reqs]
|
||||
self.num_accepted_tokens.copy_to_gpu(num_reqs)
|
||||
|
||||
use_spec_decode = len(scheduler_output.scheduled_spec_decode_tokens) > 0
|
||||
ubatch_slices_attn = ubatch_slices_padded if pad_attn else ubatch_slices
|
||||
@@ -219,19 +251,14 @@ class NPUGenerationModelRunner(OmniNPUModelRunner):
|
||||
if (
|
||||
cudagraph_mode == CUDAGraphMode.FULL
|
||||
or (enable_sp() and not self.model_config.use_mla)
|
||||
and self.pcp_size == 1 # TODO(lxs): fix this
|
||||
and self.pcp_size * self.dcp_size == 1
|
||||
):
|
||||
# Currently, Graph Mode and SP will both pad num_tokens,
|
||||
# Another possible condition is num_tokens_padded != num_tokens_unpadded
|
||||
# but this scope is way too big and the consequences are unpredictable
|
||||
old_num_reqs_padded = num_reqs_padded
|
||||
num_reqs_padded = self._pad_query_start_loc_for_fia(
|
||||
num_tokens_padded, num_reqs_padded, num_reqs, cudagraph_mode, batch_desc.num_reqs
|
||||
)
|
||||
if enable_sp() and num_tokens_padded == num_tokens_unpadded:
|
||||
if num_reqs_padded > old_num_reqs_padded:
|
||||
num_reqs_padded = old_num_reqs_padded
|
||||
self.query_start_loc.np[num_reqs_padded + 1] = 0
|
||||
|
||||
(attn_metadata, spec_decode_common_attn_metadata) = self._build_attention_metadata(
|
||||
num_tokens=num_tokens_unpadded
|
||||
@@ -318,7 +345,8 @@ class NPUGenerationModelRunner(OmniNPUModelRunner):
|
||||
skip_compiled=has_encoder_input,
|
||||
),
|
||||
self.maybe_get_kv_connector_output(
|
||||
scheduler_output, defer_finalize=not clear_kv_metadata
|
||||
scheduler_output,
|
||||
**({"defer_finalize": not clear_kv_metadata}),
|
||||
) as kv_connector_output,
|
||||
):
|
||||
# -------------------------------------- Omni-new -------------------------------------------------
|
||||
@@ -346,9 +374,15 @@ class NPUGenerationModelRunner(OmniNPUModelRunner):
|
||||
positions,
|
||||
ec_connector_output,
|
||||
cudagraph_stats,
|
||||
batch_desc,
|
||||
multimodal_outputs, # Omni-specific: pass multimodal_outputs to ExecuteModelState
|
||||
)
|
||||
self.kv_connector_output = kv_connector_output
|
||||
|
||||
# Now the batch has been launched we can wait for corrections from the
|
||||
# previous model forward without breaking async scheduling.
|
||||
if deferred_state_corrections_fn:
|
||||
deferred_state_corrections_fn()
|
||||
return None
|
||||
|
||||
@torch.inference_mode()
|
||||
@@ -389,6 +423,7 @@ class NPUGenerationModelRunner(OmniNPUModelRunner):
|
||||
positions,
|
||||
ec_connector_output,
|
||||
cudagraph_stats,
|
||||
_batch_desc,
|
||||
multimodal_outputs, # Omni-Specific
|
||||
) = self.execute_model_state
|
||||
# Clear ephemeral state.
|
||||
@@ -441,17 +476,28 @@ class NPUGenerationModelRunner(OmniNPUModelRunner):
|
||||
prompt_logprobs_dict={},
|
||||
pooler_output=pooler_output,
|
||||
kv_connector_output=kv_connector_output,
|
||||
num_nans_in_logits={},
|
||||
ec_connector_output=ec_connector_output if self.supports_mm_inputs else None,
|
||||
cudagraph_stats=cudagraph_stats,
|
||||
)
|
||||
# -------------------------------------- Omni-new -------------------------------------------------
|
||||
|
||||
if self.speculative_config is not None:
|
||||
self.finalize_kv_connector()
|
||||
|
||||
if self.ascend_config.profiling_chunk_config.enabled and hasattr(self, "_execution_start_time"):
|
||||
self._sync_device()
|
||||
output.execution_time_ms = (time.perf_counter() - self._execution_start_time) * 1000.0
|
||||
|
||||
if self.dynamic_eplb:
|
||||
with record_function_or_nullcontext("EPLB update"):
|
||||
self.eplb_updator.forward_end()
|
||||
|
||||
if self.debugger is not None:
|
||||
self.debugger.stop()
|
||||
self.debugger.step()
|
||||
if not self.use_async_scheduling:
|
||||
return output
|
||||
return AsyncGPUModelRunnerOutput(
|
||||
async_output = AsyncGPUModelRunnerOutput(
|
||||
model_runner_output=output,
|
||||
sampled_token_ids=torch.tensor([], device=self.device),
|
||||
invalid_req_indices=[],
|
||||
@@ -459,6 +505,11 @@ class NPUGenerationModelRunner(OmniNPUModelRunner):
|
||||
vocab_size=self.input_batch.vocab_size,
|
||||
logprobs_tensors=None,
|
||||
)
|
||||
self.input_batch.set_async_sampled_token_ids(
|
||||
async_output.sampled_token_ids_cpu,
|
||||
async_output.async_copy_ready_event,
|
||||
)
|
||||
return async_output
|
||||
|
||||
def _run_generation_model(
|
||||
self,
|
||||
@@ -522,6 +573,7 @@ class NPUGenerationModelRunner(OmniNPUModelRunner):
|
||||
is_graph_capturing: bool = False,
|
||||
num_active_loras: int = 0,
|
||||
profile_seq_lens: int | None = None,
|
||||
profile_cpp: bool = False,
|
||||
) -> tuple[torch.Tensor, torch.Tensor]:
|
||||
# only support eager mode and piecewise graph now
|
||||
assert cudagraph_runtime_mode is None or cudagraph_runtime_mode.valid_runtime_modes()
|
||||
@@ -547,11 +599,13 @@ class NPUGenerationModelRunner(OmniNPUModelRunner):
|
||||
if create_mixed_batch:
|
||||
raise NotImplementedError("create_mixed_batch is used for warmup deepgemm, vllm-ascend does not need it")
|
||||
elif uniform_decode:
|
||||
assert not create_mixed_batch
|
||||
num_reqs = min(max_num_reqs, cdiv(num_tokens, max_query_len))
|
||||
num_scheduled_tokens_list = [max_query_len] * num_reqs
|
||||
if num_tokens % max_query_len != 0:
|
||||
num_scheduled_tokens_list[-1] = num_tokens % max_query_len
|
||||
elif profile_cpp:
|
||||
num_reqs = 1
|
||||
num_scheduled_tokens_list = [num_tokens] * num_reqs
|
||||
else:
|
||||
num_reqs = min(num_tokens, max_num_reqs)
|
||||
min_tokens_per_req = num_tokens // num_reqs
|
||||
@@ -574,7 +628,7 @@ class NPUGenerationModelRunner(OmniNPUModelRunner):
|
||||
max_num_scheduled_tokens=max_query_len,
|
||||
use_cascade_attn=False,
|
||||
allow_microbatching=allow_microbatching,
|
||||
force_eager=is_profile or (cudagraph_runtime_mode == CUDAGraphMode.NONE),
|
||||
force_eager=is_profile or (cudagraph_runtime_mode == CUDAGraphMode.NONE) or profile_cpp,
|
||||
# `force_uniform_decode` is used for cudagraph capture; because for
|
||||
# capturing mixed prefill-decode batches, we sometimes use
|
||||
# num_tokens == num_reqs which looks like a uniform decode batch to the
|
||||
@@ -636,16 +690,21 @@ class NPUGenerationModelRunner(OmniNPUModelRunner):
|
||||
if is_graph_capturing and using_paged_attention(num_tokens, self.vllm_config)
|
||||
else max_query_len
|
||||
) # type: ignore[assignment]
|
||||
self.seq_lens.np[:num_reqs_padded] = seq_lens
|
||||
self.seq_lens.np[num_reqs_padded:] = 0
|
||||
self.seq_lens.copy_to_gpu()
|
||||
self.optimistic_seq_lens_cpu[:num_reqs] = seq_lens
|
||||
self.optimistic_seq_lens_cpu[num_reqs:].fill_(0)
|
||||
self.seq_lens.copy_(self.optimistic_seq_lens_cpu, non_blocking=True)
|
||||
|
||||
cum_num_tokens, _ = self._get_cumsum_and_arange(num_scheduled_tokens)
|
||||
cum_num_tokens = self._get_cumsum_and_arange(num_scheduled_tokens, self.query_pos.np)
|
||||
self.query_start_loc.np[1 : num_reqs_padded + 1] = cum_num_tokens
|
||||
self.query_start_loc.copy_to_gpu()
|
||||
num_reqs_padded = self._pad_query_start_loc_for_fia(
|
||||
num_tokens_padded, num_reqs_padded, num_reqs, cudagraph_runtime_mode, batch_desc.num_reqs
|
||||
)
|
||||
if self._has_gdn:
|
||||
self.gdn_query_start_loc.np[1 : num_reqs_padded + 1] = cum_num_tokens
|
||||
self.gdn_query_start_loc.copy_to_gpu()
|
||||
|
||||
if not profile_cpp:
|
||||
num_reqs_padded = self._pad_query_start_loc_for_fia(
|
||||
num_tokens_padded, num_reqs_padded, num_reqs, cudagraph_runtime_mode, batch_desc.num_reqs
|
||||
)
|
||||
|
||||
pad_attn = cudagraph_runtime_mode == CUDAGraphMode.FULL
|
||||
attn_metadata, _ = self._build_attention_metadata(
|
||||
@@ -670,7 +729,7 @@ class NPUGenerationModelRunner(OmniNPUModelRunner):
|
||||
):
|
||||
# Make sure padding doesn't exceed max_num_tokens
|
||||
assert num_tokens_padded <= self.max_num_tokens
|
||||
if self.is_multimodal_model and not self.model_config.is_encoder_decoder or self.enable_prompt_embeds:
|
||||
if self.supports_mm_inputs and not self.model_config.is_encoder_decoder or self.enable_prompt_embeds:
|
||||
input_ids = None
|
||||
inputs_embeds = self.inputs_embeds.gpu[:num_tokens_padded]
|
||||
else:
|
||||
@@ -695,7 +754,7 @@ class NPUGenerationModelRunner(OmniNPUModelRunner):
|
||||
elif self.uses_xdrope_dim > 0:
|
||||
positions = self.xdrope_positions.gpu[:, :num_tokens_padded]
|
||||
else:
|
||||
positions = self.positions.gpu[:num_tokens_padded]
|
||||
positions = self.positions[:num_tokens_padded]
|
||||
|
||||
# update global cos, sin
|
||||
update_cos_sin(positions)
|
||||
@@ -758,6 +817,9 @@ class NPUGenerationModelRunner(OmniNPUModelRunner):
|
||||
hidden_states, _ = outputs
|
||||
else:
|
||||
hidden_states = outputs
|
||||
# -------------------------------------- Omni-new -------------------------------------------------
|
||||
hidden_states, _ = self.extract_multimodal_outputs(hidden_states)
|
||||
# -------------------------------------------------------------------------------------------------
|
||||
dummy_compute_logits(hidden_states)
|
||||
|
||||
if self.drafter:
|
||||
@@ -773,13 +835,11 @@ class NPUGenerationModelRunner(OmniNPUModelRunner):
|
||||
is_profile=is_profile,
|
||||
)
|
||||
if is_profile and self.dynamic_eplb:
|
||||
self.model.clear_all_moe_loads()
|
||||
target = self.model.language_model if hasattr(self.model, "language_model") else self.model
|
||||
target.clear_all_moe_loads()
|
||||
if self.dynamic_eplb:
|
||||
self.eplb_updator.forward_end()
|
||||
|
||||
# -------------------------------------- Omni-new -------------------------------------------------
|
||||
hidden_states, _ = self.extract_multimodal_outputs(hidden_states)
|
||||
# -------------------------------------------------------------------------------------------------
|
||||
return hidden_states, None
|
||||
|
||||
def profile_run(self) -> None:
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
|
||||
|
||||
from functools import partial
|
||||
from typing import Any
|
||||
|
||||
import numpy as np
|
||||
@@ -15,7 +16,7 @@ from vllm.v1.worker.gpu_model_runner import PerLayerAttnMetadata
|
||||
from vllm_ascend.ascend_forward_context import get_forward_context, set_ascend_forward_context
|
||||
from vllm_ascend.attention.attention_v1 import AscendAttentionState
|
||||
from vllm_ascend.attention.utils import using_paged_attention
|
||||
from vllm_ascend.compilation.acl_graph import ACLGraphWrapper, update_full_graph_params
|
||||
from vllm_ascend.compilation.acl_graph import ACLGraphWrapper
|
||||
from vllm_ascend.ops.rotary_embedding import update_cos_sin
|
||||
from vllm_ascend.utils import enable_sp, lmhead_tp_enable
|
||||
from vllm_ascend.worker.model_runner_v1 import SEQ_LEN_WITH_MAX_PA_WORKSPACE, NPUModelRunner
|
||||
@@ -42,11 +43,9 @@ class OmniNPUModelRunner(OmniGPUModelRunner, NPUModelRunner):
|
||||
self.has_talker_mtp = True
|
||||
cudagraph_mode = self.compilation_config.cudagraph_mode
|
||||
assert cudagraph_mode is not None
|
||||
# Only wrap talker_mtp in CUDAGraphWrapper for Omni models that
|
||||
# have a separate .talker sub-module. TTS models' code predictor
|
||||
# has internal AR loops / torch.multinomial — not graph-safe.
|
||||
has_separate_talker = getattr(self.model, "talker", None) is not None
|
||||
if cudagraph_mode.has_full_cudagraphs() and has_separate_talker:
|
||||
talker_mtp_graph_safe = getattr(self.model, "talker_mtp_graph_safe", False)
|
||||
if cudagraph_mode.has_full_cudagraphs() and (has_separate_talker or talker_mtp_graph_safe):
|
||||
self.talker_mtp = ACLGraphWrapper(talker_mtp, self.vllm_config, runtime_mode=CUDAGraphMode.FULL)
|
||||
# TTS exposes mtp_hidden_size; Omni uses hf_text_config.hidden_size.
|
||||
hidden_size = int(
|
||||
@@ -76,6 +75,7 @@ class OmniNPUModelRunner(OmniGPUModelRunner, NPUModelRunner):
|
||||
is_graph_capturing: bool = False,
|
||||
num_active_loras: int = 0,
|
||||
profile_seq_lens: int | None = None,
|
||||
profile_cpp: bool = False,
|
||||
) -> tuple[torch.Tensor, torch.Tensor]:
|
||||
# only support eager mode and piecewise graph now
|
||||
assert cudagraph_runtime_mode is None or cudagraph_runtime_mode.valid_runtime_modes()
|
||||
@@ -101,11 +101,13 @@ class OmniNPUModelRunner(OmniGPUModelRunner, NPUModelRunner):
|
||||
if create_mixed_batch:
|
||||
raise NotImplementedError("create_mixed_batch is used for warmup deepgemm, vllm-ascend does not need it")
|
||||
elif uniform_decode:
|
||||
assert not create_mixed_batch
|
||||
num_reqs = min(max_num_reqs, cdiv(num_tokens, max_query_len))
|
||||
num_scheduled_tokens_list = [max_query_len] * num_reqs
|
||||
if num_tokens % max_query_len != 0:
|
||||
num_scheduled_tokens_list[-1] = num_tokens % max_query_len
|
||||
elif profile_cpp:
|
||||
num_reqs = 1
|
||||
num_scheduled_tokens_list = [num_tokens] * num_reqs
|
||||
else:
|
||||
num_reqs = min(num_tokens, max_num_reqs)
|
||||
min_tokens_per_req = num_tokens // num_reqs
|
||||
@@ -128,7 +130,7 @@ class OmniNPUModelRunner(OmniGPUModelRunner, NPUModelRunner):
|
||||
max_num_scheduled_tokens=max_query_len,
|
||||
use_cascade_attn=False,
|
||||
allow_microbatching=allow_microbatching,
|
||||
force_eager=is_profile or (cudagraph_runtime_mode == CUDAGraphMode.NONE),
|
||||
force_eager=is_profile or (cudagraph_runtime_mode == CUDAGraphMode.NONE) or profile_cpp,
|
||||
# `force_uniform_decode` is used for cudagraph capture; because for
|
||||
# capturing mixed prefill-decode batches, we sometimes use
|
||||
# num_tokens == num_reqs which looks like a uniform decode batch to the
|
||||
@@ -190,16 +192,21 @@ class OmniNPUModelRunner(OmniGPUModelRunner, NPUModelRunner):
|
||||
if is_graph_capturing and using_paged_attention(num_tokens, self.vllm_config)
|
||||
else max_query_len
|
||||
) # type: ignore[assignment]
|
||||
self.seq_lens.np[:num_reqs_padded] = seq_lens
|
||||
self.seq_lens.np[num_reqs_padded:] = 0
|
||||
self.seq_lens.copy_to_gpu()
|
||||
self.optimistic_seq_lens_cpu[:num_reqs] = seq_lens
|
||||
self.optimistic_seq_lens_cpu[num_reqs:].fill_(0)
|
||||
self.seq_lens.copy_(self.optimistic_seq_lens_cpu, non_blocking=True)
|
||||
|
||||
cum_num_tokens, _ = self._get_cumsum_and_arange(num_scheduled_tokens)
|
||||
cum_num_tokens = self._get_cumsum_and_arange(num_scheduled_tokens, self.query_pos.np)
|
||||
self.query_start_loc.np[1 : num_reqs_padded + 1] = cum_num_tokens
|
||||
self.query_start_loc.copy_to_gpu()
|
||||
num_reqs_padded = self._pad_query_start_loc_for_fia(
|
||||
num_tokens_padded, num_reqs_padded, num_reqs, cudagraph_runtime_mode, batch_desc.num_reqs
|
||||
)
|
||||
if self._has_gdn:
|
||||
self.gdn_query_start_loc.np[1 : num_reqs_padded + 1] = cum_num_tokens
|
||||
self.gdn_query_start_loc.copy_to_gpu()
|
||||
|
||||
if not profile_cpp:
|
||||
num_reqs_padded = self._pad_query_start_loc_for_fia(
|
||||
num_tokens_padded, num_reqs_padded, num_reqs, cudagraph_runtime_mode, batch_desc.num_reqs
|
||||
)
|
||||
|
||||
pad_attn = cudagraph_runtime_mode == CUDAGraphMode.FULL
|
||||
attn_metadata, _ = self._build_attention_metadata(
|
||||
@@ -224,7 +231,7 @@ class OmniNPUModelRunner(OmniGPUModelRunner, NPUModelRunner):
|
||||
):
|
||||
# Make sure padding doesn't exceed max_num_tokens
|
||||
assert num_tokens_padded <= self.max_num_tokens
|
||||
if self.is_multimodal_model and not self.model_config.is_encoder_decoder or self.enable_prompt_embeds:
|
||||
if self.supports_mm_inputs and not self.model_config.is_encoder_decoder or self.enable_prompt_embeds:
|
||||
input_ids = None
|
||||
inputs_embeds = self.inputs_embeds.gpu[:num_tokens_padded]
|
||||
else:
|
||||
@@ -236,7 +243,7 @@ class OmniNPUModelRunner(OmniGPUModelRunner, NPUModelRunner):
|
||||
elif self.uses_xdrope_dim > 0:
|
||||
positions = self.xdrope_positions.gpu[:, :num_tokens_padded]
|
||||
else:
|
||||
positions = self.positions.gpu[:num_tokens_padded]
|
||||
positions = self.positions[:num_tokens_padded]
|
||||
|
||||
# update global cos, sin
|
||||
update_cos_sin(positions)
|
||||
@@ -300,7 +307,6 @@ class OmniNPUModelRunner(OmniGPUModelRunner, NPUModelRunner):
|
||||
self.text_step.gpu[:num_tokens_padded_talker_mtp],
|
||||
)
|
||||
self.compilation_config.cache_dir = None
|
||||
# ---------------------------------------Omni-new----------------------------------------------
|
||||
# Call self.model() directly (like GPU) to avoid make_omni_output during dummy_run
|
||||
outputs = self.model(
|
||||
input_ids=input_ids,
|
||||
@@ -308,6 +314,7 @@ class OmniNPUModelRunner(OmniGPUModelRunner, NPUModelRunner):
|
||||
intermediate_tensors=intermediate_tensors,
|
||||
inputs_embeds=inputs_embeds,
|
||||
)
|
||||
# ---------------------------------------Omni-new----------------------------------------------
|
||||
if self.use_aux_hidden_state_outputs:
|
||||
hidden_states, _ = outputs
|
||||
else:
|
||||
@@ -330,7 +337,8 @@ class OmniNPUModelRunner(OmniGPUModelRunner, NPUModelRunner):
|
||||
is_profile=is_profile,
|
||||
)
|
||||
if is_profile and self.dynamic_eplb:
|
||||
self.model.clear_all_moe_loads()
|
||||
target = self.model.language_model if hasattr(self.model, "language_model") else self.model
|
||||
target.clear_all_moe_loads()
|
||||
if self.dynamic_eplb:
|
||||
self.eplb_updator.forward_end()
|
||||
return hidden_states, hidden_states
|
||||
@@ -354,17 +362,28 @@ class OmniNPUModelRunner(OmniGPUModelRunner, NPUModelRunner):
|
||||
"""
|
||||
# Omni-specific: build and inject extra model kwargs
|
||||
model_kwargs_extra = self._build_model_kwargs_extra()
|
||||
|
||||
# Call the model forward (same as NPUModelRunner)
|
||||
assert self.model is not None
|
||||
model_output = self.model(
|
||||
input_ids=input_ids,
|
||||
positions=positions,
|
||||
intermediate_tensors=intermediate_tensors,
|
||||
inputs_embeds=inputs_embeds,
|
||||
|
||||
forward_context = get_forward_context()
|
||||
assert forward_context is not None
|
||||
|
||||
model_inputs: dict[str, Any] = {
|
||||
"input_ids": input_ids,
|
||||
"positions": positions,
|
||||
"intermediate_tensors": intermediate_tensors,
|
||||
"inputs_embeds": inputs_embeds,
|
||||
**model_kwargs,
|
||||
**model_kwargs_extra,
|
||||
)
|
||||
}
|
||||
run_model = partial(self.model, **model_inputs)
|
||||
|
||||
if self.enable_enpu:
|
||||
# The soft segmentation scenario requires event.record first, then event.wait.
|
||||
self._update_full_graph_params_if_needed(forward_context, num_tokens_padded, positions)
|
||||
model_output = run_model()
|
||||
else:
|
||||
model_output = run_model()
|
||||
self._update_full_graph_params_if_needed(forward_context, num_tokens_padded, positions)
|
||||
|
||||
# Omni-specific: wrap output if needed
|
||||
if not isinstance(model_output, OmniOutput) and hasattr(self.model, "make_omni_output"):
|
||||
@@ -373,27 +392,8 @@ class OmniNPUModelRunner(OmniGPUModelRunner, NPUModelRunner):
|
||||
# Omni-specific: cache model output for later sample_tokens
|
||||
self._omni_last_model_output = model_output
|
||||
|
||||
# NPU-specific: update full graph params
|
||||
forward_context = get_forward_context()
|
||||
assert forward_context is not None
|
||||
if (
|
||||
forward_context.cudagraph_runtime_mode == CUDAGraphMode.FULL
|
||||
and not forward_context.capturing
|
||||
and not self.use_sparse
|
||||
):
|
||||
assert positions is not None
|
||||
update_full_graph_params(
|
||||
self.attn_backend,
|
||||
self.update_stream,
|
||||
forward_context,
|
||||
num_tokens_padded,
|
||||
self.vllm_config,
|
||||
self.speculative_config,
|
||||
positions.shape[0],
|
||||
)
|
||||
|
||||
# NPU-specific: all-gather for sequence parallelism
|
||||
if get_forward_context().flash_comm_v1_enabled and not isinstance(model_output, IntermediateTensors):
|
||||
if forward_context.flash_comm_v1_enabled and not isinstance(model_output, IntermediateTensors):
|
||||
model_output = self._all_gather_hidden_states_and_aux(model_output)
|
||||
|
||||
return model_output
|
||||
|
||||
Reference in new issue
Block a user