main
py 224 lines 7.18 KB
Raw
1 # SPDX-License-Identifier: Apache-2.0
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3 import pytest
4
5 from vllm._aiter_ops import is_aiter_found_and_supported
6 from vllm.platforms import current_platform
7 from vllm.utils.flashinfer import has_flashinfer
8 from vllm.v1.attention.backends.registry import AttentionBackendEnum
9
10 from .common import AttentionBackendCase, Matches, ModelFusionInfo, is_blackwell
11
12 # Attn backends
13 FLASHINFER_ATTN = pytest.param(
14 AttentionBackendCase(
15 backend=AttentionBackendEnum.FLASHINFER,
16 model_kwargs=dict(kv_cache_dtype="fp8"),
17 ),
18 id="FLASHINFER",
19 marks=pytest.mark.skipif(
20 not is_blackwell() or not has_flashinfer(),
21 reason="FI backend requires Blackwell and FlashInfer",
22 ),
23 )
24
25 TRITON_ATTN = pytest.param(
26 AttentionBackendCase(backend=AttentionBackendEnum.TRITON_ATTN), id="TRITON_ATTN"
27 )
28
29 ROCM_ATTN = pytest.param(
30 AttentionBackendCase(backend=AttentionBackendEnum.ROCM_ATTN),
31 id="ROCM_ATTN",
32 marks=pytest.mark.skipif(
33 not current_platform.is_rocm(),
34 reason="ROCm attention only for AMD",
35 ),
36 )
37
38 ROCM_AITER_UNIFIED_ATTN = pytest.param(
39 AttentionBackendCase(backend=AttentionBackendEnum.ROCM_AITER_UNIFIED_ATTN),
40 id="ROCM_AITER_UNIFIED_ATTN",
41 marks=pytest.mark.skipif(
42 not is_aiter_found_and_supported(),
43 reason="ROCM_AITER_UNIFIED_ATTN only for AMD when AITER is installed",
44 ),
45 )
46
47 ROCM_AITER_MLA_ATTN = pytest.param(
48 AttentionBackendCase(backend=AttentionBackendEnum.ROCM_AITER_MLA),
49 id="ROCM_AITER_MLA",
50 marks=pytest.mark.skipif(
51 not is_aiter_found_and_supported(),
52 reason="ROCM_AITER_MLA only for AMD when AITER is installed",
53 ),
54 )
55
56 FLASHINFER_MLA_ATTN = pytest.param(
57 AttentionBackendCase(backend=AttentionBackendEnum.FLASHINFER_MLA),
58 id="FLASHINFER_MLA",
59 marks=pytest.mark.skipif(
60 not is_blackwell() or not has_flashinfer(),
61 reason="FI backend requires Blackwell and FlashInfer",
62 ),
63 )
64
65 TRITON_MLA_ATTN = pytest.param(
66 AttentionBackendCase(backend=AttentionBackendEnum.TRITON_MLA),
67 id="TRITON_MLA",
68 )
69
70 # Models
71 llama3_8b = ModelFusionInfo(
72 model_name="meta-llama/Llama-3.1-8B-Instruct",
73 matches=lambda n_layers: Matches(
74 ar_rms_fusion=n_layers * 2 + 1,
75 aiter_ar_rms_fusion=n_layers * 2 + 1,
76 sequence_parallel=n_layers * 2 + 1,
77 async_tp=n_layers * 4,
78 ),
79 )
80
81 llama3_8b_fp8 = ModelFusionInfo(
82 model_name="RedHatAI/Meta-Llama-3.1-8B-Instruct-FP8",
83 matches=lambda n_layers: Matches(
84 rms_quant_fusion=n_layers * 2,
85 act_quant_fusion=n_layers,
86 attn_quant_fusion=n_layers,
87 ar_rms_fusion=n_layers * 2 + 1,
88 sequence_parallel=n_layers * 2 + 1,
89 async_tp=n_layers * 4,
90 ),
91 )
92
93 llama3_8b_fp4 = ModelFusionInfo(
94 model_name="nvidia/Llama-3.1-8B-Instruct-FP4",
95 matches=lambda n_layers: Matches(
96 act_quant_fusion=n_layers,
97 attn_quant_fusion=n_layers,
98 ar_rms_fusion=n_layers * 2 + 1,
99 sequence_parallel=n_layers * 2 + 1,
100 async_tp=n_layers * 4,
101 ),
102 )
103
104 # ModelOpt MIXED_PRECISION: FP8 attention projections, NVFP4 MLPs. It resolves to
105 # modelopt_mixed, not modelopt_fp4, and its MLP has no manual act+quant fusion, so
106 # every MLP boundary is left to ActivationQuantFusionPass.
107 qwen3_8_27b_mixed_fp4 = ModelFusionInfo(
108 model_name="nvidia/Qwen3.8-27B-NVFP4",
109 hf_overrides=lambda n_layers: {
110 "text_config": {
111 "num_hidden_layers": n_layers,
112 "layer_types": ["linear_attention"] * (n_layers - 1) + ["full_attention"],
113 }
114 },
115 matches=lambda n_layers: Matches(act_quant_fusion=n_layers),
116 )
117
118 # MoEs cannot do act+quant fusion because those ops are hidden from torch.compile.
119 # MoEs also only expose 1 rms+quant fusion because the quant for up_proj is hidden.
120 # TODO(luka): https://github.com/vllm-project/vllm/issues/31985
121 # Also, for MoEs, gemm+collective fusion only happens for dense GEMMs (o_proj/qkv proj)
122
123 llama4_scout_fp8 = ModelFusionInfo(
124 model_name="nvidia/Llama-4-Scout-17B-16E-Instruct-FP8",
125 hf_overrides=lambda n_layers: {"text_config": {"num_hidden_layers": n_layers}},
126 matches=lambda n_layers: Matches(
127 rms_quant_fusion=n_layers,
128 attn_quant_fusion=n_layers,
129 ar_rms_fusion=n_layers * 2,
130 sequence_parallel=n_layers * 2,
131 async_tp=n_layers * 2 - 1,
132 ),
133 )
134
135 llama4_scout_fp4 = ModelFusionInfo(
136 model_name="nvidia/Llama-4-Scout-17B-16E-Instruct-NVFP4",
137 hf_overrides=lambda n_layers: {"text_config": {"num_hidden_layers": n_layers}},
138 matches=lambda n_layers: Matches(
139 attn_quant_fusion=n_layers,
140 ar_rms_fusion=n_layers * 2,
141 sequence_parallel=n_layers * 2,
142 async_tp=n_layers * 2 - 1,
143 ),
144 )
145
146 qwen3_a3b = ModelFusionInfo(
147 model_name="Qwen/Qwen3-30B-A3B",
148 matches=lambda n_layers: Matches(
149 norm_rope_fusion=n_layers,
150 ar_rms_fusion=n_layers * 2 + 1,
151 aiter_ar_rms_fusion=n_layers * 2 + 1,
152 sequence_parallel=n_layers * 2 + 1,
153 async_tp=n_layers * 2,
154 ),
155 )
156
157 qwen3_a3b_fp8 = ModelFusionInfo(
158 model_name="Qwen/Qwen3-30B-A3B-FP8",
159 matches=lambda n_layers: Matches(
160 rms_quant_fusion=n_layers,
161 norm_rope_fusion=n_layers,
162 attn_quant_fusion=0, # attn + group quant not supported
163 ar_rms_fusion=n_layers * 2 + 1,
164 sequence_parallel=n_layers * 2 + 1,
165 async_tp=n_layers * 2,
166 ),
167 )
168
169 deepseek_coder_v2_lite_fp8 = ModelFusionInfo(
170 model_name="RedHatAI/DeepSeek-Coder-V2-Lite-Instruct-FP8",
171 matches=lambda n_layers: Matches(
172 # first_k_dense_replace=1; MoE hides most rms+quant sites
173 rms_quant_fusion=1,
174 act_quant_fusion=min(1, n_layers), # dense layers only
175 # MLA attn + static FP8 quant
176 attn_quant_fusion=n_layers,
177 ar_rms_fusion=n_layers * 2 + 1,
178 ),
179 )
180
181 deepseek_v3_fp8 = ModelFusionInfo(
182 model_name="deepseek-ai/DeepSeek-V3",
183 matches=lambda n_layers: Matches(
184 # 3 per dense layer (first 3):
185 # - input_rms + qkv_proj
186 # - q_a_layernorm + q_b_proj (inside MLA wrapper)
187 # - post_attn_layernorm + MLP
188 # 2 per MoE layer (remaining) due to MoE wrapping
189 rms_quant_fusion=n_layers * 2 + min(3, n_layers), # add for 3 dense layers
190 # silu+block quant
191 act_quant_fusion=min(3, n_layers), # dense layers only
192 # MLA attn + per-group FP8 quant
193 attn_quant_fusion=n_layers,
194 ar_rms_fusion=n_layers * 2 + 1,
195 # TODO
196 # sequence_parallel= n_layers * 2 + 1,
197 # async_tp=n_layers * 2,
198 ),
199 )
200
201 deepseek_r1_fp4 = ModelFusionInfo(
202 model_name="nvidia/DeepSeek-R1-0528-NVFP4-v2",
203 matches=lambda n_layers: Matches(
204 rms_quant_fusion=0,
205 act_quant_fusion=min(3, n_layers),
206 attn_quant_fusion=n_layers,
207 ar_rms_fusion=n_layers * 2 + 1,
208 ),
209 )
210
211 gpt_oss_20b = ModelFusionInfo(
212 model_name="openai/gpt-oss-20b",
213 matches=lambda n_layers: Matches(
214 ar_rms_fusion=n_layers * 2 + 1,
215 aiter_ar_rms_fusion=n_layers + 1,
216 sequence_parallel=n_layers * 2 + 1,
217 async_tp=n_layers * 2,
218 ),
219 model_kwargs=(
220 {"quantization_config": {"moe": {"activation": "mxfp8"}}}
221 if is_blackwell()
222 else {}
223 ),
224 )