Prhub

#46634 [Perf][1/N] Expand Triton kernel warmup coverage, DSv4

原始 PR 作者 LopezCastroRoberto 合并时间 2026-06-30 00:40 文件变更 3 提交数 15 评论 4 代码增减 +386 / -0

执行摘要

扩展 Triton kernel warmup 覆盖,支持 DSv4 稀疏 MLA 和 block table

来源于代码注释('Warmup kernels used during model execution. This is useful specifically for JIT'ed kernels as we don't want JIT'ing to happen during model execution.')。DSv4 模型使用了多种自定义 Triton kernel,首次请求时 JIT 编译耗时显著,通过预先调用典型参数组合进行预热可消除此开销。关联 Issue 未提供,但社区有性能优化需求。

值得精读。该 PR 展示了如何系统地为 Triton JIT kernel 添加启动预热,包含 backend 检测、参数空间枚举、条件过滤等模式。特别关注 sparse_mla_triton_warmup.py 中的 backend 识别和参数组合设计,以及审核中关于 MRv1/MRv2 差异的处理。可作为后续模型特定预热功能开发的参考。

讨论亮点

审核中 LucasWilkinson 询问两个问题:

1) 'why is this needed?'(为何需要 block table 预热);
2) 'does this apply to both MRV1 and MRV2?'(是否同时适用于模型运行器 v1 和 v2)。作者 LopezCastroRoberto 答复 'No, just MRV1',后修改代码添加了 not worker.use_v2_model_runner 条件。Lucas 最终批准并注明 'NOTE to community / reviewers this a temp fix while we work on a more robust solution',表明当前方案为临时,后续将替换为更鲁棒方案。

实现拆解

实现拆解:

  1. 新增 sparse MLA 预热模块:创建 sparse_mla_triton_warmup.py,定义专用 backend 集合(_DEEPSEEK_V4_SPARSE_MLA_BACKENDS 等),检测当前模型是否使用对应 backend,若是则使用典型预填充长度、压缩率等参数组合运行 _warm_sparse_swa_prefill_metadata_kernel_warm_prefill_chunk_metadata_kernel_warm_combine_topk_swa_indices_kernel,触发 Triton JIT 编译并缓存。
  2. 新增 block table 预热模块:创建 v1_block_table_warmup.py,定义 warm_v1_block_table_kernels 函数,使用固定 token 数(8)和典型 block 大小(3,16)创建 BlockTable 实例并调用 compute_slot_mapping,预热 slot mapping kernel。
  3. 集成到统一预热入口:在 kernel_warmup.py 中新增导入和条件调用:仅在 worker.use_v2_model_runner 为 False(即 v1 模型运行器)且非 pooling 模型时执行 block table 预热;在原有 sparse MLA 相关预热之前插入 sparse_mla_triton_warmup_if_needed
  4. 无测试配套:本次变更未新增测试,但改动处于预热路径,不直接影响推理正确性。
文件 模块 状态 重要度
vllm/model_executor/warmup/sparse_mla_triton_warmup.py 稀疏 MLA 预热 added 9.28
vllm/model_executor/warmup/v1_block_table_warmup.py 块表预热 added 7.24
vllm/model_executor/warmup/kernel_warmup.py 预热调度 modified 5.8

关键符号

sparse_mla_triton_warmup_if_needed warm_v1_block_table_kernels _warm_sparse_swa_prefill_metadata_kernel _warm_prefill_chunk_metadata_kernel _warm_combine_topk_swa_indices_kernel _has_attention_backend

关键源码片段

vllm/model_executor/warmup/sparse_mla_triton_warmup.py core-logic

核心新增文件,负责检测 DSv4 稀疏 MLA 后端并预热元数据 kernel。定义了关键常量和多个 warmup 函数。

# SPDX-License-Identifier: Apache-2.0
"""Warm up sparse-MLA Triton metadata kernels."""from typing import TYPE_CHECKING
import torch
from vllm.logger import init_loggerif TYPE_CHECKING:
    from vllm.v1.worker.gpu_model_runner import GPUModelRunner
    from vllm.v1.worker.gpu_worker import Workerlogger = init_logger(__name__)# 定义需要预热的 DSv4 专用 sparse MLA backend 名称集合
_DEEPSEEK_V4_SPARSE_MLA_BACKENDS = frozenset({
    "FLASHMLA_SPARSE_DSV4",
    "FLASHINFER_MLA_SPARSE_DSV4",
    "ROCM_FLASHMLA_SPARSE_DSV4",
    "DEEPSEEK_SPARSE_SWA",
})
# 通用 sparse MLA backend(非 DSv4)
_GENERIC_SPARSE_MLA_BACKENDS = frozenset({
    "FLASHMLA_SPARSE",
    "FLASHINFER_MLA_SPARSE",
    "FLASHINFER_MLA_SPARSE_SM120",
})# 预热参数组合:预填充数量、解码数量、压缩比等
_SPARSE_PREFILL_METADATA_NUM_PREFILLS = (1, 2, 4, 8)
_SPARSE_PREFILL_METADATA_NUM_DECODES = (0, 1, 2)
_DSV4_PREFILL_CHUNK_METADATA_COMPRESS_RATIOS = (4, 128)def _next_power_of_2(x: int) -> int:
    # 计算大于等于 x 的最小 2 的幂
    return 1 << (x - 1).bit_length()def _warm_sparse_swa_prefill_metadata_kernel(
    device: torch.device,
    window_size: int,
    prefill_tokens: int,
) -> None:
    from vllm.v1.attention.backends.mla.sparse_swa import _compute_prefill_metadata_kernel
​
    # 枚举预填充和解码个数的组合,触发 kernel 编译
    for num_prefills in _SPARSE_PREFILL_METADATA_NUM_PREFILLS:
        for num_decodes in _SPARSE_PREFILL_METADATA_NUM_DECODES:
            query_lens = [1] * num_decodes
            query_lens += [prefill_tokens] * num_prefills
            # 手动累积构造 query_start_loc
            query_start_locs = [0]
            for q_len in query_lens:
                query_start_locs.append(query_start_locs[-1] + q_len)
            query_start_loc = torch.tensor(
                query_start_locs, dtype=torch.int32, device=device
            )
            seq_lens = torch.tensor(
                [1] * num_decodes + [window_size + q for q in query_lens[num_decodes:]],
                dtype=torch.int32, device=device
            )
            prefill_gather_lens = torch.empty(num_prefills, dtype=torch.int32, device=device)
            # 调用 Triton kernel(仅编译,无实际作用)
            _compute_prefill_metadata_kernel[(1,)](
                prefill_gather_lens,
                seq_lens,
                query_start_loc,
                num_prefills,
                num_decodes,
                window_size,
                BLOCK_SIZE=_next_power_of_2(num_prefills),
            )

(其他类似函数如 _warm_prefill_chunk_metadata_kernel 遵循相同模式)

vllm/model_executor/warmup/v1_block_table_warmup.py core-logic

新增 block table slot mapping 预热模块,确保 v1 模型运行器首次请求时不触发 JIT。

# SPDX-License-Identifier: Apache-2.0
"""Warm up v1 block-table Triton kernels."""import torch# 预热用的固定 token 数
_SLOT_MAPPING_WARMUP_TOKENS = 8
# 预热覆盖的 block 大小组合
_SLOT_MAPPING_WARMUP_BLOCK_SIZES = (3, 16)
# CPU-KV-cache interleave 大小(v1 固定值)
_SLOT_MAPPING_WARMUP_CP_KV_CACHE_INTERLEAVE_SIZE = 1
​
​
def warm_v1_block_table_kernels(
    device: torch.device,
    max_tokens: int,
) -> None:
    """预热 v1 block table 的 slot mapping kernel。    参数:
        device: CUDA 设备
        max_tokens: 最大批处理 token 数,用于计算 block 数
    """
    from vllm.v1.worker.block_table import BlockTable
​
    # 限制预热 token 数不超过 max_tokens
    num_tokens = max(0, min(_SLOT_MAPPING_WARMUP_TOKENS, max_tokens))
    if num_tokens <= 0:
        return
​
    query_start_loc = torch.tensor([0, num_tokens], dtype=torch.int32, device=device)
    positions = torch.arange(num_tokens, dtype=torch.int64, device=device)
​
    # 分别用两种 block size 预热
    for block_size in _SLOT_MAPPING_WARMUP_BLOCK_SIZES:
        max_num_blocks_per_req = max(
            1, (max(num_tokens, max_tokens) + block_size - 1) // block_size
        )
        # 对齐到 16 的倍数
        max_num_blocks_per_req = ((max_num_blocks_per_req + 15) // 16) * 16
        block_table = BlockTable(
            block_size=block_size,
            max_num_reqs=1,
            max_num_blocks_per_req=max_num_blocks_per_req,
            max_num_batched_tokens=max(num_tokens, max_tokens),
            pin_memory=False,
            device=device,
            kernel_block_size=block_size,
            cp_kv_cache_interleave_size=_SLOT_MAPPING_WARMUP_CP_KV_CACHE_INTERLEAVE_SIZE,
        )
        block_table.add_row(list(range(max_num_blocks_per_req)), 0)
        block_table.commit_block_table(1)
        block_table.compute_slot_mapping(1, query_start_loc, positions)
        # compute_slot_mapping 触发 Triton kernel 编译,预热完成

评论区精华

block table warmup 的必要性 question

LucasWilkinson 询问 'why is this needed?',对新增 block table 预热的目的提出疑问。

结论:作者未直接回复,但最终代码保留了该预热,表明有必要。Lucas 在后续批准中确认了临时方案。 · 已解决

预热覆盖的模型运行器范围 正确性

LucasWilkinson 问 'does this apply to both MRV1 and MRV2?' 作者回复 'No, just MRV1'。随后代码被修改添加 `not worker.use_v2_model_runner` 条件。

结论:确认只适用于 MRv1,代码已修改。 · 已解决

风险与影响

  • 无测试覆盖:新增代码无配套测试,但预热路径为启动阶段且不影响推理结果,风险可控。
  • 条件分支可能遗漏:block table 预热依赖 use_v2_model_runneris_pooling_model 标志,若这些标志设置不当可能错误跳过预热(仅导致首次请求略慢,无功能问题)。
  • 参数未涵盖生产场景:预热使用的参数组合(如固定 token 数 8、block size 3 和 16)可能不完全匹配实际请求,但预热为尽力而为,不要求完全一致。
  • 临时方案稳定性:审核中指出当前方案为 temp fix,未来可能被重构,需留意后续 PR 的兼容性。
  • 用户影响:使用 DeepSeek V4 模型的用户将体验到首次推理延迟降低(数十毫秒级),其他模型用户无感知。
  • 系统影响:启动时增加约几十毫秒的预热编译时间,一旦预热后续无额外开销。
  • 团队影响:提供了一种为特定模型扩展 kernel warmup 的模板,便于后续为更多模型添加类似预热。同时也预留了后续重构空间。
无测试覆盖 临时方案待重构 条件分支可能遗漏 预热参数与生产不完全匹配

关联 Issue

未识别关联 Issue

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

完整报告

参与讨论