Prhub

#51389 [Profiler] Stamp vLLM version/commit into torch profiler trace metadata

原始 PR 作者 elvircrn 合并时间 2026-08-08 04:29 文件变更 1 提交数 4 评论 8 代码增减 +32 / -0

执行摘要

profiler trace 内嵌 vLLM 版本与 commit 元数据

PR body 指出:对于 source 安装,setuptools_scm 版本字符串内嵌 git commit hash,因此把版本写入 trace 元数据就等于记录“哪次 commit 产生了这份 trace”;trace 消费方可以把 with_stack=True 时 Call stack 帧里的 file:line 精确对应到具体的 vLLM 检出版本,而不是靠猜测。这是 profiling 可复现性与 trace 归档可追溯性的直接诉求。

值得快速精读,核心看点有二:一是 torch profiler schedule 与 Kineto 初始化时序的坑(WAIT 阶段 add_metadata_json 静默丢弃),二是“一次性标记 + 双调用点 + 容错”的幂等注入模式,可用作在框架回调中安全注入元数据的参考实现。若团队深度依赖 torch profiler 做性能分析与 trace 归档,该 PR 价值会更高;由于无自动化测试,建议后续补一个 vllm/profiler 的单元或 e2e 回归用例。

讨论亮点

核心讨论围绕 schedule 场景下元数据被打点失败展开:

  • tlrmchlsmth 在 vllm/profiler/wrapper.py 第 278 行评论指出:当 wait_iterations > 0 时,profile.start() 使 Kineto 处于 ProfilerAction.NONE,PyTorch 会静默跳过 add_metadata_json(提示 “Profiler is not initialized”),建议推迟到 wait 阶段结束后再打元数据。
  • elvircrn 回复确认 root cause 并给出修复:WAIT 阶段 Kineto 未初始化,add_metadata_json 被静默丢弃;修复方案为从 _start() 与每次 _profiler_step() 尝试打戳,用一次性标记保证恰好打一次,且在调度导出的 RECORD_AND_SAVE 之前完成;并通过隔离的 torch 测试验证了 WAIT 阶段 profile.profiler 为 None、WARMUP 后变为非 None,同时端到端验证导出发生在 step() 而非 stop()。
  • 作者还补充了一个作用域说明:vllm/v1/engine/async_llm.py 里的 AsyncLLM 前端 profiler 是独立的 torch profiler 实例,不在本次改动覆盖范围内(评论原文被截断)。

实现拆解

  1. 变更入口与导入:唯一改动文件 vllm/profiler/wrapper.py,新增 import json 与 import vllm.version,为后续 JSON 编码与版本读取做准备。
  2. 状态标记初始化:在 TorchProfilerWrapper.__init__ 末尾新增 self._version_metadata_added = False,作为一次性打点标记,保证整个 profiling 会话只写一次元数据。
  3. 核心方法 _maybe_add_version_metadata:先检查 _version_metadata_added 是否已置位;再检查 self.profiler.profiler is None(该属性为 None 说明 schedule 仍处于 WAIT 阶段、Kineto 尚未初始化);随后通过 add_metadata_json 写入 vllm_version 与 vllm_version_tuple 两个键,值经 json.dumps 编码;整个写入包在 try/except 中,失败只记 warning,且无论成败都置位标记,避免每个 step 重复尝试。
  4. 双调用点接入:在 _start() 中 self.profiler.start() 之后立即调用(无 schedule 场景 Kineto 立即 live,可马上打点);在 _profiler_step() 中 self.profiler.step() 之后调用(有 schedule 场景,WAIT 阶段结束后 Kineto live,且此时打点能赶上 RECORD_AND_SAVE 的 trace 导出)。作者明确说明不在 _stop() 打点的原因:schedule 场景的导出发生在 step() 期间的 RECORD_AND_SAVE 阶段,stop() 时导出已发生,会错过。
  5. 测试配套:无自动化测试文件变更;作者用 CPU 构建(VLLM_TARGET_DEVICE=cpu)+ vllm serve facebook/opt-125m + --profiler-config wait=1, warmup=1, active=3,驱动 POST /start_profile → /v1/completions → POST /stop_profile,在 worker trace 顶层 metadata 中确认出现 vllm_version : 0.1.dev2+gf393622a1.d20260807 与对应的 vllm_version_tuple。
文件 模块 状态 重要度
vllm/profiler/wrapper.py 性能分析 modified 6.27

关键符号

_maybe_add_version_metadata _start _profiler_step

关键源码片段

vllm/profiler/wrapper.py core-logic

唯一改动文件,在 TorchProfilerWrapper 中新增 `_maybe_add_version_metadata` 方法,并在 `_start()` 与 `_profiler_step()` 两个调用点接入,实现 vLLM 版本元数据写入;同时新增 `json` 与 `vllm.version` 导入及一次性标记状态。

# vllm/profiler/wrapper.py(TorchProfilerWrapper 关键片段)
​
    def _maybe_add_version_metadata(self) -> None:
        """把 vLLM 版本(内含 git commit)写入 trace 元数据。
​
        add_metadata_json 在 Kineto 初始化前是 no-op;带 schedule 时,
        WAIT 阶段 Kineto 尚未启动,因此要等它 live 后再打戳。
        """
        if self._version_metadata_added:
            return
        # profiler.profiler 为 None 说明还在 WAIT 阶段,Kineto 未初始化。
        if self.profiler.profiler is None:
            return
        try:
            self.profiler.add_metadata_json(
                "vllm_version", json.dumps(vllm.version.__version__)
            )
            self.profiler.add_metadata_json(
                "vllm_version_tuple",
                json.dumps([str(p) for p in vllm.version.__version_tuple__]),
            )
        except Exception as e:
            logger.warning("Failed to add vLLM version to profiler metadata: %s", e)
        # 失败也置位,避免每个 step 都重试。
        self._version_metadata_added = True
​
    @override
    def _start(self) -> None:
        self.profiler.start()
        # 无 schedule 时 Kineto 立即生效;有 schedule 时这里 no-op,
        # 由 _profiler_step 在 WAIT 结束后接续打戳。
        self._maybe_add_version_metadata()
​
    @override
    def _profiler_step(self) -> bool:
        """调用 profiler.step() 推进 schedule 并返回该 step 是否有效。"""
        if self._uses_schedule:
            self.profiler.step()
            # 离开 WAIT 后 Kineto live,此时打戳能赶上 RECORD_AND_SAVE 导出。
            self._maybe_add_version_metadata()
            # 只把 active 步计入 max_iterations,warmup 步数据会被丢弃。
            if self._warmup_steps_remaining > 0:
                self._warmup_steps_remaining -= 1
                return False
        return True

评论区精华

wait_iterations > 0 时 add_metadata_json 被静默丢弃 正确性

tlrmchlsmth 在 `vllm/profiler/wrapper.py` 第 278 行指出:当 `wait_iterations > 0` 时,`profile.start()` 使 Kineto 处于 `ProfilerAction.NONE`,PyTorch 会静默跳过 `add_metadata_json`(提示 “Profiler is not initialized”),建议推迟到 wait 阶段结束后。作者 elvircrn 回复已通过推迟到 Kineto live 后打戳解决,并从 `_start()` 与每个 `_profiler_step()` 双路径尝试、以一次性标记保证恰好执行一次,且验证了 `profile.profiler` 在 WAIT 阶段为 `None`、WARMUP 后变为非 `None`。

结论:采用 `_maybe_add_version_metadata` + `_version_metadata_added` 一次性标记的方案,在 `_start()` 与 `_profiler_step()` 中调用,确保 schedule 场景下元数据在 Kineto 初始化后、`RECORD_AND_SAVE` 导出前写入。 · 已解决

风险与影响

风险点集中在 vllm/profiler/wrapper.py 新增逻辑上:

  • 依赖 self.profiler.profiler 这一 torch 内部属性作为 Kineto 是否初始化的探针,属于非公开 API,torch 版本升级可能改变其语义或命名。
  • 依赖 add_metadata_json 在 Kineto 未初始化时静默 no-op 的现有语义,若未来 torch 改动该行为,元数据可能在不报错的情况下丢失。
  • 无自动化测试覆盖,该打点逻辑未来被重构或误删时难以被 CI 发现,回归只能依赖手工验证。
  • 缓解措施到位:整个写入过程有 try/except 容错 + 一次性标记,版本读取失败或打点失败都不会影响 profiling 主流程。

影响范围集中于使用 torch profiler 的性能分析与问题定位场景:

  • 用户侧:任何通过 --profiler-config 生成的 Chrome/Perfetto trace,顶层 metadata 会新增 vllm_version、vllm_version_tuple 两个键,现有 trace 消费工具兼容(新增键不破坏旧结构)。
  • 系统侧:无运行时性能影响,仅一次 JSON 编码与 metadata 写入;多 worker 场景下每个 rank 的 trace 各自携带版本信息。
  • 团队侧:提升了 profiling 可复现性,trace 可直接与代码 commit 对齐,为后续 trace 归档、跨版本对比工具链打下基础;改动影响程度低、增量式。
无自动化测试覆盖 依赖 torch 内部属性 profiler.profiler Kineto 初始化时序语义

关联 Issue

未识别关联 Issue

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

完整报告

参与讨论