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 13, 08:15 UTC
[Performance][B200] Fix deepgemm prologue (#27897)
Signed-off-by: Varun Sundar Rabindranath <vsundarr@redhat.com> Co-authored-by: Varun Sundar Rabindranath <vsundarr@redhat.com>
Varun Sundar Rabindranath committed
Nov 12, 2025 at 16:13 UTC
74a9a9faad062e6fe90f7d238b5fad7e2f3da237
6 files changed
+163
-48
vllm/model_executor/layers/fused_moe/batched_deep_gemm_moe.py
+7
@@ -232,6 +232,7 @@ class BatchedDeepGemmExperts(mk.FusedMoEPermuteExpertsUnpermute):
232
"""
233
super().__init__(quant_config)
234
assert self.block_shape == get_mk_alignment_for_contiguous_layout()
235
+ assert self.quant_config.use_fp8_w8a8
236
self.max_num_tokens = max_num_tokens
237
self.num_dispatchers = num_dispatchers
238
@@ -250,6 +251,12 @@ class BatchedDeepGemmExperts(mk.FusedMoEPermuteExpertsUnpermute):
251
def supports_expert_map(self) -> bool:
252
return False
253
254
+ def supports_packed_ue8m0_act_scales(self) -> bool:
255
+ """
256
+ DeepGemm supports packed ue8m0 activation scales format in devices == sm100
257
+ """
258
+ return current_platform.is_device_capability(100)
259
+
260
def finalize_weight_and_reduce_impl(self) -> mk.TopKWeightAndReduce:
261
# Let PrepareAndFinalize::finalize() decide the impl.
262
return TopKWeightAndReduceDelegate()
vllm/model_executor/layers/fused_moe/deepep_ll_prepare_finalize.py
+29
@@ -6,6 +6,7 @@ import deep_ep
6
import torch
7
8
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 FusedMoEQuantConfig
11
from vllm.model_executor.layers.fused_moe.topk_weight_and_reduce import (
12
TopKWeightAndReduceDelegate,
@@ -20,6 +21,8 @@ from vllm.v1.worker.ubatching import (
21
dbo_maybe_run_recv_hook,
22
)
23
24
+logger = init_logger(__name__)
25
+
26
# DeepEP kernels quantize dispatch inputs in 128 element chunks.
27
DEEPEP_QUANT_BLOCK_SIZE = 128
28
DEEPEP_QUANT_BLOCK_SHAPE = [DEEPEP_QUANT_BLOCK_SIZE, DEEPEP_QUANT_BLOCK_SIZE]
@@ -94,6 +97,29 @@ class DeepEPLLPrepareAndFinalize(mk.FusedMoEPrepareAndFinalize):
97
self.handles: list[tuple | None] = [None, None]
98
self.num_dispatchers_ = num_dispatchers
99
100
+ # We don't have enough information to determine if we should dispatch
101
+ # activation scales in a packed ue8m0 format during object construction
102
+ # time. This setting is handled by post_init_setup.
103
+ self.use_ue8m0_dispatch = False
104
+
105
+ def post_init_setup(self, fused_experts: mk.FusedMoEPermuteExpertsUnpermute):
106
+ if not fused_experts.supports_packed_ue8m0_act_scales():
107
+ # Early exit.
108
+ return
109
+
110
+ if self.use_fp8_dispatch:
111
+ logger.debug_once(
112
+ "Update DeepEPLLPrepareFinalize to do packed ue8m0 scales dispatch."
113
+ )
114
+ self.use_ue8m0_dispatch = True
115
+ else:
116
+ logger.warning_once(
117
+ "DeepEPLLPrepareAndFinalize is setup to dispatch raw/unquantized "
118
+ f"activations despite ({fused_experts.__class__.__name__}) being able "
119
+ "to support quantized activations.",
120
+ scope="local",
121
+ )
122
+
123
def num_dispatchers(self) -> int:
124
return self.num_dispatchers_
125
@@ -206,6 +232,9 @@ class DeepEPLLPrepareAndFinalize(mk.FusedMoEPrepareAndFinalize):
232
self.max_tokens_per_rank,
233
num_experts,
234
use_fp8=self.use_fp8_dispatch,
235
+ # round_scale needs to be set to dispatch in ue8m0
236
+ round_scale=self.use_ue8m0_dispatch,
237
+ use_ue8m0=self.use_ue8m0_dispatch,
238
async_finish=False,
239
return_recv_hook=True,
240
)
vllm/model_executor/layers/fused_moe/modular_kernel.py
+25
@@ -149,6 +149,15 @@ class FusedMoEPrepareAndFinalize(ABC):
149
described above.
150
"""
151
152
+ def post_init_setup(self, fused_experts: "FusedMoEPermuteExpertsUnpermute"):
153
+ """
154
+ Initialize FusedMoEPrepareAndFinalize settings that depend on
155
+ FusedMoEPermuteExpertsUnpermute experts object.
156
+ The FusedMoEPrepareAndFinalize implementations that have such
157
+ dependencies may choose to override this function.
158
+ """
159
+ return
160
+
161
@abstractmethod
162
def prepare(
163
self,
@@ -503,6 +512,13 @@ class FusedMoEPermuteExpertsUnpermute(ABC):
512
"""
513
raise NotImplementedError
514
515
+ def supports_packed_ue8m0_act_scales(self) -> bool:
516
+ """
517
+ A flag indicating whether or not this class can process packed ue8m0
518
+ activation scales.
519
+ """
520
+ return False
521
+
522
def workspace_dtype(self, act_dtype: torch.dtype) -> torch.dtype:
523
"""
524
Workspace type: The dtype to use for the workspace tensors.
@@ -698,6 +714,8 @@ class FusedMoEModularKernel(torch.nn.Module):
714
self.prepare_finalize = prepare_finalize
715
self.fused_experts = fused_experts
716
self.shared_experts = shared_experts
717
+
718
+ self._post_init_setup()
719
assert (
720
prepare_finalize.activation_format == fused_experts.activation_formats[0]
721
), (
@@ -707,6 +725,13 @@ class FusedMoEModularKernel(torch.nn.Module):
725
f"{fused_experts.activation_formats[0]}"
726
)
727
728
+ def _post_init_setup(self):
729
+ """
730
+ Resolve any leftover setup dependencies between self.prepare_finalize
731
+ and self.fused_experts here.
732
+ """
733
+ self.prepare_finalize.post_init_setup(self.fused_experts)
734
+
735
def supports_expert_map(self) -> bool:
736
"""
737
A flag indicating whether or not this class supports expert maps.
vllm/model_executor/layers/quantization/fp8.py
+34
-40
@@ -60,11 +60,10 @@ from vllm.model_executor.layers.quantization.utils.fp8_utils import (
60
create_fp8_input_scale,
61
create_fp8_scale_parameter,
62
create_fp8_weight_parameter,
63
- expert_weight_is_col_major,
63
+ deepgemm_post_process_fp8_weight_block,
64
maybe_post_process_fp8_weight_block,
65
process_fp8_weight_block_strategy,
66
process_fp8_weight_tensor_strategy,
67
- requant_weight_ue8m0_inplace,
67
validate_fp8_block_shape,
68
)
69
from vllm.model_executor.layers.quantization.utils.marlin_utils_fp8 import (
@@ -94,7 +93,6 @@ from vllm.model_executor.utils import set_weight_attrs
93
from vllm.platforms import current_platform
94
from vllm.scalar_type import scalar_types
95
from vllm.utils.deep_gemm import (
97
- get_col_major_tma_aligned_tensor,
96
is_deep_gemm_e8m0_used,
97
is_deep_gemm_supported,
98
)
@@ -846,15 +844,31 @@ class Fp8MoEMethod(FusedMoEMethodBase):
844
845
# DeepGemm scales need to be transposed and aligned. We try to do
846
# it ahead of time for performance reasons.
849
- if self.allow_deep_gemm and not is_deep_gemm_e8m0_used():
850
- if expert_weight_is_col_major(layer.w13_weight_scale_inv):
851
- layer.w13_weight_scale_inv = get_col_major_tma_aligned_tensor(
852
- layer.w13_weight_scale_inv
847
+ if self.allow_deep_gemm:
848
+ dg_w13_weight, dg_w13_weight_scale_inv = (
849
+ deepgemm_post_process_fp8_weight_block(
850
+ wq=layer.w13_weight.data,
851
+ ws=layer.w13_weight_scale_inv.data,
852
+ quant_block_shape=tuple(layer.weight_block_size),
853
+ use_e8m0=is_deep_gemm_e8m0_used(),
854
)
854
- if expert_weight_is_col_major(layer.w2_weight_scale_inv):
855
- layer.w2_weight_scale_inv = get_col_major_tma_aligned_tensor(
856
- layer.w2_weight_scale_inv
855
+ )
856
+ dg_w2_weight, dg_w2_weight_scale_inv = (
857
+ deepgemm_post_process_fp8_weight_block(
858
+ wq=layer.w2_weight.data,
859
+ ws=layer.w2_weight_scale_inv.data,
860
+ quant_block_shape=tuple(layer.weight_block_size),
861
+ use_e8m0=is_deep_gemm_e8m0_used(),
862
)
863
+ )
864
+ layer.w13_weight = Parameter(dg_w13_weight, requires_grad=False)
865
+ layer.w13_weight_scale_inv = Parameter(
866
+ dg_w13_weight_scale_inv, requires_grad=False
867
+ )
868
+ layer.w2_weight = Parameter(dg_w2_weight, requires_grad=False)
869
+ layer.w2_weight_scale_inv = Parameter(
870
+ dg_w2_weight_scale_inv, requires_grad=False
871
+ )
872
873
# If checkpoint is fp16, quantize in place.
874
elif not self.quant_config.is_checkpoint_fp8_serialized:
@@ -990,31 +1004,6 @@ class Fp8MoEMethod(FusedMoEMethodBase):
1004
del layer.w13_input_scale
1005
del layer.w2_input_scale
1006
993
- if is_deep_gemm_e8m0_used() and self.block_quant:
994
- assert layer.weight_block_size is not None
995
- # Re-quantise the expert weights so their scales are UE8M0.
996
- block_sz = tuple(layer.weight_block_size)
997
- requant_weight_ue8m0_inplace(
998
- layer.w13_weight.data,
999
- layer.w13_weight_scale_inv.data,
1000
- block_sz,
1001
- )
1002
- requant_weight_ue8m0_inplace(
1003
- layer.w2_weight.data,
1004
- layer.w2_weight_scale_inv.data,
1005
- block_sz,
1006
- )
1007
-
1008
- # Ensure column-major TMA alignment expected by DeepGEMM.
1009
- if expert_weight_is_col_major(layer.w13_weight_scale_inv):
1010
- layer.w13_weight_scale_inv = get_col_major_tma_aligned_tensor(
1011
- layer.w13_weight_scale_inv
1012
- )
1013
- if expert_weight_is_col_major(layer.w2_weight_scale_inv):
1014
- layer.w2_weight_scale_inv = get_col_major_tma_aligned_tensor(
1015
- layer.w2_weight_scale_inv
1016
- )
1017
-
1007
def maybe_make_prepare_finalize(self) -> mk.FusedMoEPrepareAndFinalize | None:
1008
if (
1009
self.rocm_aiter_moe_enabled
@@ -1037,7 +1026,8 @@ class Fp8MoEMethod(FusedMoEMethodBase):
1026
layer: torch.nn.Module,
1027
) -> FusedMoEPermuteExpertsUnpermute:
1028
from vllm.model_executor.layers.fused_moe import (
1040
- BatchedTritonOrDeepGemmExperts,
1029
+ BatchedDeepGemmExperts,
1030
+ BatchedTritonExperts,
1031
TritonOrDeepGemmExperts,
1032
)
1033
@@ -1053,20 +1043,24 @@ class Fp8MoEMethod(FusedMoEMethodBase):
1043
):
1044
max_num_tokens_per_rank = prepare_finalize.max_num_tokens_per_rank()
1045
assert max_num_tokens_per_rank is not None
1046
+
1047
+ experts_impl = (
1048
+ BatchedDeepGemmExperts if self.allow_deep_gemm else BatchedTritonExperts
1049
+ )
1050
logger.debug(
1057
- "BatchedTritonOrDeepGemmExperts(%s): "
1058
- "max_tokens_per_rank=%s, block_size=%s, per_act_token=%s",
1051
+ "%s(%s): max_tokens_per_rank=%s, block_size=%s, per_act_token=%s",
1052
+ experts_impl.__name__,
1053
self.__class__.__name__,
1054
max_num_tokens_per_rank,
1055
self.weight_block_size,
1056
False,
1057
)
1064
- return BatchedTritonOrDeepGemmExperts(
1058
+ return experts_impl(
1059
max_num_tokens=max_num_tokens_per_rank,
1060
num_dispatchers=prepare_finalize.num_dispatchers(),
1061
quant_config=self.moe_quant_config,
1068
- allow_deep_gemm=self.allow_deep_gemm,
1062
)
1063
+
1064
elif self.flashinfer_moe_backend == FlashinferMoeBackend.CUTLASS:
1065
experts = select_cutlass_fp8_gemm_impl(
1066
self.moe,
vllm/model_executor/layers/quantization/utils/fp8_utils.py
+53
-4
@@ -34,6 +34,7 @@ from vllm.utils.deep_gemm import (
34
is_deep_gemm_e8m0_used,
35
is_deep_gemm_supported,
36
should_use_deepgemm_for_fp8_linear,
37
+ transform_sf_into_required_layout,
38
)
39
from vllm.utils.torch_utils import direct_register_custom_op
40
@@ -929,6 +930,50 @@ def requant_weight_ue8m0_inplace(
930
s_old.copy_(s_requant)
931
932
933
+def deepgemm_post_process_fp8_weight_block(
934
+ wq: torch.Tensor, ws: torch.Tensor, quant_block_shape: tuple[int], use_e8m0: bool
935
+) -> tuple[torch.Tensor, torch.Tensor]:
936
+ assert wq.dtype == torch.float8_e4m3fn, (
937
+ "Expected quantized tensor dtype "
938
+ f"to be torch.float8_e4m3fn, got {wq.dtype} instead."
939
+ )
940
+ assert ws.dtype == torch.float32, (
941
+ f"Expected tensor scales dtype to be torch.float32, got {ws.dtype} instead"
942
+ )
943
+
944
+ if use_e8m0:
945
+ requant_weight_ue8m0_inplace(wq, ws, block_size=quant_block_shape)
946
+
947
+ original_ndim = wq.ndim
948
+ if wq.ndim == 2:
949
+ assert ws.ndim == 2
950
+ wq = wq.unsqueeze(0)
951
+ ws = ws.unsqueeze(0)
952
+
953
+ # From https://github.com/deepseek-ai/DeepGEMM/blob/c9f8b34dcdacc20aa746b786f983492c51072870/csrc/utils/layout.hpp#L46
954
+ recipe = (1, 128, 128)
955
+
956
+ # Ref : https://github.com/deepseek-ai/DeepGEMM/blob/c9f8b34dcdacc20aa746b786f983492c51072870/csrc/apis/gemm.hpp
957
+ # DeepGemm uses the `transform_sf_into_required_layout` function to
958
+ # represent scales in the correct format.
959
+ dg_ws = transform_sf_into_required_layout(
960
+ sf=ws,
961
+ mn=wq.size(1),
962
+ k=wq.size(2),
963
+ recipe=recipe,
964
+ num_groups=wq.size(0),
965
+ # is the scale factors for A in (Refers to the argument A in A @ B).
966
+ # Weights are B.
967
+ is_sfa=False,
968
+ )
969
+
970
+ if original_ndim == 2:
971
+ wq = wq.squeeze(0)
972
+ dg_ws = dg_ws.squeeze(0)
973
+
974
+ return wq, dg_ws
975
+
976
+
977
def _maybe_pad_fp8_weight(weight: torch.Tensor) -> torch.Tensor:
978
"""Pad the weight tensor. This is an optimization on ROCm platform, which
979
can benefit from tensors located far enough from one another in memory"""
@@ -1141,11 +1186,15 @@ def maybe_post_process_fp8_weight_block(layer: torch.nn.Module):
1186
should_use_deepgemm = should_use_deepgemm_for_fp8_linear(
1187
layer.orig_dtype, layer.weight
1188
)
1144
- if is_deep_gemm_e8m0_used() and should_use_deepgemm:
1145
- block_sz = tuple(layer.weight_block_size)
1146
- requant_weight_ue8m0_inplace(
1147
- layer.weight.data, layer.weight_scale.data, block_sz
1189
+ if should_use_deepgemm:
1190
+ dg_weight, dg_weight_scale = deepgemm_post_process_fp8_weight_block(
1191
+ wq=layer.weight.data,
1192
+ ws=layer.weight_scale.data,
1193
+ quant_block_shape=tuple(layer.weight_block_size),
1194
+ use_e8m0=is_deep_gemm_e8m0_used(),
1195
)
1196
+ layer.weight = torch.nn.Parameter(dg_weight, requires_grad=False)
1197
+ layer.weight_scale = torch.nn.Parameter(dg_weight_scale, requires_grad=False)
1198
1199
1200
def expert_weight_is_col_major(x: torch.Tensor) -> bool:
vllm/utils/deep_gemm.py
+15
-4
@@ -49,10 +49,6 @@ def is_deep_gemm_e8m0_used() -> bool:
49
logger.info_once("DeepGEMM E8M0 disabled: _fp8_gemm_nt_impl not found")
50
return False
51
52
- if envs.VLLM_USE_FLASHINFER_MOE_FP8:
53
- logger.info_once("DeepGEMM E8M0 disabled: FlashInfer MOE is enabled.")
54
- return False
55
-
52
if envs.VLLM_USE_DEEP_GEMM_E8M0:
53
logger.info_once("DeepGEMM E8M0 enabled on current platform.")
54
return True
@@ -77,6 +73,7 @@ _fp8_paged_mqa_logits_impl: Callable[..., Any] | None = None
73
_get_paged_mqa_logits_metadata_impl: Callable[..., Any] | None = None
74
_get_mn_major_tma_aligned_tensor_impl: Callable[..., Any] | None = None
75
_get_mk_alignment_for_contiguous_layout_impl: Callable[..., Any] | None = None
76
+_transform_sf_into_required_layout_impl: Callable[..., Any] | None = None
77
78
79
def _lazy_init() -> None:
@@ -86,6 +83,7 @@ def _lazy_init() -> None:
83
global _get_paged_mqa_logits_metadata_impl
84
global _get_mn_major_tma_aligned_tensor_impl
85
global _get_mk_alignment_for_contiguous_layout_impl
86
+ global _transform_sf_into_required_layout_impl
87
# fast path
88
if (
89
_fp8_gemm_nt_impl is not None
@@ -95,6 +93,7 @@ def _lazy_init() -> None:
93
or _fp8_paged_mqa_logits_impl is not None
94
or _get_paged_mqa_logits_metadata_impl is not None
95
or _get_mk_alignment_for_contiguous_layout_impl is not None
96
+ or _transform_sf_into_required_layout_impl is not None
97
):
98
return
99
@@ -124,6 +123,9 @@ def _lazy_init() -> None:
123
_get_mk_alignment_for_contiguous_layout_impl = getattr(
124
_dg, "get_mk_alignment_for_contiguous_layout", None
125
)
126
+ _transform_sf_into_required_layout_impl = getattr(
127
+ _dg, "transform_sf_into_required_layout", None
128
+ )
129
130
131
def get_num_sms() -> int:
@@ -179,6 +181,15 @@ def fp8_m_grouped_gemm_nt_masked(*args, **kwargs):
181
)
182
183
184
+def transform_sf_into_required_layout(*args, **kwargs):
185
+ _lazy_init()
186
+ if _transform_sf_into_required_layout_impl is None:
187
+ return _missing(*args, **kwargs)
188
+ return _transform_sf_into_required_layout_impl(
189
+ *args, disable_ue8m0_cast=not is_deep_gemm_e8m0_used(), **kwargs
190
+ )
191
+
192
+
193
def fp8_mqa_logits(
194
q: torch.Tensor,
195
kv: tuple[torch.Tensor, torch.Tensor],