执行摘要
- 一句话:补充ROCm量化注册并修复测试
- 推荐动作:该PR已合并,值得所有维护ROCm后端的工程师精读。核心学习点包括:(1)平台抽象层如何通过
supported_quantization列表控制量化方法可见性;(2)测试中通过is_cuda_alike()而非is_cuda()实现多平台兼容的模式;(3)get_current_memory_usage应使用max_memory_allocated而非total-free以确保准确性。对于关注Quark量化或MXFP4 MoE的开发者,quark_moe.py中的仿真条件设计值得参考。
功能与动机
PR body指出:Several quantization methods were failing on ROCm because they weren't registered in the platform's supported_quantization list, even though the underlying kernels already support them (either natively or through emulation fallbacks). 同时测试中峰值内存统计被is_cuda()门控从而在ROCm上未触发,且内存阈值硬编码为CUDA的格式,导致test_online_quant_peak_mem失败。
实现拆解
实现拆解分为6步:
-
注册缺失量化方法到平台支持列表(vllm/platforms/rocm.py)
在RocmPlatform.supported_quantization中添加fp8_per_tensor、fp8_per_block、online、mxfp8、modelopt、modelopt_mxfp8、modelopt_mixed、gpt_oss_mxfp4(原有顺序调整)。这些方法已有对应的ROCmFP8ScaledMMLinearKernel或EmulationMxfp8LinearKernel(#39205)等kernel。
-
修复峰值内存日志与计算方式(vllm/platforms/rocm.py、vllm/model_executor/model_loader/base_loader.py)
在base_loader.py中将is_cuda()条件改为is_cuda_alike()使日志在ROCm上输出;在rocm.py中将get_current_memory_usage从total - free改为torch.cuda.max_memory_allocated(device)(带前缀empty_cache()清零)、保持与CUDA一致的内存统计语义。
-
修复native路径的bias遗漏(vllm/model_executor/layers/quantization/quark/schemes/quark_ocp_mx.py)
apply_weights的gemm_with_dynamic_quant分支缺少bias加法,现在在外部补加y = y + bias if bias is not None,使native路径行为与emulate路径的F.linear一致。
-
严格化MoE仿真条件(vllm/model_executor/layers/quantization/quark/quark_moe.py)
将原先not self.ocp_mx_scheme.startswith('w_mxfp4')替换为self.ocp_mx_scheme not in ('w_mxfp4',)常量,明确只有纯w4方案允许跳过仿真,混合方案(如w_mxfp4_a_mxfp6_*)强制走仿真避免不支持的kernel dispatch。
-
处理1-D scaling張量兼容性(vllm/model_executor/kernels/linear/scaled_mm/pytorch.py)
TorchFP8ScaledMMLinearKernel.apply_scaled_mm中对Bs和As增加维度检测:若为一维则reshape为(1, n)或(-1, 1),满足torch._scaled_mm的rowwise契约。
-
修复测试中CPU LAPACK依赖(tests/quantization/test_turboquant.py)
generate_rotation_matrix将QR分解移至GPU执行,因为ROCm PyTorch wheel不带CPU LAPACK。
配套测试调整:tests/quantization/utils.py中的内存阈值改为平台无关;tests/quantization/test_configs.py和tests/quantization/test_mixed_precision.py把is_cuda()改为is_cuda_alike()。
关键文件:
vllm/platforms/rocm.py(模块 平台层;类别 source;类型 core-logic;符号 supported_quantization, get_current_memory_usage): 核心变更:添加8种量化方法到supported_quantization列表,并重构get_current_memory_usage内存计算方法,直接影响ROCm上所有量化方法的可用性和峰值内存日志的正确性。
vllm/model_executor/layers/quantization/quark/schemes/quark_ocp_mx.py(模块 量化层;类别 source;类型 data-contract;符号 apply_weights): 修复native路径下gemm_with_dynamic_quant缺失bias的问题,确保与emulate路径行为一致,影响所有使用OCP_MX方案的线性层(如qkv_proj)。
vllm/model_executor/kernels/linear/scaled_mm/pytorch.py(模块 线性层;类别 source;类型 data-contract;符号 TorchFP8ScaledMMLinearKernel.apply_scaled_mm): 修复torch._scaled_mm在1-D scale张量上的兼容性,使ModelOpt FP8_PER_CHANNEL_PER_TOKEN路径可正常工作于ROCm。
vllm/model_executor/model_loader/base_loader.py(模块 模型加载;类别 source;类型 data-contract): 将峰值内存日志条件从is_cuda()改为is_cuda_alike(),使ROCm平台能记录模型加载后的峰值内存,是测试修复的关键一环。
tests/quantization/test_turboquant.py(模块 测试;类别 test;类型 test-coverage;符号 generate_rotation_matrix): 修复ROCm上缺失CPU LAPACK导致的QR分解失败,将计算移至GPU。
tests/quantization/utils.py(模块 测试工具;类别 test;类型 test-coverage): 调整峰值内存测试的期望值以匹配平台无关计算,配合get_current_memory_usage修改。
tests/quantization/test_configs.py(模块 测试;类别 test;类型 test-coverage): 将is_cuda()改为is_cuda_alike()以兼容ROCm。
tests/quantization/test_mixed_precision.py(模块 测试;类别 test;类型 test-coverage): 将is_cuda()改为is_cuda_alike()以兼容ROCm。
vllm/model_executor/layers/quantization/quark/quark_moe.py(模块 量化层;类别 source;类型 data-contract;符号 init): 修复MoE仿真条件,避免不支持的OCP MX方案错误地跳过仿真导致kernel dispatch失败。
关键符号:apply_weights, init, apply_scaled_mm, get_current_memory_usage, verify_quantization, generate_rotation_matrix
关键源码片段
vllm/platforms/rocm.py
核心变更:添加8种量化方法到supported_quantization列表,并重构get_current_memory_usage内存计算方法,直接影响ROCm上所有量化方法的可用性和峰值内存日志的正确性。
class RocmPlatform(Platform):
# ... ( 其他成员 )
supported_quantization: list[str] = [
"awq",
"awq_marlin",
"gptq",
"gptq_marlin",
"fp8",
"compressed-tensors",
"fbgemm_fp8",
"gguf",
"quark",
"mxfp4",
"mxfp8", # 新增:MXFP8 量化
"torchao",
"bitsandbytes",
"modelopt", # 新增:ModelOpt 基础框架
"modelopt_fp4",
"modelopt_mxfp8", # 新增:ModelOpt MXFP8
"modelopt_mixed", # 新增:ModelOpt 混合精度
"fp8_per_tensor", # 新增:每张量 FP8 缩放
"fp8_per_block", # 新增:每块 FP8 缩放
"online", # 新增:在线量化
"gpt_oss_mxfp4",
]
@classmethod
def get_current_memory_usage(
cls, device: torch.types.Device | None = None
) -> float:
# 先清空缓存再重置峰值计数器,确保统计的是模型加载后的峰值
torch.cuda.empty_cache()
torch.cuda.reset_peak_memory_stats(device)
# 使用 max_memory_allocated 返回峰值分配内存,
# 而非 total - free(后者受 HIP 运行时等后台开销影响)
return torch.cuda.max_memory_allocated(device)
vllm/model_executor/layers/quantization/quark/schemes/quark_ocp_mx.py
修复native路径下gemm_with_dynamic_quant缺失bias的问题,确保与emulate路径行为一致,影响所有使用OCP_MX方案的线性层(如qkv_proj)。
def apply_weights(
self,
layer: torch.nn.Module,
x: torch.Tensor,
bias: torch.Tensor | None = None,
) -> torch.Tensor:
if self.emulate:
# 仿真路径:先反量化权重、量化再反量化激活,然后使用 F.linear
dq_w = self.dequant_func(layer.weight, layer.weight_scale, x.dtype)
qdq_x = self.quant_dequant_func(x)
return F.linear(qdq_x, dq_w, bias)
# native 路径:调用自定义 GEMM 算子(无 bias 参数)
y = torch.ops.vllm.gemm_with_dynamic_quant(
x,
layer.weight,
layer.weight_scale,
self.rocm_use_aiter_fp4_asm_gemm,
self.out_dtype,
)
# gemm_with_dynamic_quant 不携带 bias,手动补加使其与
# F.linear 行为一致(例如 qkv_proj 使用 qkv_bias=True 时)
if bias is not None:
y = y + bias
return y
vllm/model_executor/layers/quantization/quark/quark_moe.py
修复MoE仿真条件,避免不支持的OCP MX方案错误地跳过仿真导致kernel dispatch失败。
# 在 QuarkOCP_MX_MoEMethod.__init__ 中
# 定义当前被 AITER 原生支持的 OCP MX 方案元组
# TODO(aiter): 待 rocm_aiter_fused_experts 扩展后更新此列表
_AITER_NATIVE_OCP_MX_SCHEMES = ("w_mxfp4",)
self.emulate = (
not current_platform.supports_mx()
or self.ocp_mx_scheme not in _AITER_NATIVE_OCP_MX_SCHEMES
) and (
self.mxfp4_backend is Mxfp4MoeBackend.NONE or not self.use_rocm_aiter_moe
)
# 原先使用 `not self.ocp_mx_scheme.startswith("w_mxfp4")`,
# 会错误地将 w_mxfp4_a_mxfp6_* 等混合方案也判为 native,
# 但 AITER kernel 不支持这些混合方案,会导致 QuantMethod.NO 错误。
# 改用显式元组后只有纯 w4a16 方案能走 native,其余走仿真。
评论区精华
代码审查中有一重要讨论:
风险与影响
- 风险:
- 新增量化方法未经验证:虽然注册了列表,但部分方法(如
modelopt_mxfp8)可能仅在仿真模式下可用,性能或数值精度未在PR中充分验证,可能出现推理结果错误。
- bias添加可能影响eager模式:
quark_ocp_mx.py的bias加法在已有联合bias的模型中工作正常,但若gemm_with_dynamic_quant内部已经处理bias则会导致重复添加。从当前逻辑看gemm_with_dynamic_quant无bias参数,因此是安全的,但需注意未来kernel变更。
- 内存计算改变对其他平台的影响:
get_current_memory_usage改为max_memory_allocated对CUDA平台同样生效,若CUDA那边之前依赖total - free的语义(例如计算峰值)可能发生变化,但新定义更符合“峰值分配”的意图。
- 仿真MoE性能回退:
quark_moe.py的仿真条件严格化后,w_mxfp4以外的MX方案(如w_mxfp4_a_mxfp6_*)将走仿真,在ROCm上性能可能下降超过90%(如Rohan138在评论中担心的),这是故意牺牲性能换取正确性。
- 影响:
- 用户影响:ROCm平台用户现在可以使用包括
fp8_per_tensor、modelopt系列在内的多种量化方法,提升了推理精度和部署灵活性。test_online_quant_peak_mem在ROCm上通过,增强了CI可靠性。
- 系统影响:无外部API或配置变更。
- 团队影响:加强了ROCm平台与CUDA平台在量化支持上的对齐,后续新增量化方法时需同时更新平台列表。内存统计方式的统一减少了平台差异。
- 风险标记:核心路径变更, 仿真路径性能回退, 测试阈值调整
关联脉络
- PR #39205 [ROCm] Introduce EmulationMxfp8LinearKernel: 本PR为ROCm注册mxfp8和modelopt_mxfp8量化方法时依赖此PR引入的EmulationMxfp8LinearKernel作为仿真回退。
参与讨论