Prhub

#55283 [Doc] Clarify ITL vs TPOT Prometheus metrics

原始 PR 作者 ItsRoy69 合并时间 2026-09-15 14:35 文件变更 2 提交数 244 评论 11 代码增减 +186 / -5

执行摘要

修正 ITL/TPOT 指标文档并为 Grafana 新增 TPOT 面板

Issue #55276 指出文档把 vllm:inter_token_latency_seconds 描述为 'Inter-token latency (Time Per Output Token, TPOT) in seconds' 与 benchmark CLI 文档的术语体系冲突:ITL 是相邻流式输出之间的间隔,而 TPOT 是每个请求结束时按 (end-to-end latency - TTFT) / (number of output tokens - 1) 记录一次。Issue 还强调两点差异会被实际观测到——一次输出携带多个 token(如投机解码)时 Mean ITL 约为 TPOT 乘以每次输出 token 数;以及 token-weighted TPOT 与 mean request TPOT 的聚合权重不同会给出不同数值。PR body 直接写明 Resolves #55276,并明确 'This is a documentation-only change; no runtime code was modified'。

  • 值得精读(约 5 分钟):负责延迟监控、SLO 定义或性能回归看板的人应重点读 docs/design/metrics.md 新增的 ITL/TPOT 对比段落,它解释了为什么两个『看起来都是 TPOT』的指标在投机解码下会差数倍。
  • 可以跳过:grafana.json 的 167 行新增大部分是面板样板(fieldConfig、options、thresholds),只有 targets 里的 5 条 PromQL 值得复制到自建监控,尤其是 rate(_sum)/rate(_count) 这种 request-weighted mean 写法。
  • 值得关注的设计决策:(1) 选择『新增面板 + 保留旧面板』而非重命名替换,保持向后兼容;(2) 定义统一指向 docs/benchmarking/cli.md,避免同一概念在多处文档各写一套;(3) 把『单 token 记为 0、bench 会排除』直接写进面板 description,把口径差异前置给使用者而不是藏在文档角落。
  • 建议跟进:在 metrics.md 补充 token-weighted TPOT 的 PromQL 并标注『不推荐/仅供 debug』,以及直方图 bucket 对 P99 精度的影响;这些是 Issue #55276 提出但本 PR 未闭环的部分。
讨论亮点
  • 维护者要求视觉验证:DarkLight1337 首条评论即问 "Could you provide a screenshot of what the dashboard looks like before vs after your changes?"。ItsRoy69 随后贴出前后两张截图,确认新增 TPOT 面板与既有 ITL 面板并排、无布局冲突——这是该 JSON 变更唯一可行的验证手段,也解释了为什么最终要 maintainer 人工 APPROVED。

定义来源对齐:DarkLight1337 在 docs/design/metrics.md 行内评论建议 "Maybe we could just copy the definitions from docs/benchmarking/cli.md?"。ItsRoy69 回复 "Good suggestion. I've aligned the metric definitions with the 'Understanding the latency metrics' section in docs/benchmarking/cli.md",并把早期版本自创的措辞 'Inter-output latency in seconds' 收敛为 'Inter-token latency in seconds, measured between consecutive streamed outputs',避免文档间术语漂移。

静态分析补充的单 token 语义:coderabbitai[bot] 认为需要写明 vllm:request_time_per_output_token_seconds 在 num_generation_tokens <= 1 时记为 0,并限定与 vllm bench serve 的等价性(bench 会把这些请求排除在 TPOT 统计之外)。该意见最终落地在 metrics.md 说明段落与 grafana.json 面板 description 中,属已解决项,但也是评审中最有信息量的一条。

  • 最终结论:DarkLight1337 以 "Sorry missed your latest comment" 收尾并 APPROVED,讨论未留下阻塞项;claude[bot] 因 fork 分支禁用自动 review,未产生额外结论。

实现拆解

1. 修正文档中的 ITL 定义(docs/design/metrics.md)

  • 在 v1 指标总览列表里,把 vllm:inter_token_latency_seconds 的描述从笼统的 'Inter-token latency' 细化为 'Inter-token latency (time between consecutive streamed outputs)',明确采样粒度是「流式输出事件之间」而非「token 之间」。
  • 在 Grafana Dashboard 指标子集小节中,删掉原有的 '(Time Per Output Token, TPOT)' 后缀,这正是 Issue #55276 指出的错误标注。
  • 把文末迭代流程说明里的 'The inter-token intervals (Time Per Output Token, TPOT)' 改回 'The inter-token intervals',避免同一文档内两处口径互相矛盾。

2. 新增真正的 TPOT 指标条目与解释段落(docs/design/metrics.md)

  • 新增 vllm:request_time_per_output_token_seconds 条目,写明计算口径 (end-to-end latency - TTFT) / (number of output tokens - 1),并注明单 token 请求记为 0。
  • 新增一段说明:两个指标在「一次输出携带多个 token」(例如投机解码)或「mean ITL 与 mean per-request TPOT 聚合权重不同」时会出现数值差异;并指出 vllm bench serve 会排除单 token 请求,因此 Prometheus 直方图统计与 benchmark TPOT 统计不可直接等同,最后链接到 docs/benchmarking/cli.md#understanding-the-latency-metrics 作为唯一定义来源。

3. 示例 Grafana 面板新增 TPOT 视图(examples/observability/prometheus_grafana/grafana.json,+167/-1)

  • 改写既有 'Inter Token Latency' 面板的 description,说明它衡量的是连续流式输出之间的间隔,仅在「一次输出一个 token」时近似 TPOT。
  • 新增 'Time Per Output Token (TPOT)' 时间序列面板:id 为 17,gridPos 为 {h:8, w:12, x:12, y:48}(与既有 ITL 面板并排),单位 s,数据源全部指向 vllm:request_time_per_output_token_seconds。
  • 面板包含 5 条 target:P99/P95/P90/P50 使用 histogram_quantile(q, sum by(le) (rate(..._bucket[$__rate_interval])));Mean 使用 rate(..._sum) / rate(..._count),即按请求加权的平均 TPOT。

4. 验证与配套

  • 无运行时代码、无指标定义或采集路径改动(vllm/v1/metrics/loggers.py、vllm/v1/metrics/stats.py 未触碰)。
  • 作者用 python -c "import json; json.load(open('examples/observability/prometheus_grafana/grafana.json'))" 校验 dashboard JSON;CI 侧只有文档构建(label build-docs、Read the Docs 预览 https://vllm--55283.org.readthedocs.build/en/55283/)。
  • 无新增测试:Grafana 面板语义不在现有测试覆盖范围内,PR body 按 AGENTS.md 说明跳过 model eval。
  • 演进上,作者侧共有 3 个内容 commit(初版文档 → 'fixing documentation in metrics.md' → 'Align ITL and TPOT metric definitions with benchmark docs'),期间多次 merge main,branch 上的 244 个 commit 主要是主线同步带来的噪声。
文件 模块 状态 重要度
examples/observability/prometheus_grafana/grafana.json 监控面板 modified 5.28
docs/design/metrics.md 指标文档 modified 2.84

关键源码片段

examples/observability/prometheus_grafana/grafana.json configuration

新增 'Time Per Output Token (TPOT)' 面板并改写 ITL 面板描述,是本次唯一带可执行内容的变更(5 条 PromQL target),也是社区复制参考监控栈的直接来源。

// examples/observability/prometheus_grafana/grafana.json
// 新增面板:Time Per Output Token (TPOT),与既有 Inter Token Latency 面板并排展示
{
  "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" },

  // 面板描述显式写明口径:每个请求结束时记录一次,即 decode 时间除以(输出 token 数 - 1)
  // 并前置提示单 token 请求记为 0、vllm bench serve 会把这些请求排除在 TPOT 统计之外
  "description": "Per-request Time Per Output Token (TPOT) in seconds, computed as (end-to-end latency - TTFT) divided by the number of output tokens minus one. Requests with no more than one output token are recorded as zero; vllm bench serve excludes these requests from its TPOT statistics.",

  // 其余样式字段(fieldConfig、options、thresholds 等)与既有 ITL 面板保持一致,此处省略
  "fieldConfig": { "defaults": { "unit": "s" }, "overrides": [] },

  // x = 12 表示放在右半屏,y = 48 表示紧跟在上一行之后,避免与既有面板重叠
  "gridPos": { "h": 8, "w": 12, "x": 12, "y": 48 },
  "id": 17,

  "targets": [
    // P99:先对 bucket 做 rate,再按 le 聚合,最后取直方图分位
    {
      "expr": "histogram_quantile(0.99, sum by(le) (rate(vllm:request_time_per_output_token_seconds_bucket{model_name=\"$model_name\"}[$__rate_interval])))",
      "legendFormat": "P99",
      "refId": "A"
    },
    // P95 / P90 / P50 复用同一条查询骨架,仅替换分位参数,这里省略重复项
    {
      "expr": "histogram_quantile(0.5, sum by(le) (rate(vllm:request_time_per_output_token_seconds_bucket{model_name=\"$model_name\"}[$__rate_interval])))",
      "legendFormat": "P50",
      "refId": "D"
    },
    // Mean 使用 request-weighted 平均:sum / count 的 rate 比值,相当于每个请求先算 TPOT 再取均值
    {
      "editorMode": "code",
      "expr": "rate(vllm:request_time_per_output_token_seconds_sum{model_name=\"$model_name\"}[$__rate_interval])\n/\nrate(vllm:request_time_per_output_token_seconds_count{model_name=\"$model_name\"}[$__rate_interval])",
      "legendFormat": "Mean",
      "refId": "E"
    }
  ],

  "title": "Time Per Output Token (TPOT)",
  "type": "timeseries"
}

评论区精华

ITL/TPOT 定义与 benchmark 文档对齐 documentation

DarkLight1337 在 docs/design/metrics.md 的行内评论提出 "Maybe we could just copy the definitions from `docs/benchmarking/cli.md`?",认为不应在 metrics 文档里自创一套 ITL/TPOT 措辞;作者早期版本使用的是 'Inter-output latency in seconds'。

结论:作者将两个指标的定义改写为与 `docs/benchmarking/cli.md#understanding-the-latency-metrics` 一致的表述,并把 ITL 描述收敛为 'Inter-token latency in seconds, measured between consecutive streamed outputs',随后获得 maintainer APPROVED。 · 已解决

单 token 请求 TPOT 记为 0 与 bench 统计差异 正确性

coderabbitai[bot] 通过静态分析(检索 stats.py / loggers.py / benchmarks serve.py)指出:文档需写明 `vllm:request_time_per_output_token_seconds` 在 `num_generation_tokens <= 1` 时记为 0,并限定与 `vllm bench serve` 的等价性,因为 bench 会把这些请求排除在 TPOT 统计之外;同一意见要求同步到 grafana.json 的面板 description。

结论:最终 head 版本在 metrics.md 说明段落与面板 description 中都加入了单 token 记为 0、bench 会排除该请求的说明,意见已落地,但该差异仍是使用者最容易忽略的点。 · 已解决

Dashboard 变更的视觉验证方式 设计

DarkLight1337 要求作者提供 'screenshot of what the dashboard looks like before vs after your changes',因为 Grafana JSON 无法通过单元测试验证面板布局与 id 唯一性。

结论:ItsRoy69 贴出前后两张截图,确认新增的 TPOT 面板落在右半屏(x=12, y=48)且与既有 ITL 面板并排无冲突,随后 maintainer 批准合并。 · 已解决

风险与影响

  1. 无运行时风险:PR 未触碰 Python/CUDA 代码,vllm:inter_token_latency_seconds 与 vllm:request_time_per_output_token_seconds 的采集逻辑保持原样,不影响 serving、精度或性能。
  2. 观测语义仍有残余歧义:指标名依旧是 inter_token_latency_seconds,在投机解码下 Mean ITL ≈ TPOT × 每次 decode 输出的平均 token 数;已有告警和第三方看板若沿用旧读法不会被自动纠正,只能靠读者看到新描述。
  3. 直方图分位精度依赖 bucket 边界:新面板的 P50/P90/P95/P99 走 histogram_quantile,默认 bucket 下高百分位可能因 +Inf 桶内插值而失真。同周期合入主干的 #48866(统一 Prometheus 直方图 bucket 默认值)说明这是活跃关注点,但本 PR 未提及 bucket 配置。
  4. 面板唯一性与布局只能靠人眼确认:新增面板 id=17、gridPos {x:12, y:48},Grafana 导入时若与既有面板 id 冲突会出现面板丢失或错位;验证手段仅 JSON 合法性校验 + 截图,没有自动化回归。
  5. Issue 中的关键问题未完全闭环:Issue #55276 明确问『哪种 PromQL 才是生产环境推荐的 canonical TPOT』,给出 request-weighted、token-weighted、mean ITL 三种候选。本 PR 只落地了 request-weighted mean 面板,token-weighted 公式(request_decode_time_seconds / (request_generation_tokens - count))以及『哪个指标对应 vllm bench serve 的百分位 TPOT』仍未在文档中给出明确推荐。
  • 对用户/运维:任何基于官方参考 dashboard 搭建监控的团队会新增一个语义正确的 TPOT 面板;此前把 Inter Token Latency 当 TPOT 使用的人能读到纠偏说明。影响面广(所有用 Prometheus 监控 vLLM 的人)但强度低——不改变任何已有指标数值,只改变读法。
  • 对 benchmark 使用者:文档首次明确 Prometheus 直方图与 vllm bench serve 的 TPOT 口径差异(后者排除单 token 请求,前者记为 0),可减少『线上监控和压测结果对不上』的排查成本。
  • 对系统/部署:无影响。Grafana JSON 需要重新导入才生效,已导入旧版 dashboard 的实例不会自动更新,也不涉及任何配置项或接口兼容性变化。
  • 对团队:改动集中在 docs/design 与 examples/observability,review 成本低(1 位 maintainer 批准即可),但把 token-weighted TPOT 等缺口留给了后续 PR。
无运行时风险 无自动化测试覆盖 token-weighted TPOT 未文档化 面板 id/ 布局依赖人工确认

关联 Issue

#55276 [Doc]: Clarify ITL vs. TPOT Prometheus metrics

完整报告

参与讨论