@tduncan / Nvidia-Optimization-Stack-VLLM Mirror · read-only / commits / 0ae89f18fd
Fork 0

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 5, 22:15 UTC

[Refactor] Move FusedMoE hidden_size roundup to quant_method (#34285)

Signed-off-by: Bowen Bao <bowenbao@amd.com>

Bowen Bao committed Mar 26, 2026 at 23:38 UTC 0ae89f18fd75c5fcac48ff711ed84464c3da5d33
12 files changed +204 -222
vllm/model_executor/layers/fused_moe/config.py
+12 -3
@@ -229,9 +229,6 @@ class FusedMoEQuantConfig:
229 _w1: FusedMoEQuantDesc
230 _w2: FusedMoEQuantDesc
231 is_nvfp4_scale_swizzled: bool = True
232 - # CK MXFP4 (gfx950) padding info for rocm_aiter_ops.fused_moe()
233 - hidden_pad: int = 0
234 - intermediate_pad: int = 0
232
233 def __post_init__(self):
234 assert not self.per_act_token_quant or self.block_shape is None, (
@@ -1172,6 +1169,11 @@ class FusedMoEConfig:
1169 # Defaults to in_dtype if not specified.
1170 router_logits_dtype: torch.dtype | None = None
1171
1172 + # Defaults to hidden_dim if not specified.
1173 + hidden_dim_unpadded: int | None = None
1174 + # Defaults to intermediate_size_per_partition if not specified.
1175 + intermediate_size_per_partition_unpadded: int | None = None
1176 +
1177 moe_backend: str = "auto"
1178 max_num_tokens: int = envs.VLLM_MOE_DP_CHUNK_SIZE
1179 has_bias: bool = False
@@ -1195,6 +1197,13 @@ class FusedMoEConfig:
1197 if self.router_logits_dtype is None:
1198 self.router_logits_dtype = self.in_dtype
1199
1200 + if self.hidden_dim_unpadded is None:
1201 + self.hidden_dim_unpadded = self.hidden_dim
1202 + if self.intermediate_size_per_partition_unpadded is None:
1203 + self.intermediate_size_per_partition_unpadded = (
1204 + self.intermediate_size_per_partition
1205 + )
1206 +
1207 @property
1208 def tp_size(self):
1209 return self.moe_parallel_config.tp_size
vllm/model_executor/layers/fused_moe/fused_moe_method_base.py
+33
@@ -9,6 +9,7 @@ import vllm.model_executor.layers.fused_moe.modular_kernel as mk
9 from vllm.logger import init_logger
10 from vllm.model_executor.layers.fused_moe.config import (
11 FusedMoEConfig,
12 + FusedMoEParallelConfig,
13 FusedMoEQuantConfig,
14 )
15 from vllm.model_executor.layers.fused_moe.modular_kernel import (
@@ -65,6 +66,38 @@ class FusedMoEMethodBase(QuantizeMethodBase):
66 """
67 return False
68
69 + def maybe_roundup_sizes(
70 + self,
71 + hidden_size: int,
72 + intermediate_size_per_partition: int,
73 + act_dtype: torch.dtype,
74 + moe_parallel_config: FusedMoEParallelConfig,
75 + ) -> tuple[int, int]:
76 + """
77 + Given layer hidden size and intermediate size per partition and MoE
78 + configurations, round up hidden_size and intermediate_size_per_partition
79 + if necessary.
80 +
81 + Args:
82 + hidden_size: Layer hidden-size
83 + intermediate_size_per_partition: Intermediate size per partition for
84 + the layer.
85 + act_dtype: Data type of the layer activations.
86 + moe_parallel_config: Fused MoE parallelization strategy configuration.
87 +
88 + Return:
89 + A tuple of (rounded_hidden_size, rounded_intermediate_size_per_partition),
90 + where:
91 + - rounded_hidden_size is the possibly rounded up hidden size.
92 + - rounded_intermediate_size_per_partition is the possibly rounded
93 + up intermediate size per partition.
94 + """
95 + from .all2all_utils import maybe_roundup_layer_hidden_size
96 +
97 + return maybe_roundup_layer_hidden_size(
98 + hidden_size, act_dtype, moe_parallel_config
99 + ), intermediate_size_per_partition
100 +
101 def maybe_make_prepare_finalize(
102 self,
103 routing_tables: tuple[torch.Tensor, torch.Tensor, torch.Tensor] | None = None,
vllm/model_executor/layers/fused_moe/gpt_oss_triton_kernels_moe.py
+3 -13
@@ -428,13 +428,9 @@ def triton_kernel_fused_mxfp4_w4a8_experts(
428 assert quant_config.w1_bias is None or quant_config.w1_bias.dtype == torch.float32
429 assert quant_config.w2_bias is None or quant_config.w2_bias.dtype == torch.float32
430
431 - # Shape check: when weights are padded (e.g. hidden_size padded for
432 - # GFX950 swizzle), unpadded_K_w1 carries the original dimension.
433 - expected_K_w1 = unpadded_K_w1 if unpadded_K_w1 is not None else w1.shape[-2]
434 - assert hidden_states.shape[-1] == expected_K_w1, (
435 - f"hidden_states K={hidden_states.shape[-1]} != "
436 - f"expected K={expected_K_w1} (w1 K={w1.shape[-2]})"
437 - )
431 + # Shape check: weights are padded (e.g. hidden_size padded for
432 + # GFX950 swizzle).
433 + assert hidden_states.shape[-1] == w1.shape[-2]
434 assert w2.shape[-1] == w1.shape[1]
435
436 E, _, N = w1.shape
@@ -494,12 +490,6 @@ def triton_kernel_fused_mxfp4_w4a8_experts(
490 unpadded_K=unpadded_K_w2,
491 )
492
497 - # When hidden_size was padded for alignment (e.g. GFX950 swizzle),
498 - # the kernel output has the padded dimension. Slice back to the
499 - # original hidden_size so downstream layers see the expected shape.
500 - if unpadded_N_w2 is not None and intermediate_cache3.shape[-1] != unpadded_N_w2:
501 - intermediate_cache3 = intermediate_cache3[..., :unpadded_N_w2].contiguous()
502 -
493 return intermediate_cache3
494
495
vllm/model_executor/layers/fused_moe/layer.py
+63 -71
@@ -210,42 +210,6 @@ def get_compressed_expert_map(expert_map: torch.Tensor) -> str:
210 )
211
212
213 -# TODO(rob): move this down to the kernel.
214 -def maybe_roundup_hidden_size(
215 - hidden_size: int,
216 - act_dtype: torch.dtype,
217 - moe_parallel_config: FusedMoEParallelConfig,
218 - is_lora_enabled: bool,
219 - model_type: str | None,
220 -) -> int:
221 - """
222 - Given layer hidden size and MoE configurations, round up hidden_size
223 - if necessary.
224 -
225 - Args:
226 - hidden_size: Layer hidden-size
227 - act_dtype: Data type of the layer activations.
228 - moe_parallel_config: Fused MoE parallelization strategy configuration.
229 - is_lora_enabled: True if the engine is enabled with LoRA. This
230 - is used in the case of mxfp4 quantization in selecting the
231 - MxFP4Backend.
232 - model_type: for checking if gpt-oss
233 -
234 - Return:
235 - Rounded up hidden_size if rounding up is required based on the configs.
236 - Original hidden size otherwise.
237 - """
238 - from vllm.model_executor.layers.fused_moe.all2all_utils import (
239 - maybe_roundup_layer_hidden_size,
240 - )
241 -
242 - hidden_size = maybe_roundup_layer_hidden_size(
243 - hidden_size, act_dtype, moe_parallel_config
244 - )
245 -
246 - return hidden_size
247 -
248 -
213 # --8<-- [start:fused_moe]
214 @CustomOp.register("fused_moe")
215 class FusedMoE(CustomOp):
@@ -459,7 +423,7 @@ class FusedMoE(CustomOp):
423 ), "Aiter Fused MoE kernel only supports expert_map with 0 and 1s."
424
425 assert intermediate_size % self.tp_size == 0
462 - self.intermediate_size_per_partition = intermediate_size // self.tp_size
426 + intermediate_size_per_partition = intermediate_size // self.tp_size
427 self.reduce_results = reduce_results
428 self.renormalize = renormalize
429
@@ -501,28 +465,13 @@ class FusedMoE(CustomOp):
465 )
466 self.routing_method_type: RoutingMethodType = self.router.routing_method_type
467
504 - # Round up hidden size before creating moe_config.
505 - # This way moe_config is created with the correct hidden_size from the start.
506 - unpadded_hidden_size = hidden_size
507 - self.model_type = (
508 - self.vllm_config.model_config.hf_config.model_type
509 - if self.vllm_config.model_config is not None
510 - else None
511 - )
512 - hidden_size = maybe_roundup_hidden_size(
513 - hidden_size=hidden_size,
514 - act_dtype=moe_in_dtype,
515 - moe_parallel_config=self.moe_parallel_config,
516 - is_lora_enabled=vllm_config.lora_config is not None,
517 - model_type=self.model_type,
518 - )
519 - self.hidden_size = hidden_size
520 -
468 self.moe_config: FusedMoEConfig = FusedMoEConfig(
469 num_experts=self.global_num_experts,
470 experts_per_token=top_k,
471 hidden_dim=hidden_size,
525 - intermediate_size_per_partition=self.intermediate_size_per_partition,
472 + hidden_dim_unpadded=hidden_size,
473 + intermediate_size_per_partition=intermediate_size_per_partition,
474 + intermediate_size_per_partition_unpadded=intermediate_size_per_partition,
475 num_local_experts=self.local_num_experts,
476 num_logical_experts=self.logical_num_experts,
477 moe_parallel_config=self.moe_parallel_config,
@@ -567,13 +516,6 @@ class FusedMoE(CustomOp):
516 # for heuristic purposes, so it must be initialized first.
517 self.quant_method: FusedMoEMethodBase = _get_quant_method()
518
570 - # Quant methods (e.g. Mxfp4MoEMethod) may round up hidden_dim
571 - # and intermediate_size in moe_config during __init__. Sync
572 - # self.hidden_size so downstream consumers (e.g. LoRA) see the
573 - # padded value.
574 - if self.moe_config.hidden_dim != self.hidden_size:
575 - self.hidden_size = self.moe_config.hidden_dim
576 -
519 if not self.moe_config.is_act_and_mul and not current_platform.is_cuda_alike():
520 raise NotImplementedError(
521 "is_act_and_mul=False is supported only for CUDA and ROCm for now"
@@ -591,11 +533,24 @@ class FusedMoE(CustomOp):
533 f"EPLB is not supported {self.quant_method.__class__.__name__}."
534 )
535
536 + # Round up hidden size and update moe_config.
537 + hidden_size, intermediate_size_per_partition = (
538 + self.quant_method.maybe_roundup_sizes(
539 + hidden_size,
540 + intermediate_size_per_partition,
541 + moe_in_dtype,
542 + self.moe_parallel_config,
543 + )
544 + )
545 + self.moe_config.hidden_dim = hidden_size
546 + self.moe_config.intermediate_size_per_partition = (
547 + intermediate_size_per_partition
548 + )
549 +
550 moe_quant_params = {
551 "num_experts": self.local_num_experts,
596 - "hidden_size": self.hidden_size,
597 - "unpadded_hidden_size": unpadded_hidden_size,
598 - "intermediate_size_per_partition": self.intermediate_size_per_partition,
552 + "hidden_size": hidden_size,
553 + "intermediate_size_per_partition": intermediate_size_per_partition,
554 "params_dtype": params_dtype,
555 "weight_loader": self.weight_loader,
556 "global_num_experts": self.global_num_experts,
@@ -933,9 +888,17 @@ class FusedMoE(CustomOp):
888 # Only narrow if the loaded_weight is not a scalar (0-dim tensor)
889 # and we're not loading the full weight
890 if not load_full and loaded_weight.ndim > 0:
936 - loaded_weight = loaded_weight.narrow(
937 - shard_dim, shard_size * tp_rank, shard_size
938 - )
891 + # Handle padding: loaded_weight might be smaller than shard_size on last
892 + # TP rank
893 + start_offset = shard_size * tp_rank
894 + available = loaded_weight.shape[shard_dim] - start_offset
895 + if available <= 0:
896 + # If there is no available weight to load for this TP rank
897 + # (can happen on last TP rank with padding), we can skip
898 + # loading and return early
899 + return
900 + narrow_size = min(shard_size, available)
901 + loaded_weight = loaded_weight.narrow(shard_dim, start_offset, narrow_size)
902 # Narrow parameter and load.
903 # w1, gate_proj: Load into first logical weight of w13.
904 if shard_id == "w1":
@@ -944,6 +907,13 @@ class FusedMoE(CustomOp):
907 else:
908 assert shard_id == "w3"
909 expert_data = expert_data.narrow(shard_dim, shard_size, shard_size)
910 +
911 + # Handle padding: if loaded_weight is smaller than expert_data (can happen
912 + # on last TP shard with padding), copy to top-left corner
913 + if expert_data.shape != loaded_weight.shape:
914 + expert_data = expert_data[
915 + : loaded_weight.shape[0], : loaded_weight.shape[1]
916 + ]
917 expert_data.copy_(loaded_weight)
918
919 def _load_w2(
@@ -961,10 +931,24 @@ class FusedMoE(CustomOp):
931 # Only narrow if the loaded_weight is not a scalar (0-dim tensor)
932 # and we're not loading the full weight
933 if not load_full and loaded_weight.ndim > 0:
964 - loaded_weight = loaded_weight.narrow(
965 - shard_dim, shard_size * tp_rank, shard_size
966 - )
934 + # Handle padding: loaded_weight might be smaller than shard_size on last
935 + # TP rank
936 + start_offset = shard_size * tp_rank
937 + available = loaded_weight.shape[shard_dim] - start_offset
938 + if available <= 0:
939 + # If there is no available weight to load for this TP rank
940 + # (can happen on last TP rank with padding), we can skip
941 + # loading and return early
942 + return
943 + narrow_size = min(shard_size, available)
944 + loaded_weight = loaded_weight.narrow(shard_dim, start_offset, narrow_size)
945 # w2, down_proj: Load into only logical weight of w2.
946 + # Handle padding: if loaded_weight is smaller than expert_data (can happen
947 + # on last TP shard with padding), copy to top-left corner
948 + if expert_data.shape != loaded_weight.shape:
949 + expert_data = expert_data[
950 + : loaded_weight.shape[0], : loaded_weight.shape[1]
951 + ]
952 expert_data.copy_(loaded_weight)
953
954 def _load_single_value(
@@ -1549,6 +1533,14 @@ class FusedMoE(CustomOp):
1533 ]
1534 ]
1535
1536 + @property
1537 + def hidden_size(self) -> int:
1538 + return self.moe_config.hidden_dim
1539 +
1540 + @property
1541 + def intermediate_size_per_partition(self) -> int:
1542 + return self.moe_config.intermediate_size_per_partition
1543 +
1544 def extra_repr(self) -> str:
1545 s = (
1546 f"global_num_experts={self.global_num_experts}, "
vllm/model_executor/layers/fused_moe/oracle/mxfp4.py
+1 -7
@@ -779,8 +779,6 @@ def make_mxfp4_moe_quant_config(
779 w2_scale: Union[torch.Tensor, "PrecisionConfig"],
780 w1_bias: torch.Tensor | None = None,
781 w2_bias: torch.Tensor | None = None,
782 - hidden_pad: int = 0,
783 - intermediate_pad: int = 0,
782 ) -> FusedMoEQuantConfig | None:
783 """Create a FusedMoEQuantConfig for the given MXFP4 backend."""
784 if mxfp4_backend in (
@@ -802,16 +800,12 @@ def make_mxfp4_moe_quant_config(
800 Mxfp4MoeBackend.FLASHINFER_CUTLASS_MXFP4_BF16,
801 Mxfp4MoeBackend.CK,
802 ):
805 - config = mxfp4_w4a16_moe_quant_config(
803 + return mxfp4_w4a16_moe_quant_config(
804 w1_bias=w1_bias,
805 w2_bias=w2_bias,
806 w1_scale=w1_scale,
807 w2_scale=w2_scale,
808 )
811 - if mxfp4_backend == Mxfp4MoeBackend.CK:
812 - config.hidden_pad = hidden_pad
813 - config.intermediate_pad = intermediate_pad
814 - return config
809 else:
810 return ocp_mx_moe_quant_config(
811 quant_dtype="mxfp4",
vllm/model_executor/layers/fused_moe/rocm_aiter_fused_moe.py
+16 -2
@@ -10,6 +10,7 @@ from vllm._aiter_ops import rocm_aiter_ops
10 from vllm.model_executor.layers.fused_moe.activation import MoEActivation
11 from vllm.model_executor.layers.fused_moe.config import (
12 FUSED_MOE_UNQUANTIZED_CONFIG,
13 + FusedMoEConfig,
14 FusedMoEParallelConfig,
15 FusedMoEQuantConfig,
16 )
@@ -186,6 +187,7 @@ def rocm_aiter_fused_experts(
187 w2: torch.Tensor,
188 topk_weights: torch.Tensor,
189 topk_ids: torch.Tensor,
190 + moe_config: FusedMoEConfig,
191 activation: MoEActivation = MoEActivation.SILU,
192 apply_router_weight_on_input: bool = False,
193 expert_map: torch.Tensor | None = None,
@@ -276,6 +278,17 @@ def rocm_aiter_fused_experts(
278 "Only support topk=1 when `apply_router_weight_on_input` is True"
279 )
280
281 + # Compute padding on-the-fly for CK MXFP4 kernels
282 + hidden_pad = 0
283 + intermediate_pad = 0
284 + assert moe_config.hidden_dim_unpadded is not None
285 + assert moe_config.intermediate_size_per_partition_unpadded is not None
286 + hidden_pad = hidden_states.shape[1] - moe_config.hidden_dim_unpadded
287 + intermediate_pad = (
288 + moe_config.intermediate_size_per_partition
289 + - moe_config.intermediate_size_per_partition_unpadded
290 + )
291 +
292 return rocm_aiter_ops.fused_moe(
293 hidden_states,
294 w1,
@@ -292,8 +305,8 @@ def rocm_aiter_fused_experts(
305 doweight_stage1=apply_router_weight_on_input,
306 num_local_tokens=num_local_tokens,
307 output_dtype=output_dtype,
295 - hidden_pad=quant_config.hidden_pad,
296 - intermediate_pad=quant_config.intermediate_pad,
308 + hidden_pad=hidden_pad,
309 + intermediate_pad=intermediate_pad,
310 bias1=quant_config.w1_bias if quant_config.use_mxfp4_w4a16 else None,
311 bias2=quant_config.w2_bias if quant_config.use_mxfp4_w4a16 else None,
312 )
@@ -419,6 +432,7 @@ class AiterExperts(mk.FusedMoEExpertsModular):
432 apply_router_weight_on_input=apply_router_weight_on_input,
433 expert_map=expert_map,
434 quant_config=self.quant_config,
435 + moe_config=self.moe_config,
436 a1q_scale=a1q_scale,
437 num_local_tokens=num_local_tokens,
438 output_dtype=output.dtype,
vllm/model_executor/layers/quantization/compressed_tensors/compressed_tensors_moe.py
-4
@@ -715,8 +715,6 @@ class CompressedTensorsW8A8Fp8MoEMethod(CompressedTensorsMoEMethod):
715 params_dtype: torch.dtype,
716 **extra_weight_attrs,
717 ):
718 - layer.intermediate_size_per_partition = intermediate_size_per_partition
719 - layer.hidden_size = hidden_size
718 layer.num_experts = num_experts
719 layer.orig_dtype = params_dtype
720 layer.weight_block_size = None
@@ -2274,8 +2272,6 @@ class CompressedTensorsW4A8Fp8MoEMethod(CompressedTensorsMoEMethod):
2272 params_dtype: torch.dtype,
2273 **extra_weight_attrs,
2274 ):
2277 - layer.intermediate_size_per_partition = intermediate_size_per_partition
2278 - layer.hidden_size = hidden_size
2275 layer.num_experts = num_experts
2276 layer.orig_dtype = params_dtype
2277 layer.weight_block_size = None
vllm/model_executor/layers/quantization/fp8.py
-4
@@ -672,8 +672,6 @@ class Fp8MoEMethod(FusedMoEMethodBase):
672 params_dtype: torch.dtype,
673 **extra_weight_attrs,
674 ):
675 - layer.intermediate_size_per_partition = intermediate_size_per_partition
676 - layer.hidden_size = hidden_size
675 layer.num_experts = num_experts
676 layer.orig_dtype = params_dtype
677 layer.weight_block_size = None
@@ -1011,8 +1009,6 @@ class Fp8OnlineMoEMethod(Fp8MoEMethod):
1009 params_dtype: torch.dtype,
1010 **extra_weight_attrs,
1011 ):
1014 - layer.intermediate_size_per_partition = intermediate_size_per_partition
1015 - layer.hidden_size = hidden_size
1012 layer.num_experts = num_experts
1013 layer.orig_dtype = params_dtype
1014 layer.weight_block_size = None
vllm/model_executor/layers/quantization/mxfp4.py
+25 -37
@@ -13,6 +13,7 @@ from vllm.model_executor.layers.fused_moe import (
13 )
14 from vllm.model_executor.layers.fused_moe import modular_kernel as mk
15 from vllm.model_executor.layers.fused_moe.config import (
16 + FusedMoEParallelConfig,
17 FusedMoEQuantConfig,
18 )
19 from vllm.model_executor.layers.fused_moe.oracle.mxfp4 import (
@@ -107,18 +108,6 @@ class Mxfp4MoEMethod(FusedMoEMethodBase):
108 self._cache_permute_indices: dict[torch.Size, torch.Tensor] = {}
109 self.moe_kernel: mk.FusedMoEKernel | None = None
110
110 - # Round up dims once based on backend. This mutates the shared
111 - # FusedMoEConfig in-place so that create_weights() and all
112 - # downstream code see the padded dimensions. This must happen
113 - # before create_weights() is called.
114 - self.moe.hidden_dim, self.moe.intermediate_size_per_partition = (
115 - mxfp4_round_up_hidden_size_and_intermediate_size(
116 - self.mxfp4_backend,
117 - self.moe.hidden_dim,
118 - self.moe.intermediate_size_per_partition,
119 - )
120 - )
121 -
111 # Used for triton kernel precision configs
112 self.w13_precision_config = None
113 self.w2_precision_config = None
@@ -129,6 +118,23 @@ class Mxfp4MoEMethod(FusedMoEMethodBase):
118 # so can skip the padding in the forward before applying the moe method
119 return self.mxfp4_backend == Mxfp4MoeBackend.FLASHINFER_TRTLLM_MXFP4_MXFP8
120
121 + def maybe_roundup_sizes(
122 + self,
123 + hidden_size: int,
124 + intermediate_size_per_partition: int,
125 + act_dtype: torch.dtype,
126 + moe_parallel_config: FusedMoEParallelConfig,
127 + ) -> tuple[int, int]:
128 + hidden_size, intermediate_size_per_partition = super().maybe_roundup_sizes(
129 + hidden_size=hidden_size,
130 + intermediate_size_per_partition=intermediate_size_per_partition,
131 + act_dtype=act_dtype,
132 + moe_parallel_config=moe_parallel_config,
133 + )
134 + return mxfp4_round_up_hidden_size_and_intermediate_size(
135 + self.mxfp4_backend, hidden_size, intermediate_size_per_partition
136 + )
137 +
138 def create_weights(
139 self,
140 layer: torch.nn.Module,
@@ -143,32 +149,16 @@ class Mxfp4MoEMethod(FusedMoEMethodBase):
149 scale_dtype = torch.uint8
150 mxfp4_block = 32
151
146 - # Use pre-rounded sizes from config
147 - self.intermediate_size = intermediate_size_per_partition_after_pad = (
148 - self.moe.intermediate_size_per_partition
149 - )
150 - self.hidden_size = hidden_size = self.moe.hidden_dim
151 -
152 - # Expose padded dimensions on the layer for LoRA and Marlin code
153 - # that reads layer.hidden_size / layer.intermediate_size_per_partition.
152 layer.params_dtype = params_dtype
153 layer.num_experts = num_experts
156 - layer.hidden_size = hidden_size
157 - layer.intermediate_size_per_partition = (
158 - intermediate_size_per_partition_after_pad
159 - )
160 -
161 - # CK (gfx950) padding info for rocm_aiter_ops.fused_moe()
162 - self.hidden_pad = extra_weight_attrs.get("hidden_pad", 0)
163 - self.intermediate_pad = (
164 - intermediate_size_per_partition_after_pad - intermediate_size_per_partition
165 - )
154 + self.intermediate_size = intermediate_size_per_partition
155 + self.hidden_size = hidden_size
156
157 # Fused gate_up_proj (column parallel)
158 w13_weight = torch.nn.Parameter(
159 torch.zeros(
160 num_experts,
171 - 2 * intermediate_size_per_partition_after_pad,
161 + 2 * intermediate_size_per_partition,
162 hidden_size // 2,
163 dtype=weight_dtype,
164 ),
@@ -180,7 +170,7 @@ class Mxfp4MoEMethod(FusedMoEMethodBase):
170 w13_weight_scale = torch.nn.Parameter(
171 torch.zeros(
172 num_experts,
183 - 2 * intermediate_size_per_partition_after_pad,
173 + 2 * intermediate_size_per_partition,
174 hidden_size // mxfp4_block,
175 dtype=scale_dtype,
176 ),
@@ -194,7 +184,7 @@ class Mxfp4MoEMethod(FusedMoEMethodBase):
184 torch.zeros(
185 num_experts,
186 hidden_size,
197 - intermediate_size_per_partition_after_pad // 2,
187 + intermediate_size_per_partition // 2,
188 dtype=weight_dtype,
189 ),
190 requires_grad=False,
@@ -206,7 +196,7 @@ class Mxfp4MoEMethod(FusedMoEMethodBase):
196 torch.zeros(
197 num_experts,
198 hidden_size,
209 - intermediate_size_per_partition_after_pad // mxfp4_block,
199 + intermediate_size_per_partition // mxfp4_block,
200 dtype=scale_dtype,
201 ),
202 requires_grad=False,
@@ -218,7 +208,7 @@ class Mxfp4MoEMethod(FusedMoEMethodBase):
208 w13_bias = torch.nn.Parameter(
209 torch.zeros(
210 num_experts,
221 - 2 * intermediate_size_per_partition_after_pad,
211 + 2 * intermediate_size_per_partition,
212 dtype=torch.bfloat16,
213 ),
214 requires_grad=False,
@@ -368,8 +358,6 @@ class Mxfp4MoEMethod(FusedMoEMethodBase):
358 w2_scale=w2_scale,
359 w1_bias=w1_bias,
360 w2_bias=w2_bias,
371 - hidden_pad=self.hidden_pad,
372 - intermediate_pad=self.intermediate_pad,
361 )
362
363 def select_gemm_impl(
vllm/model_executor/layers/quantization/quark/quark_moe.py
+51 -79
@@ -18,6 +18,7 @@ from vllm.model_executor.layers.fused_moe import (
18 MoEActivation,
19 )
20 from vllm.model_executor.layers.fused_moe.config import (
21 + FusedMoEParallelConfig,
22 FusedMoEQuantConfig,
23 fp8_w8a8_moe_quant_config,
24 mxfp4_w4a8_moe_quant_config,
@@ -27,13 +28,13 @@ from vllm.model_executor.layers.fused_moe.config import (
28 from vllm.model_executor.layers.fused_moe.fused_marlin_moe import fused_marlin_moe
29 from vllm.model_executor.layers.fused_moe.oracle.mxfp4 import (
30 Mxfp4MoeBackend,
31 + mxfp4_round_up_hidden_size_and_intermediate_size,
32 select_mxfp4_moe_backend,
33 )
34 from vllm.model_executor.layers.quantization.utils.marlin_utils_fp8 import (
35 prepare_fp8_moe_layer_for_marlin,
36 )
37 from vllm.model_executor.layers.quantization.utils.mxfp4_utils import (
36 - CK_MXFP4_MOE_DIM_ALIGNMENT,
38 _swizzle_mxfp4,
39 )
40 from vllm.model_executor.layers.quantization.utils.ocp_mx_utils import (
@@ -49,7 +50,6 @@ from vllm.model_executor.layers.quantization.utils.w8a8_utils import (
50 from vllm.model_executor.utils import set_weight_attrs
51 from vllm.platforms import current_platform
52 from vllm.scalar_type import scalar_types
52 -from vllm.utils.math_utils import round_up
53
54 logger = init_logger(__name__)
55
@@ -173,8 +173,6 @@ class QuarkW8A8Fp8MoEMethod(QuarkMoEMethod):
173 params_dtype: torch.dtype,
174 **extra_weight_attrs,
175 ):
176 - layer.intermediate_size_per_partition = intermediate_size_per_partition
177 - layer.hidden_size = hidden_size
176 layer.num_experts = num_experts
177 layer.orig_dtype = params_dtype
178 layer.weight_block_size = None
@@ -182,7 +180,7 @@ class QuarkW8A8Fp8MoEMethod(QuarkMoEMethod):
180
181 # WEIGHTS
182 w13_weight = torch.nn.Parameter(
185 - torch.empty(
183 + torch.zeros(
184 num_experts,
185 2 * intermediate_size_per_partition,
186 hidden_size,
@@ -194,7 +192,7 @@ class QuarkW8A8Fp8MoEMethod(QuarkMoEMethod):
192 set_weight_attrs(w13_weight, extra_weight_attrs)
193
194 w2_weight = torch.nn.Parameter(
197 - torch.empty(
195 + torch.zeros(
196 num_experts,
197 hidden_size,
198 intermediate_size_per_partition,
@@ -461,6 +459,7 @@ class QuarkW8A8Fp8MoEMethod(QuarkMoEMethod):
459 activation=layer.activation,
460 apply_router_weight_on_input=layer.apply_router_weight_on_input,
461 quant_config=self.moe_quant_config,
462 + moe_config=layer.moe_config,
463 expert_map=layer.expert_map,
464 )
465 elif self.use_marlin:
@@ -527,7 +526,7 @@ class QuarkW4A8Fp8MoEMethod(QuarkMoEMethod):
526 ):
527 params_dtype = torch.uint32
528 w13_weight = torch.nn.Parameter(
530 - torch.empty(
529 + torch.zeros(
530 num_experts,
531 2 * intermediate_size_per_partition,
532 hidden_size // 8, # INT32 packing for W4
@@ -536,7 +535,7 @@ class QuarkW4A8Fp8MoEMethod(QuarkMoEMethod):
535 requires_grad=False,
536 )
537 w2_weight = torch.nn.Parameter(
539 - torch.empty(
538 + torch.zeros(
539 num_experts,
540 hidden_size,
541 intermediate_size_per_partition // 8, # INT32 packing for W4
@@ -649,6 +648,7 @@ class QuarkW4A8Fp8MoEMethod(QuarkMoEMethod):
648 activation=layer.activation,
649 apply_router_weight_on_input=layer.apply_router_weight_on_input,
650 quant_config=self.moe_quant_config,
651 + moe_config=layer.moe_config,
652 expert_map=layer.expert_map,
653 )
654
@@ -702,6 +702,9 @@ class QuarkOCP_MX_MoEMethod(QuarkMoEMethod):
702 self.mxfp4_backend: Mxfp4MoeBackend | None = None
703 if self.ocp_mx_scheme == "w_mxfp4":
704 self.mxfp4_backend, _ = select_mxfp4_moe_backend(moe)
705 + elif self.ocp_mx_scheme.startswith("w_mxfp4"):
706 + # TODO(bowenbao): refactor and introduce backends for other OCP MX schemes.
707 + self.mxfp4_backend = Mxfp4MoeBackend.NONE
708
709 if self.input_quant is not None:
710 self.static_input_scales = not self.input_quant.get("is_dynamic")
@@ -734,36 +737,11 @@ class QuarkOCP_MX_MoEMethod(QuarkMoEMethod):
737 self.emulate = (
738 not current_platform.supports_mx()
739 or not self.ocp_mx_scheme.startswith("w_mxfp4")
737 - ) and (self.mxfp4_backend is None or not self.use_rocm_aiter_moe)
738 -
739 - # CK's pre-compiled MXFP4 MoE GEMM kernel instances have dimension
740 - # alignment requirements. When violated (e.g. MiniMax-M2.1 with
741 - # TP=4 yields intermediate_size_per_partition=384), AITER raises:
742 - # "device_gemm ... does not support this GEMM problem".
743 - # Fall back to emulation in that case.
744 - # For gpt_oss models, create_weights rounds up the dimensions
745 - # internally, so the alignment check is skipped.
746 - if (
747 - not self.emulate
748 - and self.use_rocm_aiter_moe
749 - and self.ocp_mx_scheme is not None
750 - and self.ocp_mx_scheme.startswith("w_mxfp4")
751 - and self.model_type != "gpt_oss"
752 - and moe.intermediate_size_per_partition % CK_MXFP4_MOE_DIM_ALIGNMENT != 0
753 - ):
754 - logger.warning_once(
755 - "AITER CK MXFP4 MoE GEMM does not support "
756 - "intermediate_size_per_partition=%d (not a multiple of %d). "
757 - "This typically happens when intermediate_size / "
758 - "tensor_parallel_size produces an incompatible dimension. "
759 - "Falling back to emulation mode. To avoid this overhead, "
760 - "use a compatible tensor_parallel_size or set "
761 - "VLLM_ROCM_USE_AITER_MOE=0.",
762 - moe.intermediate_size_per_partition,
763 - CK_MXFP4_MOE_DIM_ALIGNMENT,
764 - )
765 - self.use_rocm_aiter_moe = False
766 - self.emulate = True
740 + ) and (
741 + self.mxfp4_backend is None
742 + or self.mxfp4_backend is Mxfp4MoeBackend.NONE
743 + or not self.use_rocm_aiter_moe
744 + )
745
746 if self.emulate:
747 logger.warning_once(
@@ -780,6 +758,27 @@ class QuarkOCP_MX_MoEMethod(QuarkMoEMethod):
758 "The current mode supports native MoE MXFP4 computation"
759 )
760
761 + def maybe_roundup_sizes(
762 + self,
763 + hidden_size: int,
764 + intermediate_size_per_partition: int,
765 + act_dtype: torch.dtype,
766 + moe_parallel_config: FusedMoEParallelConfig,
767 + ) -> tuple[int, int]:
768 + hidden_size, intermediate_size_per_partition = super().maybe_roundup_sizes(
769 + hidden_size=hidden_size,
770 + intermediate_size_per_partition=intermediate_size_per_partition,
771 + act_dtype=act_dtype,
772 + moe_parallel_config=moe_parallel_config,
773 + )
774 + if self.mxfp4_backend is not None:
775 + hidden_size, intermediate_size_per_partition = (
776 + mxfp4_round_up_hidden_size_and_intermediate_size(
777 + self.mxfp4_backend, hidden_size, intermediate_size_per_partition
778 + )
779 + )
780 + return hidden_size, intermediate_size_per_partition
781 +
782 def get_packed_dim(self, dim: int, quant_dtype: str):
783 if quant_dtype == "mxfp4":
784 assert dim % 2 == 0
@@ -805,40 +804,12 @@ class QuarkOCP_MX_MoEMethod(QuarkMoEMethod):
804 )
805
806 params_dtype = torch.uint8
808 - self.intermediate_size_per_partition = intermediate_size_per_partition
809 - if self.model_type == "gpt_oss":
810 - if current_platform.is_rocm():
811 - intermediate_size_per_partition_after_pad = round_up(
812 - intermediate_size_per_partition, 256
813 - )
814 - else:
815 - intermediate_size_per_partition_after_pad = round_up(
816 - intermediate_size_per_partition, 64
817 - )
818 - else:
819 - intermediate_size_per_partition_after_pad = intermediate_size_per_partition
820 -
821 - self.unpadded_hidden_size = extra_weight_attrs.get(
822 - "unpadded_hidden_size", hidden_size
823 - )
824 -
825 - # On GFX950, the GFX950MXScaleLayout swizzle requires
826 - # hidden_size to be a multiple of 256 (SCALE_K = hidden_size / 32
827 - # must be divisible by 8). Pad hidden_size for weight/scale
828 - # allocation; the original value is preserved in unpadded_hidden_size.
829 - # Only applies to the native (non-emulated) CK path on GFX950.
830 - if (
831 - self.model_type == "gpt_oss"
832 - and current_platform.is_rocm()
833 - and not self.emulate
834 - ):
835 - hidden_size = round_up(hidden_size, 256)
807
808 # WEIGHTS
809 w13_weight = torch.nn.Parameter(
839 - torch.empty(
810 + torch.zeros(
811 num_experts,
841 - 2 * intermediate_size_per_partition_after_pad,
812 + 2 * intermediate_size_per_partition,
813 self.get_packed_dim(hidden_size, self.weight_dtype),
814 dtype=params_dtype,
815 ),
@@ -849,12 +820,10 @@ class QuarkOCP_MX_MoEMethod(QuarkMoEMethod):
820 set_weight_attrs(w13_weight, extra_weight_attrs)
821
822 w2_weight = torch.nn.Parameter(
852 - torch.empty(
823 + torch.zeros(
824 num_experts,
825 hidden_size,
855 - self.get_packed_dim(
856 - intermediate_size_per_partition_after_pad, self.weight_dtype
857 - ),
826 + self.get_packed_dim(intermediate_size_per_partition, self.weight_dtype),
827 dtype=params_dtype,
828 ),
829 requires_grad=False,
@@ -867,7 +836,7 @@ class QuarkOCP_MX_MoEMethod(QuarkMoEMethod):
836 w13_weight_scale = torch.nn.Parameter(
837 torch.ones(
838 num_experts,
870 - 2 * intermediate_size_per_partition_after_pad,
839 + 2 * intermediate_size_per_partition,
840 hidden_size // OCP_MX_BLOCK_SIZE,
841 dtype=params_dtype,
842 ),
@@ -877,7 +846,7 @@ class QuarkOCP_MX_MoEMethod(QuarkMoEMethod):
846 torch.ones(
847 num_experts,
848 hidden_size,
880 - intermediate_size_per_partition_after_pad // OCP_MX_BLOCK_SIZE,
849 + intermediate_size_per_partition // OCP_MX_BLOCK_SIZE,
850 dtype=params_dtype,
851 ),
852 requires_grad=False,
@@ -892,7 +861,7 @@ class QuarkOCP_MX_MoEMethod(QuarkMoEMethod):
861 w13_bias = torch.nn.Parameter(
862 torch.zeros(
863 num_experts,
895 - 2 * intermediate_size_per_partition_after_pad,
864 + 2 * intermediate_size_per_partition,
865 dtype=torch.float32,
866 ),
867 requires_grad=False,
@@ -1072,6 +1041,7 @@ class QuarkOCP_MX_MoEMethod(QuarkMoEMethod):
1041 topk_ids=topk_ids,
1042 activation=layer.activation,
1043 quant_config=self.moe_quant_config,
1044 + moe_config=layer.moe_config,
1045 expert_map=layer.expert_map,
1046 )
1047 else:
@@ -1204,6 +1174,8 @@ class QuarkOCP_MX_MoEMethod_OSS(QuarkOCP_MX_MoEMethod):
1174 triton_kernel_moe_forward,
1175 )
1176
1177 + assert self.moe.hidden_dim_unpadded is not None
1178 + assert self.moe.intermediate_size_per_partition_unpadded is not None
1179 return triton_kernel_moe_forward(
1180 hidden_states=x,
1181 w1=self.w13_weight_triton_tensor,
@@ -1215,8 +1187,8 @@ class QuarkOCP_MX_MoEMethod_OSS(QuarkOCP_MX_MoEMethod):
1187 expert_map=expert_map,
1188 quant_config=self.moe_quant_config,
1189 apply_router_weight_on_input=layer.apply_router_weight_on_input,
1218 - unpadded_N_w1=self.intermediate_size_per_partition * 2,
1219 - unpadded_K_w1=self.unpadded_hidden_size,
1220 - unpadded_N_w2=self.unpadded_hidden_size,
1221 - unpadded_K_w2=self.intermediate_size_per_partition,
1190 + unpadded_N_w1=self.moe.intermediate_size_per_partition_unpadded * 2,
1191 + unpadded_K_w1=self.moe.hidden_dim_unpadded,
1192 + unpadded_N_w2=self.moe.hidden_dim_unpadded,
1193 + unpadded_K_w2=self.moe.intermediate_size_per_partition_unpadded,
1194 )
vllm/model_executor/layers/quantization/utils/flashinfer_fp4_moe.py
-1
@@ -254,7 +254,6 @@ def prepare_nvfp4_moe_layer_for_fi_or_cutlass(
254 w13, w13_scale, w2, w2_scale, is_act_and_mul, min_alignment
255 )
256 )
257 - layer.intermediate_size_per_partition = padded_intermediate
257 layer.moe_config.intermediate_size_per_partition = padded_intermediate
258
259 w13, w13_scale, w2, w2_scale = prepare_static_weights_for_trtllm_fp4_moe(
vllm/model_executor/layers/quantization/utils/flashinfer_utils.py
-1
@@ -439,7 +439,6 @@ def prepare_fp8_moe_layer_for_fi(
439 layer.moe_config.is_act_and_mul,
440 min_alignment,
441 )
442 - layer.intermediate_size_per_partition = new_intermediate
442 layer.moe_config.intermediate_size_per_partition = new_intermediate
443
444 # FI kernels require W31 layout rather than W13.