main
py 575 lines 21 KB
Raw
1 # SPDX-License-Identifier: Apache-2.0
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3 from unittest.mock import patch
4
5 import pytest
6 import torch
7
8 from tests.kernels.moe.utils import make_dummy_moe_config
9 from vllm.model_executor.layers.fused_moe.activation import MoEActivation
10 from vllm.model_executor.layers.fused_moe.config import RoutingMethodType
11 from vllm.model_executor.layers.fused_moe.oracle.unquantized import (
12 UnquantizedMoeBackend,
13 backend_to_kernel_cls,
14 select_unquantized_moe_backend,
15 )
16 from vllm.platforms import CpuArchEnum, current_platform
17
18 skipif_not_cuda_rocm = pytest.mark.skipif(
19 not (current_platform.is_cuda() or current_platform.is_rocm()),
20 reason="Only supported on CUDA/ROCm platforms.",
21 )
22
23
24 @pytest.mark.parametrize(
25 ("amx_supported", "in_dtype", "expect_amx_kernel"),
26 [
27 (True, torch.bfloat16, True),
28 (False, torch.bfloat16, False),
29 (True, torch.float16, False),
30 ],
31 )
32 def test_x86_cpu_unquantized_kernel_selection(
33 amx_supported: bool,
34 in_dtype: torch.dtype,
35 expect_amx_kernel: bool,
36 ):
37 from vllm.model_executor.layers.fused_moe.experts.cpu_moe import (
38 CPUUnquantizedExperts,
39 X86CPUUnquantizedExperts,
40 )
41
42 with (
43 patch.object(current_platform, "is_cpu", return_value=True),
44 patch.object(
45 current_platform,
46 "get_cpu_architecture",
47 return_value=CpuArchEnum.X86,
48 ),
49 patch("torch.cpu._is_amx_tile_supported", return_value=amx_supported),
50 ):
51 moe_config = make_dummy_moe_config(
52 hidden_dim=128,
53 intermediate_size=128,
54 in_dtype=in_dtype,
55 )
56 kernel_cls = next(
57 cls
58 for cls in backend_to_kernel_cls(UnquantizedMoeBackend.CPU)
59 if cls.is_supported_config(
60 cls,
61 moe_config,
62 None,
63 None,
64 CPUUnquantizedExperts.activation_format(),
65 )[0]
66 )
67
68 expected_kernel_cls = (
69 X86CPUUnquantizedExperts if expect_amx_kernel else CPUUnquantizedExperts
70 )
71 assert kernel_cls is expected_kernel_cls
72
73
74 @pytest.mark.parametrize(
75 ("platform", "in_dtype", "expect_arm_kernel"),
76 [
77 ("linux", torch.bfloat16, True),
78 ("linux", torch.float16, False),
79 ("darwin", torch.bfloat16, False),
80 ],
81 )
82 def test_arm_cpu_unquantized_kernel_selection(
83 platform: str,
84 in_dtype: torch.dtype,
85 expect_arm_kernel: bool,
86 ):
87 from vllm.model_executor.layers.fused_moe.experts.cpu_moe import (
88 ArmCPUUnquantizedExperts,
89 CPUUnquantizedExperts,
90 )
91
92 with (
93 patch.object(current_platform, "is_cpu", return_value=True),
94 patch.object(
95 current_platform,
96 "get_cpu_architecture",
97 return_value=CpuArchEnum.ARM,
98 ),
99 patch(
100 "vllm.model_executor.layers.fused_moe.experts.cpu_moe.sys.platform",
101 platform,
102 ),
103 ):
104 moe_config = make_dummy_moe_config(
105 hidden_dim=128,
106 intermediate_size=128,
107 in_dtype=in_dtype,
108 )
109 kernel_cls = next(
110 cls
111 for cls in backend_to_kernel_cls(UnquantizedMoeBackend.CPU)
112 if cls.is_supported_config(
113 cls,
114 moe_config,
115 None,
116 None,
117 CPUUnquantizedExperts.activation_format(),
118 )[0]
119 )
120
121 expected_kernel_cls = (
122 ArmCPUUnquantizedExperts if expect_arm_kernel else CPUUnquantizedExperts
123 )
124 assert kernel_cls is expected_kernel_cls
125
126
127 @pytest.mark.parametrize(
128 "platform_method,expected_backend",
129 [
130 ("is_cuda", UnquantizedMoeBackend.TRITON), # Default CUDA without FlashInfer
131 ("is_rocm", UnquantizedMoeBackend.TRITON), # ROCm without AITER
132 ("is_cpu", UnquantizedMoeBackend.CPU),
133 ("is_xpu", UnquantizedMoeBackend.XPU),
134 ("is_tpu", UnquantizedMoeBackend.TPU),
135 ("is_out_of_tree", UnquantizedMoeBackend.OOT),
136 ],
137 )
138 @patch(
139 "vllm.utils.flashinfer.has_flashinfer",
140 return_value=False,
141 )
142 @patch(
143 "vllm.model_executor.layers.fused_moe.oracle.unquantized.rocm_aiter_ops.is_fused_moe_enabled",
144 return_value=False,
145 )
146 def test_select_default_backend_by_platform(
147 mock_aiter_enabled,
148 mock_has_flashinfer,
149 monkeypatch,
150 platform_method,
151 expected_backend,
152 ):
153 """Test default backend selection per platform with all optional
154 accelerators (FlashInfer, AITER) disabled."""
155 with patch(
156 "vllm.model_executor.layers.fused_moe.oracle.unquantized.current_platform"
157 ) as mock_platform:
158 # Set all platform checks to False
159 mock_platform.is_cuda.return_value = False
160 mock_platform.is_rocm.return_value = False
161 mock_platform.is_cpu.return_value = False
162 mock_platform.is_xpu.return_value = False
163 mock_platform.is_tpu.return_value = False
164 mock_platform.is_out_of_tree.return_value = False
165
166 # Set only the specified platform to True
167 getattr(mock_platform, platform_method).return_value = True
168
169 with (
170 patch.object(current_platform, "is_cuda", return_value=False),
171 patch.object(current_platform, "is_rocm", return_value=False),
172 patch.object(current_platform, "is_cpu", return_value=False),
173 patch.object(current_platform, "is_xpu", return_value=False),
174 patch.object(current_platform, "is_tpu", return_value=False),
175 patch.object(current_platform, "is_out_of_tree", return_value=False),
176 patch.object(current_platform, platform_method, return_value=True),
177 ):
178 # CPU's grouped-gemm kernels require hidden/intermediate sizes
179 # aligned to 32; the size-1 defaults only work for backends that
180 # don't check shapes at selection time.
181 moe_config = (
182 make_dummy_moe_config(hidden_dim=128, intermediate_size=128)
183 if expected_backend == UnquantizedMoeBackend.CPU
184 else make_dummy_moe_config()
185 )
186 selected_backend, expert_cls = select_unquantized_moe_backend(
187 moe_config=moe_config
188 )
189
190 assert selected_backend == expected_backend
191 if expected_backend in [
192 UnquantizedMoeBackend.OOT,
193 UnquantizedMoeBackend.TPU,
194 ]:
195 assert expert_cls is None
196 else:
197 assert expert_cls is not None
198
199
200 @patch(
201 "vllm.utils.flashinfer.has_flashinfer",
202 return_value=False,
203 )
204 @patch(
205 "vllm.model_executor.layers.fused_moe.oracle.unquantized.rocm_aiter_ops.is_fused_moe_enabled",
206 return_value=True,
207 )
208 @pytest.mark.skipif(
209 not current_platform.is_rocm(), reason="ROCm-specific backend selection test"
210 )
211 def test_select_rocm_aiter_backend(mock_aiter_enabled, mock_has_flashinfer):
212 """Test ROCm backend selection when AITER is available."""
213 with patch(
214 "vllm.model_executor.layers.fused_moe.oracle.unquantized.current_platform"
215 ) as mock_platform:
216 mock_platform.is_cuda.return_value = False
217 mock_platform.is_rocm.return_value = True
218 mock_platform.is_cpu.return_value = False
219 mock_platform.is_xpu.return_value = False
220 mock_platform.is_tpu.return_value = False
221 mock_platform.is_out_of_tree.return_value = False
222
223 moe_config = make_dummy_moe_config()
224 selected_backend, expert_cls = select_unquantized_moe_backend(
225 moe_config=moe_config,
226 )
227
228 assert selected_backend == UnquantizedMoeBackend.AITER
229 assert expert_cls is not None
230
231
232 @patch(
233 "vllm.utils.flashinfer.has_flashinfer",
234 return_value=False,
235 )
236 @patch(
237 "vllm.model_executor.layers.fused_moe.oracle.unquantized.rocm_aiter_ops."
238 "is_fused_moe_enabled",
239 return_value=True,
240 )
241 @patch(
242 "vllm.model_executor.layers.fused_moe.oracle.unquantized.rocm_aiter_ops."
243 "is_rdna_aiter_enabled",
244 return_value=False,
245 )
246 @pytest.mark.skipif(
247 not current_platform.is_rocm(), reason="ROCm-specific backend selection test"
248 )
249 def test_select_rocm_aiter_backend_non_gated_activation_falls_back(
250 mock_rdna_disabled, mock_aiter_enabled, mock_has_flashinfer, monkeypatch
251 ):
252 """Test ROCm backend selection falls back (not raises) for non-gated MoE."""
253 monkeypatch.setenv("VLLM_ROCM_USE_AITER", "1")
254 monkeypatch.setenv("VLLM_ROCM_USE_AITER_MOE", "1")
255
256 with patch(
257 "vllm.model_executor.layers.fused_moe.oracle.unquantized.current_platform"
258 ) as mock_platform:
259 mock_platform.is_cuda.return_value = False
260 mock_platform.is_rocm.return_value = True
261 mock_platform.is_cpu.return_value = False
262 mock_platform.is_xpu.return_value = False
263 mock_platform.is_tpu.return_value = False
264 mock_platform.is_out_of_tree.return_value = False
265
266 moe_config = make_dummy_moe_config(activation=MoEActivation.SILU_NO_MUL)
267 assert moe_config.is_act_and_mul is False
268
269 selected_backend, expert_cls = select_unquantized_moe_backend(
270 moe_config=moe_config,
271 )
272
273 assert selected_backend != UnquantizedMoeBackend.AITER
274 assert expert_cls is not None
275
276
277 @pytest.mark.skipif(
278 not current_platform.is_rocm(), reason="ROCm-specific backend selection test"
279 )
280 def test_explicit_aiter_backend_non_gated_activation_still_raises():
281 """Explicit `--moe-backend aiter` still raises for non-gated MoE;
282 only the env-var opt-in path falls back."""
283 moe_config = make_dummy_moe_config(activation=MoEActivation.SILU_NO_MUL)
284 moe_config.moe_backend = "aiter" # explicit pin, not "auto"
285 assert moe_config.is_act_and_mul is False
286
287 with pytest.raises(ValueError):
288 select_unquantized_moe_backend(moe_config=moe_config)
289
290
291 @patch(
292 "vllm.model_executor.layers.fused_moe.experts.trtllm_bf16_moe.TrtLlmBf16ExpertsMonolithic.is_supported_config",
293 return_value=(True, None),
294 )
295 @pytest.mark.skipif(
296 not current_platform.is_cuda(), reason="Only supported on NVIDIA platforms."
297 )
298 def test_select_cuda_flashinfer_trtllm_backend(
299 mock_is_supported_trtllm_monolithic,
300 ):
301 """Test CUDA backend selection when FlashInfer TRTLLM is available and enabled."""
302 with (
303 patch.object(current_platform, "is_cuda", return_value=True),
304 patch.object(current_platform, "is_rocm", return_value=False),
305 patch.object(current_platform, "is_cpu", return_value=False),
306 patch.object(current_platform, "is_xpu", return_value=False),
307 patch.object(current_platform, "is_tpu", return_value=False),
308 patch.object(current_platform, "is_out_of_tree", return_value=False),
309 patch.object(current_platform, "has_device_capability", return_value=True),
310 ):
311 moe_config = make_dummy_moe_config()
312 moe_config.moe_backend = "flashinfer_trtllm"
313 # TRTLLM requires EP and does not support DP
314 moe_config.moe_parallel_config.use_ep = True
315 moe_config.moe_parallel_config.use_dp = False
316
317 selected_backend, experts_cls = select_unquantized_moe_backend(
318 moe_config=moe_config
319 )
320
321 assert selected_backend == UnquantizedMoeBackend.FLASHINFER_TRTLLM
322 assert experts_cls is not None
323 assert experts_cls.__name__ == "TrtLlmBf16ExpertsMonolithic"
324
325
326 @patch(
327 "vllm.model_executor.layers.fused_moe.experts.trtllm_bf16_moe.TrtLlmBf16ExpertsMonolithic.is_supported_config",
328 return_value=(False, "monolithic unsupported"),
329 )
330 @patch(
331 "vllm.model_executor.layers.fused_moe.experts.trtllm_bf16_moe.TrtLlmBf16ExpertsModular.is_supported_config",
332 return_value=(True, None),
333 )
334 @pytest.mark.skipif(
335 not current_platform.is_cuda(), reason="Only supported on NVIDIA platforms."
336 )
337 def test_select_cuda_flashinfer_trtllm_modular_backend(
338 mock_is_supported_trtllm_modular,
339 mock_is_supported_trtllm_monolithic,
340 ):
341 """Test CUDA backend selection falls back to FlashInfer TRTLLM modular."""
342 with (
343 patch.object(current_platform, "is_cuda", return_value=True),
344 patch.object(current_platform, "is_rocm", return_value=False),
345 patch.object(current_platform, "is_cpu", return_value=False),
346 patch.object(current_platform, "is_xpu", return_value=False),
347 patch.object(current_platform, "is_tpu", return_value=False),
348 patch.object(current_platform, "is_out_of_tree", return_value=False),
349 patch.object(current_platform, "has_device_capability", return_value=True),
350 ):
351 moe_config = make_dummy_moe_config()
352 moe_config.moe_backend = "flashinfer_trtllm"
353 moe_config.moe_parallel_config.use_ep = True
354 moe_config.moe_parallel_config.use_dp = False
355
356 selected_backend, experts_cls = select_unquantized_moe_backend(
357 moe_config=moe_config
358 )
359
360 assert selected_backend == UnquantizedMoeBackend.FLASHINFER_TRTLLM
361 assert experts_cls is not None
362 assert experts_cls.__name__ == "TrtLlmBf16ExpertsModular"
363
364
365 @patch(
366 "vllm.model_executor.layers.fused_moe.experts.trtllm_bf16_moe.TrtLlmBf16ExpertsBase._supports_current_device",
367 return_value=True,
368 )
369 @pytest.mark.parametrize(
370 "all2all_backend",
371 [
372 "mori_high_throughput",
373 "mori_low_latency",
374 "flashinfer_nvlink_two_sided",
375 "flashinfer_nvlink_one_sided",
376 ],
377 )
378 def test_select_cuda_flashinfer_trtllm_modular_for_standard_all2all(
379 mock_supports_current_device,
380 all2all_backend,
381 ):
382 """Test non-AG/RS standard-format all2all backends select modular BF16."""
383 with (
384 patch.object(current_platform, "is_cuda", return_value=True),
385 patch.object(current_platform, "is_rocm", return_value=False),
386 patch.object(current_platform, "is_cpu", return_value=False),
387 patch.object(current_platform, "is_xpu", return_value=False),
388 patch.object(current_platform, "is_tpu", return_value=False),
389 patch.object(current_platform, "is_out_of_tree", return_value=False),
390 patch.object(
391 current_platform, "is_device_capability_family", return_value=False
392 ),
393 ):
394 moe_config = make_dummy_moe_config(num_experts=4, num_local_experts=2)
395 moe_config.moe_backend = "flashinfer_trtllm"
396 moe_config.moe_parallel_config.use_ep = True
397 moe_config.moe_parallel_config.dp_size = 2
398 moe_config.moe_parallel_config.ep_size = 2
399 moe_config.moe_parallel_config.all2all_backend = all2all_backend
400
401 selected_backend, experts_cls = select_unquantized_moe_backend(
402 moe_config=moe_config
403 )
404
405 assert selected_backend == UnquantizedMoeBackend.FLASHINFER_TRTLLM
406 assert experts_cls is not None
407 assert experts_cls.__name__ == "TrtLlmBf16ExpertsModular"
408
409
410 @patch(
411 "vllm.model_executor.layers.fused_moe.experts.trtllm_bf16_moe.TrtLlmBf16ExpertsBase._supports_current_device",
412 return_value=True,
413 )
414 def test_select_cuda_deepep_ht_falls_back_from_trtllm(
415 mock_supports_current_device,
416 ):
417 """Test DeepEP HT avoids the unsupported BF16 TRTLLM modular path."""
418 with (
419 patch.object(current_platform, "is_cuda", return_value=True),
420 patch.object(current_platform, "is_rocm", return_value=False),
421 patch.object(current_platform, "is_cpu", return_value=False),
422 patch.object(current_platform, "is_xpu", return_value=False),
423 patch.object(current_platform, "is_tpu", return_value=False),
424 patch.object(current_platform, "is_out_of_tree", return_value=False),
425 patch.object(
426 current_platform, "is_device_capability_family", return_value=False
427 ),
428 ):
429 moe_config = make_dummy_moe_config(num_experts=4, num_local_experts=2)
430 moe_config.moe_backend = "auto"
431 moe_config.moe_parallel_config.use_ep = True
432 moe_config.moe_parallel_config.dp_size = 2
433 moe_config.moe_parallel_config.ep_size = 2
434 moe_config.moe_parallel_config.all2all_backend = "deepep_high_throughput"
435
436 selected_backend, experts_cls = select_unquantized_moe_backend(
437 moe_config=moe_config
438 )
439
440 assert selected_backend == UnquantizedMoeBackend.TRITON
441 assert experts_cls is not None
442 assert experts_cls.__name__ == "TritonExperts"
443
444
445 @patch(
446 "vllm.model_executor.layers.fused_moe.experts.trtllm_bf16_moe.TrtLlmBf16ExpertsBase._supports_current_device",
447 return_value=True,
448 )
449 def test_select_cuda_flashinfer_trtllm_ag_rs_uses_monolithic(
450 mock_supports_current_device,
451 ):
452 """Test AG/RS stays on BF16 TRTLLM monolithic when TRTLLM is supported."""
453 with (
454 patch.object(current_platform, "is_cuda", return_value=True),
455 patch.object(current_platform, "is_rocm", return_value=False),
456 patch.object(current_platform, "is_cpu", return_value=False),
457 patch.object(current_platform, "is_xpu", return_value=False),
458 patch.object(current_platform, "is_tpu", return_value=False),
459 patch.object(current_platform, "is_out_of_tree", return_value=False),
460 patch.object(
461 current_platform, "is_device_capability_family", return_value=False
462 ),
463 ):
464 moe_config = make_dummy_moe_config(num_experts=4, num_local_experts=2)
465 moe_config.moe_backend = "flashinfer_trtllm"
466 moe_config.routing_method = RoutingMethodType.Renormalize
467 moe_config.moe_parallel_config.use_ep = True
468 moe_config.moe_parallel_config.dp_size = 2
469 moe_config.moe_parallel_config.ep_size = 2
470 moe_config.moe_parallel_config.all2all_backend = "allgather_reducescatter"
471
472 selected_backend, experts_cls = select_unquantized_moe_backend(
473 moe_config=moe_config
474 )
475
476 assert selected_backend == UnquantizedMoeBackend.FLASHINFER_TRTLLM
477 assert experts_cls is not None
478 assert experts_cls.__name__ == "TrtLlmBf16ExpertsMonolithic"
479
480
481 @patch(
482 "vllm.utils.flashinfer.has_flashinfer",
483 return_value=True,
484 )
485 @patch(
486 "vllm.model_executor.layers.fused_moe.experts.trtllm_bf16_moe.TrtLlmBf16ExpertsMonolithic.is_supported_config",
487 return_value=(False, None),
488 )
489 @patch(
490 "vllm.model_executor.layers.fused_moe.experts.trtllm_bf16_moe.TrtLlmBf16ExpertsModular.is_supported_config",
491 return_value=(False, None),
492 )
493 @patch(
494 "vllm.model_executor.layers.fused_moe.experts.flashinfer_cutlass_moe.FlashInferExperts.is_supported_config",
495 return_value=(True, None),
496 )
497 @pytest.mark.skipif(
498 not current_platform.is_cuda(), reason="Only supported on NVIDIA platforms."
499 )
500 def test_select_cuda_flashinfer_cutlass_backend(
501 mock_is_supported_cutlass,
502 mock_is_supported_trtllm_modular,
503 mock_is_supported_trtllm_monolithic,
504 mock_has_flashinfer,
505 ):
506 """Test CUDA backend selection when FlashInfer TRTLLM is not available
507 and FlashInfer CUTLASS is available."""
508 with (
509 patch.object(current_platform, "is_cuda", return_value=True),
510 patch.object(current_platform, "is_rocm", return_value=False),
511 patch.object(current_platform, "is_cpu", return_value=False),
512 patch.object(current_platform, "is_xpu", return_value=False),
513 patch.object(current_platform, "is_tpu", return_value=False),
514 patch.object(current_platform, "is_out_of_tree", return_value=False),
515 patch.object(current_platform, "has_device_capability", return_value=True),
516 ):
517 moe_config = make_dummy_moe_config()
518 # Select FlashInfer CUTLASS explicitly
519 moe_config.moe_backend = "flashinfer_cutlass"
520 # CUTLASS requires EP and does not support DP
521 moe_config.moe_parallel_config.use_ep = True
522 moe_config.moe_parallel_config.use_dp = False
523
524 selected_backend, experts_cls = select_unquantized_moe_backend(
525 moe_config=moe_config
526 )
527
528 assert selected_backend == UnquantizedMoeBackend.FLASHINFER_CUTLASS
529 assert experts_cls is not None
530
531
532 @skipif_not_cuda_rocm
533 def test_select_lora_backend_prefers_triton():
534 """LoRA-enabled unquantized MoE should select Triton backend."""
535 moe_config = make_dummy_moe_config()
536 moe_config.is_lora_enabled = True
537 selected_backend, experts_cls = select_unquantized_moe_backend(
538 moe_config=moe_config
539 )
540
541 assert selected_backend == UnquantizedMoeBackend.TRITON
542 assert experts_cls is not None
543
544
545 @skipif_not_cuda_rocm
546 def test_select_lora_explicit_non_triton_backend():
547 """LoRA should override explicit non-Triton backend to Triton."""
548 moe_config = make_dummy_moe_config()
549 moe_config.is_lora_enabled = True
550
551 # Use string from mapping in function map_unquantized_backend()
552 moe_config.moe_backend = "flashinfer_cutlass"
553
554 selected_backend, experts_cls = select_unquantized_moe_backend(
555 moe_config=moe_config
556 )
557
558 assert selected_backend == UnquantizedMoeBackend.TRITON
559 assert experts_cls is not None
560
561
562 @skipif_not_cuda_rocm
563 @pytest.mark.parametrize("is_lora_enabled", [False, True])
564 def test_select_explicit_triton_backend(is_lora_enabled):
565 """Explicit triton backend selection should return Triton."""
566 moe_config = make_dummy_moe_config()
567 moe_config.is_lora_enabled = is_lora_enabled
568 moe_config.moe_backend = "triton"
569
570 selected_backend, experts_cls = select_unquantized_moe_backend(
571 moe_config=moe_config
572 )
573
574 assert selected_backend == UnquantizedMoeBackend.TRITON
575 assert experts_cls is not None