Prhub

#48287 add pad-aware swiglu limit kernel

原始 PR 作者 gnovack 合并时间 2026-07-14 07:48 文件变更 4 提交数 4 评论 0 代码增减 +140 / -4

执行摘要

新增填充感知的 SwiGLU limit Triton 内核

当启用 expert parallelism 时,MoE 输入和中间状态会沿 token 维度填充以处理最坏情况(所有 token 路由到一个 rank)。大多数 MoE kernel 在 GEMM 执行时内部会忽略填充 token,但 MoE 激活 kernel 目前不会。本 PR 引入了一个填充感知的 swiglu limit 激活变体以消除冗余计算。

此 PR 值得精读,特别是 Triton kernel 的设计模式(persistent row、列分块)以及如何通过 topk_idsexpert_map 实现填充感知。设计决策上将 pad-aware 功能无缝集成到现有接口中,保持向后兼容,是很好的工程实践。建议后续补充正式的单元测试。

讨论亮点

无实质性 review 讨论,PR 获得批准合并。

实现拆解

  1. 实现 pad-aware Triton kernel:在 utils.py 中新增 _swiglu_limit_pad_aware_kernel(Triton JIT kernel),采用 persistent row 模式:每个 CTA(线程块)负责一列分片,遍历所有 token 行,仅当 topk_ids 不等于 -1 且(若提供 expert_map)本地 expert ID 不为 -1 时才执行激活计算。包装函数 _swiglu_limit_pad_aware 设置 grid 并启动 kernel。
  2. 重构 swiglu_limit_func:将原来的 swiglu_limit_func 重命名为 _swiglu_limit_torch(保留 @torch.compile 优化),并新增统一的 swiglu_limit_func 作为入口:当传入 topk_ids 时调用 pad-aware 版本,否则 fallback 到 torch 版本。
  3. 修改激活函数入口:在 activation.py 中新增 silu_and_mul_with_clamp 函数,接受 topk_idsexpert_map,在两者均提供时调用 swiglu_limit_func,否则使用原有的 C++ kernel。修改 apply_moe_activation 添加 topk_idsexpert_map 参数,并在 SILU 分支调用 silu_and_mul_with_clamp
  4. 透传参数到调用链:在 modular_kernel.pyactivation 方法和 marlin_moe.py_fused_marlin_moefused_marlin_moeactivation_with_lora 中增加 topk_idsexpert_map 参数并传递,确保 pad-aware kernel 在相关路径被启用。
文件 模块 状态 重要度
vllm/model_executor/layers/fused_moe/utils.py MoE 融合层 modified 8.75
vllm/model_executor/layers/fused_moe/activation.py MoE 融合层 modified 7.04
vllm/model_executor/layers/fused_moe/modular_kernel.py MoE 融合层 modified 5.58
vllm/model_executor/layers/fused_moe/experts/marlin_moe.py MoE 融合层 modified 5.48

关键符号

swiglu_limit_func _swiglu_limit_torch _swiglu_limit_pad_aware_kernel _swiglu_limit_pad_aware silu_and_mul_with_clamp apply_moe_activation activation (modular_kernel) _fused_marlin_moe fused_marlin_moe activation_with_lora

关键源码片段

vllm/model_executor/layers/fused_moe/utils.py core-logic

核心变更文件,实现了 pad-aware Triton kernel 并重构了 swiglu_limit_func 作为路由入口。

@triton.jit
def _swiglu_limit_pad_aware_kernel(
    input_ptr,
    output_ptr,
    topk_ids_ptr,
    expert_map_ptr,
    hidden_size,
    input_row_stride,
    num_tokens,
    swiglu_limit,
    HAS_LIMIT: tl.constexpr,
    HAS_EXPERT_MAP: tl.constexpr,
    BLOCK_SIZE: tl.constexpr,
):
    # 持久化行模式:每个 CTA 拥有一个列分片,并按步进方式遍历 token 行
    pid = tl.program_id(0)
    row_stride = tl.num_programs(0)
    column_tile = tl.program_id(1) * BLOCK_SIZE + tl.arange(0, BLOCK_SIZE)
    mask = column_tile < hidden_size
​
    for row in tl.range(pid, num_tokens, row_stride):
        expert_id = tl.load(topk_ids_ptr + row)
        should_compute = expert_id != -1
        if HAS_EXPERT_MAP:
            local_expert_id = tl.load(
                expert_map_ptr + expert_id,
                mask=expert_id >= 0,
                other=-1,
            )
            should_compute = should_compute & (local_expert_id != -1)
​
        if should_compute:
            gate_offsets = row.to(tl.int64) * input_row_stride + column_tile
            up_offsets = gate_offsets + hidden_size
​
            gate = tl.load(input_ptr + gate_offsets, mask=mask, other=0.0).to(tl.float32)
            up = tl.load(input_ptr + up_offsets, mask=mask, other=0.0).to(tl.float32)
​
            if HAS_LIMIT:
                gate = tl.minimum(gate, swiglu_limit)
                up = tl.maximum(up, -swiglu_limit)
                up = tl.minimum(up, swiglu_limit)
​
            silu_gate = gate / (1.0 + tl.exp(-gate))
            result = silu_gate * up
            tl.store(
                output_ptr + row.to(tl.int64) * hidden_size + column_tile,
                result.to(output_ptr.dtype.element_ty),
                mask=mask,
            )
​
​
def _swiglu_limit_pad_aware(
    output: torch.Tensor,
    input: torch.Tensor,
    topk_ids: torch.Tensor,
    swiglu_limit: float,
    expert_map: torch.Tensor | None = None,
) -> None:
    num_tokens, gate_up_size = input.shape
    hidden_size = gate_up_size // 2
    if num_tokens == 0:
        return
​
    BLOCK_SIZE = 1024
    grid = (min(num_tokens, 256), triton.cdiv(hidden_size, BLOCK_SIZE))
    _swiglu_limit_pad_aware_kernel[grid](
        input, output, topk_ids, expert_map,
        hidden_size, gate_up_size, num_tokens, swiglu_limit,
        HAS_LIMIT=swiglu_limit > 0,
        HAS_EXPERT_MAP=expert_map is not None,
        BLOCK_SIZE=BLOCK_SIZE,
        num_warps=4,
    )
​
​
def swiglu_limit_func(
    output: torch.Tensor,
    input: torch.Tensor,
    swiglu_limit: float = 0.0,
    topk_ids: torch.Tensor | None = None,
    expert_map: torch.Tensor | None = None,
) -> None:
    # 当提供 topk_ids 时使用 pad-aware kernel 跳过填充行
    if topk_ids is not None:
        _swiglu_limit_pad_aware(output, input, topk_ids, swiglu_limit, expert_map)
    else:
        _swiglu_limit_torch(output, input, swiglu_limit)
vllm/model_executor/layers/fused_moe/activation.py core-logic

新增 silu_and_mul_with_clamp 函数作为统一入口,修改 apply_moe_activation 以传递 topk_ids/expert_map,使 pad-aware kernel 被激活路径调用。

def silu_and_mul_with_clamp(
    output: torch.Tensor,
    input: torch.Tensor,
    clamp_limit: float,
    topk_ids: torch.Tensor | None = None,
    expert_map: torch.Tensor | None = None,
) -> None:
    # 当提供 topk_ids 和 expert_map 时使用 pad-aware 激活
    if topk_ids is not None and expert_map is not None:
        from vllm.model_executor.layers.fused_moe.utils import swiglu_limit_func
        swiglu_limit_func(output, input, clamp_limit, topk_ids, expert_map)
    else:
        # 否则使用已有的 C++ kernel
        torch.ops._C.silu_and_mul_with_clamp(output, input, clamp_limit, 1.0, 0.0)

评论区精华

没有提炼出高价值讨论线程

当前评论区没有形成足够清晰的争议点或结论,后续有更多讨论时会体现在这里。

风险与影响

  1. 正确性风险:新 Triton kernel 需要正确跳过填充 token 和非本地 expert。PR body 附带了正确性验证脚本,但未纳入仓库测试,可能遗漏边界条件(如 hidden_size 非 1024 倍数已通过掩码处理)。
  2. 性能风险:在无填充场景下,pad-aware kernel 仍会检查 topk_ids,但开销极小且 grid 维度限制为 min(num_tokens, 256),整体性能与原始 kernel 接近(见 benchmark)。
  3. 兼容性风险:修改了 apply_moe_activationactivation 方法的签名,增加两个默认 None 的参数,不会破坏现有调用点,但若外部直接调用这些 API 并传参可能被忽略。
  4. 代码维护风险SWIGLUOAI_UNINTERLEAVE 分支仍直接调用 C++ kernel,未利用 pad-aware 路径,未来如需支持需额外改造。

对用户:在启用 expert parallelism 时,MoE 激活层的计算量随填充比例下降,可提升吞吐。对系统:新增 Triton kernel,无额外依赖。对团队:填补了 MoE 激活层对填充 token 的处理空白,使得 expert parallelism 的效率更接近理论最优。

缺少测试覆盖 核心路径变更

关联 Issue

未识别关联 Issue

当前没有检测到明确关联的 Issue 链接,后续同步到相关引用后会出现在这里。

完整报告

参与讨论