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 11, 08:45 UTC
[Perf] Do FP4 quant before All gather on flashinfer trtllmgen MOE (#30014)
Signed-off-by: jiahanc <173873397+jiahanc@users.noreply.github.com>
jiahanc committed
Dec 16, 2025 at 13:01 UTC
254a7f8fd613d6b6964abc277b73ca1f0b823cdb
9 files changed
+165
-31
vllm/distributed/device_communicators/all2all.py
+24
-5
@@ -64,7 +64,12 @@ class NaiveAll2AllManager(All2AllManagerBase):
64
hidden_states: torch.Tensor,
65
router_logits: torch.Tensor,
66
is_sequence_parallel: bool = False,
67
+ extra_tensors: list[torch.Tensor] | None = None,
68
) -> tuple[torch.Tensor, torch.Tensor]:
69
+ if extra_tensors is not None:
70
+ raise NotImplementedError(
71
+ "extra_tensors is not supported for NaiveAll2AllManager"
72
+ )
73
sp_size = self.tp_group.world_size if is_sequence_parallel else 1
74
dp_metadata = get_forward_context().dp_metadata
75
assert dp_metadata is not None
@@ -76,6 +81,7 @@ class NaiveAll2AllManager(All2AllManagerBase):
81
router_logits = self.naive_multicast(
82
router_logits, cu_tokens_across_sp_cpu, is_sequence_parallel
83
)
84
+
85
return hidden_states, router_logits
86
87
def combine(
@@ -113,7 +119,11 @@ class AgRsAll2AllManager(All2AllManagerBase):
119
hidden_states: torch.Tensor,
120
router_logits: torch.Tensor,
121
is_sequence_parallel: bool = False,
116
- ) -> tuple[torch.Tensor, torch.Tensor]:
122
+ extra_tensors: list[torch.Tensor] | None = None,
123
+ ) -> (
124
+ tuple[torch.Tensor, torch.Tensor]
125
+ | tuple[torch.Tensor, torch.Tensor, list[torch.Tensor]]
126
+ ):
127
"""
128
Gather hidden_states and router_logits from all dp ranks.
129
"""
@@ -121,15 +131,22 @@ class AgRsAll2AllManager(All2AllManagerBase):
131
assert dp_metadata is not None
132
sizes = dp_metadata.get_chunk_sizes_across_dp_rank()
133
assert sizes is not None
124
-
134
dist_group = get_ep_group() if is_sequence_parallel else get_dp_group()
135
assert sizes[dist_group.rank_in_group] == hidden_states.shape[0]
127
- hidden_states, router_logits = dist_group.all_gatherv(
128
- [hidden_states, router_logits],
136
+
137
+ tensors_to_gather = [hidden_states, router_logits]
138
+ if extra_tensors is not None:
139
+ tensors_to_gather.extend(extra_tensors)
140
+
141
+ gathered_tensors = dist_group.all_gatherv(
142
+ tensors_to_gather,
143
dim=0,
144
sizes=sizes,
145
)
132
- return hidden_states, router_logits
146
+
147
+ if extra_tensors is not None:
148
+ return (gathered_tensors[0], gathered_tensors[1], gathered_tensors[2:])
149
+ return gathered_tensors[0], gathered_tensors[1]
150
151
def combine(
152
self, hidden_states: torch.Tensor, is_sequence_parallel: bool = False
@@ -204,6 +221,7 @@ class PPLXAll2AllManager(All2AllManagerBase):
221
hidden_states: torch.Tensor,
222
router_logits: torch.Tensor,
223
is_sequence_parallel: bool = False,
224
+ extra_tensors: list[torch.Tensor] | None = None,
225
) -> tuple[torch.Tensor, torch.Tensor]:
226
raise NotImplementedError
227
@@ -251,6 +269,7 @@ class DeepEPAll2AllManagerBase(All2AllManagerBase):
269
hidden_states: torch.Tensor,
270
router_logits: torch.Tensor,
271
is_sequence_parallel: bool = False,
272
+ extra_tensors: list[torch.Tensor] | None = None,
273
) -> tuple[torch.Tensor, torch.Tensor]:
274
raise NotImplementedError
275
vllm/distributed/device_communicators/base_device_communicator.py
+6
-1
@@ -1,6 +1,7 @@
1
# SPDX-License-Identifier: Apache-2.0
2
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
import threading
4
+from typing import Any
5
from weakref import WeakValueDictionary
6
7
import torch
@@ -68,7 +69,11 @@ class All2AllManagerBase:
69
hidden_states: torch.Tensor,
70
router_logits: torch.Tensor,
71
is_sequence_parallel: bool = False,
71
- ):
72
+ extra_tensors: list[torch.Tensor] | None = None,
73
+ ) -> Any:
74
+ # Subclasses should either:
75
+ # - implement handling for extra_tensors, or
76
+ # - raise a clear error if extra_tensors is not supported.
77
raise NotImplementedError
78
79
def set_num_sms(self, num_sms: int):
vllm/distributed/device_communicators/cuda_communicator.py
+11
-5
@@ -318,17 +318,23 @@ class CudaCommunicator(DeviceCommunicatorBase):
318
319
return output_list
320
321
- def dispatch(
321
+ def dispatch( # type: ignore[override]
322
self,
323
hidden_states: torch.Tensor,
324
router_logits: torch.Tensor,
325
is_sequence_parallel: bool = False,
326
- ) -> tuple[torch.Tensor, torch.Tensor]:
326
+ extra_tensors: list[torch.Tensor] | None = None,
327
+ ) -> (
328
+ tuple[torch.Tensor, torch.Tensor]
329
+ | tuple[torch.Tensor, torch.Tensor, list[torch.Tensor]]
330
+ ):
331
assert self.all2all_manager is not None
328
- hidden_states, router_logits = self.all2all_manager.dispatch(
329
- hidden_states, router_logits, is_sequence_parallel
332
+ return self.all2all_manager.dispatch(
333
+ hidden_states,
334
+ router_logits,
335
+ is_sequence_parallel,
336
+ extra_tensors, # type: ignore[call-arg]
337
)
331
- return hidden_states, router_logits
338
339
def combine(
340
self, hidden_states: torch.Tensor, is_sequence_parallel: bool = False
vllm/distributed/parallel_state.py
+10
-3
@@ -1007,10 +1007,17 @@ class GroupCoordinator:
1007
hidden_states: torch.Tensor,
1008
router_logits: torch.Tensor,
1009
is_sequence_parallel: bool = False,
1010
- ) -> tuple[torch.Tensor, torch.Tensor]:
1010
+ extra_tensors: list[torch.Tensor] | None = None,
1011
+ ) -> (
1012
+ tuple[torch.Tensor, torch.Tensor]
1013
+ | tuple[torch.Tensor, torch.Tensor, list[torch.Tensor]]
1014
+ ):
1015
if self.device_communicator is not None:
1012
- return self.device_communicator.dispatch(
1013
- hidden_states, router_logits, is_sequence_parallel
1016
+ return self.device_communicator.dispatch( # type: ignore[call-arg]
1017
+ hidden_states,
1018
+ router_logits,
1019
+ is_sequence_parallel,
1020
+ extra_tensors,
1021
)
1022
else:
1023
return hidden_states, router_logits
vllm/model_executor/layers/fused_moe/fused_moe_method_base.py
+12
@@ -71,6 +71,18 @@ class FusedMoEMethodBase(QuantizeMethodBase):
71
"implementation based on the prepare_finalize"
72
)
73
74
+ def prepare_dp_allgather_tensor(
75
+ self,
76
+ layer: "FusedMoE", # type: ignore[name-defined] # noqa: F821
77
+ hidden_states: torch.Tensor,
78
+ router_logits: torch.Tensor,
79
+ ) -> tuple[torch.Tensor, list[torch.Tensor]]:
80
+ """Hook to prepare tensors and extra tensors for DP allgather + EP dispatch."""
81
+ raise NotImplementedError(
82
+ "Method 'prepare_dp_allgather_tensor' is not implemented in "
83
+ f"{self.__class__.__name__}."
84
+ )
85
+
86
@abstractmethod
87
def get_fused_moe_quant_config(
88
self, layer: torch.nn.Module
vllm/model_executor/layers/fused_moe/layer.py
+39
-2
@@ -44,6 +44,7 @@ from vllm.model_executor.layers.quantization.utils.flashinfer_utils import (
44
is_flashinfer_supporting_global_sf,
45
)
46
from vllm.platforms import current_platform
47
+from vllm.utils.flashinfer import has_flashinfer_trtllm_fused_moe
48
from vllm.utils.math_utils import cdiv, round_up
49
from vllm.utils.torch_utils import (
50
aux_stream,
@@ -1933,10 +1934,46 @@ class FusedMoE(CustomOp):
1934
)
1935
1936
with sp_ctx:
1937
+ extra_tensors = None
1938
if do_naive_dispatch_combine:
1937
- hidden_states_combined, router_logits = get_ep_group().dispatch(
1938
- hidden_states, router_logits, self.is_sequence_parallel
1939
+ # Avoid circular import
1940
+ from vllm.model_executor.layers.quantization.modelopt import (
1941
+ ModelOptNvFp4FusedMoE,
1942
+ )
1943
+
1944
+ post_quant_allgather = (
1945
+ has_flashinfer_trtllm_fused_moe()
1946
+ and self.quant_method is not None
1947
+ and self.dp_size > 1
1948
+ and self.use_ep
1949
+ and isinstance(self.quant_method, ModelOptNvFp4FusedMoE)
1950
+ )
1951
+ if post_quant_allgather:
1952
+ hidden_states_to_dispatch, extra_tensors = (
1953
+ self.quant_method.prepare_dp_allgather_tensor(
1954
+ self, hidden_states, router_logits
1955
+ )
1956
+ )
1957
+ else:
1958
+ hidden_states_to_dispatch = hidden_states
1959
+
1960
+ dispatch_res = get_ep_group().dispatch(
1961
+ hidden_states_to_dispatch,
1962
+ router_logits,
1963
+ self.is_sequence_parallel,
1964
+ extra_tensors=extra_tensors,
1965
)
1966
+ if extra_tensors is not None:
1967
+ hidden_states_combined, router_logits, extra_tensors_combined = (
1968
+ dispatch_res
1969
+ )
1970
+ hidden_states_combined = (
1971
+ hidden_states_combined,
1972
+ extra_tensors_combined[0],
1973
+ )
1974
+ else:
1975
+ hidden_states_combined, router_logits = dispatch_res
1976
+
1977
# Run shared experts before matrix multiply.
1978
# because matrix multiply maybe modify the hidden_states.
1979
if has_separate_shared_experts and not use_shared_experts_stream:
vllm/model_executor/layers/quantization/modelopt.py
+24
-1
@@ -1522,6 +1522,24 @@ class ModelOptNvFp4FusedMoE(FusedMoEMethodBase):
1522
w2_blockscale_swizzled, requires_grad=False
1523
)
1524
1525
+ def prepare_dp_allgather_tensor(
1526
+ self,
1527
+ layer: FusedMoE,
1528
+ hidden_states: torch.Tensor,
1529
+ router_logits: torch.Tensor,
1530
+ ) -> tuple[torch.Tensor, list[torch.Tensor]]:
1531
+ """Optionally prepare extra tensors to carry through DP allgather/EP."""
1532
+ import flashinfer
1533
+
1534
+ a1_gscale = layer.w13_input_scale_quant
1535
+ hidden_states_fp4, hidden_states_sf = flashinfer.fp4_quantize(
1536
+ hidden_states,
1537
+ a1_gscale,
1538
+ is_sf_swizzled_layout=False,
1539
+ )
1540
+ extra_tensors: list[torch.Tensor] = [hidden_states_sf]
1541
+ return hidden_states_fp4, extra_tensors
1542
+
1543
def get_fused_moe_quant_config(
1544
self, layer: torch.nn.Module
1545
) -> FusedMoEQuantConfig | None:
@@ -1576,8 +1594,13 @@ class ModelOptNvFp4FusedMoE(FusedMoEMethodBase):
1594
e_score_correction_bias=layer.e_score_correction_bias,
1595
)
1596
1597
+ # Hidden_states in select_experts is only used to extract metadata
1598
+ if isinstance(x, tuple):
1599
+ x_routing, _ = x
1600
+ else:
1601
+ x_routing = x
1602
topk_weights, topk_ids, _ = layer.select_experts(
1580
- hidden_states=x,
1603
+ hidden_states=x_routing,
1604
router_logits=router_logits,
1605
)
1606
vllm/model_executor/layers/quantization/utils/flashinfer_fp4_moe.py
+22
-14
@@ -238,7 +238,7 @@ def prepare_static_weights_for_trtllm_fp4_moe(
238
239
def flashinfer_trtllm_fp4_moe(
240
layer: torch.nn.Module,
241
- x: torch.Tensor,
241
+ x: torch.Tensor | tuple[torch.Tensor, torch.Tensor],
242
router_logits: torch.Tensor,
243
top_k: int,
244
global_num_experts: int,
@@ -269,12 +269,16 @@ def flashinfer_trtllm_fp4_moe(
269
from vllm.model_executor.models.llama4 import Llama4MoE
270
271
# Quantize input to FP4
272
- a1_gscale = layer.w13_input_scale_quant
273
- (hidden_states_fp4, hidden_states_scale_linear_fp4) = flashinfer.fp4_quantize(
274
- x,
275
- a1_gscale,
276
- is_sf_swizzled_layout=False,
277
- )
272
+ if isinstance(x, tuple):
273
+ hidden_states_fp4, hidden_states_scale_linear_fp4 = x
274
+ else:
275
+ # hidden_states is the already quantized
276
+ a1_gscale = layer.w13_input_scale_quant
277
+ (hidden_states_fp4, hidden_states_scale_linear_fp4) = flashinfer.fp4_quantize(
278
+ x,
279
+ a1_gscale,
280
+ is_sf_swizzled_layout=False,
281
+ )
282
283
# Determine routing method type
284
use_llama4_routing = custom_routing_function is Llama4MoE.custom_routing_function
@@ -360,13 +364,17 @@ def flashinfer_trtllm_fp4_routed_moe(
364
torch.bfloat16
365
).view(torch.int16)
366
363
- # Quantize input to FP4
364
- a1_gscale = layer.w13_input_scale_quant
365
- (hidden_states_fp4, hidden_states_scale_linear_fp4) = flashinfer.fp4_quantize(
366
- x,
367
- a1_gscale,
368
- is_sf_swizzled_layout=False,
369
- )
367
+ if isinstance(x, tuple):
368
+ # Hidden_states is the already quantized
369
+ hidden_states_fp4, hidden_states_scale_linear_fp4 = x
370
+ else:
371
+ # Quantize input to FP4
372
+ a1_gscale = layer.w13_input_scale_quant
373
+ (hidden_states_fp4, hidden_states_scale_linear_fp4) = flashinfer.fp4_quantize(
374
+ x,
375
+ a1_gscale,
376
+ is_sf_swizzled_layout=False,
377
+ )
378
379
# Call TRT-LLM FP4 block-scale MoE kernel
380
out = flashinfer.fused_moe.trtllm_fp4_block_scale_routed_moe(
vllm/utils/flashinfer.py
+17
@@ -184,6 +184,23 @@ def has_flashinfer_cutedsl() -> bool:
184
)
185
186
187
+@functools.cache
188
+def has_flashinfer_trtllm_fused_moe() -> bool:
189
+ """Return `True` if FlashInfer TRTLLM fused MoE is available."""
190
+ if not has_flashinfer_moe():
191
+ return False
192
+ required_functions = [
193
+ ("flashinfer.fused_moe", "trtllm_fp8_block_scale_moe"),
194
+ ("flashinfer.fused_moe", "trtllm_fp8_per_tensor_scale_moe"),
195
+ ("flashinfer.fused_moe", "trtllm_fp4_block_scale_moe"),
196
+ ]
197
+ for module_name, attr_name in required_functions:
198
+ mod = _get_submodule(module_name)
199
+ if not mod or not hasattr(mod, attr_name):
200
+ return False
201
+ return True
202
+
203
+
204
@functools.cache
205
def has_flashinfer_cutlass_fused_moe() -> bool:
206
"""Return `True` if FlashInfer CUTLASS fused MoE is available."""