执行摘要
- 一句话:Flashinfer 非门控 MoE bf16 支持
- 推荐动作:值得精读。本 PR 展示了在统一 MoE 抽象层中新增非门控支持的典型步骤:能力声明、权重格式适配、后端编排和激活传递。设计决策(如对齐策略)经过 review 讨论,可作为类似扩展的参考。
功能与动机
在 NVIDIA GB200 节点上,NeMo(Nemotron-3-Nano-30B-A3B-BF16)等非门控 MoE 模型此前无法利用 Flashinfer TRTLLM 高性能内核,只能回退到 Triton 后端。PR body 数据显示 Flashinfer 相比 Triton 带来约 15% 端到端吞吐提升(12212 vs 10468 tok/s)和更低的 TTFT(714ms vs 2960ms)。
实现拆解
- 权重格式转换函数扩展(
flashinfer_utils.py):为 convert_moe_weights_to_flashinfer_trtllm_block_layout 新增 is_gated_act_gemm 参数(默认 True),非门控时跳过 permute indices 的 offset 修正步骤,因为 w13 不需要拆分为 gate/up 两部分。
- 未量化 MoE 后端编排(
unquantized.py):在 convert_to_unquantized_kernel_format 的 FLASHINFER_TRTLLM 分支中,识别非门控配置;当 is_act_and_mul=False 时,调用 align_moe_weights_for_fi 确保中间维度按 block_k=128 对齐(非门控 MoE 要求 strict alignment),并更新 moe_config.intermediate_size_per_partition;同时将 is_gated_act_gemm=False 传递给 convert_moe_weights_to_flashinfer_trtllm_block_layout。
- Expert 类能力声明(
trtllm_bf16_moe.py):将 _supports_no_act_and_mul() 从 False 改为 True,_supports_activation() 新增 MoEActivation.RELU2_NO_MUL;apply 方法中添加 assert 检查激活类型,并传递 activation_type=activation_to_flashinfer_int(activation) 给 flashinfer 内核。
- 对齐函数重命名(
flashinfer_utils.py):将 align_fp8_moe_weights_for_fi 重命名为 align_moe_weights_for_fi,使其适用于未量化场景,并更新所有调用点(包括 FP8 量化路径)。
关键文件:
vllm/model_executor/layers/quantization/utils/flashinfer_utils.py(模块 权重工具;类别 source;类型 data-contract;符号 align_fp8_moe_weights_for_fi, align_moe_weights_for_fi): 核心函数 convert_moe_weights_to_flashinfer_trtllm_block_layout 新增 is_gated_act_gemm 参数,非门控时跳过 permute 偏移;align_fp8_moe_weights_for_fi 重命名为通用 align_moe_weights_for_fi。
vllm/model_executor/layers/fused_moe/experts/trtllm_bf16_moe.py(模块 MoE 内核;类别 source;类型 data-contract): Expert 类声明支持非门控激活(RELU2_NO_MUL),并在 apply 中传递 activation_type 给 flashinfer 内核。
vllm/model_executor/layers/fused_moe/oracle/unquantized.py(模块 后端编排;类别 source;类型 data-contract): 在 FLASHINFER_TRTLLM 后端路径中处理非门控 MoE 的中间维度对齐,并传递 is_gated_act_gemm 标志。
关键符号:convert_moe_weights_to_flashinfer_trtllm_block_layout, align_moe_weights_for_fi, convert_to_unquantized_kernel_format, TrtLlmBf16Experts._supports_no_act_and_mul, TrtLlmBf16Experts._supports_activation, TrtLlmBf16Experts.apply
关键源码片段
vllm/model_executor/layers/quantization/utils/flashinfer_utils.py
核心函数 convert_moe_weights_to_flashinfer_trtllm_block_layout 新增 is_gated_act_gemm 参数,非门控时跳过 permute 偏移;align_fp8_moe_weights_for_fi 重命名为通用 align_moe_weights_for_fi。
# vllm/model_executor/layers/quantization/utils/flashinfer_utils.py
def convert_moe_weights_to_flashinfer_trtllm_block_layout(
cache_permute_indices: dict[torch.Size, torch.Tensor],
w13_weight: torch.Tensor,
w2_weight: torch.Tensor,
is_gated_act_gemm: bool = True, # 新增参数 : 非门控时跳过 offset 修正
) -> tuple[torch.Tensor, torch.Tensor]:
# ... 省略部分代码 ...
for i in range(num_experts):
w13_expert_uint8 = w13_weight[i].view(torch.uint8)
permute_indices = _maybe_get_cached_w3_w1_permute_indices(
cache_permute_indices,
w13_expert_uint8,
epilogue_tile_m,
is_gated_act_gemm=is_gated_act_gemm, # 传递参数给 flashinfer 内部
)
# 门控 MoE 需要调整 permute indices 以对齐 w3/w1 顺序
if is_gated_act_gemm:
rows = w13_expert_uint8.shape[0]
permute_indices = (permute_indices + rows // 2) % rows
_copy_permuted_expert_to_block_layout(
w13_weights_shuffled_tensor[i],
w13_expert_uint8,
permute_indices,
)
# ...
# 重命名 : 从 FP8 专用改为通用对齐函数,适用于未量化场景
def align_moe_weights_for_fi( # 原 align_fp8_moe_weights_for_fi
w13: torch.Tensor,
w2: torch.Tensor,
is_act_and_mul: bool,
min_alignment: int = 16,
) -> tuple[torch.Tensor, torch.Tensor, int]:
"""Pad intermediate size so FlashInfer kernels' alignment constraints hold."""
# ... 逻辑不变,仅重命名和更新注释
vllm/model_executor/layers/fused_moe/experts/trtllm_bf16_moe.py
Expert 类声明支持非门控激活(RELU2_NO_MUL),并在 apply 中传递 activation_type 给 flashinfer 内核。
# vllm/model_executor/layers/fused_moe/experts/trtllm_bf16_moe.py
class TrtLlmBf16Experts(mk.FusedMoEExpertsMonolithic):
# ...
@staticmethod
def _supports_no_act_and_mul() -> bool:
"""BF16 kernels support non-gated MoE via RELU2_NO_MUL."""
return True # 之前为 False,现在放开
@staticmethod
def _supports_activation(activation: MoEActivation) -> bool:
"""Supports SiLU (gated) and RELU^2 (non-gated) activations."""
return activation in [MoEActivation.SILU, MoEActivation.RELU2_NO_MUL] # 新增 RELU2_NO_MUL
def apply(self, ...) -> torch.Tensor:
import flashinfer
assert activation in [MoEActivation.SILU, MoEActivation.RELU2_NO_MUL] # 新增检查
return flashinfer.fused_moe.trtllm_bf16_moe(
# ... 其他参数不变
activation_type=activation_to_flashinfer_int(activation), # 新增 : 传递激活类型给内核
)
vllm/model_executor/layers/fused_moe/oracle/unquantized.py
在 FLASHINFER_TRTLLM 后端路径中处理非门控 MoE 的中间维度对齐,并传递 is_gated_act_gemm 标志。
# vllm/model_executor/layers/fused_moe/oracle/unquantized.py
def convert_to_unquantized_kernel_format(
unquantized_backend: UnquantizedMoeBackend,
layer: Module,
w13_weight: torch.Tensor,
w2_weight: torch.Tensor,
) -> tuple[torch.Tensor, torch.Tensor]:
# ... 其他分支 ...
elif unquantized_backend == UnquantizedMoeBackend.FLASHINFER_TRTLLM:
is_act_and_mul = layer.moe_config.is_act_and_mul
# 非门控 MoE 需要中间维度严格对齐到 128 (block_k)
if not is_act_and_mul:
w13_weight, w2_weight, padded_intermediate = align_moe_weights_for_fi(
w13_weight, w2_weight, is_act_and_mul, min_alignment=128
)
# 更新配置,确保后续内核使用正确的中间大小
layer.moe_config.intermediate_size_per_partition = padded_intermediate
_cache_permute_indices: dict[torch.Size, torch.Tensor] = {}
w13_weight, w2_weight = convert_moe_weights_to_flashinfer_trtllm_block_layout(
_cache_permute_indices,
w13_weight,
w2_weight,
is_gated_act_gemm=is_act_and_mul, # 非门控时传 False
)
return w13_weight.contiguous(), w2_weight.contiguous()
评论区精华
Reviewer tomeras91 提出了两个关键问题:
- 对非门控权重进行 128 对齐也影响了门控路径?作者 amirkl94 澄清这是预期行为,但大多数模型的 hidden_dim 已经是 128 的倍数,并将 padding 逻辑限制在非门控分支。
- 重复日志记录:
align_moe_weights_for_fi 内部已经打印 padding 警告,不应再在调用处添加额外日志。作者同意移除重复日志。
讨论最终全部 resolved,PR 获得 approval。
- 中间维度对齐是否影响门控 MoE 路径 (correctness): 作者解释说大部分模型的 hidden_dim 已经是 128 的倍数,并将 padding 逻辑限制在非门控分支,避免影响门控路径。
- 重复日志记录 (style): 作者同意移除重复日志。
风险与影响
关联脉络
- PR #45863 [DSv4 Perf] DSv4 flashinfer sparse index cache for metadata, 2%~4% TTFT improvement: 同样涉及 Flashinfer MoE 性能优化,共享部分基础设施。
- PR #45782 [ROCm][Bugfix]: Fallback GFX942 sparse MLA ops to Triton: 处理 GPU 加速器兼容性,与本 PR 的 backend 选择逻辑相关。
参与讨论