[Kernel] Avoid zero-bias allocation in fused softmax routing (#36811)

This commit is contained in:
YAMY
2026-09-01 16:29:57 -07:00
committed by GitHub
parent ed82bea146
commit 221a6273ce
3 changed files with 47 additions and 23 deletions
+29 -17
View File
@@ -1,7 +1,7 @@
from __future__ import annotations
import logging
from typing import TYPE_CHECKING, Tuple
from typing import TYPE_CHECKING, Optional, Tuple
import torch
import triton
@@ -109,6 +109,7 @@ def _router_triton_kernel(
HAS_SOFTCAP: tl.constexpr, # tanh softcapping (softmax only)
RENORMALIZE: tl.constexpr,
APPLY_SCALE: tl.constexpr, # apply_routed_scaling_factor_on_output
HAS_BIAS: tl.constexpr,
USE_PDL: tl.constexpr,
stride_sm,
stride_sn,
@@ -126,10 +127,13 @@ def _router_triton_kernel(
mask_m = offs_m < M
mask_n = offs_n < N
# prefetch bias before PDL wait
bias = tl.load(bias_ptr + offs_n, mask=mask_n, other=0.0).to(
tl.float32
) # [BLOCK_N]
# Prefetch a real bias before the PDL wait. Plain softmax routing has no
# bias, so keep the zero value in registers rather than materializing and
# clearing a device tensor for every routing call.
if HAS_BIAS:
bias = tl.load(bias_ptr + offs_n, mask=mask_n, other=0.0).to(tl.float32)
else:
bias = tl.zeros([BLOCK_N], dtype=tl.float32)
if USE_PDL:
tl.extra.cuda.gdc_wait()
@@ -254,7 +258,7 @@ def _router_triton_kernel(
@debug_kernel_api
def moe_fused_gate(
scores: torch.Tensor,
bias: torch.Tensor,
bias: Optional[torch.Tensor],
topk: int,
scoring_func: str = "sigmoid",
num_fused_shared_experts: int = 0,
@@ -282,17 +286,24 @@ def moe_fused_gate(
torch.float16,
torch.bfloat16,
), "scores must be float32/float16/bfloat16"
# The kernel loads the bias and upcasts it to fp32 in-register (see
# _router_triton_kernel), so a non-fp32 bias (DeepSeek-V4 stores the
# correction bias in bf16) needs no host-side cast/copy.
assert bias.dtype in (
torch.float32,
torch.float16,
torch.bfloat16,
), "bias must be float32/float16/bfloat16"
assert scores.ndim == 2, "scores must be 2D"
assert bias.ndim == 1, "bias must be 1D"
assert scores.size(1) == bias.size(0), "scores and bias must have same num_experts"
if bias is None:
assert (
scoring_func.lower() == "softmax"
), "bias is required for non-softmax routing"
else:
# The kernel loads the bias and upcasts it to fp32 in-register (see
# _router_triton_kernel), so a non-fp32 bias (DeepSeek-V4 stores the
# correction bias in bf16) needs no host-side cast/copy.
assert bias.dtype in (
torch.float32,
torch.float16,
torch.bfloat16,
), "bias must be float32/float16/bfloat16"
assert bias.ndim == 1, "bias must be 1D"
assert scores.size(1) == bias.size(
0
), "scores and bias must have same num_experts"
assert topk > num_fused_shared_experts, "topk must be > num_fused_shared_experts"
if routed_scaling_factor is None:
routed_scaling_factor = 1.0
@@ -349,7 +360,7 @@ def moe_fused_gate(
extra = {"launch_pdl": True} if use_pdl else {}
_router_triton_kernel[grid](
scores,
bias,
bias if bias is not None else scores,
weights,
indices,
M,
@@ -369,6 +380,7 @@ def moe_fused_gate(
HAS_SOFTCAP=bool(moe_softcapping != 0.0),
RENORMALIZE=bool(renormalize),
APPLY_SCALE=bool(apply_routed_scaling_factor_on_output),
HAS_BIAS=bias is not None,
USE_PDL=use_pdl,
stride_sm=scores.stride(0),
stride_sn=scores.stride(1),
+1 -6
View File
@@ -986,14 +986,9 @@ def fused_topk(
moe_fused_gate as _jit_moe_fused_gate,
)
zero_bias = torch.zeros(
gating_output.shape[1],
dtype=torch.float32,
device=gating_output.device,
)
topk_weights, topk_ids = _jit_moe_fused_gate(
gating_output,
zero_bias,
None,
topk,
scoring_func="softmax",
renormalize=renormalize,