Prhub

#39931 [Feature] TurboQuant: support hybrid models and uniform quantization

原始 PR 作者 JartX 合并时间 2026-05-05 08:14 文件变更 4 提交数 19 评论 49 代码增减 +186 / -27

执行摘要

TurboQuant 支持混合模型并修复页面大小对齐

TurboQuant 之前遇到 Mamba 等非注意力层时会抛出 NotImplementedError,导致混合模型无法使用。此外,混合模型的页面大小规划器使用了标准公式,与 TQ 的紧凑 KV 布局不匹配,触发断言;被跳过的层可能错误地强制使用 TURBOQUANT 注意力后端;ROCm 上的 flash_attn_varlen_func 不接受 out= 参数。

建议 TQ 使用者精读 config.py 中的 get_boundary_skip_layers_get_full_attention_layer_indices,理解混合模型识别机制;如需在新架构上启用 TQ,需对应扩展该函数。平台层面的 LCM 页面大小设计值得参考。

讨论亮点
  1. 简化边界逻辑:mgoin 建议将 arg_utils.py 中的混合判断与跳过层生成全部放到 TurboQuantConfig.get_boundary_skip_layers 中,作者 JartX 采纳。
  2. 注意力后端选择:mgoin 质疑为何在 cuda.py 中特化处理 TQ 后端选择,JartX 解释是 RDNA 场景需要,最终移除了该特化,改为依赖统一的后端注册机制。
  3. kv_cache_utils 改动争议:mgoin 要求不要在 kv_cache_utils.py 中做侵入式修改以免影响其他路径,vibhavagarwal5 同意仅合并混合模型相关修复,作者随后还原了该文件的大幅改动。
  4. 性能验证:多位贡献者(huangzhilin-hzl, vibhavagarwal5, jhsmith409, webcodes-cz)在 H20、RTX 6000 Pro Blackwell、RTX 5090、A4000 等硬件上提供了 throughput 和 accuracy 数据,确认无回归且显著提升 KV 缓存容量。

实现拆解

  1. 配置层扩展:修改 TurboQuantConfig.get_boundary_skip_layers 签名,接受 ModelConfig 而非 num_layers。对于混合模型 (is_hybrid=True),调用新增的 _get_full_attention_layer_indices 获取全注意力层全局索引并返回空跳过列表(边界保护对混合层数少的模型不适用);对于稠密模型,行为不变(跳过首尾各 2 层)。该函数统一处理三种混合模型约定:layer_types(Qwen3.5/Next)、layers_block_type(Jamba/Zamba2)、attn_type_list(MiniMax)。
  2. 引擎配置移除混合禁用:在 vllm/engine/arg_utils.py 中移除对混合模型的 raise NotImplementedError,改为直接调用 TurboQuantConfig.get_boundary_skip_layers(model_config),并简化跳过层合并逻辑。
  3. 平台页面大小适配:在 vllm/platforms/interface.py_align_hybrid_block_size 中新增 turboquant_ 分支,使用 TQFullAttentionSpec 计算 TQ 专有的页面大小。当存在跳过层时,使用 lcm(tq_page, skip_page) 统一页面大小,确保合并器能正确对齐。
  4. 测试覆盖:新增 TestHybridAttentionIndices 类,验证 _get_full_attention_layer_indices 对三种混合约定的正确性;同时调整原有边界保护测试以使用新的 _dense_model_config 辅助构造 Mock 配置。
文件 模块 状态 重要度
vllm/model_executor/layers/quantization/turboquant/config.py 量化配置 modified 8.2
tests/quantization/test_turboquant.py 测试 modified 6.87
vllm/engine/arg_utils.py 引擎配置 modified 6.8
vllm/platforms/interface.py 平台接口 modified 6.64

关键符号

TurboQuantConfig.get_boundary_skip_layers TurboQuantConfig.from_cache_dtype _get_full_attention_layer_indices _dense_model_config _fake_model_config TestHybridAttentionIndices.test_layer_types_full_attention TestHybridAttentionIndices.test_layers_block_type_jamba TestHybridAttentionIndices.test_attn_type_list_minimax TestHybridAttentionIndices.test_no_hybrid_hints_returns_empty

关键源码片段

vllm/platforms/interface.py dependency-wiring

在 `_align_hybrid_block_size` 中新增 TQ 分支,使用 `TQFullAttentionSpec` 计算页面大小,并通过 LCM 统一 TQ 层与跳过层的页面大小,确保混合模型缓存分配正确。

# interface.py 片段:混合模型中 TurboQuant 的页面大小对齐
# (位于 _align_hybrid_block_size 方法内)elif cache_config.cache_dtype.startswith("turboquant_"):
    # TQ 使用紧凑的 Key-Value 打包布局,标准 FullAttentionSpec 公式
    # 会过度估算大小,导致 unify_kv_cache_spec_page_size 断言失败。
    # 当同时存在跳过层(标准布局)时,取两者的最小公倍数(LCM)
    # 以确保所有注意力层页面大小统一。
    tq_cfg = TurboQuantConfig.from_cache_dtype(
        cache_config.cache_dtype, model_config.get_head_size()
    )
    tq_page = TQFullAttentionSpec(
        block_size=1,
        num_kv_heads=model_config.get_num_kv_heads(parallel_config),
        head_size=model_config.get_head_size(),
        head_size_v=model_config.get_head_size(),
        dtype=kv_cache_dtype,
        kv_quant_mode=kv_quant_mode,
        tq_slot_size=tq_cfg.slot_size_aligned,
    ).page_size_bytes
​
    if cache_config.kv_cache_dtype_skip_layers:
        skip_page = FullAttentionSpec(
            block_size=1,
            num_kv_heads=model_config.get_num_kv_heads(parallel_config),
            head_size=model_config.get_head_size(),
            dtype=model_config.dtype, # 跳过层使用未量化 dtype
        ).page_size_bytes
        # 使用 LCM 而非 max:skip_page 通常不是 tq_page 的整数倍,
        # max 会导致下游无法统一页面大小。
        attn_page_size_1_token = lcm(tq_page, skip_page)
    else:
        attn_page_size_1_token = tq_page

评论区精华

简化边界保护逻辑 设计

mgoin 建议将 arg_utils.py 中的混合模型判断和边界层生成全部放到 TurboQuantConfig.get_boundary_skip_layers 中,减少引擎侧的逻辑。

结论:JartX 采纳,将 Hybrid 判断移到 get_boundary_skip_layers,arg_utils 只负责调用和合并。 · 已解决

注意力后端选择特化 设计

mgoin 质疑在 cuda.py 中为 TQ 跳过层强制设置后端的做法,认为应已有通用机制处理。

结论:JartX 移除了该特化代码,依赖后端注册流程。 · 已解决

kv_cache_utils 改动范围 正确性

mgoin 要求不修改 kv_cache_utils.py 以免影响其他路径,vibhavagarwal5 建议仅合并混合模型必要修复。

结论:JartX 还原了 kv_cache_utils 的大幅改动,将 LCM 逻辑移入 platforms/interface.py。 · 已解决

性能与精度验证 性能

多位贡献者提供了 H20、RTX 6000 Pro Blackwell、RTX 5090、A4000 上的 throughput 和 accuracy 数据,确认混合模型无损且 KV 容量显著提升。

结论:验证通过,混合模型基线保持,显存压缩 2-4x。 · 已解决

风险与影响

  1. 全注意力层索引可能遗漏_get_full_attention_layer_indices 目前覆盖三种主流约定,但未来新架构可能使用不同的字段名,若未扩展则会抛出 NotImplementedError(还原为之前行为)或静默返回空列表(导致 TQ 完全不会启用,用户无提示)。
  2. 页面大小 LCM 可能膨胀lcm(tq_page, skip_page) 当两个页面大小互质时可能产生较大值,可能导致 Mamba 块 padding 效率下降,但实际测试中未发现显著问题。
  3. ROCm 兼容性:为 flash_attn_varlen_func 添加轻量包装仅解决已知不兼容点,其他 flash-attn 路径(如 sliding window)可能仍有隐藏问题,但 PR 中未涉及。
  4. 边界保护移除:混合模型放弃了首尾层边界保护,基准测试中准确性未下降,但在极端低比特(3bit)下可能仍有风险,目前缺乏针对混合模型的 ablation 实验。

用户:Qwen3.5、Jamba、MiniMax 等混合模型现在可以使用 --kv-cache-dtype turboquant_* 进行 KV 缓存量化,大幅降低显存占用(实测可达 3-4 倍压缩)。
系统:无 API 变更,纯注意力模型行为完全一致。扩展了 PlatformInterface._align_hybrid_block_size 的页面大小计算逻辑,但对非 TQ 路径无影响。
团队:新增的 _get_full_attention_layer_indices 提供了集中处理混合模型约定的模式,便于未来扩展。

混合模型约定覆盖不全 页面大小 LCM 可能膨胀 ROCm 兼容性未全面验证 边界保护移除缺乏 ablation

关联 Issue

未识别关联 Issue

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

完整报告

参与讨论