A high-throughput and memory-efficient inference and serving engine for LLMs
Mirrored from https://github.com/tduncan-lum/Nvidia-Optimization-Stack---VLLM.git
Synced Nov 15, 01:00 UTC
[V1] [Hybrid] Enable piecewise CUDA Graph for mamba layers (#21194)
Signed-off-by: Thomas Parnell <tpa@zurich.ibm.com>
Thomas Parnell committed
Jul 19, 2025 at 21:27 UTC
881e3cbe3b3cef5d6fc50ca0c19e30a9dd11c452
10 files changed
+100
-31
tests/models/language/generation/test_hybrid.py
-1
@@ -104,7 +104,6 @@ def test_models(
104
m.setenv("VLLM_ATTENTION_BACKEND", "FLASHINFER")
105
with vllm_runner(model,
106
max_num_seqs=MAX_NUM_SEQS,
107
- enforce_eager=True,
107
enable_prefix_caching=False) as vllm_model:
108
vllm_v1_outputs = vllm_model.generate_greedy_logprobs(
109
example_prompts, max_tokens, num_logprobs)
vllm/config.py
+1
@@ -4312,6 +4312,7 @@ class CompilationConfig:
4312
self.splitting_ops = [] if self.full_cuda_graph else [
4313
"vllm.unified_attention",
4314
"vllm.unified_attention_with_output",
4315
+ "vllm.mamba_mixer2",
4316
]
4317
4318
vllm/model_executor/layers/mamba/mamba_mixer2.py
+66
-9
@@ -13,7 +13,7 @@ from vllm.distributed import (divide, get_tensor_model_parallel_rank,
13
get_tensor_model_parallel_world_size,
14
tensor_model_parallel_all_gather,
15
tensor_model_parallel_all_reduce)
16
-from vllm.forward_context import get_forward_context
16
+from vllm.forward_context import ForwardContext, get_forward_context
17
from vllm.model_executor.custom_op import CustomOp
18
from vllm.model_executor.layers.linear import (ColumnParallelLinear,
19
RowParallelLinear)
@@ -33,6 +33,8 @@ from vllm.model_executor.model_loader.weight_utils import (
33
LoaderFunction, composed_weight_loader, sharded_weight_loader)
34
from vllm.model_executor.models.mamba_cache import MambaCacheParams
35
from vllm.model_executor.utils import set_weight_attrs
36
+from vllm.platforms import current_platform
37
+from vllm.utils import direct_register_custom_op
38
from vllm.v1.attention.backends.mamba_attn import Mamba2AttentionMetadata
39
40
# Added by the IBM Team, 2024
@@ -424,14 +426,36 @@ class MambaMixer2(MambaBase, CustomOp):
426
def forward_native(
427
self,
428
hidden_states: torch.Tensor,
427
- conv_state: torch.Tensor,
428
- ssm_state: torch.Tensor,
429
+ output: torch.Tensor,
430
+ mamba_cache_params: MambaCacheParams,
431
+ mamba2_metadata: Mamba2Metadata,
432
+ mup_vector: Optional[torch.Tensor] = None,
433
):
434
pass
435
436
+ def forward(
437
+ self,
438
+ hidden_states: torch.Tensor,
439
+ output: torch.Tensor,
440
+ mamba_cache_params: MambaCacheParams,
441
+ mamba2_metadata: Mamba2Metadata,
442
+ mup_vector: Optional[torch.Tensor] = None,
443
+ ):
444
+ if not envs.VLLM_USE_V1:
445
+ CustomOp.forward(self, hidden_states, output, mamba_cache_params,
446
+ mamba2_metadata, mup_vector)
447
+ else:
448
+ torch.ops.vllm.mamba_mixer2(
449
+ hidden_states,
450
+ output,
451
+ self.prefix,
452
+ mup_vector,
453
+ )
454
+
455
def forward_cuda(
456
self,
457
hidden_states: torch.Tensor,
458
+ output: torch.Tensor,
459
mamba_cache_params: MambaCacheParams,
460
mamba2_metadata: Mamba2Metadata,
461
mup_vector: Optional[torch.Tensor] = None,
@@ -517,6 +541,7 @@ class MambaMixer2(MambaBase, CustomOp):
541
num_prefill_tokens = attn_metadata.num_prefill_tokens # token count
542
has_prefill = num_prefills > 0
543
has_decode = num_decodes > 0
544
+ num_actual_tokens = num_prefill_tokens + num_decodes
545
546
# NOTE: V0 put prefill before decode, v1 puts decode before prefill
547
# Separate prefill and decode by splitting varlen input
@@ -524,18 +549,18 @@ class MambaMixer2(MambaBase, CustomOp):
549
# NOTE: V0 put prefill before decode, v1 puts decode before prefill
550
if envs.VLLM_USE_V1:
551
hidden_states_B_C_d, hidden_states_B_C_p = torch.split(
527
- hidden_states_B_C,
552
+ hidden_states_B_C[:num_actual_tokens],
553
[num_decodes, num_prefill_tokens],
554
dim=0,
555
)
556
dt_d, dt_p = torch.split(
532
- dt,
557
+ dt[:num_actual_tokens],
558
[num_decodes, num_prefill_tokens],
559
dim=0,
560
)
561
# Split along batch dimension
562
state_indices_tensor_d, state_indices_tensor_p = torch.split(
538
- state_indices_tensor,
563
+ state_indices_tensor[:num_actual_tokens],
564
[num_decodes, num_prefills],
565
dim=0,
566
)
@@ -696,11 +721,10 @@ class MambaMixer2(MambaBase, CustomOp):
721
# GatedRMSNorm internally applying SiLU to the gate
722
# SiLU is applied internally before normalization, unlike standard
723
# norm usage
699
- hidden_states = self.norm(hidden_states, gate)
724
+ hidden_states = self.norm(hidden_states, gate[:num_actual_tokens])
725
726
# 5. Final linear projection
702
- out, _ = self.out_proj(hidden_states)
703
- return out
727
+ output[:num_actual_tokens], _ = self.out_proj(hidden_states)
728
729
def get_state_shape(self) -> tuple[tuple[int, ...], tuple[int, ...]]:
730
return get_mamba_state_shape(
@@ -712,3 +736,36 @@ class MambaMixer2(MambaBase, CustomOp):
736
state_size=self.ssm_state_size,
737
conv_kernel=self.conv_kernel_size,
738
)
739
+
740
+
741
+def mamba_mixer2(
742
+ hidden_states: torch.Tensor,
743
+ output: torch.Tensor,
744
+ layer_name: str,
745
+ mup_vector: Optional[torch.Tensor] = None,
746
+) -> None:
747
+ forward_context: ForwardContext = get_forward_context()
748
+ self = forward_context.no_compile_layers[layer_name]
749
+ self.forward_cuda(hidden_states=hidden_states,
750
+ output=output,
751
+ mamba_cache_params=None,
752
+ mamba2_metadata=None,
753
+ mup_vector=mup_vector)
754
+
755
+
756
+def mamba_mixer2_fake(
757
+ hidden_states: torch.Tensor,
758
+ output: torch.Tensor,
759
+ layer_name: str,
760
+ mup_vector: Optional[torch.Tensor] = None,
761
+) -> None:
762
+ return
763
+
764
+
765
+direct_register_custom_op(
766
+ op_name="mamba_mixer2",
767
+ op_func=mamba_mixer2,
768
+ mutates_args=["output"],
769
+ fake_impl=mamba_mixer2_fake,
770
+ dispatch_key=current_platform.dispatch_key,
771
+)
vllm/model_executor/models/bamba.py
+6
-5
@@ -11,6 +11,7 @@ from transformers import BambaConfig
11
12
from vllm import envs
13
from vllm.attention.layer import Attention
14
+from vllm.compilation.decorators import support_torch_compile
15
from vllm.config import CacheConfig, VllmConfig
16
from vllm.distributed import get_tensor_model_parallel_world_size
17
from vllm.distributed.parallel_state import get_pp_group
@@ -122,11 +123,10 @@ class BambaMixerDecoderLayer(nn.Module):
123
hidden_states, residual = self.input_layernorm(
124
hidden_states, residual)
125
125
- hidden_states = self.mamba(hidden_states, mamba_cache_params,
126
- mamba2_metadata)
126
+ output = torch.empty_like(hidden_states)
127
+ self.mamba(hidden_states, output, mamba_cache_params, mamba2_metadata)
128
# Fully Connected
128
- hidden_states, residual = self.pre_ff_layernorm(
129
- hidden_states, residual)
129
+ hidden_states, residual = self.pre_ff_layernorm(output, residual)
130
hidden_states = self.feed_forward(hidden_states)
131
return hidden_states, residual
132
@@ -169,7 +169,7 @@ class BambaAttentionDecoderLayer(nn.Module):
169
self.max_position_embeddings = max_position_embeddings
170
171
if hasattr(config, "partial_rotary_factor"):
172
- rotary_dim = self.head_dim * config.partial_rotary_factor
172
+ rotary_dim = int(self.head_dim * config.partial_rotary_factor)
173
elif hasattr(config, "attn_rotary_emb"):
174
rotary_dim = config.attn_rotary_emb # for backward compatibility
175
else:
@@ -258,6 +258,7 @@ ALL_DECODER_LAYER_TYPES = {
258
}
259
260
261
+@support_torch_compile
262
class BambaModel(nn.Module):
263
264
def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""):
vllm/model_executor/models/falcon_h1.py
+6
-2
@@ -10,6 +10,7 @@ from transformers import FalconH1Config
10
11
from vllm import envs
12
from vllm.attention.layer import Attention
13
+from vllm.compilation.decorators import support_torch_compile
14
from vllm.config import CacheConfig, VllmConfig
15
from vllm.distributed import get_tensor_model_parallel_world_size
16
from vllm.distributed.parallel_state import get_pp_group
@@ -179,13 +180,15 @@ class FalconH1SSMDecoderLayer(nn.Module):
180
mamba2_metadata: Mamba2Metadata,
181
**kwargs,
182
):
182
- hidden_states = self.mamba(
183
+ output = torch.empty_like(hidden_states)
184
+ self.mamba(
185
hidden_states,
186
+ output,
187
mamba_cache_params,
188
mamba2_metadata=mamba2_metadata,
189
mup_vector=self.mup_vector,
190
)
188
- return hidden_states, residual
191
+ return output, residual
192
193
194
class FalconH1AttentionDecoderLayer(nn.Module):
@@ -398,6 +401,7 @@ class FalconH1ParallelHybrid(nn.Module):
401
return hidden_states
402
403
404
+@support_torch_compile
405
class FalconH1Model(nn.Module):
406
407
def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""):
vllm/model_executor/models/granitemoehybrid.py
+5
-3
@@ -11,6 +11,7 @@ from transformers import GraniteMoeHybridConfig
11
12
from vllm import envs
13
from vllm.attention.layer import Attention
14
+from vllm.compilation.decorators import support_torch_compile
15
from vllm.config import CacheConfig, VllmConfig
16
from vllm.distributed import get_tensor_model_parallel_world_size
17
from vllm.distributed.parallel_state import get_pp_group
@@ -104,9 +105,9 @@ class GraniteMoeHybridMambaDecoderLayer(nn.Module):
105
):
106
residual = hidden_states
107
hidden_states = self.input_layernorm(hidden_states)
107
- hidden_states = self.mamba(hidden_states, mamba_cache_params,
108
- mamba2_metadata)
109
- hidden_states = residual + hidden_states * self.residual_multiplier
108
+ output = torch.empty_like(hidden_states)
109
+ self.mamba(hidden_states, output, mamba_cache_params, mamba2_metadata)
110
+ hidden_states = residual + output * self.residual_multiplier
111
112
residual = hidden_states
113
hidden_states = self.post_attention_layernorm(hidden_states)
@@ -307,6 +308,7 @@ ALL_DECODER_LAYER_TYPES = {
308
}
309
310
311
+@support_torch_compile
312
class GraniteMoeHybridModel(nn.Module):
313
314
def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""):
vllm/model_executor/models/mamba2.py
+5
-3
@@ -10,6 +10,7 @@ from transformers import MambaConfig
10
11
from vllm import envs
12
from vllm.attention.backends.abstract import AttentionMetadata
13
+from vllm.compilation.decorators import support_torch_compile
14
from vllm.config import VllmConfig
15
from vllm.distributed.parallel_state import get_pp_group
16
from vllm.forward_context import get_forward_context
@@ -79,11 +80,12 @@ class Mamba2DecoderLayer(nn.Module):
80
else:
81
hidden_states, residual = self.norm(hidden_states, residual)
82
82
- hidden_states = self.mixer(hidden_states, mamba_cache_params,
83
- mamba2_metadata)
84
- return hidden_states, residual
83
+ output = torch.empty_like(hidden_states)
84
+ self.mixer(hidden_states, output, mamba_cache_params, mamba2_metadata)
85
+ return output, residual
86
87
88
+@support_torch_compile
89
class Mamba2Model(nn.Module):
90
91
def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""):
vllm/model_executor/models/nemotron_h.py
+5
-3
@@ -25,6 +25,7 @@ from torch import nn
25
26
from vllm import envs
27
from vllm.attention.layer import Attention
28
+from vllm.compilation.decorators import support_torch_compile
29
from vllm.config import CacheConfig, VllmConfig
30
from vllm.distributed import get_tensor_model_parallel_world_size
31
from vllm.distributed.parallel_state import get_pp_group
@@ -172,9 +173,9 @@ class NemotronHMambaDecoderLayer(nn.Module):
173
else:
174
hidden_states, residual = self.norm(hidden_states, residual)
175
175
- hidden_states = self.mixer(hidden_states, mamba_cache_params,
176
- mamba2_metadata)
177
- return hidden_states, residual
176
+ output = torch.empty_like(hidden_states)
177
+ self.mixer(hidden_states, output, mamba_cache_params, mamba2_metadata)
178
+ return output, residual
179
180
181
class NemotronHAttention(nn.Module):
@@ -292,6 +293,7 @@ ALL_DECODER_LAYER_TYPES = {
293
}
294
295
296
+@support_torch_compile
297
class NemotronHModel(nn.Module):
298
299
def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""):
vllm/model_executor/models/zamba2.py
+6
-2
@@ -17,6 +17,7 @@ from transformers import Zamba2Config
17
18
from vllm import envs
19
from vllm.attention.layer import Attention
20
+from vllm.compilation.decorators import support_torch_compile
21
from vllm.config import CacheConfig, VllmConfig
22
from vllm.distributed import get_tensor_model_parallel_world_size
23
from vllm.forward_context import get_forward_context
@@ -548,14 +549,16 @@ class Zamba2MambaDecoderLayer(nn.Module):
549
hidden_states = self.input_layernorm(hidden_states)
550
551
# Process through Mamba mixer
551
- hidden_states = self.mamba(
552
+ output = torch.empty_like(hidden_states)
553
+ self.mamba(
554
hidden_states,
555
+ output,
556
mamba_cache_params=mamba_cache_params,
557
mamba2_metadata=mamba2_metadata,
558
)
559
560
# residual connection after mamba
558
- hidden_states = residual + hidden_states
561
+ hidden_states = residual + output
562
563
return hidden_states
564
@@ -646,6 +649,7 @@ class Zamba2HybridLayer(nn.Module):
649
return layer_outputs
650
651
652
+@support_torch_compile
653
class Zamba2Model(nn.Module):
654
"""Core Zamba2 model combining transformer and Mamba architectures.
655
vllm/v1/worker/gpu_model_runner.py
-3
@@ -2753,9 +2753,6 @@ class GPUModelRunner(LoRAModelRunnerMixin):
2753
if self.vllm_config.speculative_config is not None:
2754
raise NotImplementedError(
2755
"Mamba with speculative decoding is not supported yet.")
2756
- if not self.vllm_config.model_config.enforce_eager:
2757
- raise NotImplementedError(
2758
- "Mamba with cuda graph is not supported yet.")
2756
if self.vllm_config.cache_config.enable_prefix_caching:
2757
raise NotImplementedError(
2758
"Prefix caching is not supported for Mamba yet.")