Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
928a8e5
per-token FP8/MXFP4 fused AR+RMSNorm+quant
mqhc2020 Jun 30, 2026
56b919a
add benchmark for per-token fp8 and mxfp4
mqhc2020 Jun 30, 2026
7d8576d
fix gemini-code-assist comments and lint
mqhc2020 Jun 30, 2026
de7acb7
add FP8 per-token fusion for self-attn path
mqhc2020 Jun 30, 2026
fd23e99
fix lint
mqhc2020 Jun 30, 2026
0127720
cleanup logging code
mqhc2020 Jul 24, 2026
76f037e
Merge branch 'main' into marv/ar_norm_per_token_quant_fusion
mqhc2020 Jul 27, 2026
175a3d4
add CI test script
mqhc2020 Jul 28, 2026
fbed1e3
fix low-level CI error
mqhc2020 Aug 1, 2026
d57379b
prevent this change from affecting other models
mqhc2020 Aug 1, 2026
5a49833
consider more datatypes
mqhc2020 Aug 1, 2026
1226eec
use the same CI test script instead of creating a new one
mqhc2020 Aug 2, 2026
f316a92
prevent the MXFP4 fallback from using the wrong norm weight
mqhc2020 Aug 3, 2026
781fca8
Merge branch 'main' into marv/ar_norm_per_token_quant_fusion
sogalin Aug 4, 2026
f063f3f
use get_parallel to fix CI error
mqhc2020 Aug 4, 2026
aaa6522
Merge branch 'main' into marv/ar_norm_per_token_quant_fusion
yctseng0211 Aug 4, 2026
933baf8
Merge branch 'upstream-main' into marv/ar_norm_per_token_quant_fusion
mqhc2020 Aug 7, 2026
fdffe85
Merge branch 'main' into marv/ar_norm_per_token_quant_fusion
mqhc2020 Aug 10, 2026
cfaa0ff
fix CI error due to newly-added support to Intern-S2-Mobius
mqhc2020 Aug 10, 2026
1be831a
fix GDN in_proj_ba fused-AR tuple crash
mqhc2020 Aug 12, 2026
cff2fe5
modify PR CI test script from #24651
mqhc2020 Aug 13, 2026
74c58ab
Merge branch 'main' into marv/ar_norm_per_token_quant_fusion
mqhc2020 Aug 13, 2026
4def648
trigger ci
bingxche Aug 14, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
393 changes: 276 additions & 117 deletions benchmark/kernels/all_reduce/benchmark_fused_ar_rms_quant_amd.py

Large diffs are not rendered by default.

31 changes: 31 additions & 0 deletions python/sglang/srt/distributed/communication_op.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,19 @@ def tensor_model_parallel_fused_allreduce_rmsnorm(
return get_tp_group().fused_allreduce_rmsnorm(input_, residual_inp_, weight_, eps)


def tensor_model_parallel_fused_allreduce_rmsnorm_mxfp4_quant(
input_: torch.Tensor,
residual_inp_: torch.Tensor,
weight_: torch.Tensor,
eps: float,
emit_bf16: bool = False,
):
"""Fused TP all-reduce + RMSNorm + MXFP4 quant."""
return get_tp_group().fused_allreduce_rmsnorm_mxfp4_quant(
input_, residual_inp_, weight_, eps, emit_bf16=emit_bf16
)


def tensor_model_parallel_fused_allreduce_rmsnorm_quant_per_group(
input_: torch.Tensor,
residual_inp_: torch.Tensor,
Expand All @@ -63,6 +76,24 @@ def tensor_model_parallel_fused_allreduce_rmsnorm_quant_per_group(
)


def tensor_model_parallel_fused_allreduce_rmsnorm_quant_per_token(
input_: torch.Tensor,
residual_inp_: torch.Tensor,
weight_: torch.Tensor,
eps: float,
) -> Optional[Tuple[torch.Tensor, ...]]:
"""Fused TP all-reduce + RMSNorm + per-token FP8 quant in a single kernel.

Returns ``(fp8_output, residual_out, per_token_scale)`` with
``per_token_scale`` shaped ``(M, 1)``, or ``None`` when the backend cannot
service the request. Callers MUST handle ``None`` by falling back to the
fused-AR-RMSNorm + separate per-token-quant path.
"""
return get_tp_group().fused_allreduce_rmsnorm_quant_per_token(
input_, residual_inp_, weight_, eps
)


def tensor_model_parallel_all_gather(
input_: torch.Tensor, dim: int = -1
) -> torch.Tensor:
Expand Down
102 changes: 102 additions & 0 deletions python/sglang/srt/distributed/parallel_state.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,17 @@
_MODEL_PARALLEL_GROUP_TIMEOUT: Optional[timedelta] = None


def _should_use_1stage_mxfp4_ar(input_: torch.Tensor) -> bool:
hidden_size = input_.shape[-1]
tokens = input_.numel() // hidden_size
if hidden_size == 7168:
# CUDA-graph microbench: direct MXFP4 epilogue is faster through 56
# tokens, while fallback wins from 64 tokens onward.
return tokens <= 56

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P0] This can select the 1-stage kernel above its documented 80-token hard limit. For example BF16 [128, 512] is exactly 128 KiB and returns true here despite having 128 tokens. Please require tokens <= 80 for every default 1-stage decision; keep the measured K=7168 cutoff as an additional restriction, not a replacement for the hard limit. Add boundary tests for 80/81 tokens.

total_bytes = input_.numel() * input_.element_size()
return total_bytes <= 128 * 1024
Comment thread
mqhc2020 marked this conversation as resolved.


def get_torch_distributed_pg_options(group_name=None):
if not _is_npu:
return None
Expand Down Expand Up @@ -838,6 +849,41 @@ def fused_allreduce_rmsnorm(
)
return fused_outputs

def fused_allreduce_rmsnorm_mxfp4_quant(
self,
input_: torch.Tensor,
residual_inp_: torch.Tensor,
weight_: torch.Tensor,
eps: float,
emit_bf16: bool = False,
):
"""Attempt fused all-reduce + RMSNorm + MXFP4 quant via AITER custom AR."""
if not (is_hip() and is_gfx95_supported()):
return None

ca_comm = self.ca_comm
if ca_comm is None or getattr(ca_comm, "disabled", True):
return None
if not hasattr(ca_comm, "custom_fused_ar_rms_mxfp4_quant"):
return None

if envs.SGLANG_USE_1STAGE_ALLREDUCE.is_set():
use_1stage_ar = envs.SGLANG_USE_1STAGE_ALLREDUCE.get()
else:
use_1stage_ar = _should_use_1stage_mxfp4_ar(input_)

try:
return ca_comm.custom_fused_ar_rms_mxfp4_quant(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P0] The new quantized collectives omit the existing TC-piecewise CUDA-graph guard. AITER's custom communicator can return dummy zero outputs when its global capture state is active but the current stream is not capturing; this non-None tuple is then treated as real activations/residuals/scales. Mirror fused_allreduce_rmsnorm's capture-state handling before calling the MXFP4 and per-token wrappers, and add piecewise capture/replay correctness tests for both formats.

input_,
residual_inp_,
weight_,
eps,
use_1stage_ar,
emit_bf16=emit_bf16,
)
except Exception:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P0] An arbitrary collective runtime failure cannot safely become a per-rank local fallback. Once one or more ranks have entered the fused collective, independently returning None can make peers hang, run a second all-reduce, or consume partially mutated communicator/tensor state. Return None only from deterministic preflight checks before collective entry. Once the backend call begins, propagate the exception or implement a rank-consistent failure protocol. The same issue exists in the per-token wrapper's broad catch.

return None

def fused_allreduce_rmsnorm_quant_per_group(
self,
input_: torch.Tensor,
Expand Down Expand Up @@ -909,6 +955,62 @@ def fused_allreduce_rmsnorm_quant_per_group(
except Exception:
return None

def fused_allreduce_rmsnorm_quant_per_token(
self,
input_: torch.Tensor,
residual_inp_: torch.Tensor,
weight_: torch.Tensor,
eps: float,
) -> Optional[Tuple[torch.Tensor, ...]]:
"""Attempt fused all-reduce + RMSNorm + per-token FP8 quant in ONE kernel.

ROCm/aiter/gfx95-only entry point backed by the aiter custom-all-reduce
``custom_fused_ar_rms_quant`` (``post_per_token_quant=True``). Returns
``(fp8, residual_out, per_token_scale)`` with ``per_token_scale`` shaped
``(M, 1)``, or ``None`` when the backend cannot service the request so
the caller can fall back to the ``fused_allreduce_rmsnorm`` + separate
per-token-quant path.

Unlike the per-group entry point this kernel does not emit a bf16
sidecar, so GDN-style layers that need an unquantized normed output must
use the 2-kernel fallback.
"""
if not (is_hip() and is_gfx95_supported()):
return None

ca_comm = self.ca_comm
if ca_comm is None or getattr(ca_comm, "disabled", True):
return None
if not hasattr(ca_comm, "custom_fused_ar_rms_quant"):
return None

# Mirror the per-group eligibility gate so we fail fast without entering
# the HIP kernel dispatch.
K = input_.shape[-1]
if K > 16384:
return None
total_bytes = input_.numel() * input_.element_size()
if total_bytes == 0 or total_bytes > 8 * 1024 * 8192:
return None
if self.world_size == 6:
return None

if envs.SGLANG_USE_1STAGE_ALLREDUCE.is_set():
use_1stage_ar = envs.SGLANG_USE_1STAGE_ALLREDUCE.get()
else:
use_1stage_ar = total_bytes <= 128 * 1024

try:
return ca_comm.custom_fused_ar_rms_quant(
input_,
residual_inp_,
weight_,
eps,
use_1stage_ar,
)
except Exception:
return None
Comment thread
mqhc2020 marked this conversation as resolved.

def _resolve_outplace_all_reduce_method(
self,
input_: torch.Tensor,
Expand Down
Loading
Loading