main
py 708 lines 23 KB
Raw
1 # SPDX-License-Identifier: Apache-2.0
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
4 import copy
5 import inspect
6 import textwrap
7 import traceback
8 from itertools import product
9 from typing import Any
10 from unittest import mock
11
12 import pytest
13 import torch
14
15 import vllm.model_executor.layers.fused_moe.modular_kernel as mk
16 from vllm._aiter_ops import rocm_aiter_ops
17 from vllm.config import VllmConfig, set_current_vllm_config
18 from vllm.model_executor.layers.fused_moe.activation import MoEActivation
19 from vllm.model_executor.layers.fused_moe.experts.rocm_aiter_moe import AiterExperts
20 from vllm.platforms import current_platform
21 from vllm.utils.flashinfer import has_flashinfer_cutlass_fused_moe
22 from vllm.utils.import_utils import has_aiter, has_deep_ep, has_deep_gemm
23 from vllm.utils.torch_utils import set_random_seed
24 from vllm.v1.worker.workspace import init_workspace_manager
25
26 from .modular_kernel_tools.common import (
27 Config,
28 RankTensors,
29 WeightTensors,
30 reference_moe_impl,
31 run_modular_kernel,
32 )
33 from .modular_kernel_tools.mk_objects import (
34 MK_FUSED_EXPERT_TYPES,
35 MK_MULTI_GPU_PREPARE_FINALIZE_TYPES,
36 MK_QUANT_CONFIGS,
37 MK_SINGLE_GPU_PREPARE_FINALIZE_TYPES,
38 TestMoEQuantConfig,
39 expert_info,
40 )
41 from .modular_kernel_tools.parallel_utils import (
42 ProcessGroupInfo,
43 parallel_launch_with_config,
44 )
45 from .utils import check_accuracy
46
47 has_any_multi_gpu_package = (
48 has_deep_ep() or has_deep_gemm() or has_flashinfer_cutlass_fused_moe()
49 )
50
51 meets_multi_gpu_requirements = pytest.mark.skipif(
52 not has_any_multi_gpu_package,
53 reason="Requires deep_ep or deep_gemm or flashinfer packages",
54 )
55
56
57 def format_result(verbose, msg, ex=None):
58 if ex is not None:
59 x = str(ex)
60 newx = x.strip(" \n\t")[:16]
61 if len(newx) < len(x):
62 newx = newx + " ..."
63
64 prefix = "E\t"
65 print(f"{textwrap.indent(traceback.format_exc(), prefix)}")
66 print(f"FAILED {msg} - {newx}\n")
67 elif verbose:
68 print(f"PASSED {msg}")
69 else:
70 print(".", end="")
71
72
73 def assert_aiter_quant_scheme_case(config: Config) -> None:
74 """Make the AITER (weight_quant_key, activation_quant_key) pair this
75 config exercises explicit, instead of AiterExperts being reached only
76 indirectly through the general quant-config sweep.
77 See https://github.com/vllm-project/vllm/issues/54966."""
78 fe_cls = config.fused_experts_type
79 if fe_cls is not AiterExperts:
80 return
81
82 if config.quant_config is None:
83 w_key, a_key = None, None
84 else:
85 w_key, a_key = config.fp8_quant_key_pair()
86
87 assert fe_cls._supports_quant_scheme(w_key, a_key), (
88 f"AITER case (weight_key={w_key}, activation_key={a_key}) reached "
89 "the modular-kernel harness, but AiterExperts._supports_quant_scheme "
90 "does not declare it supported."
91 )
92 print(f"[AITER case] weight_key={w_key}, activation_key={a_key}")
93
94
95 def assert_aiter_activation_case(config: Config) -> None:
96 """Make the AITER activation this config exercises explicit, instead of
97 AiterExperts being reached only indirectly through the general
98 activation sweep. See https://github.com/vllm-project/vllm/issues/54966."""
99 fe_cls = config.fused_experts_type
100 if fe_cls is not AiterExperts:
101 return
102
103 assert fe_cls._supports_activation(config.activation), (
104 f"AITER case activation={config.activation} reached the "
105 "modular-kernel harness, but AiterExperts._supports_activation "
106 "does not declare it supported."
107 )
108 print(f"[AITER case] activation={config.activation}")
109
110
111 def rank_worker(
112 pgi: ProcessGroupInfo,
113 vllm_config: VllmConfig,
114 cpu_group,
115 base_config: Config,
116 weights: WeightTensors,
117 verbose: bool,
118 ):
119 # Initialize workspace manager in child process
120 device = torch.device(f"cuda:{pgi.local_rank}")
121 init_workspace_manager(device)
122
123 set_random_seed(pgi.rank)
124
125 # get weights to this device
126 weights.to_current_device()
127
128 Ms = base_config.Ms
129 assert isinstance(Ms, list)
130 TOPKs = base_config.topks
131 assert isinstance(TOPKs, list)
132
133 exceptions = []
134 count = 0
135
136 for m, topk in product(Ms, TOPKs):
137 # override m and topk
138 config = copy.deepcopy(base_config)
139 config.Ms = m
140 config.topks = topk
141
142 try:
143 print(f"Running[{pgi.rank}]: m={m}, topk={topk} ...")
144 count = count + 1
145
146 # inputs for rank
147 rank_tensors = RankTensors.make(config, pgi)
148
149 # Skip unsupported: AITER block-scaled MoE does not
150 # support apply_router_weight_on_input (topk=1 path).
151 # https://github.com/ROCm/aiter/issues/2418
152 if (
153 topk == 1
154 and config.supports_apply_weight_on_input()
155 and config.fused_experts_type is AiterExperts
156 and config.quant_block_shape is not None
157 ):
158 print(
159 f"Skipping[{pgi.rank}]: m={m}, topk={topk}"
160 " (AITER block-scaled + weight-on-input,"
161 " https://github.com/ROCm/aiter/issues/2418)"
162 )
163 count -= 1
164 continue
165
166 # Skip unsupported: AITER x DeepEP-HT/Mori dispatch crashes
167 # with an illegal memory access at world_size>1 on gfx942.
168 # https://github.com/vllm-project/vllm/issues/57029
169 if (
170 config.world_size > 1
171 and config.fused_experts_type is AiterExperts
172 and getattr(config.prepare_finalize_type, "__name__", "")
173 in ("DeepEPHTPrepareAndFinalize", "MoriPrepareAndFinalize")
174 ):
175 print(
176 f"Skipping[{pgi.rank}]: m={m}, topk={topk}"
177 " (AITER x DeepEP-HT/Mori illegal memory access,"
178 " https://github.com/vllm-project/vllm/issues/57029)"
179 )
180 count -= 1
181 continue
182
183 assert_aiter_quant_scheme_case(config)
184 assert_aiter_activation_case(config)
185
186 # modular kernel out
187 mk_out = run_modular_kernel(pgi, vllm_config, config, weights, rank_tensors)
188
189 with set_current_vllm_config(vllm_config):
190 ref_out = reference_moe_impl(config, weights, rank_tensors)
191
192 if config.quant_dtype == "nvfp4":
193 atol = 1e-1 if config.K < 4096 else 2e-1
194 rtol = 1e-1 if config.K < 4096 else 2e-1
195 else:
196 atol = 3e-2
197 rtol = 3e-2
198
199 # On ROCm, AITER FP8 fused MoE uses hardware FP8
200 # dot-product which can produce slightly larger error
201 # than dequant+f32 matmul at FP8 representable-value
202 # boundaries. Allow a small percentage of elements to
203 # exceed the base tolerance by a bounded margin.
204 # https://github.com/ROCm/aiter/issues/2421
205 from vllm.platforms import current_platform as _cp
206
207 is_aiter_fp8 = (
208 _cp.is_rocm()
209 and config.fused_experts_type is AiterExperts
210 and config.quant_config is not None
211 )
212 if is_aiter_fp8:
213 check_accuracy(ref_out, mk_out, atol=atol, rtol=rtol, percent=0.9)
214 else:
215 torch.testing.assert_close(ref_out, mk_out, atol=atol, rtol=rtol)
216 format_result(verbose, config.describe())
217 except Exception as ex:
218 format_result(verbose, config.describe(), ex)
219 exceptions.append(ex)
220
221 if len(exceptions) > 0:
222 raise RuntimeError(
223 f"{len(exceptions)} of {count} tests failed in child process, "
224 f"rank={pgi.rank}."
225 )
226 else:
227 print(f"{count} of {count} tests passed in child process, rank={pgi.rank}.")
228
229
230 def run(config: Config, verbose: bool):
231 assert config.is_valid()[0]
232 assert not is_nyi_config(config)
233
234 weights: WeightTensors = WeightTensors.make(config)
235
236 vllm_config, env_dict = config.make_env_data()
237 parallel_launch_with_config(
238 config.world_size, rank_worker, vllm_config, env_dict, config, weights, verbose
239 )
240
241
242 Ms = [32, 64]
243 # hidden sizes, making this too large will cause fp4 tests to fail.
244 # Also needs to be a multiple of 1024 for deep_gemm.
245 Ks = [2048]
246 Ns = [1024]
247 TOPKs = [4, 1]
248 Es = [32]
249 DTYPEs = [torch.bfloat16]
250 MK_ACTIVATIONS = [
251 MoEActivation.SILU,
252 MoEActivation.GELU,
253 ]
254
255
256 def is_nyi_config(config: Config) -> bool:
257 # We know these configs to be legitimate. but still fail.
258 info = expert_info(config.fused_experts_type)
259 if info.needs_matching_quant:
260 # The triton kernels expect both per-act-token-quant and
261 # per-out-ch-quant or neither.
262 unsupported_quant_config = (
263 config.is_per_act_token_quant + config.is_per_out_ch_quant
264 ) == 1
265 if unsupported_quant_config:
266 return True
267
268 if config.activation != MoEActivation.SILU:
269 if config.fused_experts_type is not AiterExperts:
270 return True # AITER-only for this axis, for now
271 if config.quant_dtype is not None:
272 return True # unquantized-only for this axis, for now
273
274 return False
275
276
277 def generate_valid_test_cases(
278 world_size: int, prepare_finalize_types
279 ) -> list[tuple[Any, ...]]:
280 cases = []
281 total = 0
282
283 for k, n, e, dtype, quant_config, activation, combination in product(
284 Ks,
285 Ns,
286 Es,
287 DTYPEs,
288 MK_QUANT_CONFIGS,
289 MK_ACTIVATIONS,
290 product(prepare_finalize_types, MK_FUSED_EXPERT_TYPES),
291 ):
292 total = total + 1
293
294 config = Config(
295 Ms=Ms,
296 K=k,
297 N=n,
298 E=e,
299 topks=TOPKs,
300 dtype=dtype,
301 quant_config=quant_config,
302 activation=activation,
303 prepare_finalize_type=combination[0],
304 fused_experts_type=combination[1],
305 world_size=world_size,
306 )
307
308 # TODO(bnell): figure out how to get verbose flag here.
309 verbose = False # pytestconfig.getoption('verbose') > 0
310
311 valid, reason = config.is_valid()
312
313 if not valid:
314 if verbose:
315 print(f"Test config {config} is not valid: {reason}")
316 continue
317
318 if is_nyi_config(config):
319 if verbose:
320 print(f"Test config {config} is nyi.")
321 continue
322
323 cases.append(
324 (
325 k,
326 n,
327 e,
328 dtype,
329 quant_config,
330 activation,
331 combination[0],
332 combination[1],
333 world_size,
334 )
335 )
336
337 print(f"{len(cases)} of {total} valid configs generated.")
338
339 return cases
340
341
342 @pytest.mark.parametrize(
343 "k,n,e,dtype,quant_config,activation,"
344 "prepare_finalize_type,fused_experts_type,world_size",
345 generate_valid_test_cases(
346 world_size=2, prepare_finalize_types=MK_MULTI_GPU_PREPARE_FINALIZE_TYPES
347 ),
348 )
349 @meets_multi_gpu_requirements
350 def test_modular_kernel_combinations_multigpu(
351 k: int,
352 n: int,
353 e: int,
354 dtype: torch.dtype,
355 quant_config: TestMoEQuantConfig | None,
356 activation: MoEActivation,
357 prepare_finalize_type: mk.FusedMoEPrepareAndFinalize,
358 fused_experts_type: mk.FusedMoEExperts,
359 world_size: int,
360 pytestconfig,
361 ):
362 if current_platform.device_count() < world_size:
363 pytest.skip(
364 f"Not enough GPUs available to run, got "
365 f"{current_platform.device_count()} expected "
366 f"{world_size}."
367 )
368
369 config = Config(
370 Ms=Ms,
371 K=k,
372 N=n,
373 E=e,
374 topks=TOPKs,
375 dtype=dtype,
376 quant_config=quant_config,
377 activation=activation,
378 prepare_finalize_type=prepare_finalize_type,
379 fused_experts_type=fused_experts_type,
380 world_size=world_size,
381 )
382 verbosity = pytestconfig.getoption("verbose")
383 run(config, verbosity > 0)
384
385
386 @pytest.mark.parametrize(
387 "k,n,e,dtype,quant_config,activation,"
388 "prepare_finalize_type,fused_experts_type,world_size",
389 generate_valid_test_cases(
390 world_size=1, prepare_finalize_types=MK_SINGLE_GPU_PREPARE_FINALIZE_TYPES
391 ),
392 )
393 def test_modular_kernel_combinations_singlegpu(
394 k: int,
395 n: int,
396 e: int,
397 dtype: torch.dtype,
398 quant_config: TestMoEQuantConfig | None,
399 activation: MoEActivation,
400 prepare_finalize_type: mk.FusedMoEPrepareAndFinalize,
401 fused_experts_type: mk.FusedMoEExperts,
402 world_size: int,
403 pytestconfig,
404 workspace_init,
405 ):
406 """Note: float8_e4m3fn is not supported on CUDA architecture < 89,
407 and those tests will be skipped on unsupported hardware."""
408 config = Config(
409 Ms=Ms,
410 K=k,
411 N=n,
412 E=e,
413 topks=TOPKs,
414 dtype=dtype,
415 quant_config=quant_config,
416 activation=activation,
417 prepare_finalize_type=prepare_finalize_type,
418 fused_experts_type=fused_experts_type,
419 world_size=world_size,
420 )
421
422 if (
423 quant_config is not None and quant_config.quant_dtype == torch.float8_e4m3fn
424 ) and not current_platform.has_device_capability(89):
425 pytest.skip(
426 "Triton limitation: fp8e4nv data type is not supported on CUDA arch < 89"
427 )
428 verbosity = pytestconfig.getoption("verbose")
429 run(config, verbosity > 0)
430
431
432 # AITER sorting-backend dispatch env-var matrix (issue #54966 step 3) -------
433 #
434 # AITER_USE_CK_MOE_SORTING / AITER_USE_FLYDSL_MOE_SORTING are aiter globals
435 # read once at import time, so each combo needs a fresh child process (see
436 # parallel_launch_with_config) rather than in-process monkeypatching.
437 #
438 # AITER_MOE_SORT_BACKEND is not covered: it only matters when output_aux=True,
439 # which vLLM only sets on the MXFP4 path -- not yet wired into AiterExperts.
440
441
442 def _aiter_sort_backend_hooks_available() -> bool:
443 """Whether the installed aiter build exposes the sort-backend hooks that
444 AITER_USE_CK_MOE_SORTING / AITER_USE_FLYDSL_MOE_SORTING select between.
445 """
446 try:
447 import aiter.fused_moe as aiter_fused_moe
448 except ImportError:
449 return False
450
451 required = (
452 "_moe_sorting_impl",
453 "_flydsl_moe_sorting",
454 "_USE_CK_MOE_SORTING",
455 "_USE_FLYDSL_MOE_SORTING",
456 )
457 return all(hasattr(aiter_fused_moe, name) for name in required)
458
459
460 # Deliberately does NOT call _aiter_sort_backend_hooks_available() here: this
461 # marker is evaluated at module-import time, which also happens inside each
462 # spawned child (to unpickle its worker) -- before that case's env vars are
463 # applied, permanently freezing aiter's sort-backend globals. The hooks check
464 # instead runs inside the test body below (parent process only).
465 require_aiter_moe = pytest.mark.skipif(
466 not (
467 current_platform.is_rocm()
468 and has_aiter()
469 and rocm_aiter_ops.is_fused_moe_enabled()
470 ),
471 reason=(
472 "AITER MoE sorting-backend dispatch needs ROCm + AITER, with "
473 "VLLM_ROCM_USE_AITER=1 and VLLM_ROCM_USE_AITER_MOE=1 set before "
474 "the test process starts."
475 ),
476 )
477
478 # (AITER_USE_CK_MOE_SORTING, AITER_USE_FLYDSL_MOE_SORTING, expected_backend)
479 AITER_SORTING_BACKEND_ENV_CASES = [
480 (1, 0, "ck"),
481 (1, 1, "ck"), # CK wins over FlyDSL even when both are requested.
482 (0, 1, "flydsl"),
483 (0, 0, "opus"), # default when neither flag is set.
484 ]
485
486
487 def _aiter_sorting_backend_worker(
488 pgi: ProcessGroupInfo,
489 vllm_config: VllmConfig,
490 cpu_group,
491 config: Config,
492 weights: WeightTensors,
493 verbose: bool,
494 ck: int,
495 flydsl: int,
496 expected_backend: str,
497 ):
498 # Imported lazily in the spawned child, after env vars are applied, so
499 # aiter's import-time globals pick up this case's env.
500 import aiter.fused_moe as aiter_fused_moe
501
502 # Assert the env vars actually stuck in aiter's import-time globals,
503 # rather than inferring it indirectly from which sorting function fires.
504 assert bool(ck) == aiter_fused_moe._USE_CK_MOE_SORTING, (
505 f"aiter.fused_moe._USE_CK_MOE_SORTING={aiter_fused_moe._USE_CK_MOE_SORTING} "
506 f"but AITER_USE_CK_MOE_SORTING={ck} was set before this process started -- "
507 "the env var never reached aiter's import-time globals."
508 )
509 assert bool(flydsl) == aiter_fused_moe._USE_FLYDSL_MOE_SORTING, (
510 "aiter.fused_moe._USE_FLYDSL_MOE_SORTING="
511 f"{aiter_fused_moe._USE_FLYDSL_MOE_SORTING} but AITER_USE_FLYDSL_MOE_SORTING="
512 f"{flydsl} was set before this process started -- the env var never "
513 "reached aiter's import-time globals."
514 )
515
516 # Captured before patching, since the patched attribute would resolve to
517 # the mock's own (*args, **kwargs) signature instead of the real one.
518 original_moe_sorting_impl = aiter_fused_moe._moe_sorting_impl
519
520 with (
521 mock.patch.object(
522 aiter_fused_moe,
523 "_moe_sorting_impl",
524 wraps=original_moe_sorting_impl,
525 ) as sorting_impl_mock,
526 mock.patch.object(
527 aiter_fused_moe,
528 "_flydsl_moe_sorting",
529 wraps=aiter_fused_moe._flydsl_moe_sorting,
530 ) as flydsl_mock,
531 ):
532 # Reuses the real correctness/accuracy checking rank_worker already
533 # does for every other AiterExperts combo, instead of duplicating it.
534 rank_worker(pgi, vllm_config, cpu_group, config, weights, verbose)
535
536 if expected_backend == "flydsl":
537 assert flydsl_mock.call_count > 0, "Expected FlyDSL sorting to fire."
538 assert sorting_impl_mock.call_count == 0, (
539 "FlyDSL was requested and eligible, but the opus/CK sorting "
540 "path fired instead -- a silent fallback."
541 )
542 else:
543 assert flydsl_mock.call_count == 0, (
544 f"Expected the '{expected_backend}' sorting path, but FlyDSL "
545 "fired instead."
546 )
547 assert sorting_impl_mock.call_count > 0, (
548 f"Expected the '{expected_backend}' sorting path to fire."
549 )
550 # Bind by signature so this works regardless of whether use_opus
551 # is passed positionally or by keyword.
552 call = sorting_impl_mock.call_args
553 bound = inspect.signature(original_moe_sorting_impl).bind(
554 *call.args, **call.kwargs
555 )
556 use_opus = bound.arguments["use_opus"]
557 assert use_opus == (expected_backend == "opus"), (
558 f"Expected use_opus={expected_backend == 'opus'} for the "
559 f"'{expected_backend}' sorting path, got use_opus={use_opus}."
560 )
561
562
563 @require_aiter_moe
564 @pytest.mark.parametrize("ck,flydsl,expected_backend", AITER_SORTING_BACKEND_ENV_CASES)
565 def test_aiter_moe_sorting_backend_dispatch_env_matrix(
566 ck: int, flydsl: int, expected_backend: str
567 ):
568 """See https://github.com/vllm-project/vllm/issues/54966 ("Test AITER
569 backend and dispatch settings")."""
570 if not _aiter_sort_backend_hooks_available():
571 pytest.skip(
572 "Installed aiter build predates the sort-backend hooks "
573 "(_moe_sorting_impl, _flydsl_moe_sorting, _USE_CK_MOE_SORTING, "
574 "_USE_FLYDSL_MOE_SORTING)."
575 )
576
577 from vllm.model_executor.layers.fused_moe.prepare_finalize import (
578 MoEPrepareAndFinalizeNoDPEPModular,
579 )
580
581 config = Config(
582 Ms=[32],
583 K=2048,
584 N=1024,
585 E=32,
586 topks=[4],
587 dtype=torch.bfloat16,
588 quant_config=None,
589 prepare_finalize_type=MoEPrepareAndFinalizeNoDPEPModular,
590 fused_experts_type=AiterExperts,
591 world_size=1,
592 )
593 assert config.is_valid()[0]
594
595 weights = WeightTensors.make(config)
596 vllm_config, env_dict = config.make_env_data()
597 env_dict = {
598 **env_dict,
599 "AITER_USE_CK_MOE_SORTING": str(ck),
600 "AITER_USE_FLYDSL_MOE_SORTING": str(flydsl),
601 }
602
603 parallel_launch_with_config(
604 config.world_size,
605 _aiter_sorting_backend_worker,
606 vllm_config,
607 env_dict,
608 config,
609 weights,
610 False,
611 ck,
612 flydsl,
613 expected_backend,
614 )
615
616
617 def _aiter_dispatch_policy_worker(
618 pgi: ProcessGroupInfo,
619 vllm_config: VllmConfig,
620 cpu_group,
621 config: Config,
622 weights: WeightTensors,
623 verbose: bool,
624 dispatch_policy: int,
625 ):
626 # Drives the real AiterExperts.apply() -> rocm_aiter_fused_experts() call
627 # through the modular kernel, so it also catches a regression in apply()'s
628 # own forwarding (unlike test_rocm_aiter_moe.py's version, which supplies
629 # the policy value directly to rocm_aiter_fused_experts()).
630 with mock.patch.object(
631 rocm_aiter_ops, "fused_moe", wraps=rocm_aiter_ops.fused_moe
632 ) as fused_moe_mock:
633 rank_worker(pgi, vllm_config, cpu_group, config, weights, verbose)
634
635 assert fused_moe_mock.call_count > 0, (
636 "Expected AiterExperts.apply() to call rocm_aiter_ops.fused_moe."
637 )
638 for call in fused_moe_mock.call_args_list:
639 forwarded = call.kwargs["moe_sorting_dispatch_policy"]
640 assert forwarded == dispatch_policy, (
641 f"AiterExperts.apply() forwarded moe_sorting_dispatch_policy="
642 f"{forwarded}, but VLLM_ROCM_AITER_MOE_DISPATCH_POLICY="
643 f"{dispatch_policy} was set before this process started."
644 )
645
646
647 @require_aiter_moe
648 @pytest.mark.parametrize("dispatch_policy", [0, 1, 2])
649 def test_aiter_moe_dispatch_policy_forwarded_through_apply(dispatch_policy: int):
650 """See https://github.com/vllm-project/vllm/issues/54966 ("Test AITER
651 backend and dispatch settings").
652
653 Complements test_aiter_moe_dispatch_policy_forwarded_to_fused_moe in
654 test_rocm_aiter_moe.py by driving the real AiterExperts.apply() call
655 instead of supplying moe_sorting_dispatch_policy directly.
656 """
657 from vllm.model_executor.layers.fused_moe.prepare_finalize import (
658 MoEPrepareAndFinalizeNoDPEPModular,
659 )
660
661 config = Config(
662 Ms=[32],
663 K=2048,
664 N=1024,
665 E=32,
666 topks=[4],
667 dtype=torch.bfloat16,
668 quant_config=None,
669 prepare_finalize_type=MoEPrepareAndFinalizeNoDPEPModular,
670 fused_experts_type=AiterExperts,
671 world_size=1,
672 )
673 assert config.is_valid()[0]
674
675 weights = WeightTensors.make(config)
676 vllm_config, env_dict = config.make_env_data()
677 env_dict = {
678 **env_dict,
679 "VLLM_ROCM_AITER_MOE_DISPATCH_POLICY": str(dispatch_policy),
680 }
681
682 parallel_launch_with_config(
683 config.world_size,
684 _aiter_dispatch_policy_worker,
685 vllm_config,
686 env_dict,
687 config,
688 weights,
689 False,
690 dispatch_policy,
691 )
692
693
694 if __name__ == "__main__":
695 # Ability to test individual PrepareAndFinalize and FusedExperts combination
696 from .modular_kernel_tools.cli_args import make_config, make_config_arg_parser
697
698 parser = make_config_arg_parser(
699 description=(
700 "Run single prepare-finalize & fused-experts combination test"
701 "Example : python3 -m tests.kernels.moe.test_modular_kernel_combinations "
702 "--pf-type DeepEPLLPrepareAndFinalize --experts-type BatchedTritonExperts"
703 )
704 )
705 args = parser.parse_args()
706 config = make_config(args)
707
708 run(config, True)