Fix dynamo recompile limit in allreduce and bf16 gemm (#32239)
This commit is contained in:
@@ -631,7 +631,10 @@ class GroupCoordinator:
|
||||
|
||||
In addition, PyTorch custom ops do not support mutation or returning
|
||||
a new tensor in the same op. So we need to figure out if the op is
|
||||
in-place or out-of-place ahead of time.
|
||||
in-place or out-of-place ahead of time — except under Dynamo tracing,
|
||||
where the method selection would guard on the symbolic shape; there we
|
||||
always emit the out-of-place op with method "auto" and resolve the
|
||||
method at runtime inside the op.
|
||||
"""
|
||||
# Bypass the function if we are using only 1 GPU.
|
||||
if self.world_size == 1:
|
||||
@@ -661,6 +664,32 @@ class GroupCoordinator:
|
||||
if self.npu_communicator is not None and not self.npu_communicator.disabled:
|
||||
return self.npu_communicator.all_reduce(input_)
|
||||
|
||||
if torch.compiler.is_compiling():
|
||||
# Byte-size thresholds in method selection (e.g. `_pick_algo` or
|
||||
# `should_mscclpp_allreduce`) would guard on the symbolic token dim
|
||||
# and recompile per shape; defer the selection to runtime inside
|
||||
# the opaque custom op. Groups without any accelerated
|
||||
# communicator keep the inplace split op so their collective
|
||||
# stays outside captured graphs. The symmetric-memory in-place
|
||||
# path below is deliberately bypassed under compile: its raw
|
||||
# pynccl call is untraceable (hard error with fullgraph, graph
|
||||
# break otherwise) and its in-place contract does not fit the
|
||||
# outplace custom op.
|
||||
if (
|
||||
self.ca_comm is None
|
||||
and self.qr_comm is None
|
||||
and self.pymscclpp_comm is None
|
||||
and self.torch_symm_mem_comm is None
|
||||
and self.pynccl_comm is None
|
||||
):
|
||||
inplace_all_reduce(input_, group_name=self.unique_name)
|
||||
return input_
|
||||
return outplace_all_reduce(
|
||||
input_,
|
||||
group_name=self.unique_name,
|
||||
outplace_all_reduce_method="auto",
|
||||
)
|
||||
|
||||
should_use_pymscclpp_allreduce = (
|
||||
self.pymscclpp_comm is not None
|
||||
and self.pymscclpp_comm.should_mscclpp_allreduce(input_)
|
||||
@@ -675,31 +704,10 @@ class GroupCoordinator:
|
||||
self.pynccl_comm.all_reduce(input_)
|
||||
return input_
|
||||
|
||||
outplace_all_reduce_method = None
|
||||
if (
|
||||
self.ca_comm is not None
|
||||
and not self.ca_comm.disabled
|
||||
and not should_use_pymscclpp_allreduce
|
||||
and self.ca_comm.should_custom_ar(input_)
|
||||
):
|
||||
outplace_all_reduce_method = "ca"
|
||||
elif (
|
||||
self.qr_comm is not None
|
||||
and not self.qr_comm.disabled
|
||||
and self.qr_comm.should_quick_allreduce(input_)
|
||||
):
|
||||
outplace_all_reduce_method = "qr"
|
||||
elif self.pymscclpp_comm is not None and should_use_pymscclpp_allreduce:
|
||||
outplace_all_reduce_method = "pymscclpp"
|
||||
elif (
|
||||
self.torch_symm_mem_comm is not None
|
||||
and not self.torch_symm_mem_comm.disabled
|
||||
and self.torch_symm_mem_comm.should_torch_symm_mem_allreduce(input_)
|
||||
):
|
||||
outplace_all_reduce_method = "torch_symm_mem"
|
||||
elif is_in_tc_piecewise_cuda_graph() and self.pynccl_comm is not None:
|
||||
# For piecewise cuda graph, we use pynccl outplace allreduce
|
||||
outplace_all_reduce_method = "pynccl"
|
||||
outplace_all_reduce_method = self._resolve_outplace_all_reduce_method(
|
||||
input_=input_,
|
||||
should_use_pymscclpp_allreduce=should_use_pymscclpp_allreduce,
|
||||
)
|
||||
if outplace_all_reduce_method is not None:
|
||||
return outplace_all_reduce(
|
||||
input_,
|
||||
@@ -856,9 +864,63 @@ class GroupCoordinator:
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
def _resolve_outplace_all_reduce_method(
|
||||
self,
|
||||
input_: torch.Tensor,
|
||||
should_use_pymscclpp_allreduce: Optional[bool] = None,
|
||||
) -> Optional[str]:
|
||||
if should_use_pymscclpp_allreduce is None:
|
||||
should_use_pymscclpp_allreduce = (
|
||||
self.pymscclpp_comm is not None
|
||||
and self.pymscclpp_comm.should_mscclpp_allreduce(input_)
|
||||
)
|
||||
if (
|
||||
self.ca_comm is not None
|
||||
and not self.ca_comm.disabled
|
||||
and not should_use_pymscclpp_allreduce
|
||||
and self.ca_comm.should_custom_ar(input_)
|
||||
):
|
||||
return "ca"
|
||||
if (
|
||||
self.qr_comm is not None
|
||||
and not self.qr_comm.disabled
|
||||
and self.qr_comm.should_quick_allreduce(input_)
|
||||
):
|
||||
return "qr"
|
||||
if self.pymscclpp_comm is not None and should_use_pymscclpp_allreduce:
|
||||
return "pymscclpp"
|
||||
if (
|
||||
self.torch_symm_mem_comm is not None
|
||||
and not self.torch_symm_mem_comm.disabled
|
||||
and self.torch_symm_mem_comm.should_torch_symm_mem_allreduce(input_)
|
||||
):
|
||||
return "torch_symm_mem"
|
||||
if is_in_tc_piecewise_cuda_graph() and self.pynccl_comm is not None:
|
||||
# For piecewise cuda graph, we use pynccl outplace allreduce
|
||||
return "pynccl"
|
||||
return None
|
||||
|
||||
def _all_reduce_out_place(
|
||||
self, input_: torch.Tensor, outplace_all_reduce_method: str
|
||||
) -> torch.Tensor:
|
||||
if outplace_all_reduce_method == "auto":
|
||||
outplace_all_reduce_method = self._resolve_outplace_all_reduce_method(
|
||||
input_
|
||||
)
|
||||
if outplace_all_reduce_method == "pymscclpp":
|
||||
# pymscclpp reduces in place and returns its input; feed it a
|
||||
# clone to honor the op's no-mutation contract.
|
||||
input_ = input_.clone()
|
||||
elif outplace_all_reduce_method is None:
|
||||
# Force pynccl over the in-place fallback: it is graph-capture
|
||||
# safe and NCCL is natively out-of-place, avoiding the clone
|
||||
# the in-place fallback needs.
|
||||
if self.pynccl_comm is not None:
|
||||
outplace_all_reduce_method = "pynccl"
|
||||
else:
|
||||
out = input_.clone()
|
||||
self._all_reduce_in_place(out)
|
||||
return out
|
||||
ca_comm = self.ca_comm
|
||||
qr_comm = self.qr_comm
|
||||
pymscclpp_comm = self.pymscclpp_comm
|
||||
|
||||
@@ -167,11 +167,14 @@ def apply_flashinfer_allreduce_fusion(batch_size: int):
|
||||
# Ref: https://github.com/sgl-project/sglang/issues/17237
|
||||
(_is_sm90_supported or _is_sm100_supported)
|
||||
and _is_flashinfer_available
|
||||
and batch_size > 0
|
||||
and batch_size <= FUSE_ALLREDUCE_MAX_BATCH_SIZE
|
||||
and not is_dp_attention_enabled()
|
||||
and get_server_args().flashinfer_allreduce_fusion_backend is not None
|
||||
and not is_flashinfer_allreduce_unavailable()
|
||||
# Symbolic size checks stay last: under Dynamo tracing they guard on
|
||||
# the dynamic token dim, so statically-off configs must short-circuit
|
||||
# before reaching them.
|
||||
and batch_size > 0
|
||||
and batch_size <= FUSE_ALLREDUCE_MAX_BATCH_SIZE
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -40,6 +40,7 @@ from sglang.srt.utils import (
|
||||
use_intel_amx_backend,
|
||||
use_intel_xpu_backend,
|
||||
)
|
||||
from sglang.srt.utils.custom_op import register_custom_op
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from sglang.srt.layers.moe.token_dispatcher import (
|
||||
@@ -109,6 +110,25 @@ def initialize_bf16_gemm_config(server_args: ServerArgs) -> None:
|
||||
_BF16_GEMM_BACKEND = backend
|
||||
|
||||
|
||||
def _bf16_gemm_dispatch_fake(
|
||||
x: torch.Tensor, weight: torch.Tensor, bias: Optional[torch.Tensor]
|
||||
) -> torch.Tensor:
|
||||
return x.new_empty((*x.shape[:-1], weight.shape[0]))
|
||||
|
||||
|
||||
@register_custom_op(fake_impl=_bf16_gemm_dispatch_fake)
|
||||
def bf16_gemm_dispatch(
|
||||
x: torch.Tensor, weight: torch.Tensor, bias: Optional[torch.Tensor]
|
||||
) -> torch.Tensor:
|
||||
if _use_cutedsl_bf16_gemm is not None and _use_cutedsl_bf16_gemm(
|
||||
x.numel() // x.shape[-1], weight.shape[0], weight.shape[1]
|
||||
):
|
||||
return _cutedsl_bf16_gemm(x.view(-1, x.shape[-1]), weight, bias).view(
|
||||
*x.shape[:-1], -1
|
||||
)
|
||||
return F.linear(x, weight, bias)
|
||||
|
||||
|
||||
def get_bf16_gemm_backend() -> Bf16GemmBackend:
|
||||
global _BF16_GEMM_BACKEND
|
||||
if _BF16_GEMM_BACKEND is None:
|
||||
@@ -214,15 +234,24 @@ class UnquantizedLinearMethod(LinearMethodBase):
|
||||
and (bias is None or bias.dtype == torch.bfloat16)
|
||||
and not layer.weight.requires_grad
|
||||
and (bias is None or not bias.requires_grad)
|
||||
and _use_cutedsl_bf16_gemm(
|
||||
):
|
||||
if torch.compiler.is_compiling():
|
||||
# The m-dependent kernel heuristic would guard on the symbolic
|
||||
# token dim under Dynamo and recompile per shape bucket; the
|
||||
# opaque op resolves it at runtime with concrete shapes,
|
||||
# keeping the per-shape kernel choice.
|
||||
return bf16_gemm_dispatch(x, layer.weight, bias)
|
||||
if _use_cutedsl_bf16_gemm(
|
||||
x.numel() // x.shape[-1],
|
||||
layer.weight.shape[0],
|
||||
layer.weight.shape[1],
|
||||
)
|
||||
):
|
||||
x_shapes = x.shape
|
||||
output = _cutedsl_bf16_gemm(x.view(-1, x_shapes[-1]), layer.weight, bias)
|
||||
return output.view(*x_shapes[:-1], -1)
|
||||
):
|
||||
x_shapes = x.shape
|
||||
output = _cutedsl_bf16_gemm(
|
||||
x.view(-1, x_shapes[-1]), layer.weight, bias
|
||||
)
|
||||
return output.view(*x_shapes[:-1], -1)
|
||||
return F.linear(x, layer.weight, bias)
|
||||
|
||||
return F.linear(x, layer.weight, bias)
|
||||
|
||||
|
||||
@@ -4176,6 +4176,19 @@ class ServerArgs:
|
||||
"decode context parallel (dcp_size > 1)",
|
||||
lambda: self.dcp_size > 1,
|
||||
),
|
||||
# TcPiecewise makes the trtllm_mla prefill fall back to the
|
||||
# flashinfer-MLA implementation, which faults (illegal address)
|
||||
# on an FP8 KV cache.
|
||||
(
|
||||
"MLA attention with FP8 KV cache",
|
||||
lambda: self.kv_cache_dtype.startswith("fp8")
|
||||
and (
|
||||
_resolved_view(self).attention_backend
|
||||
in ("trtllm_mla", "flashinfer_mla")
|
||||
or _resolved_view(self).prefill_attention_backend
|
||||
in ("trtllm_mla", "flashinfer_mla")
|
||||
),
|
||||
),
|
||||
]
|
||||
for _name, predicate in rules:
|
||||
if predicate():
|
||||
|
||||
Reference in New Issue
Block a user