执行摘要
- 一句话:修复 AMD DSV4 MoE 性能:传递 intermediate_pad 参数
- 推荐动作:该 PR 值得精读,特别是性能调优方法:通过微观基准定位到内核参数缺失,再用端到端测试验证。设计上记录了填充量并在内核调用处传递,是一种清晰的解耦方式。Shuffle 函数的清理也提高了代码可维护性。
功能与动机
在 AMD MI355X/gfx950 上,aiter fused_moe 内核性能明显低于参考 ATOM 引擎。经过微观基准测试定位到 intermediate_pad 未正确设置(默认为 0),导致内核计算了填充的零值通道。PR body 提到:“AITER FP4 MoE kernel requires the per-partition intermediate size to be 256-aligned, so during weight loading we pad it (…). But the pad amount was never propagated to fused_moe.”
实现拆解
- 记录填充量到 layer 属性:在
Fp8MoEMethod.process_weights_after_loading_block_quant 中,计算 padded_inter 后,将 layer.intermediate_pad = padded_inter - inter_per_part 和 layer.hidden_pad = 0 保存到 layer 上。
- 传递 intermediate_pad 到 fused_moe:在
maybe_get_hip_aiter_quant_info 中,新增 hidden_pad 和 intermediate_pad 参数,通过 getattr(layer, ...) 读取并传入 AiterMoeQuantInfo,使得内核跳过填充计算。
- 统一 FP4 权重/缩放洗牌:移除对
shuffle_scale_a16w4 / shuffle_weight_a16w4 的导入,改用通用 shuffle_scale / shuffle_weight,并引入 gu_intv 参数(来自环境变量 SGLANG_USE_AITER_MOE_GU_ITLV),使交错布局可配置。
- 根据 review 建议将 gu_intv 改为局部变量:初始提交将
gu_intv 设为实例属性,审查要求改为局部变量,最终实现在 process_weights_after_loading_block_quant 中直接读取。
- 数值等价性:填充通道权重/缩放为零,计算与否数值等效,GSM8K 精度测试确认无回归(0.970 vs 0.965,在运行间噪声范围内)。
关键文件:
python/sglang/srt/layers/quantization/fp8.py(模块 量化层;类别 source;类型 core-logic;符号 process_weights_after_loading_block_quant, maybe_get_hip_aiter_quant_info): 所有性能修复和代码清理均在此文件:记录 intermediate_pad/hidden_pad 到 layer 属性、在 maybe_get_hip_aiter_quant_info 中传递给 AiterMoeQuantInfo、统一 shuffle 函数并引入 gu_intv 局部变量。
关键符号:process_weights_after_loading_block_quant, maybe_get_hip_aiter_quant_info
关键源码片段
python/sglang/srt/layers/quantization/fp8.py
所有性能修复和代码清理均在此文件:记录 intermediate_pad/hidden_pad 到 layer 属性、在 maybe_get_hip_aiter_quant_info 中传递给 AiterMoeQuantInfo、统一 shuffle 函数并引入 gu_intv 局部变量。
# python/sglang/srt/layers/quantization/fp8.py
# 在 process_weights_after_loading_block_quant 中记录填充量
fp4_k_align = 256
padded_inter = (
(inter_per_part + fp4_k_align - 1) // fp4_k_align * fp4_k_align
)
# 记录填充量,告诉 fused_moe 实际中间大小
# aiter fused_moe 需要 intermediate_pad = padded - real # ATOM 传 128,
# SGLang 之前默认为 0 导致计算了填充区域
layer.intermediate_pad = padded_inter - inter_per_part # 例如 128
layer.hidden_pad = 0
if padded_inter != inter_per_part:
# pad 权重逻辑不变 ...
# 在 maybe_get_hip_aiter_quant_info 中传递到 AiterMoeQuantInfo
AiterMoeQuantInfo(
...
# 以下两个参数是关键修复:让内核跳过填充通道的计算
hidden_pad=getattr(layer, "hidden_pad", 0),
intermediate_pad=getattr(layer, "intermediate_pad", 0),
)
评论区精华
Review 评论仅一条:HaiShaw 要求将 gu_intv 从实例属性(self.gu_intv)改为 process_weights_after_loading_block_quant 内的局部变量,作者在第二个 commit 中已采纳。无其他争议。
- gu_intv 应设为局部变量而非实例属性 (design): 作者采纳建议,在第二个 commit 中将
gu_intv 改为函数内局部变量。
风险与影响
关联脉络
- PR #27747 fix: DSV4 BCG compress-prefill plan OOB on underfilled (tiny) prefill replay: 同为 DeepSeek-V4 相关修复,但领域不同(BCG vs MoE),无直接依赖。
- PR #27811 [AMD] Restore AMD piecewise CUDA graph support dropped by #23906: 同为 AMD 平台性能修复,但领域不同(CUDA graph vs MoE),无直接依赖。
参与讨论