Compare commits

..
Author SHA1 Message Date
Tyler Michael Smith fcec8c8827 add debug cruft
Signed-off-by: Tyler Michael Smith <tysmith@redhat.com>
2025-06-20 20:37:37 +00:00
Tyler Michael Smith 850dafea92 update
Signed-off-by: Tyler Michael Smith <tysmith@redhat.com>
2025-06-20 19:57:07 +00:00
Tyler Michael Smith b4f17e12a4 tolerances
Signed-off-by: Tyler Michael Smith <tysmith@redhat.com>
2025-06-20 19:47:25 +00:00
Tyler Michael Smith 21ffc7353a fixup
Signed-off-by: Tyler Michael Smith <tysmith@redhat.com>
2025-06-20 15:56:05 +00:00
Tyler Michael Smith 39d5d33f8f tweaks
Signed-off-by: Tyler Michael Smith <tysmith@redhat.com>
2025-06-20 15:36:59 +00:00
Tyler Michael Smith 7a821f0e7f precommit
Signed-off-by: Tyler Michael Smith <tysmith@redhat.com>
2025-06-20 14:41:20 +00:00
Tyler Michael Smith 26fd8ca33c fixes
Signed-off-by: Tyler Michael Smith <tysmith@redhat.com>
2025-06-20 14:40:21 +00:00
Tyler Michael Smith d5f206767c Unit test
Signed-off-by: Tyler Michael Smith <tysmith@redhat.com>
2025-06-20 14:39:58 +00:00
Varun Sundar RabindranathandTyler Michael Smith 2b5ad9f233 fixes - use-fp8-dispatch
Signed-off-by: Varun Sundar Rabindranath <vsundarr@redhat.com>
2025-06-18 21:46:22 +00:00
Tyler Michael Smith 299f829180 DeepGEMM LL optimizations
- Quantized dispatch
- Fused act-and-mul-and-quant in the right layout for DeepGEMM

Signed-off-by: Tyler Michael Smith <tysmith@redhat.com>
2025-06-18 20:09:51 +00:00
Tyler Michael Smith 104a984e6a Merge remote-tracking branch 'nm/varun/deepep-fp8-dispatch' into ll_deepgemm_opt 2025-06-18 19:35:02 +00:00
Varun Sundar Rabindranath 8de2fd39fc deep_ep + use_fp8_dispatch
Signed-off-by: Varun Sundar Rabindranath <vsundarr@redhat.com>
2025-06-18 07:32:15 -07:00
63 changed files with 520 additions and 837 deletions
+1 -1
View File
@@ -156,7 +156,7 @@ If you use vLLM for your research, please cite our [paper](https://arxiv.org/abs
- For technical questions and feature requests, please use GitHub [Issues](https://github.com/vllm-project/vllm/issues) or [Discussions](https://github.com/vllm-project/vllm/discussions) - For technical questions and feature requests, please use GitHub [Issues](https://github.com/vllm-project/vllm/issues) or [Discussions](https://github.com/vllm-project/vllm/discussions)
- For discussing with fellow users, please use the [vLLM Forum](https://discuss.vllm.ai) - For discussing with fellow users, please use the [vLLM Forum](https://discuss.vllm.ai)
- For coordinating contributions and development, please use [Slack](https://slack.vllm.ai) - coordinating contributions and development, please use [Slack](https://slack.vllm.ai)
- For security disclosures, please use GitHub's [Security Advisories](https://github.com/vllm-project/vllm/security/advisories) feature - For security disclosures, please use GitHub's [Security Advisories](https://github.com/vllm-project/vllm/security/advisories) feature
- For collaborations and partnerships, please contact us at [vllm-questions@lists.berkeley.edu](mailto:vllm-questions@lists.berkeley.edu) - For collaborations and partnerships, please contact us at [vllm-questions@lists.berkeley.edu](mailto:vllm-questions@lists.berkeley.edu)
@@ -4,12 +4,12 @@ import argparse
import itertools import itertools
import torch import torch
import triton
from vllm import _custom_ops as ops from vllm import _custom_ops as ops
from vllm.model_executor.layers.fused_moe.moe_align_block_size import ( from vllm.model_executor.layers.fused_moe.moe_align_block_size import (
moe_align_block_size_triton, moe_align_block_size_triton,
) )
from vllm.triton_utils import triton
def get_topk_ids(num_tokens: int, num_experts: int, topk: int) -> torch.Tensor: def get_topk_ids(num_tokens: int, num_experts: int, topk: int) -> torch.Tensor:
-1
View File
@@ -122,7 +122,6 @@ function (get_torch_gpu_compiler_flags OUT_GPU_FLAGS GPU_LANG)
"-DENABLE_FP8" "-DENABLE_FP8"
"-U__HIP_NO_HALF_CONVERSIONS__" "-U__HIP_NO_HALF_CONVERSIONS__"
"-U__HIP_NO_HALF_OPERATORS__" "-U__HIP_NO_HALF_OPERATORS__"
"-Werror=unused-variable"
"-fno-gpu-rdc") "-fno-gpu-rdc")
endif() endif()
@@ -34,7 +34,6 @@ you may contact the following individuals:
- Simon Mo - simon.mo@hey.com - Simon Mo - simon.mo@hey.com
- Russell Bryant - rbryant@redhat.com - Russell Bryant - rbryant@redhat.com
- Huzaifa Sidhpurwala - huzaifas@redhat.com
## Slack Discussion ## Slack Discussion
+5 -5
View File
@@ -18,9 +18,9 @@ setuptools==78.1.0
--find-links https://storage.googleapis.com/libtpu-releases/index.html --find-links https://storage.googleapis.com/libtpu-releases/index.html
--find-links https://storage.googleapis.com/jax-releases/jax_nightly_releases.html --find-links https://storage.googleapis.com/jax-releases/jax_nightly_releases.html
--find-links https://storage.googleapis.com/jax-releases/jaxlib_nightly_releases.html --find-links https://storage.googleapis.com/jax-releases/jaxlib_nightly_releases.html
torch==2.8.0.dev20250618 torch==2.8.0.dev20250617
torchvision==0.23.0.dev20250618 torchvision==0.23.0.dev20250617
torch_xla[tpu, pallas] @ https://storage.googleapis.com/pytorch-xla-releases/wheels/tpuvm/torch_xla-2.8.0.dev20250618-cp39-cp39-linux_x86_64.whl ; python_version == "3.9" torch_xla[tpu, pallas] @ https://storage.googleapis.com/pytorch-xla-releases/wheels/tpuvm/torch_xla-2.8.0.dev20250617-cp39-cp39-linux_x86_64.whl ; python_version == "3.9"
torch_xla[tpu, pallas] @ https://storage.googleapis.com/pytorch-xla-releases/wheels/tpuvm/torch_xla-2.8.0.dev20250618-cp310-cp310-linux_x86_64.whl ; python_version == "3.10" torch_xla[tpu, pallas] @ https://storage.googleapis.com/pytorch-xla-releases/wheels/tpuvm/torch_xla-2.8.0.dev20250617-cp310-cp310-linux_x86_64.whl ; python_version == "3.10"
torch_xla[tpu, pallas] @ https://storage.googleapis.com/pytorch-xla-releases/wheels/tpuvm/torch_xla-2.8.0.dev20250618-cp311-cp311-linux_x86_64.whl ; python_version == "3.11" torch_xla[tpu, pallas] @ https://storage.googleapis.com/pytorch-xla-releases/wheels/tpuvm/torch_xla-2.8.0.dev20250617-cp311-cp311-linux_x86_64.whl ; python_version == "3.11"
+22 -19
View File
@@ -1,10 +1,14 @@
# SPDX-License-Identifier: Apache-2.0 # SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
import pytest import pytest
import torch
import vllm import vllm
from vllm.compilation.counter import compilation_counter from vllm.compilation.counter import compilation_counter
from vllm.config import VllmConfig from vllm.config import (CompilationConfig, CompilationLevel, VllmConfig,
set_current_vllm_config)
from .piecewise.test_simple import SillyModel
def test_use_cudagraphs_dynamic(monkeypatch): def test_use_cudagraphs_dynamic(monkeypatch):
@@ -18,24 +22,23 @@ def test_use_cudagraphs_dynamic(monkeypatch):
@pytest.mark.parametrize("enabled", [True, False]) @pytest.mark.parametrize("enabled", [True, False])
def test_use_cudagraphs(vllm_runner, monkeypatch, enabled): def test_use_cudagraphs(enabled):
assert vllm.envs.VLLM_USE_V1 assert vllm.envs.VLLM_USE_V1
vllm_config = VllmConfig(compilation_config=CompilationConfig(
level=CompilationLevel.PIECEWISE,
use_cudagraph=enabled,
cudagraph_capture_sizes=[100],
))
with set_current_vllm_config(vllm_config):
model = SillyModel(vllm_config=vllm_config, prefix='')
# Disable multiprocessing so that the counter is in the same process inputs = torch.randn(100, device="cuda")
monkeypatch.setenv('VLLM_ENABLE_V1_MULTIPROCESSING', '0')
compilation_config = { with compilation_counter.expect(
"cudagraph_capture_sizes": [100], num_graphs_seen=1, # one graph for the model
"use_cudagraph": enabled, num_cudagraph_captured=1 if enabled else 0,
} ):
with ( # first run is warmup
compilation_counter.expect( model(inputs)
num_graphs_seen=1, # second run does CUDAGraphs recording (if enabled)
num_gpu_runner_capture_triggers=1 if enabled else 0, model(inputs)
num_cudagraph_captured=13 if enabled else 0,
),
# loading the model causes compilation (if enabled) to happen
vllm_runner('facebook/opt-125m',
compilation_config=compilation_config,
gpu_memory_utilization=0.4) as _):
pass
@@ -0,0 +1,83 @@
# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
import pytest
import torch
from vllm.model_executor.layers.fused_moe.batched_deep_gemm_moe import (
silu_mul_fp8_quant_deep_gemm)
from vllm.platforms import current_platform
# (E, T, H, group_size, seed)
CASES = [
(1, 1, 128, 64, 0),
(1, 4, 128, 128, 0),
(2, 4, 256, 128, 0),
(32, 64, 256, 128, 0),
(17, 31, 768, 128, 0),
]
@pytest.mark.parametrize("E,T,H,group_size,seed", CASES)
@torch.inference_mode()
def test_silu_mul_fp8_quant_deep_gemm(E, T, H, group_size, seed):
current_platform.seed_everything(seed)
# Input tensor of shape (E, T, 2*H)
y = torch.randn((E, T, 2 * H), dtype=torch.float32, device="cuda")
tokens_per_expert = torch.randint(
low=0,
high=T,
size=(E, ),
dtype=torch.int32,
device="cuda",
)
# Run the Triton kernel
y_q, y_s = silu_mul_fp8_quant_deep_gemm(y,
tokens_per_expert,
group_size=group_size,
eps=1e-10)
# Reference implementation
fp8_info = torch.finfo(torch.float8_e4m3fn)
fp8_max = fp8_info.max
fp8_min = fp8_info.min
eps = 1e-10
# Compute silu activation and elementwise multiplication
y1 = y[..., :H]
y2 = y[..., H:]
silu_x = y1 * torch.sigmoid(y1)
merged = silu_x * y2
# Compute reference scales and quantized output, skipping padded tokens
for e in range(E):
nt = tokens_per_expert[e].item()
ref_s = torch.empty((T, H // group_size),
dtype=torch.float32,
device="cuda")
ref_q = torch.empty((T, H), dtype=torch.float8_e4m3fn, device="cuda")
for t in range(nt):
data = merged[e, t]
data_grp = data.view(H // group_size, group_size)
amax = data_grp.abs().amax(dim=1).clamp(min=eps)
scale = amax / fp8_max
scaled = data / scale.repeat_interleave(group_size)
clamped = scaled.clamp(fp8_min, fp8_max)
q = clamped.to(torch.float8_e4m3fn)
ref_s[t] = scale
ref_q[t] = q
y_se = y_s[e]
y_qe = y_q[e]
torch.testing.assert_close(y_se[:nt], ref_s[:nt])
torch.testing.assert_close(
y_qe[:nt].to(torch.float32),
ref_q[:nt].to(torch.float32),
atol=2,
rtol=2e-1,
)
+12 -43
View File
@@ -17,10 +17,9 @@ SSM_MODELS = [
"state-spaces/mamba-130m-hf", "state-spaces/mamba-130m-hf",
"tiiuae/falcon-mamba-tiny-dev", "tiiuae/falcon-mamba-tiny-dev",
# TODO: Compare to a Mamba2 model. The HF transformers implementation of # TODO: Compare to a Mamba2 model. The HF transformers implementation of
# Mamba2 is buggy for Codestral as it doesn't handle n_groups, so the test # Mamba2 is buggy for Codestral as it doesn't handle n_groups.
# doesn't compare vLLM output with HF output.
# See https://github.com/huggingface/transformers/pull/35943 # See https://github.com/huggingface/transformers/pull/35943
"mistralai/Mamba-Codestral-7B-v0.1", # "mistralai/Mamba-Codestral-7B-v0.1",
] ]
HYBRID_MODELS = [ HYBRID_MODELS = [
@@ -36,10 +35,6 @@ HYBRID_MODELS = [
"hmellor/tiny-random-BambaForCausalLM", "hmellor/tiny-random-BambaForCausalLM",
] ]
V1_SUPPORTED_MODELS = [
"mistralai/Mamba-Codestral-7B-v0.1",
]
# Avoid OOM # Avoid OOM
MAX_NUM_SEQS = 4 MAX_NUM_SEQS = 4
@@ -51,50 +46,24 @@ def test_models(
hf_runner, hf_runner,
vllm_runner, vllm_runner,
example_prompts, example_prompts,
monkeypatch,
model: str, model: str,
max_tokens: int, max_tokens: int,
num_logprobs: int, num_logprobs: int,
) -> None: ) -> None:
with hf_runner(model) as hf_model: with hf_runner(model) as hf_model:
if model != "mistralai/Mamba-Codestral-7B-v0.1": hf_outputs = hf_model.generate_greedy_logprobs_limit(
hf_outputs = hf_model.generate_greedy_logprobs_limit(
example_prompts, max_tokens, num_logprobs)
else:
hf_outputs = None
with vllm_runner(model, max_num_seqs=MAX_NUM_SEQS) as vllm_model:
vllm_v0_outputs = vllm_model.generate_greedy_logprobs(
example_prompts, max_tokens, num_logprobs) example_prompts, max_tokens, num_logprobs)
if model in V1_SUPPORTED_MODELS: with vllm_runner(model, max_num_seqs=MAX_NUM_SEQS) as vllm_model:
with monkeypatch.context() as m: vllm_outputs = vllm_model.generate_greedy_logprobs(
m.setenv("VLLM_USE_V1", "1") example_prompts, max_tokens, num_logprobs)
with vllm_runner(model,
max_num_seqs=MAX_NUM_SEQS,
enforce_eager=True,
enable_prefix_caching=False) as vllm_model:
vllm_v1_outputs = vllm_model.generate_greedy_logprobs(
example_prompts, max_tokens, num_logprobs)
else:
vllm_v1_outputs = None
if hf_outputs is not None: check_logprobs_close(
check_logprobs_close( outputs_0_lst=hf_outputs,
outputs_0_lst=hf_outputs, outputs_1_lst=vllm_outputs,
outputs_1_lst=vllm_v0_outputs, name_0="hf",
name_0="hf", name_1="vllm",
name_1="vllm-v0", )
)
if model in V1_SUPPORTED_MODELS:
ref_outputs = hf_outputs if hf_outputs is not None else vllm_v0_outputs
check_logprobs_close(
outputs_0_lst=ref_outputs,
outputs_1_lst=vllm_v1_outputs,
name_0="hf" if hf_outputs is not None else "vllm-v0",
name_1="vllm-v1",
)
@pytest.mark.parametrize("model", SSM_MODELS + HYBRID_MODELS) @pytest.mark.parametrize("model", SSM_MODELS + HYBRID_MODELS)
-12
View File
@@ -60,15 +60,3 @@ def test_hash_collision_array_shape():
hasher = MultiModalHasher hasher = MultiModalHasher
assert hasher.hash_kwargs(data=arr1) != hasher.hash_kwargs(data=arr2) assert hasher.hash_kwargs(data=arr1) != hasher.hash_kwargs(data=arr2)
def test_hash_non_contiguous_array():
arr = np.arange(24).reshape(4, 6).T
assert not arr.flags.c_contiguous
arr_c = np.ascontiguousarray(arr)
assert arr_c.flags.c_contiguous
hasher = MultiModalHasher
# Both should be hashable and produce the same hashes
assert hasher.hash_kwargs(data=arr) == hasher.hash_kwargs(data=arr_c)
+1 -1
View File
@@ -12,7 +12,7 @@ from vllm.engine.async_llm_engine import AsyncLLMEngine
UNSUPPORTED_MODELS_V1 = [ UNSUPPORTED_MODELS_V1 = [
"openai/whisper-large-v3", # transcription "openai/whisper-large-v3", # transcription
"facebook/bart-large-cnn", # encoder decoder "facebook/bart-large-cnn", # encoder decoder
"state-spaces/mamba-130m-hf", # mamba1 "mistralai/Mamba-Codestral-7B-v0.1", # mamba
"hmellor/tiny-random-BambaForCausalLM", # hybrid "hmellor/tiny-random-BambaForCausalLM", # hybrid
"BAAI/bge-m3", # embedding "BAAI/bge-m3", # embedding
] ]
-10
View File
@@ -14,12 +14,6 @@ ALLOWED_LINES = {
"from vllm.triton_utils import tl, triton", "from vllm.triton_utils import tl, triton",
} }
ALLOWED_FILES = {"vllm/triton_utils/importing.py"}
def is_allowed_file(current_file: str) -> bool:
return current_file in ALLOWED_FILES
def is_forbidden_import(line: str) -> bool: def is_forbidden_import(line: str) -> bool:
stripped = line.strip() stripped = line.strip()
@@ -31,14 +25,10 @@ def parse_diff(diff: str) -> list[str]:
violations = [] violations = []
current_file = None current_file = None
current_lineno = None current_lineno = None
skip_allowed_file = False
for line in diff.splitlines(): for line in diff.splitlines():
if line.startswith("+++ b/"): if line.startswith("+++ b/"):
current_file = line[6:] current_file = line[6:]
skip_allowed_file = is_allowed_file(current_file)
elif skip_allowed_file:
continue
elif line.startswith("@@"): elif line.startswith("@@"):
match = re.search(r"\+(\d+)", line) match = re.search(r"\+(\d+)", line)
if match: if match:
-3
View File
@@ -15,9 +15,6 @@ class CompilationCounter:
# not including the splitting ops # not including the splitting ops
num_piecewise_capturable_graphs_seen: int = 0 num_piecewise_capturable_graphs_seen: int = 0
num_backend_compilations: int = 0 num_backend_compilations: int = 0
# Number of gpu_model_runner attempts to trigger CUDAGraphs capture
num_gpu_runner_capture_triggers: int = 0
# Number of CUDAGraphs captured
num_cudagraph_captured: int = 0 num_cudagraph_captured: int = 0
# InductorAdapter.compile calls # InductorAdapter.compile calls
num_inductor_compiles: int = 0 num_inductor_compiles: int = 0
@@ -138,9 +138,29 @@ class DeepEPAll2AllManagerBase(All2AllManagerBase):
super().__init__(cpu_group) super().__init__(cpu_group)
self.handle_cache = Cache() self.handle_cache = Cache()
# This is the DeepEP default. Stick to it till we can establish # Use all SMs for all2all communication
# reasonable defaults based on profiling. # This will need to be adjusted for dual-batch overlap
self.num_sms = 20 device = self.dp_group.device
props = torch.cuda.get_device_properties(device)
self.num_sms = props.multi_processor_count
print(f"Setting num sms to {self.num_sms}")
print(f"Setting num sms to {self.num_sms}")
print(f"Setting num sms to {self.num_sms}")
print(f"Setting num sms to {self.num_sms}")
print(f"Setting num sms to {self.num_sms}")
print(f"Setting num sms to {self.num_sms}")
print(f"Setting num sms to {self.num_sms}")
print(f"Setting num sms to {self.num_sms}")
print(f"Setting num sms to {self.num_sms}")
print(f"Setting num sms to {self.num_sms}")
print(f"Setting num sms to {self.num_sms}")
print(f"Setting num sms to {self.num_sms}")
print(f"Setting num sms to {self.num_sms}")
print(f"Setting num sms to {self.num_sms}")
print(f"Setting num sms to {self.num_sms}")
print(f"Setting num sms to {self.num_sms}")
print(f"Setting num sms to {self.num_sms}")
print(f"Setting num sms to {self.num_sms}")
def get_handle(self, kwargs): def get_handle(self, kwargs):
raise NotImplementedError raise NotImplementedError
+1 -6
View File
@@ -1355,17 +1355,12 @@ class EngineArgs:
recommend_to_remove=False) recommend_to_remove=False)
return False return False
# No Encoder-Decoder, not all Mamba so far. # No Mamba or Encoder-Decoder so far.
if not model_config.is_v1_compatible: if not model_config.is_v1_compatible:
_raise_or_fallback(feature_name=model_config.architectures, _raise_or_fallback(feature_name=model_config.architectures,
recommend_to_remove=False) recommend_to_remove=False)
return False return False
# V1 mamba models are unoptimized.
if model_config.has_inner_state and _warn_or_fallback(
feature_name="Mamba"):
return False
# No Concurrent Partial Prefills so far. # No Concurrent Partial Prefills so far.
if (self.max_num_partial_prefills if (self.max_num_partial_prefills
!= SchedulerConfig.max_num_partial_prefills != SchedulerConfig.max_num_partial_prefills
+11 -41
View File
@@ -326,9 +326,8 @@ class ChatCompletionRequest(OpenAIBaseModel):
) )
chat_template_kwargs: Optional[dict[str, Any]] = Field( chat_template_kwargs: Optional[dict[str, Any]] = Field(
default=None, default=None,
description=( description=("Additional kwargs to pass to the template renderer. "
"Additional keyword args to pass to the template renderer. " "Will be accessible by the chat template."),
"Will be accessible by the chat template."),
) )
mm_processor_kwargs: Optional[dict[str, Any]] = Field( mm_processor_kwargs: Optional[dict[str, Any]] = Field(
default=None, default=None,
@@ -415,12 +414,6 @@ class ChatCompletionRequest(OpenAIBaseModel):
default=None, default=None,
description="KVTransfer parameters used for disaggregated serving.") description="KVTransfer parameters used for disaggregated serving.")
vllm_xargs: Optional[dict[str, Union[str, int, float]]] = Field(
default=None,
description=("Additional request parameters with string or "
"numeric values, used by custom extensions."),
)
# --8<-- [end:chat-completion-extra-params] # --8<-- [end:chat-completion-extra-params]
# Default sampling parameters for chat completion requests # Default sampling parameters for chat completion requests
@@ -530,10 +523,6 @@ class ChatCompletionRequest(OpenAIBaseModel):
structural_tag=self.structural_tag, structural_tag=self.structural_tag,
) )
extra_args: dict[str, Any] = self.vllm_xargs if self.vllm_xargs else {}
if self.kv_transfer_params:
# Pass in kv_transfer_params via extra_args
extra_args["kv_transfer_params"] = self.kv_transfer_params
return SamplingParams.from_optional( return SamplingParams.from_optional(
n=self.n, n=self.n,
best_of=self.best_of, best_of=self.best_of,
@@ -564,8 +553,8 @@ class ChatCompletionRequest(OpenAIBaseModel):
logit_bias=self.logit_bias, logit_bias=self.logit_bias,
bad_words= self.bad_words, bad_words= self.bad_words,
allowed_token_ids=self.allowed_token_ids, allowed_token_ids=self.allowed_token_ids,
extra_args=extra_args or None, extra_args=({"kv_transfer_params": self.kv_transfer_params}
) if self.kv_transfer_params else None))
def _get_guided_json_from_tool( def _get_guided_json_from_tool(
self) -> Optional[Union[str, dict, BaseModel]]: self) -> Optional[Union[str, dict, BaseModel]]:
@@ -882,12 +871,6 @@ class CompletionRequest(OpenAIBaseModel):
default=None, default=None,
description="KVTransfer parameters used for disaggregated serving.") description="KVTransfer parameters used for disaggregated serving.")
vllm_xargs: Optional[dict[str, Union[str, int, float]]] = Field(
default=None,
description=("Additional request parameters with string or "
"numeric values, used by custom extensions."),
)
# --8<-- [end:completion-extra-params] # --8<-- [end:completion-extra-params]
# Default sampling parameters for completion requests # Default sampling parameters for completion requests
@@ -985,10 +968,6 @@ class CompletionRequest(OpenAIBaseModel):
whitespace_pattern=self.guided_whitespace_pattern, whitespace_pattern=self.guided_whitespace_pattern,
) )
extra_args: dict[str, Any] = self.vllm_xargs if self.vllm_xargs else {}
if self.kv_transfer_params:
# Pass in kv_transfer_params via extra_args
extra_args["kv_transfer_params"] = self.kv_transfer_params
return SamplingParams.from_optional( return SamplingParams.from_optional(
n=self.n, n=self.n,
best_of=self.best_of, best_of=self.best_of,
@@ -1018,8 +997,8 @@ class CompletionRequest(OpenAIBaseModel):
guided_decoding=guided_decoding, guided_decoding=guided_decoding,
logit_bias=self.logit_bias, logit_bias=self.logit_bias,
allowed_token_ids=self.allowed_token_ids, allowed_token_ids=self.allowed_token_ids,
extra_args=extra_args or None, extra_args=({"kv_transfer_params": self.kv_transfer_params}
) if self.kv_transfer_params else None))
@model_validator(mode="before") @model_validator(mode="before")
@classmethod @classmethod
@@ -1138,9 +1117,8 @@ class EmbeddingChatRequest(OpenAIBaseModel):
) )
chat_template_kwargs: Optional[dict[str, Any]] = Field( chat_template_kwargs: Optional[dict[str, Any]] = Field(
default=None, default=None,
description=( description=("Additional kwargs to pass to the template renderer. "
"Additional keyword args to pass to the template renderer. " "Will be accessible by the chat template."),
"Will be accessible by the chat template."),
) )
mm_processor_kwargs: Optional[dict[str, Any]] = Field( mm_processor_kwargs: Optional[dict[str, Any]] = Field(
default=None, default=None,
@@ -1645,9 +1623,8 @@ class TokenizeChatRequest(OpenAIBaseModel):
) )
chat_template_kwargs: Optional[dict[str, Any]] = Field( chat_template_kwargs: Optional[dict[str, Any]] = Field(
default=None, default=None,
description=( description=("Additional kwargs to pass to the template renderer. "
"Additional keyword args to pass to the template renderer. " "Will be accessible by the chat template."),
"Will be accessible by the chat template."),
) )
mm_processor_kwargs: Optional[dict[str, Any]] = Field( mm_processor_kwargs: Optional[dict[str, Any]] = Field(
default=None, default=None,
@@ -1759,12 +1736,6 @@ class TranscriptionRequest(OpenAIBaseModel):
# Flattened stream option to simplify form data. # Flattened stream option to simplify form data.
stream_include_usage: Optional[bool] = False stream_include_usage: Optional[bool] = False
stream_continuous_usage_stats: Optional[bool] = False stream_continuous_usage_stats: Optional[bool] = False
vllm_xargs: Optional[dict[str, Union[str, int, float]]] = Field(
default=None,
description=("Additional request parameters with string or "
"numeric values, used by custom extensions."),
)
# --8<-- [end:transcription-extra-params] # --8<-- [end:transcription-extra-params]
# --8<-- [start:transcription-sampling-params] # --8<-- [start:transcription-sampling-params]
@@ -1852,8 +1823,7 @@ class TranscriptionRequest(OpenAIBaseModel):
presence_penalty=self.presence_penalty, presence_penalty=self.presence_penalty,
output_kind=RequestOutputKind.DELTA output_kind=RequestOutputKind.DELTA
if self.stream \ if self.stream \
else RequestOutputKind.FINAL_ONLY, else RequestOutputKind.FINAL_ONLY)
extra_args=self.vllm_xargs)
@model_validator(mode="before") @model_validator(mode="before")
@classmethod @classmethod
@@ -6,14 +6,179 @@ import torch
import vllm.model_executor.layers.fused_moe.modular_kernel as mk import vllm.model_executor.layers.fused_moe.modular_kernel as mk
from vllm.logger import init_logger from vllm.logger import init_logger
from vllm.model_executor.layers.fused_moe.utils import ( from vllm.model_executor.layers.fused_moe.utils import _resize_cache
_resize_cache, per_token_group_quant_fp8) from vllm.triton_utils import tl, triton
logger = init_logger(__name__) logger = init_logger(__name__)
has_deep_gemm = importlib.util.find_spec("deep_gemm") is not None has_deep_gemm = importlib.util.find_spec("deep_gemm") is not None
@triton.jit
def _silu_mul_fp8_quant_deep_gemm(
# Pointers ------------------------------------------------------------
input_ptr, # 16-bit activations (E, T, 2*H)
y_q_ptr, # fp8 quantized activations (E, T, H)
y_s_ptr, # 16-bit scales (E, T, G)
counts_ptr, # int32 num tokens per expert (E)
# Sizes ---------------------------------------------------------------
H: tl.constexpr, # hidden dimension (per output)
GROUP_SIZE: tl.constexpr, # elements per group (usually 128)
# Strides for input (elements) ---------------------------------------
stride_i_e,
stride_i_t,
stride_i_h,
# Strides for y_q (elements) -----------------------------------------
stride_yq_e,
stride_yq_t,
stride_yq_h,
# Strides for y_s (elements) -----------------------------------------
stride_ys_e,
stride_ys_t,
stride_ys_g,
# Stride for counts (elements)
stride_counts_e,
# Numeric params ------------------------------------------------------
eps: tl.constexpr,
fp8_min: tl.constexpr,
fp8_max: tl.constexpr,
# Meta ---------------------------------------------------------------
BLOCK: tl.constexpr,
):
G = H // GROUP_SIZE
# map program id -> (e, g)
pid = tl.program_id(0)
e = pid // G
g = pid % G
e = e.to(tl.int64)
g = g.to(tl.int64)
# number of valid tokens for this expert
n_tokens = tl.load(counts_ptr + e * stride_counts_e).to(tl.int64)
cols = tl.arange(0, BLOCK)
cols = cols.to(tl.int64)
mask_h = cols < BLOCK
t = tl.zeros([], tl.int64)
while t < n_tokens:
base_i_offset = (e * stride_i_e + t * stride_i_t +
g * GROUP_SIZE * stride_i_h)
base_yq_offset = (e * stride_yq_e + t * stride_yq_t +
g * GROUP_SIZE * stride_yq_h)
base_ys_offset = e * stride_ys_e + t * stride_ys_t + g * stride_ys_g
mask = mask_h
x = tl.load(input_ptr + base_i_offset + cols * stride_i_h,
mask=mask,
other=0.0).to(tl.float32)
y2 = tl.load(input_ptr + base_i_offset + H * stride_i_h +
cols * stride_i_h,
mask=mask,
other=0.0).to(tl.float32)
x = x * (1.0 / (1.0 + tl.exp(-x)))
y = x * y2
_absmax = tl.maximum(tl.max(tl.abs(y)), eps)
y_s = _absmax / fp8_max
y_q = tl.clamp(y / y_s, fp8_min, fp8_max).to(y_q_ptr.dtype.element_ty)
tl.store(y_q_ptr + base_yq_offset + cols * stride_yq_h, y_q, mask=mask)
tl.store(y_s_ptr + base_ys_offset, y_s)
t += 1
def silu_mul_fp8_quant_deep_gemm(
y: torch.Tensor, # (E, T, 2*H) float32
tokens_per_expert: torch.Tensor, # (E,) number of valid tokens per expert
group_size: int = 128,
eps: float = 1e-10,
):
"""Quantize silu(y[..., :H]) * y[..., H:] to FP8 with group per-token scales
y has shape (E, T, 2*H). The first half of the last dimension is
silu-activated, multiplied by the second half, then quantized into FP8.
Returns `(y_q, y_s)` where
* `y_q` is the FP8 tensor of shape `(E, T, H)`, same layout as `y[..., :H]`.
* `y_s` has shape `(E, T, H // group_size)` and strides `(T*G, 1, T)`
"""
assert y.ndim == 3, "y must be (E, T, 2*H)"
E, T, H2 = y.shape
assert H2 % 2 == 0, "last dim of y must be even (2*H)"
H = H2 // 2
G = H // group_size
assert H % group_size == 0, "H must be divisible by group_size"
assert tokens_per_expert.ndim == 1 and tokens_per_expert.shape[0] == E, \
"tokens_per_expert must be shape (E,)"
tokens_per_expert = tokens_per_expert.to(device=y.device,
dtype=torch.int32)
# allocate outputs
fp8_dtype = torch.float8_e4m3fn
y_q = torch.empty((E, T, H), dtype=fp8_dtype, device=y.device)
# strides (elements)
stride_i_e, stride_i_t, stride_i_h = y.stride()
stride_yq_e, stride_yq_t, stride_yq_h = y_q.stride()
# desired scale strides (elements): (T*G, 1, T)
stride_ys_e = T * G
stride_ys_t = 1
stride_ys_g = T
y_s = torch.empty_strided((E, T, G),
(stride_ys_e, stride_ys_t, stride_ys_g),
dtype=torch.float32,
device=y.device)
stride_cnt_e = tokens_per_expert.stride()[0]
# static grid over experts and H-groups.
# A loop inside the kernel handles the token dim
grid = (E * G, )
f_info = torch.finfo(fp8_dtype)
fp8_max = f_info.max
fp8_min = f_info.min
_silu_mul_fp8_quant_deep_gemm[grid](
y,
y_q,
y_s,
tokens_per_expert,
H,
group_size,
stride_i_e,
stride_i_t,
stride_i_h,
stride_yq_e,
stride_yq_t,
stride_yq_h,
stride_ys_e,
stride_ys_t,
stride_ys_g,
stride_cnt_e,
eps,
fp8_min,
fp8_max,
BLOCK=group_size,
num_warps=4,
)
return y_q, y_s
class BatchedDeepGemmExperts(mk.FusedMoEPermuteExpertsUnpermute): class BatchedDeepGemmExperts(mk.FusedMoEPermuteExpertsUnpermute):
# The Deep Gemm kernels only support block size of 128 # The Deep Gemm kernels only support block size of 128
@@ -96,7 +261,6 @@ class BatchedDeepGemmExperts(mk.FusedMoEPermuteExpertsUnpermute):
hidden_states, w1, w2, topk_ids) hidden_states, w1, w2, topk_ids)
workspace1 = _resize_cache(workspace13, (E, max_num_tokens, N)) workspace1 = _resize_cache(workspace13, (E, max_num_tokens, N))
workspace2 = _resize_cache(workspace2, (E, max_num_tokens, N // 2))
# (from deepgemm docs) : A value hint (which is a value on CPU) # (from deepgemm docs) : A value hint (which is a value on CPU)
# for the M expectation of each batch, correctly setting this value # for the M expectation of each batch, correctly setting this value
@@ -109,19 +273,9 @@ class BatchedDeepGemmExperts(mk.FusedMoEPermuteExpertsUnpermute):
masked_m=expert_num_tokens, masked_m=expert_num_tokens,
expected_m=expected_m) expected_m=expected_m)
# TODO (varun) [Optimization]: Use a batched version of activation. assert expert_num_tokens is not None
# Similarly for the quant below. a2q, a2q_scale = silu_mul_fp8_quant_deep_gemm(workspace1,
self.activation(activation, workspace2, workspace1.view(-1, N)) expert_num_tokens)
w2_hidden_size = workspace2.size(-1)
workspace2 = workspace2.view(-1, w2_hidden_size)
a2q_scale: Optional[torch.Tensor] = None
a2q, a2q_scale = per_token_group_quant_fp8(workspace2,
self.block_shape[1],
column_major_scales=False)
a2q = a2q.view(E, max_num_tokens, -1)
a2q_scale = a2q_scale.view(E, max_num_tokens, -1)
dg.m_grouped_gemm_fp8_fp8_bf16_nt_masked((a2q, a2q_scale), dg.m_grouped_gemm_fp8_fp8_bf16_nt_masked((a2q, a2q_scale),
(w2, w2_scale), (w2, w2_scale),
+10 -2
View File
@@ -45,7 +45,8 @@ if current_platform.is_cuda_alike():
from .pplx_prepare_finalize import PplxPrepareAndFinalize from .pplx_prepare_finalize import PplxPrepareAndFinalize
if has_deepep: if has_deepep:
from .deepep_ht_prepare_finalize import DeepEPHTPrepareAndFinalize from .deepep_ht_prepare_finalize import DeepEPHTPrepareAndFinalize
from .deepep_ll_prepare_finalize import DeepEPLLPrepareAndFinalize from .deepep_ll_prepare_finalize import (DEEPEP_QUANT_BLOCK_SIZE,
DeepEPLLPrepareAndFinalize)
else: else:
fused_experts = None # type: ignore fused_experts = None # type: ignore
FusedMoEPermuteExpertsUnpermute = None # type: ignore FusedMoEPermuteExpertsUnpermute = None # type: ignore
@@ -377,6 +378,13 @@ class FusedMoEMethodBase(QuantizeMethodBase):
all2all_manager.world_size) all2all_manager.world_size)
handle = all2all_manager.get_handle(all_to_all_args) handle = all2all_manager.get_handle(all_to_all_args)
# Note : We may want to use FP8 dispatch even otherwise just to
# reduce datamovement
assert act_quant_block_size is not None
use_fp8_dispatch = (quant_dtype == current_platform.fp8_dtype()
and act_quant_block_size[1]
== DEEPEP_QUANT_BLOCK_SIZE)
# Note (varun): Whether to use FP8 dispatch or not needs some # Note (varun): Whether to use FP8 dispatch or not needs some
# profiling. Turning it off for now. # profiling. Turning it off for now.
prepare_finalize = DeepEPLLPrepareAndFinalize( prepare_finalize = DeepEPLLPrepareAndFinalize(
@@ -386,7 +394,7 @@ class FusedMoEMethodBase(QuantizeMethodBase):
max_tokens_per_rank=moe.max_num_tokens, max_tokens_per_rank=moe.max_num_tokens,
quant_dtype=quant_dtype, quant_dtype=quant_dtype,
block_shape=act_quant_block_size, block_shape=act_quant_block_size,
use_fp8_dispatch=False, use_fp8_dispatch=use_fp8_dispatch,
) )
self.topk_indices_dtype = None self.topk_indices_dtype = None
+60 -175
View File
@@ -6,9 +6,7 @@ from typing import Optional, Union
import torch import torch
from torch import nn from torch import nn
from vllm import envs
from vllm.attention.backends.abstract import AttentionMetadata from vllm.attention.backends.abstract import AttentionMetadata
from vllm.config import get_current_vllm_config
from vllm.distributed import (divide, get_tensor_model_parallel_rank, from vllm.distributed import (divide, get_tensor_model_parallel_rank,
get_tensor_model_parallel_world_size, get_tensor_model_parallel_world_size,
tensor_model_parallel_all_gather, tensor_model_parallel_all_gather,
@@ -29,7 +27,6 @@ from vllm.model_executor.model_loader.weight_utils import (
LoaderFunction, composed_weight_loader, sharded_weight_loader) LoaderFunction, composed_weight_loader, sharded_weight_loader)
from vllm.model_executor.models.mamba_cache import MambaCacheParams from vllm.model_executor.models.mamba_cache import MambaCacheParams
from vllm.model_executor.utils import set_weight_attrs from vllm.model_executor.utils import set_weight_attrs
from vllm.v1.attention.backends.mamba_attn import Mamba2AttentionMetadata
# Added by the IBM Team, 2024 # Added by the IBM Team, 2024
@@ -230,22 +227,20 @@ class MambaMixer2(CustomOp):
""" """
def __init__( def __init__(
self, self,
hidden_size: int, hidden_size: int,
ssm_state_size: int, ssm_state_size: int,
conv_kernel_size: int, conv_kernel_size: int,
intermediate_size: int, intermediate_size: int,
use_conv_bias: bool, use_conv_bias: bool,
use_bias: bool, use_bias: bool,
n_groups: int = 1, n_groups: int = 1,
num_heads: int = 128, num_heads: int = 128,
head_dim: int = 64, head_dim: int = 64,
rms_norm_eps: float = 1e-5, rms_norm_eps: float = 1e-5,
activation: str = "silu", activation: str = "silu",
use_rms_norm: bool = True, use_rms_norm: bool = True,
quant_config: Optional[QuantizationConfig] = None, quant_config: Optional[QuantizationConfig] = None,
prefix: str = "",
chunk_size: int = -1, # the chunk size used by v1
): ):
super().__init__() super().__init__()
@@ -278,7 +273,6 @@ class MambaMixer2(CustomOp):
), "Tensor parallel currently not supported for quantized models." ), "Tensor parallel currently not supported for quantized models."
self.ssm_state_size = ssm_state_size self.ssm_state_size = ssm_state_size
self.conv_kernel_size = conv_kernel_size
self.activation = activation self.activation = activation
self.intermediate_size = intermediate_size self.intermediate_size = intermediate_size
@@ -417,22 +411,6 @@ class MambaMixer2(CustomOp):
self.use_rms_norm, self.use_rms_norm,
eps=rms_norm_eps) eps=rms_norm_eps)
if envs.VLLM_USE_V1:
compilation_config = get_current_vllm_config().compilation_config
if prefix in compilation_config.static_forward_context:
raise ValueError(f"Duplicate layer name: {prefix}")
compilation_config.static_forward_context[prefix] = self
# The outer list is for v0 PP virtual engine. Though this code path
# only runs for v1, we have to do this to unify with the interface
# of Attention + v0 PP.
# The inner tuple is (conv_state, ssm_state)
self.kv_cache = [(torch.tensor([]), torch.tensor([]))]
assert chunk_size != -1, "chunk_size must be set for v1"
# NOTE: chunk_size may be -1 for models without v1 support
self.chunk_size = chunk_size
self.prefix = prefix
def forward_native( def forward_native(
self, self,
hidden_states: torch.Tensor, hidden_states: torch.Tensor,
@@ -448,37 +426,17 @@ class MambaMixer2(CustomOp):
mamba2_metadata: Mamba2Metadata, mamba2_metadata: Mamba2Metadata,
mup_vector: Optional[torch.Tensor] = None, mup_vector: Optional[torch.Tensor] = None,
): ):
forward_context = get_forward_context()
# mamba2_metadata contains metadata necessary for the mamba2 triton # mamba2_metadata contains metadata necessary for the mamba2 triton
# kernels to operate in continuous batching and in chunked prefill # kernels to operate in continuous batching and in chunked prefill
# modes; they are computed at top-level model forward since they # modes; they are computed at top-level model forward since they
# stay the same and reused for all mamba layers in the same iteration # stay the same and reused for all mamba layers in the same iteration
attn_metadata: AttentionMetadata = forward_context.attn_metadata attn_metadata: AttentionMetadata = get_forward_context().attn_metadata
if envs.VLLM_USE_V1:
if attn_metadata is not None: num_prefills = attn_metadata.num_prefills # request count
assert isinstance(attn_metadata, dict) num_decodes = attn_metadata.num_decode_tokens # token count (=request)
attn_metadata = attn_metadata[self.prefix] num_prefill_tokens = attn_metadata.num_prefill_tokens # token count
assert isinstance(attn_metadata, Mamba2AttentionMetadata) has_prefill = num_prefills > 0
self_kv_cache = self.kv_cache[forward_context.virtual_engine] has_decode = num_decodes > 0
conv_state = self_kv_cache[0]
ssm_state = self_kv_cache[1]
state_indices_tensor = attn_metadata.state_indices_tensor
has_initial_states_p = attn_metadata.has_initial_states
prep_initial_states = attn_metadata.prep_initial_states
chunk_size = attn_metadata.chunk_size
seq_idx_p = attn_metadata.seq_idx
chunk_indices_p = attn_metadata.chunk_indices
chunk_offsets_p = attn_metadata.chunk_offsets
else:
conv_state = mamba_cache_params.conv_state
ssm_state = mamba_cache_params.ssm_state
state_indices_tensor = mamba_cache_params.state_indices_tensor
has_initial_states_p = mamba2_metadata.has_initial_states
prep_initial_states = mamba2_metadata.prep_initial_states
chunk_size = mamba2_metadata.chunk_size
seq_idx_p = mamba2_metadata.seq_idx
chunk_indices_p = mamba2_metadata.chunk_indices
chunk_offsets_p = mamba2_metadata.chunk_offsets
groups_time_state_size = self.n_groups * self.ssm_state_size groups_time_state_size = self.n_groups * self.ssm_state_size
@@ -501,6 +459,27 @@ class MambaMixer2(CustomOp):
conv_weights = self.conv1d.weight.view(self.conv1d.weight.size(0), conv_weights = self.conv1d.weight.view(self.conv1d.weight.size(0),
self.conv1d.weight.size(2)) self.conv1d.weight.size(2))
# Separate prefill and decode by splitting varlen input
# Split along token dimension
hidden_states_B_C_p, hidden_states_B_C_d = torch.split(
hidden_states_B_C,
[num_prefill_tokens, num_decodes],
dim=0,
)
dt_p, dt_d = torch.split(
dt,
[num_prefill_tokens, num_decodes],
dim=0,
)
# Split along batch dimension
state_indices_tensor_p, state_indices_tensor_d = torch.split(
mamba_cache_params.state_indices_tensor,
[num_prefills, num_decodes],
dim=0,
)
query_start_loc_p = (attn_metadata.query_start_loc[:num_prefills + 1]
if has_prefill else None)
# - get hidden_states, B and C after depthwise convolution. # - get hidden_states, B and C after depthwise convolution.
split_hidden_states_B_C_fn = lambda hidden_states_B_C: torch.split( split_hidden_states_B_C_fn = lambda hidden_states_B_C: torch.split(
hidden_states_B_C, hidden_states_B_C,
@@ -512,80 +491,20 @@ class MambaMixer2(CustomOp):
dim=-1, dim=-1,
) )
if envs.VLLM_USE_V1 and attn_metadata is None:
# V1 profile run
hidden_states_B_C = (hidden_states_B_C.transpose(
0, 1).clone().transpose(0, 1)).contiguous()
hidden_states, _B, _C = split_hidden_states_B_C_fn(
hidden_states_B_C)
hidden_states = self.norm(hidden_states, gate)
out, _ = self.out_proj(hidden_states)
return out
num_prefills = attn_metadata.num_prefills # request count
num_decodes = attn_metadata.num_decode_tokens # token count (=request)
num_prefill_tokens = attn_metadata.num_prefill_tokens # token count
has_prefill = num_prefills > 0
has_decode = num_decodes > 0
# NOTE: V0 put prefill before decode, v1 puts decode before prefill
# Separate prefill and decode by splitting varlen input
# Split along token dimension
if envs.VLLM_USE_V1:
hidden_states_B_C_d, hidden_states_B_C_p = torch.split(
hidden_states_B_C,
[num_decodes, num_prefill_tokens],
dim=0,
)
dt_d, dt_p = torch.split(
dt,
[num_decodes, num_prefill_tokens],
dim=0,
)
# Split along batch dimension
state_indices_tensor_d, state_indices_tensor_p = torch.split(
state_indices_tensor,
[num_decodes, num_prefills],
dim=0,
)
query_start_loc_p = (
attn_metadata.query_start_loc[-num_prefills - 1:] -
num_decodes if has_prefill else None)
else:
hidden_states_B_C_p, hidden_states_B_C_d = torch.split(
hidden_states_B_C,
[num_prefill_tokens, num_decodes],
dim=0,
)
dt_p, dt_d = torch.split(
dt,
[num_prefill_tokens, num_decodes],
dim=0,
)
# Split along batch dimension
state_indices_tensor_p, state_indices_tensor_d = torch.split(
state_indices_tensor,
[num_prefills, num_decodes],
dim=0,
)
query_start_loc_p = (attn_metadata.query_start_loc[:num_prefills +
1]
if has_prefill else None)
ssd_output_list = [] ssd_output_list = []
# Process prefill requests # Process prefill requests
if has_prefill: if has_prefill:
# 2. Convolution sequence transformation # 2. Convolution sequence transformation
# - "cache_indices" updates the conv_state cache in positions # - "cache_indices" updates the conv_state cache in positions
# pointed to by "state_indices_tensor" # pointed to by "mamba_cache_params.state_indices_tensor"
hidden_states_B_C_p = causal_conv1d_fn( hidden_states_B_C_p = causal_conv1d_fn(
hidden_states_B_C_p.transpose(0, 1), hidden_states_B_C_p.transpose(0, 1),
conv_weights, conv_weights,
self.conv1d.bias, self.conv1d.bias,
activation=self.activation, activation=self.activation,
conv_states=conv_state, conv_states=mamba_cache_params.conv_state,
has_initial_state=has_initial_states_p, has_initial_state=mamba2_metadata.has_initial_states,
cache_indices=state_indices_tensor_p, cache_indices=state_indices_tensor_p,
query_start_loc=query_start_loc_p).transpose( query_start_loc=query_start_loc_p).transpose(
0, 1)[:num_prefill_tokens] 0, 1)[:num_prefill_tokens]
@@ -597,11 +516,12 @@ class MambaMixer2(CustomOp):
# 3. State Space Model sequence transformation # 3. State Space Model sequence transformation
initial_states = None initial_states = None
if (has_initial_states_p is not None and prep_initial_states): if (mamba2_metadata.has_initial_states is not None
and mamba2_metadata.prep_initial_states):
# making a copy of the states # making a copy of the states
initial_states = torch.where( initial_states = torch.where(
has_initial_states_p[:, None, None, None], mamba2_metadata.has_initial_states[:, None, None, None],
ssm_state[state_indices_tensor_p], 0) mamba_cache_params.ssm_state[state_indices_tensor_p], 0)
scan_output, varlen_state = mamba_chunk_scan_combined( scan_output, varlen_state = mamba_chunk_scan_combined(
hidden_states_p.view(1, num_prefill_tokens, hidden_states_p.view(1, num_prefill_tokens,
@@ -613,14 +533,14 @@ class MambaMixer2(CustomOp):
-1), -1),
C_p.view(1, num_prefill_tokens, self.n_groups // self.tp_size, C_p.view(1, num_prefill_tokens, self.n_groups // self.tp_size,
-1), -1),
chunk_size=chunk_size, chunk_size=mamba2_metadata.chunk_size,
D=self.D, D=self.D,
z=None, z=None,
dt_bias=self.dt_bias, dt_bias=self.dt_bias,
seq_idx=seq_idx_p, seq_idx=mamba2_metadata.seq_idx,
chunk_indices=chunk_indices_p, chunk_indices=mamba2_metadata.chunk_indices,
chunk_offsets=chunk_offsets_p, chunk_offsets=mamba2_metadata.chunk_offsets,
cu_seqlens=query_start_loc_p, cu_seqlens=attn_metadata.query_start_loc[:num_prefills + 1],
initial_states=initial_states, initial_states=initial_states,
return_varlen_states=True, return_varlen_states=True,
return_final_states=False, return_final_states=False,
@@ -630,7 +550,7 @@ class MambaMixer2(CustomOp):
# update ssm states # update ssm states
# - varlen state is a (num_prefills, nheads, headdim, dstate) tensor # - varlen state is a (num_prefills, nheads, headdim, dstate) tensor
ssm_state[state_indices_tensor_p] = varlen_state mamba_cache_params.ssm_state[state_indices_tensor_p] = varlen_state
# - reshape # - reshape
ssd_output_list.append(scan_output.view(num_prefill_tokens, -1)) ssd_output_list.append(scan_output.view(num_prefill_tokens, -1))
@@ -640,7 +560,7 @@ class MambaMixer2(CustomOp):
# 2. Convolution sequence transformation # 2. Convolution sequence transformation
hidden_states_B_C_d = causal_conv1d_update( hidden_states_B_C_d = causal_conv1d_update(
hidden_states_B_C_d, hidden_states_B_C_d,
conv_state, mamba_cache_params.conv_state,
conv_weights, conv_weights,
self.conv1d.bias, self.conv1d.bias,
self.activation, self.activation,
@@ -666,7 +586,7 @@ class MambaMixer2(CustomOp):
# using state_indices_tensor_d # using state_indices_tensor_d
hidden_states_d = selective_state_update( hidden_states_d = selective_state_update(
ssm_state, mamba_cache_params.ssm_state,
hidden_states_d, hidden_states_d,
dt_d, dt_d,
A_d, A_d,
@@ -678,16 +598,9 @@ class MambaMixer2(CustomOp):
dt_softplus=True, dt_softplus=True,
state_batch_indices=state_indices_tensor_d, state_batch_indices=state_indices_tensor_d,
) )
ssd_output_list.append(
if envs.VLLM_USE_V1: hidden_states_d.view(-1, (self.num_heads // self.tp_size) *
ssd_output_list.insert( self.head_dim))
0,
hidden_states_d.view(-1, (self.num_heads // self.tp_size) *
self.head_dim))
else:
ssd_output_list.append(
hidden_states_d.view(-1, (self.num_heads // self.tp_size) *
self.head_dim))
# Merge prefill and decode outputs before passing to gated MLP # Merge prefill and decode outputs before passing to gated MLP
hidden_states = torch.vstack(ssd_output_list) hidden_states = torch.vstack(ssd_output_list)
@@ -701,31 +614,3 @@ class MambaMixer2(CustomOp):
# 5. Final linear projection # 5. Final linear projection
out, _ = self.out_proj(hidden_states) out, _ = self.out_proj(hidden_states)
return out return out
def get_state_shape(self) -> tuple[tuple[int, ...], tuple[int, ...]]:
world_size = get_tensor_model_parallel_world_size()
conv_state_shape, temporal_state_shape = None, None
# if n_groups is not divisible by world_size, need to extend the shards
# to ensure all groups needed by a head is sharded along with it
n_groups = (self.n_groups +
extra_groups_for_head_shards(self.n_groups, world_size))
# - heads and n_groups are TP-ed
conv_dim = (self.intermediate_size +
2 * n_groups * self.ssm_state_size)
conv_state_shape = (
divide(conv_dim, world_size),
self.conv_kernel_size - 1,
)
# These are not TP-ed as they depend on A, dt_bias, D
# - they are typically small
# e.g., (h_heads, d_head, d_state) = (128, 64, 128)
temporal_state_shape = (
divide(self.num_heads, world_size),
self.head_dim,
self.ssm_state_size,
)
return conv_state_shape, temporal_state_shape
@@ -492,6 +492,8 @@ class BitsAndBytesModelLoader(BaseModelLoader):
raise ValueError("Following weights were not initialized from " raise ValueError("Following weights were not initialized from "
f"checkpoint: {weights_not_loaded}") f"checkpoint: {weights_not_loaded}")
torch.cuda.empty_cache()
param_dict = dict(model.named_parameters()) param_dict = dict(model.named_parameters())
stacked_quant_state_dict: dict[str, dict[int, Any]] = {} stacked_quant_state_dict: dict[str, dict[int, Any]] = {}
# TODO: Change this lazy import to normal import # TODO: Change this lazy import to normal import
@@ -543,8 +545,6 @@ class BitsAndBytesModelLoader(BaseModelLoader):
for param_name, param in param_dict.items(): for param_name, param in param_dict.items():
if param_name in stacked_quant_state_dict: if param_name in stacked_quant_state_dict:
quant_states = stacked_quant_state_dict[param_name] quant_states = stacked_quant_state_dict[param_name]
# Dequantize double quantized values during weight loading.
dequantize_dq(quant_states)
set_weight_attrs(param, {"bnb_quant_state": quant_states}) set_weight_attrs(param, {"bnb_quant_state": quant_states})
pack_ratio = getattr(param, "pack_factor", -1) pack_ratio = getattr(param, "pack_factor", -1)
@@ -565,28 +565,6 @@ class BitsAndBytesModelLoader(BaseModelLoader):
if load_8bit: if load_8bit:
set_weight_attrs( set_weight_attrs(
param, {"matmul_state": [None] * len(quant_states)}) param, {"matmul_state": [None] * len(quant_states)})
torch.cuda.empty_cache()
def download_model(self, model_config: ModelConfig) -> None: def download_model(self, model_config: ModelConfig) -> None:
self._prepare_weights(model_config.model, model_config.revision) self._prepare_weights(model_config.model, model_config.revision)
def dequantize_dq(quant_states: dict) -> None:
"""
When BNB employs Double Quantization, we perform the dequantization of
these constants during weight loading rather than at inference time,
thereby avoiding this computational overhead during inference. This comes
at the cost of increased memory usage.
"""
from bitsandbytes.functional import dequantize_blockwise
for _, quant_state in quant_states.items():
# Copied from: https://github.com/bitsandbytes-foundation/bitsandbytes/blob/0.45.3/bitsandbytes/functional.py#L1352-#L1356
if quant_state.nested:
absmax = dequantize_blockwise(quant_state.absmax,
quant_state.state2)
absmax += quant_state.offset
if absmax.dtype != torch.float32:
absmax = absmax.float()
quant_state.absmax = absmax
quant_state.nested = False
quant_state.offset = None
quant_state.state2 = None
+1 -2
View File
@@ -620,8 +620,7 @@ class AriaForConditionalGeneration(nn.Module, SupportsMultiModal):
multimodal_embeddings: Optional[MultiModalEmbeddings] = None, multimodal_embeddings: Optional[MultiModalEmbeddings] = None,
) -> torch.Tensor: ) -> torch.Tensor:
inputs_embeds = self.language_model.get_input_embeddings(input_ids) inputs_embeds = self.language_model.get_input_embeddings(input_ids)
if multimodal_embeddings is not None \ if multimodal_embeddings is not None:
and len(multimodal_embeddings) != 0:
inputs_embeds = merge_multimodal_embeddings( inputs_embeds = merge_multimodal_embeddings(
input_ids, inputs_embeds, multimodal_embeddings, input_ids, inputs_embeds, multimodal_embeddings,
self.config.image_token_index) self.config.image_token_index)
+1 -2
View File
@@ -430,8 +430,7 @@ class AyaVisionForConditionalGeneration(nn.Module, SupportsMultiModal,
multimodal_embeddings: Optional[MultiModalEmbeddings] = None, multimodal_embeddings: Optional[MultiModalEmbeddings] = None,
) -> torch.Tensor: ) -> torch.Tensor:
inputs_embeds = self.language_model.get_input_embeddings(input_ids) inputs_embeds = self.language_model.get_input_embeddings(input_ids)
if multimodal_embeddings is not None \ if multimodal_embeddings is not None:
and len(multimodal_embeddings) != 0:
inputs_embeds = merge_multimodal_embeddings( inputs_embeds = merge_multimodal_embeddings(
input_ids=input_ids, input_ids=input_ids,
inputs_embeds=inputs_embeds, inputs_embeds=inputs_embeds,
+1 -2
View File
@@ -641,8 +641,7 @@ class Blip2ForConditionalGeneration(nn.Module, SupportsMultiModal, SupportsPP,
multimodal_embeddings: Optional[MultiModalEmbeddings] = None, multimodal_embeddings: Optional[MultiModalEmbeddings] = None,
) -> torch.Tensor: ) -> torch.Tensor:
inputs_embeds = self.language_model.get_input_embeddings(input_ids) inputs_embeds = self.language_model.get_input_embeddings(input_ids)
if multimodal_embeddings is not None \ if multimodal_embeddings is not None:
and len(multimodal_embeddings) != 0:
inputs_embeds = merge_multimodal_embeddings( inputs_embeds = merge_multimodal_embeddings(
input_ids, inputs_embeds, multimodal_embeddings, input_ids, inputs_embeds, multimodal_embeddings,
_IMAGE_TOKEN_ID) _IMAGE_TOKEN_ID)
+1 -2
View File
@@ -1005,8 +1005,7 @@ class ChameleonForConditionalGeneration(nn.Module, SupportsMultiModal,
) -> torch.Tensor: ) -> torch.Tensor:
inputs_embeds = self.model.get_input_embeddings(input_ids) inputs_embeds = self.model.get_input_embeddings(input_ids)
if multimodal_embeddings is not None \ if multimodal_embeddings is not None:
and len(multimodal_embeddings) != 0:
inputs_embeds = merge_multimodal_embeddings( inputs_embeds = merge_multimodal_embeddings(
input_ids, inputs_embeds, multimodal_embeddings, input_ids, inputs_embeds, multimodal_embeddings,
self.model.vocabulary_mapping.image_token_id) self.model.vocabulary_mapping.image_token_id)
+1 -2
View File
@@ -600,8 +600,7 @@ class DeepseekVLV2ForCausalLM(nn.Module, SupportsMultiModal, SupportsPP):
multimodal_embeddings: Optional[MultiModalEmbeddings] = None, multimodal_embeddings: Optional[MultiModalEmbeddings] = None,
) -> torch.Tensor: ) -> torch.Tensor:
inputs_embeds = self.language_model.get_input_embeddings(input_ids) inputs_embeds = self.language_model.get_input_embeddings(input_ids)
if multimodal_embeddings is not None \ if multimodal_embeddings is not None:
and len(multimodal_embeddings) != 0:
inputs_embeds = merge_multimodal_embeddings( inputs_embeds = merge_multimodal_embeddings(
input_ids, inputs_embeds, multimodal_embeddings, input_ids, inputs_embeds, multimodal_embeddings,
self.image_token_id) self.image_token_id)
+1 -2
View File
@@ -1046,8 +1046,7 @@ class Florence2ForConditionalGeneration(nn.Module, SupportsMultiModal,
multimodal_embeddings: Optional[MultiModalEmbeddings] = None, multimodal_embeddings: Optional[MultiModalEmbeddings] = None,
) -> torch.Tensor: ) -> torch.Tensor:
inputs_embeds = self.language_model.get_input_embeddings(input_ids) inputs_embeds = self.language_model.get_input_embeddings(input_ids)
if multimodal_embeddings is not None \ if multimodal_embeddings is not None:
and len(multimodal_embeddings) != 0:
inputs_embeds = merge_multimodal_embeddings( inputs_embeds = merge_multimodal_embeddings(
input_ids, inputs_embeds, multimodal_embeddings, input_ids, inputs_embeds, multimodal_embeddings,
self.pad_token_id) self.pad_token_id)
+1 -2
View File
@@ -345,8 +345,7 @@ class FuyuForCausalLM(nn.Module, SupportsMultiModal, SupportsPP):
multimodal_embeddings: Optional[MultiModalEmbeddings] = None, multimodal_embeddings: Optional[MultiModalEmbeddings] = None,
) -> torch.Tensor: ) -> torch.Tensor:
inputs_embeds = self.language_model.get_input_embeddings(input_ids) inputs_embeds = self.language_model.get_input_embeddings(input_ids)
if multimodal_embeddings is not None \ if multimodal_embeddings is not None:
and len(multimodal_embeddings) != 0:
inputs_embeds = merge_multimodal_embeddings( inputs_embeds = merge_multimodal_embeddings(
input_ids, input_ids,
inputs_embeds, inputs_embeds,
+1 -2
View File
@@ -592,8 +592,7 @@ class Gemma3ForConditionalGeneration(nn.Module, SupportsMultiModal, SupportsPP,
multimodal_embeddings: Optional[MultiModalEmbeddings] = None, multimodal_embeddings: Optional[MultiModalEmbeddings] = None,
) -> torch.Tensor: ) -> torch.Tensor:
inputs_embeds = self.language_model.get_input_embeddings(input_ids) inputs_embeds = self.language_model.get_input_embeddings(input_ids)
if multimodal_embeddings is not None \ if multimodal_embeddings is not None:
and len(multimodal_embeddings) != 0:
inputs_embeds = merge_multimodal_embeddings( inputs_embeds = merge_multimodal_embeddings(
input_ids, input_ids,
inputs_embeds, inputs_embeds,
+1 -2
View File
@@ -609,8 +609,7 @@ class GLM4VForCausalLM(ChatGLMBaseModel, SupportsLoRA, SupportsPP,
) -> torch.Tensor: ) -> torch.Tensor:
inputs_embeds = self.transformer.get_input_embeddings(input_ids) inputs_embeds = self.transformer.get_input_embeddings(input_ids)
if multimodal_embeddings is not None \ if multimodal_embeddings is not None:
and len(multimodal_embeddings) != 0:
inputs_embeds = merge_multimodal_embeddings( inputs_embeds = merge_multimodal_embeddings(
input_ids=input_ids, input_ids=input_ids,
inputs_embeds=inputs_embeds, inputs_embeds=inputs_embeds,
+1 -2
View File
@@ -721,8 +721,7 @@ class GraniteSpeechForConditionalGeneration(
multimodal_embeddings: Optional[MultiModalEmbeddings] = None, multimodal_embeddings: Optional[MultiModalEmbeddings] = None,
) -> torch.Tensor: ) -> torch.Tensor:
"""Compute the merged LLM / audio embeddings.""" """Compute the merged LLM / audio embeddings."""
if multimodal_embeddings is None \ if multimodal_embeddings is None:
or len(multimodal_embeddings) == 0:
return self.language_model.get_input_embeddings(input_ids) return self.language_model.get_input_embeddings(input_ids)
inputs_embeds = embed_multimodal( inputs_embeds = embed_multimodal(
+1 -2
View File
@@ -720,8 +720,7 @@ class Idefics3ForConditionalGeneration(nn.Module, SupportsMultiModal,
multimodal_embeddings: Optional[MultiModalEmbeddings] = None, multimodal_embeddings: Optional[MultiModalEmbeddings] = None,
) -> torch.Tensor: ) -> torch.Tensor:
inputs_embeds = self.model.get_input_embeddings(input_ids) inputs_embeds = self.model.get_input_embeddings(input_ids)
if multimodal_embeddings is not None \ if multimodal_embeddings is not None:
and len(multimodal_embeddings) != 0:
inputs_embeds = merge_multimodal_embeddings( inputs_embeds = merge_multimodal_embeddings(
input_ids, input_ids,
inputs_embeds, inputs_embeds,
+1 -2
View File
@@ -1336,8 +1336,7 @@ class InternVLChatModel(nn.Module, SupportsMultiModal, SupportsPP,
multimodal_embeddings: Optional[MultiModalEmbeddings] = None, multimodal_embeddings: Optional[MultiModalEmbeddings] = None,
) -> torch.Tensor: ) -> torch.Tensor:
inputs_embeds = self.language_model.get_input_embeddings(input_ids) inputs_embeds = self.language_model.get_input_embeddings(input_ids)
if multimodal_embeddings is not None \ if multimodal_embeddings is not None:
and len(multimodal_embeddings) != 0:
context_token_ids = [ context_token_ids = [
token_id for token_id in (self.img_context_token_id, token_id for token_id in (self.img_context_token_id,
self.video_context_token_id) self.video_context_token_id)
+1 -2
View File
@@ -393,8 +393,7 @@ class KimiVLForConditionalGeneration(nn.Module, SupportsMultiModal):
# model as one of the requirements of basic vLLM model implementation. # model as one of the requirements of basic vLLM model implementation.
inputs_embeds = self.language_model.get_input_embeddings(input_ids) inputs_embeds = self.language_model.get_input_embeddings(input_ids)
if multimodal_embeddings is not None and len( if multimodal_embeddings is not None:
multimodal_embeddings) != 0:
inputs_embeds = merge_multimodal_embeddings( inputs_embeds = merge_multimodal_embeddings(
input_ids=input_ids, input_ids=input_ids,
inputs_embeds=inputs_embeds, inputs_embeds=inputs_embeds,
+1 -2
View File
@@ -683,8 +683,7 @@ class LlavaForConditionalGeneration(nn.Module, SupportsMultiModal, SupportsPP):
multimodal_embeddings: Optional[MultiModalEmbeddings] = None, multimodal_embeddings: Optional[MultiModalEmbeddings] = None,
) -> torch.Tensor: ) -> torch.Tensor:
inputs_embeds = self.language_model.get_input_embeddings(input_ids) inputs_embeds = self.language_model.get_input_embeddings(input_ids)
if multimodal_embeddings is not None \ if multimodal_embeddings is not None:
and len(multimodal_embeddings) != 0:
inputs_embeds = merge_multimodal_embeddings( inputs_embeds = merge_multimodal_embeddings(
input_ids, input_ids,
inputs_embeds, inputs_embeds,
+1 -2
View File
@@ -502,8 +502,7 @@ class LlavaNextForConditionalGeneration(nn.Module, SupportsMultiModal,
multimodal_embeddings: Optional[MultiModalEmbeddings] = None, multimodal_embeddings: Optional[MultiModalEmbeddings] = None,
) -> torch.Tensor: ) -> torch.Tensor:
if multimodal_embeddings is None \ if not multimodal_embeddings:
or len(multimodal_embeddings) == 0:
return self.language_model.get_input_embeddings(input_ids) return self.language_model.get_input_embeddings(input_ids)
inputs_embeds = embed_multimodal( inputs_embeds = embed_multimodal(
@@ -426,8 +426,7 @@ class LlavaNextVideoForConditionalGeneration(nn.Module, SupportsMultiModal,
multimodal_embeddings: Optional[MultiModalEmbeddings] = None, multimodal_embeddings: Optional[MultiModalEmbeddings] = None,
) -> torch.Tensor: ) -> torch.Tensor:
inputs_embeds = self.language_model.get_input_embeddings(input_ids) inputs_embeds = self.language_model.get_input_embeddings(input_ids)
if multimodal_embeddings is not None \ if multimodal_embeddings is not None:
and len(multimodal_embeddings) != 0:
inputs_embeds = merge_multimodal_embeddings( inputs_embeds = merge_multimodal_embeddings(
input_ids, inputs_embeds, multimodal_embeddings, input_ids, inputs_embeds, multimodal_embeddings,
self.config.video_token_index) self.config.video_token_index)
@@ -881,8 +881,7 @@ class LlavaOnevisionForConditionalGeneration(nn.Module, SupportsMultiModal,
multimodal_embeddings: Optional[MultiModalEmbeddings] = None, multimodal_embeddings: Optional[MultiModalEmbeddings] = None,
) -> torch.Tensor: ) -> torch.Tensor:
inputs_embeds = self.language_model.get_input_embeddings(input_ids) inputs_embeds = self.language_model.get_input_embeddings(input_ids)
if multimodal_embeddings is not None \ if multimodal_embeddings is not None:
and len(multimodal_embeddings) != 0:
inputs_embeds = merge_multimodal_embeddings( inputs_embeds = merge_multimodal_embeddings(
input_ids, inputs_embeds, multimodal_embeddings, input_ids, inputs_embeds, multimodal_embeddings,
[self.config.image_token_index, self.config.video_token_index]) [self.config.image_token_index, self.config.video_token_index])
+20 -32
View File
@@ -8,7 +8,6 @@ import torch
from torch import nn from torch import nn
from transformers import MambaConfig from transformers import MambaConfig
from vllm import envs
from vllm.attention.backends.abstract import AttentionMetadata from vllm.attention.backends.abstract import AttentionMetadata
from vllm.config import VllmConfig from vllm.config import VllmConfig
from vllm.distributed import divide, get_tensor_model_parallel_world_size from vllm.distributed import divide, get_tensor_model_parallel_world_size
@@ -26,7 +25,8 @@ from vllm.model_executor.layers.vocab_parallel_embedding import (
DEFAULT_VOCAB_PADDING_SIZE, ParallelLMHead, VocabParallelEmbedding) DEFAULT_VOCAB_PADDING_SIZE, ParallelLMHead, VocabParallelEmbedding)
from vllm.model_executor.model_loader.weight_utils import default_weight_loader from vllm.model_executor.model_loader.weight_utils import default_weight_loader
from vllm.model_executor.models.interfaces import (HasInnerState, from vllm.model_executor.models.interfaces import (HasInnerState,
IsAttentionFree) IsAttentionFree,
SupportsV0Only)
from vllm.model_executor.models.mamba_cache import (MambaCacheManager, from vllm.model_executor.models.mamba_cache import (MambaCacheManager,
MambaCacheParams) MambaCacheParams)
from vllm.model_executor.sampling_metadata import SamplingMetadata from vllm.model_executor.sampling_metadata import SamplingMetadata
@@ -44,8 +44,7 @@ class Mamba2DecoderLayer(nn.Module):
def __init__(self, def __init__(self,
config: MambaConfig, config: MambaConfig,
quant_config: Optional[QuantizationConfig] = None, quant_config: Optional[QuantizationConfig] = None) -> None:
prefix: str = "") -> None:
super().__init__() super().__init__()
self.config = config self.config = config
self.mixer = MambaMixer2(hidden_size=config.hidden_size, self.mixer = MambaMixer2(hidden_size=config.hidden_size,
@@ -61,9 +60,7 @@ class Mamba2DecoderLayer(nn.Module):
head_dim=config.head_dim, head_dim=config.head_dim,
rms_norm_eps=config.layer_norm_epsilon, rms_norm_eps=config.layer_norm_epsilon,
activation=config.hidden_act, activation=config.hidden_act,
quant_config=quant_config, quant_config=quant_config)
prefix=f"{prefix}.mixer",
chunk_size=config.chunk_size)
self.norm = RMSNorm(config.hidden_size, eps=config.layer_norm_epsilon) self.norm = RMSNorm(config.hidden_size, eps=config.layer_norm_epsilon)
@@ -111,8 +108,8 @@ class Mamba2Model(nn.Module):
self.start_layer, self.end_layer, self.layers = make_layers( self.start_layer, self.end_layer, self.layers = make_layers(
config.num_hidden_layers, config.num_hidden_layers,
lambda prefix: Mamba2DecoderLayer( lambda prefix: Mamba2DecoderLayer(config,
config, quant_config=quant_config, prefix=prefix), quant_config=quant_config),
prefix=f"{prefix}.layers") prefix=f"{prefix}.layers")
self.norm_f = RMSNorm(config.hidden_size, self.norm_f = RMSNorm(config.hidden_size,
@@ -145,14 +142,10 @@ class Mamba2Model(nn.Module):
attn_metadata: AttentionMetadata = get_forward_context().attn_metadata attn_metadata: AttentionMetadata = get_forward_context().attn_metadata
if not envs.VLLM_USE_V1: mamba2_metadata = prepare_mamba2_metadata(
mamba2_metadata = prepare_mamba2_metadata( chunk_size=self.config.chunk_size,
chunk_size=self.config.chunk_size, attn_metadata=attn_metadata,
attn_metadata=attn_metadata, )
)
else:
# v1 get mamba2_metadata from forward_context
mamba2_metadata = None
for i in range(len(self.layers)): for i in range(len(self.layers)):
layer = self.layers[i] layer = self.layers[i]
@@ -162,7 +155,7 @@ class Mamba2Model(nn.Module):
hidden_states=hidden_states, hidden_states=hidden_states,
residual=residual, residual=residual,
mamba_cache_params=mamba_cache_params.at_layer_idx( mamba_cache_params=mamba_cache_params.at_layer_idx(
i - self.start_layer) if mamba_cache_params else None, i - self.start_layer),
mamba2_metadata=mamba2_metadata) mamba2_metadata=mamba2_metadata)
if not get_pp_group().is_last_rank: if not get_pp_group().is_last_rank:
@@ -197,7 +190,8 @@ class Mamba2Model(nn.Module):
return loaded_params return loaded_params
class Mamba2ForCausalLM(nn.Module, HasInnerState, IsAttentionFree): class Mamba2ForCausalLM(nn.Module, HasInnerState, IsAttentionFree,
SupportsV0Only):
def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""): def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""):
config = vllm_config.model_config.hf_config config = vllm_config.model_config.hf_config
@@ -248,20 +242,14 @@ class Mamba2ForCausalLM(nn.Module, HasInnerState, IsAttentionFree):
intermediate_tensors: Optional[IntermediateTensors] = None, intermediate_tensors: Optional[IntermediateTensors] = None,
inputs_embeds: Optional[torch.Tensor] = None, inputs_embeds: Optional[torch.Tensor] = None,
**kwargs): **kwargs):
if not envs.VLLM_USE_V1: if self.mamba_cache is None:
if self.mamba_cache is None: num_mamba_layers = self.model_config.get_num_layers_by_block_type(
num_mamba_layers = ( self.vllm_config.parallel_config, LayerBlockType.mamba)
self.model_config.get_num_layers_by_block_type( self.mamba_cache = MambaCacheManager(
self.vllm_config.parallel_config, self.vllm_config, self.lm_head.weight.dtype, num_mamba_layers,
LayerBlockType.mamba)) *self._get_mamba_cache_shape())
self.mamba_cache = MambaCacheManager(
self.vllm_config, self.lm_head.weight.dtype,
num_mamba_layers, *self._get_mamba_cache_shape())
mamba_cache_params = self.mamba_cache.current_run_tensors(**kwargs) mamba_cache_params = self.mamba_cache.current_run_tensors(**kwargs)
else:
# NOTE: mamba_cache_params is not needed for v1
mamba_cache_params = None
hidden_states = self.backbone(input_ids, positions, mamba_cache_params, hidden_states = self.backbone(input_ids, positions, mamba_cache_params,
intermediate_tensors, inputs_embeds) intermediate_tensors, inputs_embeds)
+1 -2
View File
@@ -892,8 +892,7 @@ class MiniCPMVBaseModel(nn.Module, SupportsMultiModal, SupportsPP):
multimodal_embeddings: Optional[MultiModalEmbeddings] = None, multimodal_embeddings: Optional[MultiModalEmbeddings] = None,
) -> torch.Tensor: ) -> torch.Tensor:
inputs_embeds = self.llm.get_input_embeddings(input_ids) inputs_embeds = self.llm.get_input_embeddings(input_ids)
if multimodal_embeddings is not None \ if multimodal_embeddings is not None:
and len(multimodal_embeddings) != 0:
assert len(self.mm_token_ids) > 0 assert len(self.mm_token_ids) > 0
inputs_embeds = merge_multimodal_embeddings( inputs_embeds = merge_multimodal_embeddings(
input_ids, input_ids,
+1 -2
View File
@@ -201,8 +201,7 @@ class MiniMaxVL01ForConditionalGeneration(nn.Module, SupportsMultiModal,
multimodal_embeddings: Optional[MultiModalEmbeddings] = None, multimodal_embeddings: Optional[MultiModalEmbeddings] = None,
) -> torch.Tensor: ) -> torch.Tensor:
inputs_embeds = self.language_model.get_input_embeddings(input_ids) inputs_embeds = self.language_model.get_input_embeddings(input_ids)
if multimodal_embeddings is not None \ if multimodal_embeddings is not None:
and len(multimodal_embeddings) != 0:
inputs_embeds = merge_multimodal_embeddings( inputs_embeds = merge_multimodal_embeddings(
input_ids, input_ids,
inputs_embeds, inputs_embeds,
+1 -2
View File
@@ -521,8 +521,7 @@ class Mistral3ForConditionalGeneration(nn.Module, SupportsLoRA,
multimodal_embeddings: Optional[MultiModalEmbeddings] = None, multimodal_embeddings: Optional[MultiModalEmbeddings] = None,
) -> torch.Tensor: ) -> torch.Tensor:
inputs_embeds = self.language_model.get_input_embeddings(input_ids) inputs_embeds = self.language_model.get_input_embeddings(input_ids)
if multimodal_embeddings is not None \ if multimodal_embeddings is not None:
and len(multimodal_embeddings) != 0:
inputs_embeds = merge_multimodal_embeddings( inputs_embeds = merge_multimodal_embeddings(
input_ids, input_ids,
inputs_embeds, inputs_embeds,
+1 -2
View File
@@ -808,8 +808,7 @@ class Llama4ForConditionalGeneration(nn.Module, SupportsMultiModal,
) -> torch.Tensor: ) -> torch.Tensor:
inputs_embeds = self.language_model.get_input_embeddings(input_ids) inputs_embeds = self.language_model.get_input_embeddings(input_ids)
if multimodal_embeddings is not None and len( if multimodal_embeddings is not None:
multimodal_embeddings) != 0:
inputs_embeds = merge_multimodal_embeddings( inputs_embeds = merge_multimodal_embeddings(
input_ids, input_ids,
inputs_embeds, inputs_embeds,
+1 -2
View File
@@ -1487,8 +1487,7 @@ class MolmoForCausalLM(nn.Module, SupportsMultiModal, SupportsPP, SupportsLoRA,
multimodal_embeddings: Optional[MultiModalEmbeddings] = None, multimodal_embeddings: Optional[MultiModalEmbeddings] = None,
) -> torch.Tensor: ) -> torch.Tensor:
inputs_embeds = self.model.get_input_embeddings(input_ids) inputs_embeds = self.model.get_input_embeddings(input_ids)
if multimodal_embeddings is not None \ if multimodal_embeddings is not None:
and len(multimodal_embeddings) != 0:
assert self.img_patch_id is not None assert self.img_patch_id is not None
inputs_embeds = merge_multimodal_embeddings( inputs_embeds = merge_multimodal_embeddings(
+1 -2
View File
@@ -515,8 +515,7 @@ class Ovis(nn.Module, SupportsMultiModal, SupportsPP):
multimodal_embeddings: Optional[MultiModalEmbeddings] = None, multimodal_embeddings: Optional[MultiModalEmbeddings] = None,
) -> torch.Tensor: ) -> torch.Tensor:
inputs_embeds = self.llm.get_input_embeddings(input_ids) inputs_embeds = self.llm.get_input_embeddings(input_ids)
if multimodal_embeddings is not None \ if multimodal_embeddings is not None:
and len(multimodal_embeddings) != 0:
inputs_embeds = merge_multimodal_embeddings( inputs_embeds = merge_multimodal_embeddings(
input_ids, inputs_embeds, multimodal_embeddings, input_ids, inputs_embeds, multimodal_embeddings,
self.image_pad_token_id) self.image_pad_token_id)
+1 -2
View File
@@ -364,8 +364,7 @@ class PaliGemmaForConditionalGeneration(nn.Module, SupportsMultiModal,
multimodal_embeddings: Optional[MultiModalEmbeddings] = None, multimodal_embeddings: Optional[MultiModalEmbeddings] = None,
) -> torch.Tensor: ) -> torch.Tensor:
inputs_embeds = self.language_model.get_input_embeddings(input_ids) inputs_embeds = self.language_model.get_input_embeddings(input_ids)
if multimodal_embeddings is not None \ if multimodal_embeddings is not None:
and len(multimodal_embeddings) != 0:
inputs_embeds = merge_multimodal_embeddings( inputs_embeds = merge_multimodal_embeddings(
input_ids, inputs_embeds, multimodal_embeddings, input_ids, inputs_embeds, multimodal_embeddings,
self.config.image_token_index) self.config.image_token_index)
+1 -2
View File
@@ -669,8 +669,7 @@ class Phi3VForCausalLM(nn.Module, SupportsMultiModal, SupportsPP,
multimodal_embeddings: Optional[MultiModalEmbeddings] = None, multimodal_embeddings: Optional[MultiModalEmbeddings] = None,
) -> torch.Tensor: ) -> torch.Tensor:
inputs_embeds = self.embed_tokens(input_ids) inputs_embeds = self.embed_tokens(input_ids)
if multimodal_embeddings is not None \ if multimodal_embeddings:
and len(multimodal_embeddings) != 0:
inputs_embeds = merge_multimodal_embeddings( inputs_embeds = merge_multimodal_embeddings(
input_ids, inputs_embeds, multimodal_embeddings, input_ids, inputs_embeds, multimodal_embeddings,
self.image_token_id) self.image_token_id)
+1 -2
View File
@@ -1148,8 +1148,7 @@ class Phi4MMForCausalLM(nn.Module, SupportsLoRA, SupportsMultiModal):
multimodal_embeddings: Optional[MultiModalEmbeddings] = None, multimodal_embeddings: Optional[MultiModalEmbeddings] = None,
) -> torch.Tensor: ) -> torch.Tensor:
inputs_embeds = self.model.embed_tokens(input_ids) inputs_embeds = self.model.embed_tokens(input_ids)
if multimodal_embeddings is not None and len( if multimodal_embeddings is not None:
multimodal_embeddings) != 0:
inputs_embeds = merge_multimodal_embeddings( inputs_embeds = merge_multimodal_embeddings(
input_ids, inputs_embeds, multimodal_embeddings, input_ids, inputs_embeds, multimodal_embeddings,
[_IMAGE_PLACEHOLDER_TOKEN_ID, _AUDIO_PLACEHOLDER_TOKEN_ID]) [_IMAGE_PLACEHOLDER_TOKEN_ID, _AUDIO_PLACEHOLDER_TOKEN_ID])
+1 -2
View File
@@ -423,8 +423,7 @@ class PixtralForConditionalGeneration(nn.Module, SupportsMultiModal,
multimodal_embeddings: Optional[MultiModalEmbeddings] = None, multimodal_embeddings: Optional[MultiModalEmbeddings] = None,
) -> torch.Tensor: ) -> torch.Tensor:
inputs_embeds = self.language_model.get_input_embeddings(input_ids) inputs_embeds = self.language_model.get_input_embeddings(input_ids)
if multimodal_embeddings is not None \ if multimodal_embeddings is not None:
and len(multimodal_embeddings) != 0:
inputs_embeds = merge_multimodal_embeddings( inputs_embeds = merge_multimodal_embeddings(
input_ids, input_ids,
inputs_embeds, inputs_embeds,
@@ -146,11 +146,11 @@ class Qwen2_5OmniThinkerProcessingInfo(Qwen2AudioProcessingInfo,
kwargs["fps"] = fps kwargs["fps"] = fps
processor = self.ctx.get_hf_processor( processor = self.ctx.get_hf_processor(
Qwen2_5OmniProcessor, Qwen2_5OmniProcessor,
image_processor=self.get_image_processor(min_pixels=min_pixels, image_processor=self.get_image_processor(
max_pixels=max_pixels, min_pixels=min_pixels,
size=size, max_pixels=max_pixels,
use_fast=kwargs.get( size=size,
"use_fast", True)), use_fast=kwargs.get("use_fast")),
**kwargs, **kwargs,
) )
if not hasattr(processor, "audio_token"): if not hasattr(processor, "audio_token"):
@@ -805,8 +805,7 @@ class Qwen2_5OmniThinkerForConditionalGeneration(
multimodal_embeddings: Optional[MultiModalEmbeddings] = None, multimodal_embeddings: Optional[MultiModalEmbeddings] = None,
) -> torch.Tensor: ) -> torch.Tensor:
inputs_embeds = self.language_model.get_input_embeddings(input_ids) inputs_embeds = self.language_model.get_input_embeddings(input_ids)
if multimodal_embeddings is not None \ if multimodal_embeddings is not None:
and len(multimodal_embeddings) != 0:
# TODO (ywang96): support overlapping modalitiy embeddings so that # TODO (ywang96): support overlapping modalitiy embeddings so that
# `use_audio_in_video` will work on V1. # `use_audio_in_video` will work on V1.
@@ -846,7 +845,7 @@ class Qwen2_5OmniThinkerForConditionalGeneration(
multimodal_embeddings: Optional[NestedTensors] = None, multimodal_embeddings: Optional[NestedTensors] = None,
) -> torch.Tensor: ) -> torch.Tensor:
inputs_embeds = self.language_model.get_input_embeddings(input_ids) inputs_embeds = self.language_model.get_input_embeddings(input_ids)
if multimodal_embeddings is None or len(multimodal_embeddings) == 0: if multimodal_embeddings is None:
return inputs_embeds return inputs_embeds
for embeddings, modality in multimodal_embeddings: for embeddings, modality in multimodal_embeddings:
+6 -7
View File
@@ -794,11 +794,11 @@ class Qwen2_5_VLProcessingInfo(Qwen2VLProcessingInfo):
return self.ctx.get_hf_processor( return self.ctx.get_hf_processor(
Qwen2_5_VLProcessor, Qwen2_5_VLProcessor,
image_processor=self.get_image_processor(min_pixels=min_pixels, image_processor=self.get_image_processor(
max_pixels=max_pixels, min_pixels=min_pixels,
size=size, max_pixels=max_pixels,
use_fast=kwargs.get( size=size,
"use_fast", True)), use_fast=kwargs.get("use_fast")),
**kwargs, **kwargs,
) )
@@ -1046,8 +1046,7 @@ class Qwen2_5_VLForConditionalGeneration(nn.Module, SupportsMultiModal,
multimodal_embeddings: Optional[MultiModalEmbeddings] = None, multimodal_embeddings: Optional[MultiModalEmbeddings] = None,
) -> torch.Tensor: ) -> torch.Tensor:
inputs_embeds = self.language_model.get_input_embeddings(input_ids) inputs_embeds = self.language_model.get_input_embeddings(input_ids)
if multimodal_embeddings is not None \ if multimodal_embeddings is not None:
and len(multimodal_embeddings) != 0:
inputs_embeds = merge_multimodal_embeddings( inputs_embeds = merge_multimodal_embeddings(
input_ids, inputs_embeds, multimodal_embeddings, input_ids, inputs_embeds, multimodal_embeddings,
[self.config.image_token_id, self.config.video_token_id]) [self.config.image_token_id, self.config.video_token_id])
+1 -2
View File
@@ -364,8 +364,7 @@ class Qwen2AudioForConditionalGeneration(nn.Module, SupportsMultiModal,
multimodal_embeddings: Optional[MultiModalEmbeddings] = None, multimodal_embeddings: Optional[MultiModalEmbeddings] = None,
) -> torch.Tensor: ) -> torch.Tensor:
inputs_embeds = self.language_model.get_input_embeddings(input_ids) inputs_embeds = self.language_model.get_input_embeddings(input_ids)
if multimodal_embeddings is not None \ if multimodal_embeddings is not None:
and len(multimodal_embeddings) != 0:
inputs_embeds = merge_multimodal_embeddings( inputs_embeds = merge_multimodal_embeddings(
input_ids, inputs_embeds, multimodal_embeddings, input_ids, inputs_embeds, multimodal_embeddings,
self.config.audio_token_index) self.config.audio_token_index)
+6 -8
View File
@@ -759,11 +759,11 @@ class Qwen2VLProcessingInfo(BaseProcessingInfo):
) -> Qwen2VLProcessor: ) -> Qwen2VLProcessor:
return self.ctx.get_hf_processor( return self.ctx.get_hf_processor(
Qwen2VLProcessor, Qwen2VLProcessor,
image_processor=self.get_image_processor(min_pixels=min_pixels, image_processor=self.get_image_processor(
max_pixels=max_pixels, min_pixels=min_pixels,
size=size, max_pixels=max_pixels,
use_fast=kwargs.get( size=size,
"use_fast", True)), use_fast=kwargs.get("use_fast")),
**kwargs, **kwargs,
) )
@@ -808,7 +808,6 @@ class Qwen2VLProcessingInfo(BaseProcessingInfo):
size: Optional[dict[str, int]] = None, size: Optional[dict[str, int]] = None,
**kwargs: object, **kwargs: object,
) -> Qwen2VLImageProcessor: ) -> Qwen2VLImageProcessor:
kwargs["use_fast"] = kwargs.get("use_fast", True)
return cached_image_processor_from_config( return cached_image_processor_from_config(
self.ctx.model_config, self.ctx.model_config,
**self._get_image_processor_kwargs(min_pixels=min_pixels, **self._get_image_processor_kwargs(min_pixels=min_pixels,
@@ -1290,8 +1289,7 @@ class Qwen2VLForConditionalGeneration(nn.Module, SupportsMultiModal,
multimodal_embeddings: Optional[MultiModalEmbeddings] = None, multimodal_embeddings: Optional[MultiModalEmbeddings] = None,
) -> torch.Tensor: ) -> torch.Tensor:
inputs_embeds = self.language_model.get_input_embeddings(input_ids) inputs_embeds = self.language_model.get_input_embeddings(input_ids)
if multimodal_embeddings is not None \ if multimodal_embeddings is not None:
and len(multimodal_embeddings) != 0:
inputs_embeds = merge_multimodal_embeddings( inputs_embeds = merge_multimodal_embeddings(
input_ids, inputs_embeds, multimodal_embeddings, input_ids, inputs_embeds, multimodal_embeddings,
[self.config.image_token_id, self.config.video_token_id]) [self.config.image_token_id, self.config.video_token_id])
+1 -2
View File
@@ -754,8 +754,7 @@ class QwenVLForConditionalGeneration(QWenBaseModel, SupportsPP, SupportsLoRA,
) -> torch.Tensor: ) -> torch.Tensor:
inputs_embeds = self.transformer.get_input_embeddings(input_ids) inputs_embeds = self.transformer.get_input_embeddings(input_ids)
if multimodal_embeddings is not None \ if multimodal_embeddings is not None:
and len(multimodal_embeddings) != 0:
inputs_embeds = merge_multimodal_embeddings( inputs_embeds = merge_multimodal_embeddings(
input_ids, inputs_embeds, multimodal_embeddings, input_ids, inputs_embeds, multimodal_embeddings,
self.transformer.visual.image_pad_id) self.transformer.visual.image_pad_id)
+1 -2
View File
@@ -883,8 +883,7 @@ class SkyworkR1VChatModel(nn.Module, SupportsMultiModal, SupportsPP):
multimodal_embeddings: Optional[MultiModalEmbeddings] = None, multimodal_embeddings: Optional[MultiModalEmbeddings] = None,
) -> torch.Tensor: ) -> torch.Tensor:
inputs_embeds = self.language_model.get_input_embeddings(input_ids) inputs_embeds = self.language_model.get_input_embeddings(input_ids)
if multimodal_embeddings is not None \ if multimodal_embeddings is not None:
and len(multimodal_embeddings) != 0:
assert self.img_context_token_id is not None assert self.img_context_token_id is not None
self._set_visual_token_mask(input_ids) self._set_visual_token_mask(input_ids)
inputs_embeds = merge_multimodal_embeddings( inputs_embeds = merge_multimodal_embeddings(
+1 -2
View File
@@ -598,8 +598,7 @@ class TarsierForConditionalGeneration(nn.Module, SupportsMultiModal,
multimodal_embeddings: Optional[MultiModalEmbeddings] = None, multimodal_embeddings: Optional[MultiModalEmbeddings] = None,
) -> torch.Tensor: ) -> torch.Tensor:
inputs_embeds = self.language_model.get_input_embeddings(input_ids) inputs_embeds = self.language_model.get_input_embeddings(input_ids)
if multimodal_embeddings is not None \ if multimodal_embeddings is not None:
and len(multimodal_embeddings) != 0:
inputs_embeds = merge_multimodal_embeddings( inputs_embeds = merge_multimodal_embeddings(
input_ids, input_ids,
inputs_embeds, inputs_embeds,
+1 -2
View File
@@ -560,8 +560,7 @@ class UltravoxModel(nn.Module, SupportsMultiModal, SupportsPP, SupportsLoRA):
multimodal_embeddings: Optional[MultiModalEmbeddings] = None, multimodal_embeddings: Optional[MultiModalEmbeddings] = None,
) -> torch.Tensor: ) -> torch.Tensor:
inputs_embeds = self.language_model.get_input_embeddings(input_ids) inputs_embeds = self.language_model.get_input_embeddings(input_ids)
if multimodal_embeddings is not None \ if multimodal_embeddings is not None:
and len(multimodal_embeddings) != 0:
# TODO(ywang96): remove this block after v0 is deprecated. # TODO(ywang96): remove this block after v0 is deprecated.
if not envs.VLLM_USE_V1: if not envs.VLLM_USE_V1:
+10 -12
View File
@@ -3,7 +3,6 @@
import pickle import pickle
from collections.abc import Iterable, Mapping from collections.abc import Iterable, Mapping
from typing import Union
import numpy as np import numpy as np
import torch import torch
@@ -24,11 +23,11 @@ A dictionary containing hashes for items in each modality.
class MultiModalHasher: class MultiModalHasher:
@classmethod @classmethod
def serialize_item(cls, obj: object) -> Union[bytes, memoryview]: def serialize_item(cls, obj: object) -> bytes:
# Simple cases # Simple cases
if isinstance(obj, str): if isinstance(obj, str):
return obj.encode("utf-8") return obj.encode("utf-8")
if isinstance(obj, (bytes, memoryview)): if isinstance(obj, bytes):
return obj return obj
if isinstance(obj, (int, float)): if isinstance(obj, (int, float)):
return np.array(obj).tobytes() return np.array(obj).tobytes()
@@ -39,13 +38,12 @@ class MultiModalHasher:
if isinstance(obj, torch.Tensor): if isinstance(obj, torch.Tensor):
return cls.item_to_bytes("tensor", obj.numpy()) return cls.item_to_bytes("tensor", obj.numpy())
if isinstance(obj, np.ndarray): if isinstance(obj, np.ndarray):
# If the array is non-contiguous, we need to copy it first return cls.item_to_bytes(
arr_data = obj.data if obj.flags.c_contiguous else obj.tobytes() "ndarray", {
return cls.item_to_bytes("ndarray", { "dtype": obj.dtype.str,
"dtype": obj.dtype.str, "shape": obj.shape,
"shape": obj.shape, "data": obj.tobytes(),
"data": arr_data, })
})
logger.warning( logger.warning(
"No serialization method found for %s. " "No serialization method found for %s. "
@@ -66,7 +64,7 @@ class MultiModalHasher:
cls, cls,
key: str, key: str,
obj: object, obj: object,
) -> Iterable[tuple[bytes, Union[bytes, memoryview]]]: ) -> Iterable[tuple[bytes, bytes]]:
# Recursive cases # Recursive cases
if isinstance(obj, (list, tuple)): if isinstance(obj, (list, tuple)):
for i, elem in enumerate(obj): for i, elem in enumerate(obj):
@@ -75,7 +73,7 @@ class MultiModalHasher:
for k, v in obj.items(): for k, v in obj.items():
yield from cls.iter_item_to_bytes(f"{key}.{k}", v) yield from cls.iter_item_to_bytes(f"{key}.{k}", v)
else: else:
key_bytes = key.encode("utf-8") key_bytes = cls.serialize_item(key)
value_bytes = cls.serialize_item(obj) value_bytes = cls.serialize_item(obj)
yield key_bytes, value_bytes yield key_bytes, value_bytes
+1 -1
View File
@@ -255,7 +255,7 @@ class CudaPlatformBase(Platform):
"install FlashInfer for better performance.") "install FlashInfer for better performance.")
pass pass
# FlashAttention is the default for SM 8.0+ GPUs # FlashAttention is the default for SM 8.0+ GPUs
if cls.has_device_capability(80): elif cls.has_device_capability(80):
logger.info_once("Using Flash Attention backend on V1 engine.") logger.info_once("Using Flash Attention backend on V1 engine.")
return ("vllm.v1.attention.backends." return ("vllm.v1.attention.backends."
"flash_attn.FlashAttentionBackend") "flash_attn.FlashAttentionBackend")
+2 -2
View File
@@ -198,8 +198,8 @@ class SamplingParams(
processor which only retains scores for the given token ids. processor which only retains scores for the given token ids.
Defaults to None. Defaults to None.
extra_args: Arbitrary additional args, that can be used by custom extra_args: Arbitrary additional args, that can be used by custom
sampling implementations, plugins, etc. Not used by any in-tree sampling implementations. Not used by any in-tree sampling
sampling implementations. implementations.
""" """
n: int = 1 n: int = 1
-192
View File
@@ -1,192 +0,0 @@
# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
from dataclasses import dataclass
from typing import TYPE_CHECKING
import torch
from vllm.attention.backends.abstract import AttentionBackend
from vllm.config import VllmConfig, get_layers_from_vllm_config
from vllm.model_executor.layers.mamba.mamba2_metadata import (
_query_start_loc_to_chunk_indices_offsets)
from vllm.v1.attention.backends.utils import (AttentionMetadataBuilder,
CommonAttentionMetadata)
from vllm.v1.kv_cache_interface import MambaSpec
from vllm.v1.worker.block_table import BlockTable
if TYPE_CHECKING:
from vllm.v1.core.sched.output import SchedulerOutput
from vllm.v1.worker.gpu_input_batch import InputBatch
from vllm.v1.worker.gpu_model_runner import GPUModelRunner
def get_mamba2_chunk_size(vllm_config: VllmConfig) -> int:
from vllm.model_executor.layers.mamba.mamba_mixer2 import MambaMixer2
layers = get_layers_from_vllm_config(vllm_config, MambaMixer2)
chunk_sizes = set(layer.chunk_size for layer in layers.values())
assert len(
chunk_sizes) == 1, "All Mamba2 layers must have the same chunk size"
return chunk_sizes.pop()
class Mamba2AttentionBackend(AttentionBackend):
@staticmethod
def get_builder_cls() -> type["Mamba2AttentionMetadataBuilder"]:
return Mamba2AttentionMetadataBuilder
@dataclass
class Mamba2AttentionMetadata:
num_prefills: int
num_prefill_tokens: int
num_decodes: int
num_decode_tokens: int
query_start_loc: torch.Tensor
seq_lens: torch.Tensor
has_initial_states: torch.Tensor
prep_initial_states: bool
chunk_size: int
seq_idx: torch.Tensor
chunk_indices: torch.Tensor
chunk_offsets: torch.Tensor
state_indices_tensor: torch.Tensor # shape: [batch,]
class Mamba2AttentionMetadataBuilder(
AttentionMetadataBuilder[Mamba2AttentionMetadata]):
def __init__(self, runner: "GPUModelRunner", kv_cache_spec: MambaSpec,
block_table: BlockTable):
self.runner = runner
self.kv_cache_spec = kv_cache_spec
self.block_table = block_table
self.chunk_size = get_mamba2_chunk_size(runner.vllm_config)
def reorder_batch(self, input_batch: "InputBatch",
scheduler_output: "SchedulerOutput") -> bool:
# NOTE (Chen): Copied from MLACommonMetadataBuilder and
# FlashInferMetadataBuilder. Should be refactored later to avoid code
# duplication of these 3 functions.
# We now want to reorder the batch so that the "decode" requests are and
# the front and the "prefill" requests are at the using the least amount
# swaps possible. (NOTE for now we loosely use "decode" to mean requests
# where attention is likely memory-bound and "prefill" to mean requests
# where attention is likely compute-bound, TODO(lucas): figure out a
# better naming here)
decodes = []
prefills = []
num_decode_tokens = 0
num_prefill_tokens = 0
for i, req_id in enumerate(input_batch.req_ids):
num_tokens = scheduler_output.num_scheduled_tokens[req_id]
# for now treat 1 scheduled token as "decode" even if its not,
# we should update this to something like < 8 in the future but
# currently the decode run only supports num_tokens = 1
if num_tokens == 1:
decodes.append(i)
num_decode_tokens += num_tokens
else:
prefills.append(i)
num_prefill_tokens += num_tokens
# We hope that this is fairly minimal since decodes
# should be around for a number of iterations so hopefully they are
# relatively stationary (and new request are generally appended to the
# persistent batch so already should be at the back)
# To achieve this we loop over the decodes in descending order and
# the prefills in ascending order. We swap decodes from the "back"
# i.e. past where the last decode should be in the reodorered with
# prefills from the front of the batch.
# `decodes` and `prefills` are already in ascending order just based on
# the above loop
num_decodes = len(decodes)
num_prefills = len(prefills)
modified_batch = False
for i in range(1, min(num_decodes, num_prefills) + 1):
# If the decode is at the "back" of the batch, i, we can swap it
# with the prefill closest to the front of the batch
decode_idx = decodes[num_decodes - i]
if decode_idx < num_decodes:
break
input_batch.swap_states(prefills[i - 1], decode_idx)
modified_batch = True
# Save for next `build` call
# TODO(lucas): this is a bit of a hack, we should probably have a
# better way of doing this
self._num_decodes = num_decodes
self._num_prefills = num_prefills
self._num_decode_tokens = num_decode_tokens
self._num_prefill_tokens = num_prefill_tokens
return modified_batch
def build(self, common_prefix_len: int,
common_attn_metadata: CommonAttentionMetadata):
num_reqs = common_attn_metadata.num_reqs
query_start_loc = common_attn_metadata.query_start_loc
seq_lens = common_attn_metadata.seq_lens
seq_idx = None
chunk_indices, chunk_offsets = None, None
# Need flags to indicate if there are initial states
# currently we really only support the FlashAttention backend
has_initial_states = None
prep_initial_states = False
state_indices_tensor = self.block_table.block_table[:num_reqs, 0]
# Compute seq_idx, chunk_indices and chunk_offsets for prefill only
if self._num_prefills > 0:
#[batch,]
has_initial_states_cpu = (
self.runner.input_batch.
num_computed_tokens_cpu_tensor[num_reqs -
self._num_prefills:num_reqs]
> 0)
prep_initial_states = torch.any(has_initial_states_cpu).item()
has_initial_states = has_initial_states_cpu.to(
query_start_loc.device)
query_start_loc_p = common_attn_metadata.query_start_loc[
-self._num_prefills - 1:] - self._num_decode_tokens
seq_idx = torch.repeat_interleave(
torch.arange(self._num_prefills,
dtype=torch.int32,
device=query_start_loc_p.device),
query_start_loc_p.diff(),
output_size=self._num_prefill_tokens)
seq_idx.unsqueeze_(0)
# We compute metadata for chunked prefill once at the top level
# model forward and reuse them in mamba layers. If not needed,
# they will be ignored inside mamba kernels.
if prep_initial_states:
chunk_indices, chunk_offsets = (
_query_start_loc_to_chunk_indices_offsets(
query_start_loc_p, self.chunk_size,
self._num_prefill_tokens))
attn_metadata = Mamba2AttentionMetadata(
num_prefills=self._num_prefills,
num_prefill_tokens=self._num_prefill_tokens,
num_decodes=self._num_decodes,
num_decode_tokens=self._num_decode_tokens,
query_start_loc=query_start_loc,
seq_lens=seq_lens,
has_initial_states=has_initial_states,
prep_initial_states=prep_initial_states,
chunk_size=self.chunk_size,
seq_idx=seq_idx,
chunk_indices=chunk_indices,
chunk_offsets=chunk_offsets,
state_indices_tensor=state_indices_tensor,
)
return attn_metadata
+1 -42
View File
@@ -8,7 +8,7 @@ from vllm.utils import cdiv
from vllm.v1.core.block_pool import BlockPool from vllm.v1.core.block_pool import BlockPool
from vllm.v1.core.kv_cache_utils import BlockHash, KVCacheBlock from vllm.v1.core.kv_cache_utils import BlockHash, KVCacheBlock
from vllm.v1.kv_cache_interface import (FullAttentionSpec, KVCacheSpec, from vllm.v1.kv_cache_interface import (FullAttentionSpec, KVCacheSpec,
MambaSpec, SlidingWindowSpec) SlidingWindowSpec)
from vllm.v1.request import Request from vllm.v1.request import Request
@@ -52,7 +52,6 @@ class SingleTypeKVCacheManager(ABC):
self.caching_hash_fn = caching_hash_fn self.caching_hash_fn = caching_hash_fn
self.kv_cache_group_id = kv_cache_group_id self.kv_cache_group_id = kv_cache_group_id
self._null_block = block_pool.null_block
def get_num_blocks_to_allocate( def get_num_blocks_to_allocate(
self, request_id: str, num_tokens: int, self, request_id: str, num_tokens: int,
@@ -391,49 +390,9 @@ class SlidingWindowManager(SingleTypeKVCacheManager):
return 0 return 0
class MambaManager(SingleTypeKVCacheManager):
@classmethod
def find_longest_cache_hit(
cls,
block_hashes: list[BlockHash],
max_length: int,
kv_cache_group_ids: list[int],
block_pool: BlockPool,
kv_cache_spec: KVCacheSpec,
use_eagle: bool,
) -> tuple[list[KVCacheBlock], ...]:
assert isinstance(
kv_cache_spec,
MambaSpec), ("MambaManager can only be used for mamba groups")
# Prefix caching is not supported for mamba now. Always return empty
# list.
computed_blocks: tuple[list[KVCacheBlock], ...] = tuple(
[] for _ in range(len(kv_cache_group_ids)))
return computed_blocks
def remove_skipped_blocks(self, request_id: str,
num_computed_tokens: int) -> None:
# Each request will always have 1 block at this moment, so no need to
# remove blocks.
pass
def get_num_common_prefix_blocks(self, request_id: str,
num_running_requests: int) -> int:
return 0
def allocate_new_blocks(self, request_id: str,
num_tokens: int) -> list[KVCacheBlock]:
new_blocks = super().allocate_new_blocks(request_id, num_tokens)
assert len(self.req_to_blocks[request_id]) == 1, (
"MambaManager should only allocate 1 block for each request.")
return new_blocks
spec_manager_map: dict[type[KVCacheSpec], type[SingleTypeKVCacheManager]] = { spec_manager_map: dict[type[KVCacheSpec], type[SingleTypeKVCacheManager]] = {
FullAttentionSpec: FullAttentionManager, FullAttentionSpec: FullAttentionManager,
SlidingWindowSpec: SlidingWindowManager, SlidingWindowSpec: SlidingWindowManager,
MambaSpec: MambaManager,
} }
-24
View File
@@ -3,7 +3,6 @@
import copy import copy
from dataclasses import dataclass from dataclasses import dataclass
from math import prod
from typing import Optional from typing import Optional
import torch import torch
@@ -155,29 +154,6 @@ class SlidingWindowSpec(AttentionSpec):
return (cdiv(num_tokens, self.block_size) + 1) * self.page_size_bytes return (cdiv(num_tokens, self.block_size) + 1) * self.page_size_bytes
@dataclass
class MambaSpec(KVCacheSpec):
shapes: tuple[tuple[int, ...], ...]
dtype: torch.dtype
def __post_init__(self):
self.num_elements = sum(prod(shape) for shape in self.shapes)
@property
def type_id(self) -> str:
return f"mamba_{self.shapes}_{self.dtype}"
@property
def page_size_bytes(self) -> int:
return self.num_elements * get_dtype_size(self.dtype)
def max_memory_usage_bytes(self, vllm_config: VllmConfig) -> int:
# We allocate 1 block for each request now, so max_memory_usage_bytes is
# the same as page_size_bytes.
# Need to update this when supporting prefix caching.
return self.page_size_bytes
@dataclass @dataclass
class KVCacheTensor: class KVCacheTensor:
""" """
+1 -1
View File
@@ -140,7 +140,7 @@ class MsgpackEncoder:
) -> tuple[str, tuple[int, ...], Union[int, memoryview]]: ) -> tuple[str, tuple[int, ...], Union[int, memoryview]]:
assert self.aux_buffers is not None assert self.aux_buffers is not None
# If the array is non-contiguous, we need to copy it first # If the array is non-contiguous, we need to copy it first
arr_data = obj.data if obj.flags.c_contiguous else obj.tobytes() arr_data = obj.data if obj.data.c_contiguous else obj.tobytes()
if not obj.shape or obj.nbytes < self.size_threshold: if not obj.shape or obj.nbytes < self.size_threshold:
# Encode small arrays and scalars inline. Using this extension type # Encode small arrays and scalars inline. Using this extension type
# ensures we can avoid copying when decoding. # ensures we can avoid copying when decoding.
+31 -79
View File
@@ -18,7 +18,6 @@ import vllm.envs as envs
from vllm.attention import AttentionType, get_attn_backend from vllm.attention import AttentionType, get_attn_backend
from vllm.attention.backends.abstract import AttentionBackend from vllm.attention.backends.abstract import AttentionBackend
from vllm.attention.layer import Attention from vllm.attention.layer import Attention
from vllm.compilation.counter import compilation_counter
from vllm.config import (CompilationLevel, VllmConfig, from vllm.config import (CompilationLevel, VllmConfig,
get_layers_from_vllm_config) get_layers_from_vllm_config)
from vllm.distributed.kv_transfer import (get_kv_transfer_group, from vllm.distributed.kv_transfer import (get_kv_transfer_group,
@@ -30,7 +29,6 @@ from vllm.distributed.parallel_state import (
from vllm.forward_context import (DPMetadata, get_forward_context, from vllm.forward_context import (DPMetadata, get_forward_context,
set_forward_context) set_forward_context)
from vllm.logger import init_logger from vllm.logger import init_logger
from vllm.model_executor.layers.mamba.mamba_mixer2 import MambaMixer2
from vllm.model_executor.layers.rotary_embedding import MRotaryEmbedding from vllm.model_executor.layers.rotary_embedding import MRotaryEmbedding
from vllm.model_executor.model_loader import TensorizerLoader, get_model_loader from vllm.model_executor.model_loader import TensorizerLoader, get_model_loader
from vllm.multimodal import MULTIMODAL_REGISTRY from vllm.multimodal import MULTIMODAL_REGISTRY
@@ -40,14 +38,12 @@ from vllm.sampling_params import SamplingType
from vllm.sequence import IntermediateTensors from vllm.sequence import IntermediateTensors
from vllm.utils import (STR_DTYPE_TO_TORCH_DTYPE, DeviceMemoryProfiler, from vllm.utils import (STR_DTYPE_TO_TORCH_DTYPE, DeviceMemoryProfiler,
GiB_bytes, LazyLoader, async_tensor_h2d, cdiv, GiB_bytes, LazyLoader, async_tensor_h2d, cdiv,
check_use_alibi, get_dtype_size, check_use_alibi, is_pin_memory_available)
is_pin_memory_available)
from vllm.v1.attention.backends.mamba_attn import Mamba2AttentionBackend
from vllm.v1.attention.backends.utils import (AttentionMetadataBuilder, from vllm.v1.attention.backends.utils import (AttentionMetadataBuilder,
CommonAttentionMetadata) CommonAttentionMetadata)
from vllm.v1.core.encoder_cache_manager import compute_encoder_budget from vllm.v1.core.encoder_cache_manager import compute_encoder_budget
from vllm.v1.kv_cache_interface import (AttentionSpec, FullAttentionSpec, from vllm.v1.kv_cache_interface import (AttentionSpec, FullAttentionSpec,
KVCacheConfig, KVCacheSpec, MambaSpec, KVCacheConfig, KVCacheSpec,
SlidingWindowSpec) SlidingWindowSpec)
from vllm.v1.outputs import (EMPTY_MODEL_RUNNER_OUTPUT, LogprobsTensors, from vllm.v1.outputs import (EMPTY_MODEL_RUNNER_OUTPUT, LogprobsTensors,
ModelRunnerOutput) ModelRunnerOutput)
@@ -201,11 +197,9 @@ class GPUModelRunner(LoRAModelRunnerMixin):
block_sizes=[self.cache_config.block_size], block_sizes=[self.cache_config.block_size],
) )
self.use_cuda_graph = ( self.use_cuda_graph = (self.compilation_config.level
self.vllm_config.compilation_config.level == CompilationLevel.PIECEWISE
== CompilationLevel.PIECEWISE and not self.model_config.enforce_eager)
and self.vllm_config.compilation_config.use_cudagraph
and not self.model_config.enforce_eager)
# TODO(woosuk): Provide an option to tune the max cudagraph batch size. # TODO(woosuk): Provide an option to tune the max cudagraph batch size.
# The convention is different. # The convention is different.
# self.cudagraph_batch_sizes sorts in ascending order. # self.cudagraph_batch_sizes sorts in ascending order.
@@ -2061,13 +2055,10 @@ class GPUModelRunner(LoRAModelRunnerMixin):
def capture_model(self) -> None: def capture_model(self) -> None:
if not self.use_cuda_graph: if not self.use_cuda_graph:
logger.warning( logger.warning(
"Skipping CUDA graph capture. To turn on CUDA graph capture, " "Skipping CUDA graph capture. Please add "
"set -O %s and ensure `use_cudagraph` was not manually set to " "-O %s to use CUDA graphs.", CompilationLevel.PIECEWISE)
"False", CompilationLevel.PIECEWISE)
return return
compilation_counter.num_gpu_runner_capture_triggers += 1
start_time = time.perf_counter() start_time = time.perf_counter()
start_free_gpu_memory = torch.cuda.mem_get_info()[0] start_free_gpu_memory = torch.cuda.mem_get_info()[0]
@@ -2102,31 +2093,28 @@ class GPUModelRunner(LoRAModelRunnerMixin):
for i, kv_cache_group_spec in enumerate( for i, kv_cache_group_spec in enumerate(
kv_cache_config.kv_cache_groups): kv_cache_config.kv_cache_groups):
kv_cache_spec = kv_cache_group_spec.kv_cache_spec kv_cache_spec = kv_cache_group_spec.kv_cache_spec
if isinstance(kv_cache_spec, AttentionSpec): if not isinstance(kv_cache_spec, AttentionSpec):
attn_backend_i = get_attn_backend( raise NotImplementedError(
kv_cache_spec.head_size, "Only AttentionSpec is supported for now.")
self.dtype, attn_backend_i = get_attn_backend(
kv_cache_spec.dtype, kv_cache_spec.head_size,
kv_cache_spec.block_size, self.dtype,
self.model_config.is_attention_free, kv_cache_spec.dtype,
use_mla=kv_cache_spec.use_mla, kv_cache_spec.block_size,
) self.model_config.is_attention_free,
if attn_backend_i is None: use_mla=kv_cache_spec.use_mla,
error_msg = (f"Error with get_attn_backend: " )
f"{kv_cache_spec.head_size=}, " if attn_backend_i is None:
f"{self.dtype=}, {kv_cache_spec.dtype=}, " error_msg = (
f"{kv_cache_spec.block_size=}, " f"Error with get_attn_backend: {kv_cache_spec.head_size=}, "
f"{self.model_config.is_attention_free=}, " f"{self.dtype=}, {kv_cache_spec.dtype=}, "
f"{kv_cache_spec.use_mla=}") f"{kv_cache_spec.block_size=}, "
logger.error(error_msg) f"{self.model_config.is_attention_free=}, "
raise NotImplementedError( f"{kv_cache_spec.use_mla=}")
"Non-Attention backend is not supported by V1 " logger.error(error_msg)
"GPUModelRunner.") raise NotImplementedError(
elif isinstance(kv_cache_spec, MambaSpec): "Non-Attention backend is not supported by V1 "
attn_backend_i = Mamba2AttentionBackend "GPUModelRunner.")
else:
raise ValueError(
f"Unknown KV cache spec type: {type(kv_cache_spec)}")
block_table_i = self.input_batch.block_table[i] block_table_i = self.input_batch.block_table[i]
attn_metadata_builder_i = attn_backend_i.get_builder_cls()( attn_metadata_builder_i = attn_backend_i.get_builder_cls()(
@@ -2254,22 +2242,6 @@ class GPUModelRunner(LoRAModelRunnerMixin):
kv_caches[layer_name] = kv_cache_raw_tensors[ kv_caches[layer_name] = kv_cache_raw_tensors[
layer_name].view(dtype).view(kv_cache_shape).permute( layer_name].view(dtype).view(kv_cache_shape).permute(
*inv_order) *inv_order)
elif isinstance(kv_cache_spec, MambaSpec):
raw_tensor = kv_cache_raw_tensors[layer_name]
dtype = kv_cache_spec.dtype
state_tensors = []
start_pos = 0
for shape in kv_cache_spec.shapes:
target_shape = (num_blocks, *shape)
size_in_bytes = np.prod(shape) * get_dtype_size(
dtype) * num_blocks
tensor = raw_tensor[start_pos:start_pos +
size_in_bytes]
tensor = tensor.view(dtype).view(target_shape)
state_tensors.append(tensor)
start_pos += size_in_bytes
assert start_pos == raw_tensor.numel()
kv_caches[layer_name] = tuple(state_tensors)
else: else:
raise NotImplementedError raise NotImplementedError
return kv_caches return kv_caches
@@ -2335,11 +2307,11 @@ class GPUModelRunner(LoRAModelRunnerMixin):
format. Layers that do not need KV cache are not included. format. Layers that do not need KV cache are not included.
""" """
layers = get_layers_from_vllm_config(self.vllm_config, Attention)
block_size = self.vllm_config.cache_config.block_size block_size = self.vllm_config.cache_config.block_size
use_mla = self.vllm_config.model_config.use_mla use_mla = self.vllm_config.model_config.use_mla
kv_cache_spec: dict[str, KVCacheSpec] = {} kv_cache_spec: dict[str, KVCacheSpec] = {}
attn_layers = get_layers_from_vllm_config(self.vllm_config, Attention) for layer_name, attn_module in layers.items():
for layer_name, attn_module in attn_layers.items():
if (kv_tgt_layer := if (kv_tgt_layer :=
attn_module.kv_sharing_target_layer_name) is not None: attn_module.kv_sharing_target_layer_name) is not None:
# The layer doesn't need its own KV cache and will use that of # The layer doesn't need its own KV cache and will use that of
@@ -2379,24 +2351,4 @@ class GPUModelRunner(LoRAModelRunnerMixin):
raise ValueError( raise ValueError(
f"Unknown attention type: {attn_module.attn_type}") f"Unknown attention type: {attn_module.attn_type}")
mamba_layers = get_layers_from_vllm_config(self.vllm_config,
MambaMixer2)
if len(mamba_layers) > 0:
if self.vllm_config.speculative_config is not None:
raise NotImplementedError(
"Mamba with speculative decoding is not supported yet.")
if not self.vllm_config.model_config.enforce_eager:
raise NotImplementedError(
"Mamba with cuda graph is not supported yet.")
if self.vllm_config.cache_config.enable_prefix_caching:
raise NotImplementedError(
"Prefix caching is not supported for Mamba yet.")
max_model_len = self.vllm_config.model_config.max_model_len
# Set block_size to max_model_len, so that mamba model will always
# have only one block in the KV cache.
for layer_name, mamba_module in mamba_layers.items():
kv_cache_spec[layer_name] = MambaSpec(
shapes=mamba_module.get_state_shape(),
dtype=self.kv_cache_dtype,
block_size=max_model_len)
return kv_cache_spec return kv_cache_spec