Prhub

#55522 [Refactor][ROCm] Migrate the RDNA3 W4A16 MoE to the oracle/experts pa…

原始 PR 作者 JartX 合并时间 2026-09-12 21:20 文件变更 10 提交数 2 评论 4 代码增减 +453 / -377

执行摘要

RDNA3 MoE 迁入 oracle 结构并修复加载崩溃

动机是回应 #44075 落地 RDNA3 W4A16 MoE 内核时 @BowenBao 在 review 中提出的重构要求(PR body 原文:this is the follow-up refactor @BowenBao asked for when the RDNA3 W4A16 MoE kernel landed in #44075)。原实现通过一个私有 quant-method 类 CompressedTensorsWNA16RDNA3MoEMethod 加 rocm_moe_rdna.py 这个 48 行的私有 dispatcher、再配合 compressed_tensors_moe.py 内联分支接入,与仓库既有的「用类结构统一 MoE oracle」方向不一致(#37753 明确要求把 select/convert/make_kernel 逻辑收敛到统一类结构,本 PR 由 #44460 跟踪)。同时该路径阻塞在 #44570(CompressedTensorsWNA16MoEMethod 合并 Marlin 变体)之上,后者合入后本 PR 才可落地。作者还在 body 中点出一个顺带修复:RoutedExperts 依据 quant method 类名决定行为,子类不在名单上导致 gfx1100 上 W4A16 MoE 模型加载即 KeyError: 'intermediate_size_full',因为没有 gfx1100 CI 而长期无人发现。

值得精读,尤其是正在做 MoE 后端接入、oracle 迁移或 ROCm 量化内核工作的工程师——它是一份完整的「把私有量化后端迁到 oracle/experts 模式」的参照实现,也直观展示了这种架构如何把平台门禁、量化兼容性判定、权重预处理三件事收拢到统一契约。阅读时建议重点看三处设计决策:(1) workspace_shapes() 的内存声明,它是替换旧 layer.rdna3_*_buf 预分配缓冲后的新契约;(2) finalize_weight_and_reduce_impl() 返回 TopKWeightAndReduceNoOP() 所依赖的内核 output_topk 语义;(3) _backend_incompatibility_reason() 中的四条拒绝条件,它直接决定了哪些 checkpoint 会静默回退 Triton。如果你维护 gfx1100 回归,建议把非对称、channel-wise、LoRA 三种回退场景补进手动回归清单,并顺手处理 coderabbit 指出的三个空跑用例;若你负责 MoE 加载路径,routed_experts.py 的类名白名单值得单独排期改造,否则同类 KeyError 会再次出现。

讨论亮点

本 PR 的 review 讨论非常轻(review_comments_count = 0,仅 4 条 issue 评论),结论性意见集中在批准与自测说明上:

BowenBao(#44075 reviewer、本次重构的提出者):Thanks @JartX! LGTM

fxmarty-amd:LGTM

tjtanaa:批准并执行合入(merged_by)

唯一的实质性技术反馈来自 coderabbitai 的 nitpick:tests/kernels/quantization/test_rdna3_compile_guards.py 中 TestMoEDispatchMocked 的三个用例在非 ROCm 机器上「空跑」——rdna3_moe_kernel_available() 在 current_platform.is_rocm() 处就返回 False,根本不会读到被 mock 的 on_gfx1100 与 torch.ops,断言退化为 False is False;建议给该类补上文件里已有的 gfx1100_only 或 ROCm skip marker。该建议未见修复记录,PR 直接合入。

流程侧:claude[bot] 因 fork PR 自动审查被禁用(This pull request is from a fork — automated review is disabled),mergify 提示过合并冲突(作者随后 merge main 产生第二个 commit),AndreasKaratzas 触发 /ci run,Buildkite CI #88379 在 commit 8b31bef89a20 上运行。整体上,对一个改动内核分发路径、含三处行为变化的 PR,审查强度主要依赖作者自测与三位维护者的 LGTM。

实现拆解

  1. 新增标准 experts 实现(vllm/model_executor/layers/fused_moe/experts/rdna3_moe.py,+235):把原 _rdna3_fused_moe 的两次 moe_gptq_gemm_rdna3 调用重构进 Rdna3WNA16Experts.apply(),并补齐 modular kernel 契约——moe_problem_size() 针对 K 维在前的打包布局(w2.size(1) * 8 反推 hidden)重写、workspace_shapes() 声明 workspace2 必须容纳 gate_up 输出与激活输出、finalize_weight_and_reduce_impl() 返回 TopKWeightAndReduceNoOP()(top-k 归约被内核的原子写回吸收)。能力声明拆成 _supports_current_device / _supports_quant_scheme / _supports_activation / _supports_parallel_config,由 oracle 统一询问,取代原先挂在 rocm_moe_rdna.is_supported() 上的单点判断。
  2. oracle 侧接线(vllm/model_executor/layers/fused_moe/oracle/int_wna16.py,+110):新增 WNA16MoEBackend.RDNA3 枚举与 backend_to_kernel_cls() 的延迟导入分支;把 RDNA3 置于 _get_priority_backends() 列表首位,靠设备门自门控;map_wna16_backend() 增加 'rdna3' 映射从而打通 --moe-backend rdna3;make_wna16_moe_kernel() 把 Rdna3WNA16Experts 加入 allowed experts,使内核只能经 oracle 抵达。
  3. 不兼容判定集中化:_backend_incompatibility_reason() 新增 RDNA3 分支,逐条拒绝非 compressed-tensors 配置、非对称(may_have_zp)、expert bias、group actorder、非 QuantizationStrategy.GROUP 的 scale 策略,并以字符串原因返回给 oracle,由其静默回退到下一个可用后端(Triton / Marlin)。这一层把「哪些 checkpoint 能进 HIP 内核」从散落的隐式条件变成显式契约。
  4. 权重预处理下沉:新增 _synthesize_rdna3_qzeros()(复刻 GPTQv1 的 +1 怪癖,编码 bias - 1 = 7)与 _process_weights_rdna3()(逐 expert 做 ops.gptq_shuffle、合成对称零点并 expand 到全部 expert),在 convert_to_wna16_moe_kernel_format() 中按 backend 分派,与 _process_weights_cpu / marlin 系列并列;权重、scale、零点的一次性准备工作由量化类搬到 oracle 后,compressed_tensors_moe_wna16.py 只需把 RDNA3 与 CPU 一起纳入「对称量化也要 ZP」的断言。
  5. 删除旧接线:rocm_moe_rdna.py(is_supported / make_method,-48)与 compressed_tensors_moe_wna16_rdna3.py(CompressedTensorsWNA16RDNA3MoEMethod、_synthesize_qzeros、_rdna3_fused_moe,-260)整体删除,compressed_tensors_moe.py 移除内联 ROCm 分支(-7)。删除后 ROCm 上的 WNA16 MoE 只剩 oracle 一条选路,重复的权重准备逻辑不再存在。
  6. 崩溃修复与配置配套:routed_experts.py 的 weight_loader 类名白名单删除已不存在的 CompressedTensorsWNA16RDNA3MoEMethod;新后端复用父类名后走通用路径,作者描述的 gfx1100 KeyError: 'intermediate_size_full' 随之消失。vllm/config/kernel.py 把 'rdna3' 加入合法 MoE backend 列表并补充说明(ROCm gfx1100 only),否则 --moe-backend rdna3 会被配置校验拒绝。
  7. 测试配套:tests/kernels/quantization/test_rdna3_compile_guards.py 把断言对象从已删除的 rocm_moe_rdna.is_supported 换成 rdna3_moe_kernel_available()、Rdna3WNA16Experts._supports_current_device() 与 _supports_quant_scheme(),并新增 TestWNA16OracleWiring 验证 backend↔experts 映射及 RDNA3 优先级早于 Triton;tests/quantization/test_moe_wna16.py 新增三条 RDNA3 拒绝用例(AutoGPTQ 布局、asymmetric、channel-wise)。HIP 内核与 tests/kernels/quantization/test_rdna3_moe_w4a16.py 未改动。
行为 迁移前 迁移后
非对称 checkpoint 内核合成对称零点,静默忽略 checkpoint 零点 oracle 判为不兼容,回退 Triton
LoRA 适配 直接丢弃 LoRA delta 回退 Triton,保留 delta
swiglu clamp / alpha / beta 未生效 经共享激活实现生效
channel-wise / 非 GROUP 策略 可能被 RDNA3 接管 明确回退 Triton
文件 模块 状态 重要度
vllm/model_executor/layers/fused_moe/experts/rdna3_moe.py 专家内核 added 9.17
vllm/model_executor/layers/fused_moe/oracle/int_wna16.py 内核选路 modified 8.45
vllm/model_executor/layers/quantization/compressed_tensors/compressed_tensors_moe/compressed_tensors_moe_wna16_rdna3.py 量化方法 removed 8.98
vllm/model_executor/layers/quantization/compressed_tensors/compressed_tensors_moe/compressed_tensors_moe_wna16.py 量化方法 modified 5.43
tests/kernels/quantization/test_rdna3_compile_guards.py 编译守卫 modified 7.0
vllm/model_executor/layers/quantization/compressed_tensors/compressed_tensors_moe/rocm_moe_rdna.py 内核调度 removed 7.53
vllm/model_executor/layers/quantization/compressed_tensors/compressed_tensors_moe/compressed_tensors_moe.py 分发入口 modified 5.41
vllm/model_executor/layers/fused_moe/routed_experts.py 权重加载 modified 4.59
tests/quantization/test_moe_wna16.py 量化测试 modified 5.07
vllm/config/kernel.py 后端配置 modified 4.12

关键符号

rdna3_moe_kernel_available Rdna3WNA16Experts.apply Rdna3WNA16Experts.workspace_shapes Rdna3WNA16Experts.moe_problem_size Rdna3WNA16Experts._supports_quant_scheme Rdna3WNA16Experts.finalize_weight_and_reduce_impl _get_priority_backends _backend_incompatibility_reason backend_to_kernel_cls map_wna16_backend make_wna16_moe_kernel _synthesize_rdna3_qzeros _process_weights_rdna3 convert_to_wna16_moe_kernel_format CompressedTensorsWNA16MoEMethod.process_weights_after_loading RoutedExperts.weight_loader

关键源码片段

vllm/model_executor/layers/quantization/compressed_tensors/compressed_tensors_moe/compressed_tensors_moe_wna16.py data-contract

零点断言条件从「仅 CPU」扩展为「CPU 与 RDNA3」,是 RDNA3 在对称量化下依赖 oracle 合成 ZP 的关键耦合点,配合 _process_weights_rdna3 才能正确写回权重。

# vllm/model_executor/layers/quantization/compressed_tensors/compressed_tensors_moe/compressed_tensors_moe_wna16.py
# process_weights_after_loading() 中的零点断言条件变化
​
# CPU 的 fused_experts_cpu 与 RDNA3 HIP 内核即使面对对称量化也需要零点
# (RDNA3 的零点由 oracle 的 _process_weights_rdna3 合成),而 EMULATION
# 会把零点烘进反量化后的 bf16 权重,所以要从断言里排除。
if (
    not self.symmetric
    or self.wna16_backend in (WNA16MoEBackend.CPU, WNA16MoEBackend.RDNA3)
) and self.wna16_backend != WNA16MoEBackend.EMULATION:
    assert w13_qzeros is not None and w2_qzeros is not None
    # 随后把 w13 / w2 的零点 replace_parameter 写回 layer

评论区精华

非 ROCm 平台上 TestMoEDispatchMocked 用例空跑 测试

coderabbitai 指出 tests/kernels/quantization/test_rdna3_compile_guards.py 中 test_kernel_unavailable_when_mocked_cdna、test_kernel_unavailable_when_op_missing、test_kernel_unavailable_when_rocm_c_absent 三个用例在非 ROCm 机器上不会真正执行被 mock 的条件:rdna3_moe_kernel_available() 在 current_platform.is_rocm() 处就返回 False,断言退化为 False is False。建议复用文件里已有的 gfx1100_only 或 ROCm-only 标记。

结论:建议被标记为 Trivial nitpick,PR 合入时未见修复记录;对新 experts 类的门禁测试仍存在「纸面覆盖」。 · unresolved

维护者对 oracle/experts 迁移方向的确认 设计

BowenBao(在 #44075 review 中要求这次重构的人)给出 Thanks @JartX! LGTM;fxmarty-amd 给出 LGTM;tjtanaa 批准并执行合入。作者在 body 中逐条说明三处行为变化与保留 HIP 内核不变,并给出 2×RX 7900 XTX 上 GSM8K 185/200 → 187/200、吞吐 ~385 tok/s 不变的对照数据。

结论:迁移方案获得原始提出者与两位维护者认可,被视为 #37753 统一 oracle 结构的落地样板。 · 已解决

fork PR 自动审查缺位与合并流程摩擦 other

claude[bot] 声明 This pull request is from a fork — automated review is disabled;mergify 提示存在合并冲突,作者随后 merge main 产生第二个 commit;AndreasKaratzas 触发 /ci run,Buildkite CI #88379 在 8b31bef89a20 上运行。review_comments_count 为 0,行级讨论完全缺失。

结论:审查主要依赖三位维护者的 LGTM 与作者本地 gfx1100 验证,过程性风险(冲突 rebase、CI 手动触发)已解决但审查深度有限。 · 已解决

风险与影响

  1. 缺少 gfx1100 自动化覆盖(最高风险):本 PR 修复的 KeyError: 'intermediate_size_full' 正是因为没有 gfx1100 CI 才长期潜伏;迁移后的 Rdna3WNA16Experts 路径同样只靠作者本地手跑 tests/kernels/quantization/test_rdna3_moe_w4a16.py 与 2×RX 7900 XTX 服务验证,test_rdna3_compile_guards.py 只覆盖门禁与选路,不覆盖端到端数值。
  2. 类名白名单式耦合未根除:vllm/model_executor/layers/fused_moe/routed_experts.py 仍以 quant method 类名字符串判断是否走 intermediate_size_full 路径,本次只是让 RDNA3 退回父类名而避开;未来任何新的 MoE 量化子类都可能重演同类加载失败。
  3. 回退面变化带来的可感知影响:非对称、channel-wise(非 GROUP strategy)、LoRA 三类配置现在一律落到 Triton。语义上更正确(不再静默忽略零点或丢弃 delta),但用户在 gfx1100 上的性能特征会改变;作者只验证了对称 int4 / group 32 场景吞吐不变(~385 tok/s),回退场景没有基准数据。
  4. 优先级首位带来的默认路径切换:_get_priority_backends() 把 RDNA3 排在 FLASHINFER_TRTLLM / MARLIN 之前,gfx1100 上所有满足 _supports_* 的 W4A16 MoE 默认走 HIP 内核;若门禁条件放宽而内核未同步演进,可能出现静默的数值错误而非报错。
  5. 隐式的 workspace 契约:workspace_shapes() 返回值必须精确覆盖 M * topk * (gate_up + N),因为输出缓冲区由 modular kernel 从 workspace13 切出、GEMM-1 的两个缓冲区复用 workspace2;尺寸声明与实际切分不一致会直接越界或写坏相邻缓冲,且没有任何断言兜底。
  6. TopKWeightAndReduceNoOP 的正确性前提:归约完全依赖 output_topk=top_k 在内核内完成,并在 apply() 中对 output 先做 zero_();一旦内核的 output_topk 语义或调用侧 apply_router_weight_on_input 分支被改动,错误会静默传播。
  7. 测试空跑问题未处理:coderabbit 指出的三个 mock 用例在非 ROCm 上不真正覆盖逻辑,使门禁回归的「纸面覆盖」高于实际覆盖。

用户侧:gfx1100(RX 7900 XTX 等 RDNA3 消费卡)用户从「W4A16 MoE 模型加载即崩」恢复到可正常加载运行,并首次可以用 --moe-backend rdna3 强制指定该内核;非对称、channel-wise、LoRA 用户得到的是 Triton 回退(结果更正确,但性能曲线可能与旧行为不同)。系统侧:ROCm 上 WNA16 MoE 的选路收敛到 oracle/int_wna16.py 单一入口,compressed_tensors_moe.py 不再持有平台专属内联分支,权重准备逻辑从两份(量化类内 + oracle 内)收敛为一份,后续新增 RDNA4/CDNA 内核只需补 backend + experts 类。日志格式随之变化:旧的 Using CompressedTensorsWNA16RDNA3MoEMethod (native RDNA3 HIP kernel) 变为 oracle 的 Using 'RDNA3' WNA16 MoE backend 与 Using Rdna3WNA16Experts,依赖旧日志的排障脚本或文档需要同步。团队侧:这是 #37753「统一 MoE oracle 类结构」在 ROCm 后端的落地样板,BowenBao 的批准意味着该模式获得原始提出者确认;代价是 gfx1100 的验证责任继续落在个别贡献者的本地硬件上,缺少 CI 兜底。

缺少 gfx1100 CI 覆盖 类名白名单耦合未根除 三处行为变化(非对称 /LoRA/ 激活) 默认后端优先级变更 workspace 尺寸契约无断言 测试用例空跑

关联 Issue

#37753 [Feature]: Unify MoE "Oracles" with Class Structure
#44570 [MoE Refactor] Combine CompressedTensorsWNA16MarlinMoEMethod with CompressedTensorsWNA16MoEMethod

完整报告

参与讨论