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 8, 16:30 UTC
[Kernel] FlashInfer: switch allreduce fusion to unified API (#33985)
Signed-off-by: Mohammad Miadh Angkad <176301910+mmangkad@users.noreply.github.com>
Mohammad Miadh Angkad committed
Feb 9, 2026 at 23:43 UTC
d4f123cc48c374f7aad48cd808d797c71711ebc7
3 files changed
+80
-114
benchmarks/kernels/benchmark_fused_collective.py
+54
-69
@@ -5,7 +5,7 @@
5
Benchmark for FlashInfer fused collective operations vs standard operations.
6
7
This benchmark compares:
8
-1. FlashInfer's trtllm_allreduce_fusion (fused allreduce + rmsnorm + optional quant)
8
+1. FlashInfer's allreduce_fusion (fused allreduce + rmsnorm + optional quant)
9
2. Standard tensor_model_parallel_all_reduce + separate rmsnorm/quant operations
10
11
Usage with torchrun:
@@ -24,7 +24,6 @@ import torch.distributed as dist # type: ignore
24
25
from vllm.config.vllm import CompilationConfig, VllmConfig, set_current_vllm_config
26
from vllm.distributed import (
27
- get_tp_group,
27
tensor_model_parallel_all_reduce,
28
)
29
from vllm.distributed.parallel_state import (
@@ -52,11 +51,12 @@ logger = init_logger(__name__)
51
try:
52
import flashinfer.comm as flashinfer_comm # type: ignore
53
55
- if not hasattr(flashinfer_comm, "trtllm_allreduce_fusion"):
54
+ if not (
55
+ hasattr(flashinfer_comm, "allreduce_fusion")
56
+ and hasattr(flashinfer_comm, "create_allreduce_fusion_workspace")
57
+ ):
58
flashinfer_comm = None
57
- logger.warning(
58
- "FlashInfer comm module found but missing trtllm_allreduce_fusion"
59
- )
59
+ logger.warning("FlashInfer comm module found but missing allreduce_fusion API")
60
except ImportError:
61
flashinfer_comm = None
62
logger.warning("FlashInfer not found, only benchmarking standard operations")
@@ -75,7 +75,7 @@ _FI_MAX_SIZES = {
75
}
76
77
# Global workspace tensor for FlashInfer
78
-_FI_WORKSPACE_TENSOR = None
78
+_FI_WORKSPACE = None
79
80
81
def setup_flashinfer_workspace(
@@ -83,10 +83,10 @@ def setup_flashinfer_workspace(
83
rank: int,
84
hidden_dim: int,
85
max_token_num: int,
86
- use_fp32_lamport: bool = False,
86
+ dtype: torch.dtype,
87
):
88
"""Setup FlashInfer workspace for fused allreduce operations."""
89
- global _FI_WORKSPACE_TENSOR
89
+ global _FI_WORKSPACE
90
91
if flashinfer_comm is None:
92
return None, None
@@ -96,33 +96,29 @@ def setup_flashinfer_workspace(
96
return None, None
97
98
try:
99
- # Create IPC workspace
100
- ipc_handles, workspace_tensor = (
101
- flashinfer_comm.trtllm_create_ipc_workspace_for_all_reduce_fusion(
102
- tp_rank=rank,
103
- tp_size=world_size,
104
- max_token_num=max_token_num,
105
- hidden_dim=hidden_dim,
106
- group=get_tp_group().device_group,
107
- use_fp32_lamport=use_fp32_lamport,
108
- )
99
+ workspace = flashinfer_comm.create_allreduce_fusion_workspace(
100
+ backend="trtllm",
101
+ world_size=world_size,
102
+ rank=rank,
103
+ max_token_num=max_token_num,
104
+ hidden_dim=hidden_dim,
105
+ dtype=dtype,
106
)
107
111
- _FI_WORKSPACE_TENSOR = workspace_tensor
112
- return ipc_handles, workspace_tensor
108
+ _FI_WORKSPACE = workspace
109
+ return workspace
110
except Exception as e:
111
logger.error("Failed to setup FlashInfer workspace: %s", e)
115
- return None, None
112
+ return None
113
114
118
-def cleanup_flashinfer_workspace(ipc_handles):
115
+def cleanup_flashinfer_workspace(workspace):
116
"""Cleanup FlashInfer workspace."""
120
- if flashinfer_comm is None or ipc_handles is None:
117
+ if flashinfer_comm is None or workspace is None:
118
return
119
120
try:
124
- group = get_tp_group().device_group
125
- flashinfer_comm.trtllm_destroy_ipc_workspace_for_all_reduce(ipc_handles, group)
121
+ workspace.destroy()
122
except Exception as e:
123
logger.error("Failed to cleanup FlashInfer workspace: %s", e)
124
@@ -132,25 +128,15 @@ class FlashInferFusedAllReduceParams:
128
129
def __init__(
130
self,
135
- rank: int,
136
- world_size: int,
137
- use_fp32_lamport: bool = False,
131
max_token_num: int = 1024,
132
):
140
- self.rank = rank
141
- self.world_size = world_size
142
- self.use_fp32_lamport = use_fp32_lamport
143
- self.trigger_completion_at_end = True
133
self.launch_with_pdl = True
134
self.fp32_acc = True
135
self.max_token_num = max_token_num
136
137
def get_trtllm_fused_allreduce_kwargs(self):
138
return {
150
- "world_rank": self.rank,
151
- "world_size": self.world_size,
139
"launch_with_pdl": self.launch_with_pdl,
153
- "trigger_completion_at_end": self.trigger_completion_at_end,
140
"fp32_acc": self.fp32_acc,
141
}
142
@@ -165,7 +151,7 @@ def flashinfer_fused_allreduce_rmsnorm(
151
norm_out: torch.Tensor | None = None,
152
):
153
"""FlashInfer fused allreduce + rmsnorm operation."""
168
- if flashinfer_comm is None or _FI_WORKSPACE_TENSOR is None:
154
+ if flashinfer_comm is None or _FI_WORKSPACE is None:
155
raise RuntimeError("FlashInfer not available or workspace not initialized")
156
157
if norm_out is None:
@@ -174,18 +160,15 @@ def flashinfer_fused_allreduce_rmsnorm(
160
else:
161
residual_out = input_tensor
162
177
- flashinfer_comm.trtllm_allreduce_fusion(
178
- allreduce_in=input_tensor,
179
- token_num=input_tensor.shape[0],
163
+ flashinfer_comm.allreduce_fusion(
164
+ input=input_tensor,
165
+ workspace=_FI_WORKSPACE,
166
+ pattern=flashinfer_comm.AllReduceFusionPattern.kARResidualRMSNorm,
167
residual_in=residual,
168
residual_out=residual_out,
169
norm_out=norm_out,
170
rms_gamma=rms_gamma,
171
rms_eps=rms_eps,
185
- hidden_dim=input_tensor.shape[-1],
186
- workspace_ptrs=_FI_WORKSPACE_TENSOR,
187
- pattern_code=flashinfer_comm.AllReduceFusionPattern.kARResidualRMSNorm,
188
- allreduce_out=None,
172
quant_out=None,
173
scale_out=None,
174
layout_code=flashinfer_comm.QuantizationSFLayout.SWIZZLED_128x4,
@@ -207,7 +190,7 @@ def flashinfer_fused_allreduce_rmsnorm_fp8_quant(
190
quant_out: torch.Tensor | None = None,
191
):
192
"""FlashInfer fused allreduce + rmsnorm + FP8 quantization."""
210
- if flashinfer_comm is None or _FI_WORKSPACE_TENSOR is None:
193
+ if flashinfer_comm is None or _FI_WORKSPACE is None:
194
raise RuntimeError("FlashInfer not available or workspace not initialized")
195
196
if norm_out is None:
@@ -216,18 +199,15 @@ def flashinfer_fused_allreduce_rmsnorm_fp8_quant(
199
else:
200
residual_out = input_tensor
201
219
- flashinfer_comm.trtllm_allreduce_fusion(
220
- allreduce_in=input_tensor,
221
- token_num=input_tensor.shape[0],
202
+ flashinfer_comm.allreduce_fusion(
203
+ input=input_tensor,
204
+ workspace=_FI_WORKSPACE,
205
+ pattern=flashinfer_comm.AllReduceFusionPattern.kARResidualRMSNormFP8Quant,
206
residual_in=residual,
207
residual_out=residual_out,
208
norm_out=norm_out,
209
rms_gamma=rms_gamma,
210
rms_eps=rms_eps,
227
- hidden_dim=input_tensor.shape[-1],
228
- workspace_ptrs=_FI_WORKSPACE_TENSOR,
229
- pattern_code=flashinfer_comm.AllReduceFusionPattern.kARResidualRMSNormFP8Quant,
230
- allreduce_out=None,
211
quant_out=quant_out,
212
scale_out=None,
213
layout_code=flashinfer_comm.QuantizationSFLayout.SWIZZLED_128x4,
@@ -250,7 +230,7 @@ def flashinfer_fused_allreduce_rmsnorm_fp4_quant(
230
norm_out: torch.Tensor | None = None,
231
):
232
"""FlashInfer fused allreduce + rmsnorm + FP4 quantization."""
253
- if flashinfer_comm is None or _FI_WORKSPACE_TENSOR is None:
233
+ if flashinfer_comm is None or _FI_WORKSPACE is None:
234
raise RuntimeError("FlashInfer not available or workspace not initialized")
235
236
if norm_out is None:
@@ -259,18 +239,15 @@ def flashinfer_fused_allreduce_rmsnorm_fp4_quant(
239
else:
240
residual_out = input_tensor
241
262
- flashinfer_comm.trtllm_allreduce_fusion(
263
- allreduce_in=input_tensor,
264
- token_num=input_tensor.shape[0],
242
+ flashinfer_comm.allreduce_fusion(
243
+ input=input_tensor,
244
+ workspace=_FI_WORKSPACE,
245
+ pattern=flashinfer_comm.AllReduceFusionPattern.kARResidualRMSNormFP4Quant,
246
residual_in=residual,
247
residual_out=residual_out,
248
norm_out=norm_out,
249
rms_gamma=rms_gamma,
250
rms_eps=rms_eps,
270
- hidden_dim=input_tensor.shape[-1],
271
- workspace_ptrs=_FI_WORKSPACE_TENSOR,
272
- pattern_code=flashinfer_comm.AllReduceFusionPattern.kARResidualRMSNormFP4Quant,
273
- allreduce_out=None,
251
quant_out=quant_out,
252
scale_out=output_scale,
253
layout_code=flashinfer_comm.QuantizationSFLayout.SWIZZLED_128x4,
@@ -1040,23 +1017,31 @@ def main():
1017
configs = list(itertools.product(args.num_tokens, dtypes, residual_options))
1018
1019
# Setup FlashInfer workspace if available
1043
- ipc_handles = None
1020
+ workspace = None
1021
allreduce_params = None
1022
1023
if flashinfer_comm is not None:
1024
# Use the largest hidden dimension for workspace setup
1025
+ max_element_size = max(torch.finfo(dt).bits // 8 for dt in dtypes)
1026
+ workspace_dtype = (
1027
+ torch.float32
1028
+ if max_element_size == 4
1029
+ else (torch.bfloat16 if torch.bfloat16 in dtypes else torch.float16)
1030
+ )
1031
max_num_token = _FI_MAX_SIZES.get(world_size) // (
1049
- args.hidden_dim * world_size * 2
1032
+ args.hidden_dim * max_element_size
1033
)
1034
1052
- ipc_handles, workspace_tensor = setup_flashinfer_workspace(
1053
- world_size, rank, args.hidden_dim, max_num_token
1035
+ workspace = setup_flashinfer_workspace(
1036
+ world_size,
1037
+ rank,
1038
+ args.hidden_dim,
1039
+ max_num_token,
1040
+ dtype=workspace_dtype,
1041
)
1042
1056
- if workspace_tensor is not None:
1043
+ if workspace is not None:
1044
allreduce_params = FlashInferFusedAllReduceParams(
1058
- rank=rank,
1059
- world_size=world_size,
1045
max_token_num=max_num_token,
1046
)
1047
@@ -1119,8 +1104,8 @@ def main():
1104
1105
finally:
1106
# Cleanup
1122
- if ipc_handles is not None:
1123
- cleanup_flashinfer_workspace(ipc_handles)
1107
+ if workspace is not None:
1108
+ cleanup_flashinfer_workspace(workspace)
1109
1110
dist.barrier()
1111
tests/compile/passes/distributed/test_fusion_all_reduce.py
+3
-2
@@ -202,9 +202,10 @@ class TestAllReduceFusedAddRMSNormStaticQuantFP4Model(torch.nn.Module):
202
@pytest.mark.skipif(envs.VLLM_TARGET_DEVICE not in ["cuda"], reason="Only test on CUDA")
203
@pytest.mark.skipif(
204
not find_spec("flashinfer")
205
- or not has_module_attribute("flashinfer.comm", "trtllm_allreduce_fusion"),
205
+ or not has_module_attribute("flashinfer.comm", "allreduce_fusion")
206
+ or not has_module_attribute("flashinfer.comm", "create_allreduce_fusion_workspace"),
207
reason="flashinfer is not found or flashinfer "
207
- "is not compiled with trtllm_allreduce_fusion",
208
+ "is not compiled with allreduce_fusion",
209
)
210
def test_all_reduce_fusion_pass_replace(
211
test_model: torch.nn.Module,
vllm/compilation/passes/fusion/allreduce_rms_fusion.py
+23
-43
@@ -1,5 +1,6 @@
1
# SPDX-License-Identifier: Apache-2.0
2
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
+import contextlib
4
from importlib.util import find_spec
5
from types import ModuleType
6
@@ -36,7 +37,9 @@ if find_spec("flashinfer"):
37
try:
38
import flashinfer.comm as _flashinfer_comm
39
39
- if hasattr(_flashinfer_comm, "trtllm_allreduce_fusion"):
40
+ if hasattr(_flashinfer_comm, "allreduce_fusion") and hasattr(
41
+ _flashinfer_comm, "create_allreduce_fusion_workspace"
42
+ ):
43
flashinfer_comm = _flashinfer_comm
44
except ImportError:
45
pass
@@ -79,7 +82,7 @@ _FI_ALLREDUCE_ONE_SHOT_MAX_SIZES_MB: dict[int, dict[int, float]] = {
82
83
84
if flashinfer_comm is not None:
82
- _FI_WORKSPACE_TENSOR = None
85
+ _FI_WORKSPACE = None
86
MiB = 1024 * 1024
87
88
def call_trtllm_fused_allreduce_norm(
@@ -87,10 +90,8 @@ if flashinfer_comm is not None:
90
residual: torch.Tensor,
91
rms_gamma: torch.Tensor,
92
rms_eps: float,
90
- world_rank: int,
93
world_size: int,
94
launch_with_pdl: bool,
93
- trigger_completion_at_end: bool,
95
fp32_acc: bool,
96
max_token_num: int,
97
pattern_code: int,
@@ -121,7 +122,7 @@ if flashinfer_comm is not None:
122
max_one_shot_size is None or current_tensor_size <= max_one_shot_size * MiB
123
)
124
124
- assert _FI_WORKSPACE_TENSOR is not None, (
125
+ assert _FI_WORKSPACE is not None, (
126
"Flashinfer must be enabled when using flashinfer"
127
)
128
if norm_out is None:
@@ -134,24 +135,18 @@ if flashinfer_comm is not None:
135
residual_out = allreduce_in
136
# For the sizes that are smaller than the max size,
137
# we only use flashinfer one shot allreduce
137
- flashinfer_comm.trtllm_allreduce_fusion(
138
- allreduce_in=allreduce_in,
139
- token_num=allreduce_in.shape[0],
138
+ flashinfer_comm.allreduce_fusion(
139
+ input=allreduce_in,
140
+ workspace=_FI_WORKSPACE,
141
+ pattern=pattern_code,
142
residual_in=residual,
143
residual_out=residual_out,
144
norm_out=norm_out,
145
rms_gamma=rms_gamma,
146
rms_eps=rms_eps,
145
- world_rank=world_rank,
146
- world_size=world_size,
147
- hidden_dim=allreduce_in.shape[-1],
148
- workspace_ptrs=_FI_WORKSPACE_TENSOR,
147
launch_with_pdl=launch_with_pdl,
148
use_oneshot=use_oneshot,
151
- trigger_completion_at_end=trigger_completion_at_end,
149
fp32_acc=fp32_acc,
153
- pattern_code=pattern_code,
154
- allreduce_out=None,
150
quant_out=quant_out,
151
scale_out=scale_out,
152
# in vllm we only support swizzled layout
@@ -164,10 +159,8 @@ if flashinfer_comm is not None:
159
residual: torch.Tensor,
160
rms_gamma: torch.Tensor,
161
rms_eps: float,
167
- world_rank: int,
162
world_size: int,
163
launch_with_pdl: bool,
170
- trigger_completion_at_end: bool,
164
fp32_acc: bool,
165
max_token_num: int,
166
pattern_code: int,
@@ -200,25 +193,18 @@ class FlashInferFusedAllReduceParams:
193
194
def __init__(
195
self,
203
- rank: int,
196
world_size: int,
205
- use_fp32_lamport: bool = False,
197
max_token_num: int = 1024,
198
) -> None:
208
- self.rank = rank
199
self.world_size = world_size
210
- self.use_fp32_lamport = use_fp32_lamport
211
- self.trigger_completion_at_end = True
200
self.launch_with_pdl = True
201
self.fp32_acc = True
202
self.max_token_num = max_token_num
203
204
def get_trtllm_fused_allreduce_kwargs(self) -> dict[str, bool | int]:
205
return {
218
- "world_rank": self.rank,
206
"world_size": self.world_size,
207
"launch_with_pdl": self.launch_with_pdl,
221
- "trigger_completion_at_end": self.trigger_completion_at_end,
208
"fp32_acc": self.fp32_acc,
209
"max_token_num": self.max_token_num,
210
}
@@ -712,7 +698,6 @@ class AllReduceFusionPass(VllmPatternMatcherPass):
698
self.hidden_dim = config.model_config.get_hidden_size()
699
self.group = get_tp_group().device_group
700
rank = get_tensor_model_parallel_rank()
715
- use_fp32_lamport = self.model_dtype == torch.float32
701
if flashinfer_comm is None:
702
logger.warning(
703
"Flashinfer is not installed or comm module not found, "
@@ -730,7 +715,7 @@ class AllReduceFusionPass(VllmPatternMatcherPass):
715
self.tp_size,
716
)
717
return
733
- element_size = 4 if use_fp32_lamport else 2
718
+ element_size = torch.tensor([], dtype=self.model_dtype).element_size()
719
self.max_token_num = max_size // (self.hidden_dim * element_size)
720
# take the min to save workspace size and we'll never use more
721
# than max_num_batched_tokens anyways
@@ -744,23 +729,19 @@ class AllReduceFusionPass(VllmPatternMatcherPass):
729
scope="global",
730
)
731
747
- self.ipc_handles, workspace_tensor = (
748
- flashinfer_comm.trtllm_create_ipc_workspace_for_all_reduce_fusion(
749
- tp_rank=rank,
750
- tp_size=self.tp_size,
751
- max_token_num=self.max_token_num,
752
- hidden_dim=self.hidden_dim,
753
- group=self.group,
754
- use_fp32_lamport=use_fp32_lamport,
755
- )
732
+ self.workspace = flashinfer_comm.create_allreduce_fusion_workspace(
733
+ backend="trtllm",
734
+ world_size=self.tp_size,
735
+ rank=rank,
736
+ max_token_num=self.max_token_num,
737
+ hidden_dim=self.hidden_dim,
738
+ dtype=self.model_dtype,
739
)
740
758
- global _FI_WORKSPACE_TENSOR
759
- _FI_WORKSPACE_TENSOR = workspace_tensor
741
+ global _FI_WORKSPACE
742
+ _FI_WORKSPACE = self.workspace
743
self.allreduce_params = FlashInferFusedAllReduceParams(
761
- rank=rank,
744
world_size=self.tp_size,
763
- use_fp32_lamport=use_fp32_lamport,
745
max_token_num=self.max_token_num,
746
)
747
@@ -832,7 +813,6 @@ class AllReduceFusionPass(VllmPatternMatcherPass):
813
def __del__(self) -> None:
814
if getattr(self, "disabled", True):
815
return
835
- if flashinfer_comm is not None:
836
- flashinfer_comm.trtllm_destroy_ipc_workspace_for_all_reduce(
837
- self.ipc_handles, self.group
838
- )
816
+ if getattr(self, "workspace", None) is not None:
817
+ with contextlib.suppress(Exception):
818
+ self.workspace.destroy()