Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -171,10 +171,7 @@ def process_weights_after_loading(self, layer: torch.nn.Module) -> None:

if hasattr(layer, "dispatcher"):
layer.dispatcher.set_quant_config(
{
"normal_dispatcher_output_dtype": "bf16",
"low_latency_dispatcher_output_dtype": "mxfp8",
}
dict(self.w13_kernel.DISPATCHER_QUANT_CONFIG)
)
Comment on lines 172 to 175

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.

Hi! My PR include #26408 an alternative solution that makes it possible to automatically determine the type of output dispatcher (fixing a long‑broken strategy), and it is implemented for both normal and low‑latency modes.

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.

I’d also prefer a single interface for interacting with the dispatcher type, for example the change introduced in PR #39589 has now broken automatic detection of the dispatcher output dtype in the TP (tensor parallelism) case.


def apply(self, layer: torch.nn.Module, dispatch_output: "DispatchOutput"):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,15 @@ def _get_bias_args(
class NPUW4A8MXFP4MoEMethod(_NPUMoEMethodBase):
"""ModelSlim W4A8 MoE with packed MXFP4 weights and MXFP8 activations."""

# Dispatch wire format required by the W4A8 MXFP GMMs: low-latency
# dispatch quantizes activations to MXFP8 (E8M0 block scales); normal
# dispatch stays BF16 because A5 MXFP8 normal dispatch is intranode-only,
# which also preserves multi-node prefill when ``deepep-mode=auto``.
DISPATCHER_QUANT_CONFIG = {
"normal_dispatcher_output_dtype": "bf16",
"low_latency_dispatcher_output_dtype": "mxfp8",
}

def __init__(self, dynamic_quant_kwargs=_DEFAULT_DYNAMIC_QUANT):
super().__init__(quant_config=None)
self.dynamic_quant_kwargs = dynamic_quant_kwargs
Expand Down Expand Up @@ -308,17 +317,8 @@ def process_weights_after_loading(
weight.data, weight_scale.data
)

# A5 DeepEP low-latency dispatch quantizes the valid received rows to
# MXFP8 and returns the matching E8M0 block scales. Keep normal mode
# in BF16 because A5 MXFP8 normal dispatch is intranode-only; this also
# preserves multi-node prefill when ``deepep-mode=auto``.
if weight_prefix == "w13" and hasattr(layer, "dispatcher"):
layer.dispatcher.set_quant_config(
{
"normal_dispatcher_output_dtype": "bf16",
"low_latency_dispatcher_output_dtype": "mxfp8",
}
)
layer.dispatcher.set_quant_config(dict(self.DISPATCHER_QUANT_CONFIG))

def apply(
self,
Expand Down
11 changes: 10 additions & 1 deletion python/sglang/srt/layers/quantization/fp8.py
Original file line number Diff line number Diff line change
Expand Up @@ -2561,8 +2561,17 @@ def process_weights_after_loading(self, layer: Module) -> None:
self._prepare_hpc_ops_weights(layer)

if hasattr(layer, "dispatcher"):
# Execution kernels may declare the dispatch wire format they
# need (DISPATCHER_QUANT_CONFIG, e.g. the NPU W4A8 MXFP GMMs
# consume MXFP8 activations); otherwise report the checkpoint
# dtype and let the dispatcher pick.
kernel_quant_config = getattr(
getattr(layer, "w13_kernel", None), "DISPATCHER_QUANT_CONFIG", None
)
layer.dispatcher.set_quant_config(
{
kernel_quant_config
if kernel_quant_config is not None
else {
"weight_dtype": layer.w13_weight.dtype,
"use_mxfp8": self.use_mxfp8,
}
Expand Down
Loading