跳到内容

指标

vLLM 暴露了丰富的指标,以支持 V1 引擎的可观测性和容量规划。

目标

  • 提供对引擎和请求级别指标的全面覆盖,以辅助生产监控。
  • 优先考虑 Prometheus 集成,因为这是我们期望在生产环境中使用的方案。
  • 为即时测试、调试、开发和探索性用例提供日志支持(即将指标打印到 info 日志中)。

背景

vLLM 中的指标可以分为以下几类:

  1. 服务器级别指标:跟踪 LLM 引擎状态和性能的全局指标。这些通常在 Prometheus 中作为 Gauge(测量器)或 Counter(计数器)暴露。
  2. 请求级别指标:跟踪单个请求特征(例如大小和耗时)的指标。这些通常在 Prometheus 中作为 Histogram(直方图)暴露,通常是监控 vLLM 的 SRE 将要跟踪的 SLO(服务水平目标)。

其心智模型是,服务器级别指标有助于解释请求级别指标的值。

指标概述

v1 指标

在 v1 中,一组丰富的指标通过兼容 Prometheus 的 /metrics 端点暴露,并使用 vllm: 前缀,例如:

  • vllm:num_requests_running (Gauge) - 当前正在运行的请求数量。
  • vllm:kv_cache_usage_perc (Gauge) - 已使用的 KV 缓存块比例 (0–1)。
  • vllm:prefix_cache_queries (Counter) - 前缀缓存查询次数。
  • vllm:prefix_cache_hits (Counter) - 前缀缓存命中次数。
  • vllm:prompt_tokens_total (Counter) - 处理的提示词 Token 总数。
  • vllm:generation_tokens_total (Counter) - 生成的 Token 总数。
  • vllm:request_success_total (Counter) - 已完成的请求数量(按结束原因分类)。
  • vllm:request_prompt_tokens (Histogram) - 输入提示词 Token 数量的直方图。
  • vllm:request_generation_tokens (Histogram) - 生成 Token 数量的直方图。
  • vllm:time_to_first_token_seconds (Histogram) - 首字延迟 (TTFT)。
  • vllm:inter_token_latency_seconds (Histogram) - 逐字延迟(Token 间延迟)。
  • vllm:e2e_request_latency_seconds (Histogram) - 端到端请求延迟。
  • vllm:request_prefill_time_seconds (Histogram) - 请求 Prefill(预填充)时间。
  • vllm:request_decode_time_seconds (Histogram) - 请求 Decode(解码)时间。

这些在 推理与服务 -> 生产环境指标 下有详细文档。

Grafana 仪表板

vLLM 还提供了一个 参考示例,说明如何使用 Prometheus 收集和存储这些指标,并使用 Grafana 仪表板进行可视化。

Grafana 仪表板中暴露的指标子集向我们展示了哪些指标尤为重要:

  • vllm:e2e_request_latency_seconds_bucket - 以秒为单位测量的端到端请求延迟。
  • vllm:prompt_tokens - 提示词 Token 数。
  • vllm:generation_tokens - 生成 Token 数。
  • vllm:inter_token_latency_seconds - 以秒为单位的逐字延迟(每个输出 Token 的时间,TPOT)。
  • vllm:time_to_first_token_seconds - 以秒为单位的首字延迟 (TTFT)。
  • vllm:num_requests_running(以及 _swapped_waiting)- 处于 RUNNING、WAITING 和 SWAPPED 状态的请求数量。
  • vllm:kv_cache_usage_perc - vLLM 已使用的缓存块百分比。
  • vllm:request_prompt_tokens - 请求提示词长度。
  • vllm:request_generation_tokens - 请求生成长度。
  • vllm:request_success - 按结束原因分类的已完成请求数量:生成了 EOS Token 或达到了最大序列长度。
  • vllm:request_queue_time_seconds - 队列时间。
  • vllm:request_prefill_time_seconds - 请求 Prefill 时间。
  • vllm:request_decode_time_seconds - 请求 Decode 时间。
  • vllm:request_max_num_generation_tokens - 序列组中的最大生成 Token 数。

有关此处的选择的有趣且有用的背景信息,请参阅 添加该仪表板的 PR

Prometheus 客户端库

最初是 使用 aioprometheus 库 添加了 Prometheus 支持,但很快就切换到了 prometheus_client。在链接的两个 PR 中都讨论了其基本原理。

在这些迁移过程中,我们短暂地丢失了用于追踪 HTTP 指标的 MetricsMiddleware,但它后来被 使用 prometheus_fastapi_instrumentator 重新引入。

$ curl http://0.0.0.0:8000/metrics 2>/dev/null  | grep -P '^http_(?!.*(_bucket|_created|_sum)).*'
http_requests_total{handler="/v1/completions",method="POST",status="2xx"} 201.0
http_request_size_bytes_count{handler="/v1/completions"} 201.0
http_response_size_bytes_count{handler="/v1/completions"} 201.0
http_request_duration_highr_seconds_count 201.0
http_request_duration_seconds_count{handler="/v1/completions",method="POST"} 201.0

多进程模式

历史上,指标是在引擎核心进程中收集的,并使用多进程模式使其在 API 服务器进程中可用。请参阅 Pull Request #7279

最近,指标在 API 服务器进程中收集,且仅当 --api-server-count > 1 时才使用多进程模式。请参阅 Pull Request #17546 以及关于 API 服务器横向扩展的详细信息。

内置 Python/进程指标

以下指标默认由 prometheus_client 支持,但在使用多进程模式时不会暴露:

  • python_gc_objects_collected_total
  • python_gc_objects_uncollectable_total
  • python_gc_collections_total
  • python_info
  • process_virtual_memory_bytes
  • process_resident_memory_bytes
  • process_start_time_seconds
  • process_cpu_seconds_total
  • process_open_fds
  • process_max_fds

因此,当 --api-server-count > 1 时,这些指标是不可用的。这些指标的相关性存疑,因为它们没有汇总构成 vLLM 实例的所有进程的统计数据。

指标设计

“更优的可观测性”(Even Better Observability)特性是大部分指标设计的规划所在。例如,请参见其中规划的详细路线图

遗留 PR

为了帮助理解指标设计的背景,以下是一些添加了原始(现已遗留)指标的相关 PR:

指标实现 PR

作为背景,以下是与指标实现相关的 PR Issue #10582

指标收集

在 v1 中,我们希望将计算和开销移出引擎核心进程,以尽量减少每次前向传播(forward pass)之间的时间。

V1 EngineCore 设计的总体思路是:

  • EngineCore 是内层循环。这里的性能最为关键
  • AsyncLLM 是外层循环。这(理想情况下)与 GPU 执行相重叠,因此在可能的情况下,这里应该是任何“开销”所在的地方。因此,如果可行的话,AsyncLLM.output_handler_loop 是进行指标记账(metrics bookkeeping)的理想位置。

我们将通过在前端 API 服务器中收集指标来实现这一目标,并使这些指标基于我们可以从引擎核心进程返回给前端的 EngineCoreOutputs 中获取的信息。

时间间隔计算

我们的许多指标都是请求处理过程中各种事件之间的时间间隔。最佳实践是使用基于“单调时间”(monotonic time,即 time.monotonic())而非“墙上时钟时间”(wall-clock time,即 time.time())的时间戳来计算间隔,因为前者不受系统时钟更改(例如来自 NTP)的影响。

同样重要的是,进程之间的单调时钟是不同的 —— 每个进程都有自己的参考点。因此,比较不同进程的单调时间戳是毫无意义的。

因此,为了计算时间间隔,我们必须比较来自同一个进程的两个单调时间戳。

调度器统计信息

引擎核心进程将从调度器收集一些关键统计信息 —— 例如,在最后一次调度传递后已调度或等待的请求数量 —— 并将这些统计信息包含在 EngineCoreOutputs 中。

引擎核心事件

引擎核心还将记录某些每个请求事件的时间戳,以便前端计算这些事件之间的时间间隔。

这些事件包括:

  • QUEUED - 当引擎核心接收到请求并将其添加到调度器队列时。
  • SCHEDULED - 当请求首次被调度执行时。
  • PREEMPTED - 请求已被放回等待队列,以便为其他请求的完成腾出空间。它将在未来重新调度并重新启动其预填充(prefill)阶段。
  • NEW_TOKENS - 当 EngineCoreOutput 中包含的输出生成时。由于这对于给定迭代中的所有请求都是通用的,因此我们在 EngineCoreOutputs 上使用单个时间戳来记录此事件。

计算出的时间间隔为:

  • 队列间隔 - QUEUED 与最近一次 SCHEDULED 之间的时间间隔。
  • 预填充(Prefill)间隔 - 最近一次 SCHEDULED 与其后的第一次 NEW_TOKENS 之间的时间间隔。
  • 解码(Decode)间隔 - 第一次(在最近一次 SCHEDULED 之后)与最后一次 NEW_TOKENS 之间的时间间隔。
  • 推理间隔 - 最近一次 SCHEDULED 与最后一次 NEW_TOKENS 之间的时间间隔。
  • 逐字(Inter-token)间隔 - 连续的 NEW_TOKENS 之间的时间间隔。

换句话说:

Interval calculations - common case

我们探讨过让前端利用其可见事件的时间点来计算这些时间间隔的可能性。然而,前端无法得知 QUEUEDSCHEDULED 事件的具体发生时间,且由于我们需要基于同一进程的单调时间戳来计算时间间隔……因此我们需要引擎核心记录所有这些事件的时间戳。

时间间隔计算与抢占

当在解码过程中发生抢占时,由于任何已生成的 Token 都会被重用,我们认为抢占会影响逐字间隔、解码间隔和推理间隔。

Interval calculations - preempted decode

如果在预填充过程中发生抢占(假设此类事件可能发生),我们认为抢占会影响首字时间和预填充间隔。

Interval calculations - preempted prefill

前端统计信息收集

当前端处理单个 EngineCoreOutputs(即来自单个引擎核心迭代的输出)时,它会收集与该迭代相关的各种统计数据:

  • 在此迭代中生成的全新 Token 总数。
  • 在此迭代中完成的预填充所处理的提示词 Token 总数。
  • 在此迭代中调度的任何请求的队列间隔。
  • 在此迭代中完成预填充的任何请求的预填充间隔。
  • 此迭代中包含的所有请求的逐字间隔(每个输出 Token 的时间,TPOT)。
  • 在此迭代中完成预填充的任何请求的首字延迟(TTFT)。然而,我们是相对于前端首次接收到请求的时间(arrival_time)来计算此间隔的,以将输入处理时间考虑在内。目前,arrival_time 从分词(tokenization)开始时起算。

对于在给定迭代中完成的任何请求,我们还会记录:

  • 推理和解码间隔 —— 如上所述,相对于调度事件和首字事件。
  • 端到端延迟 —— 前端 arrival_time 与前端接收到最后一个 Token 之间的时间间隔。

KV 缓存驻留指标

我们还会发出一组直方图,用于描述采样的 KV 缓存块保持驻留的时间以及它们被重用的频率。采样(--kv-cache-metrics-sample)可以使开销极小;当一个块被选中时,我们会记录:

  • lifetime – 分配 ⟶ 逐出
  • idle before eviction – 最后一次触及 ⟶ 逐出
  • reuse gaps – 块被重用时,各次触及之间的停顿时间

这些直接映射到 Prometheus 指标:

  • vllm:kv_block_lifetime_seconds – 每个采样块存在的时间。
  • vllm:kv_block_idle_before_evict_seconds – 最后一次访问后的空闲尾部。
  • vllm:kv_block_reuse_gap_seconds – 连续触及之间的时间。

引擎核心仅通过 SchedulerStats 发送原始逐出事件;前端会清空它们,将其转换为 Prometheus 观测值,并在日志开启时通过 LLM.get_metrics() 暴露相同的数据。在同一个图表中查看生命周期(lifetime)和空闲时间(idle time)可以轻松发现滞留的缓存或由于解码时间长而固定提示词(pin prompts)的工作负载。

指标发布 - 日志

LoggingStatLogger 指标发布器每 5 秒输出一条包含一些关键指标的 INFO 日志消息:

  • 当前正在运行/等待的请求数量
  • 当前 GPU 缓存使用率
  • 过去 5 秒内每秒处理的提示词 Token 数量
  • 过去 5 秒内每秒生成的全新 Token 数量
  • 最近 1000 次 KV 缓存块查询的前缀缓存命中率

指标发布 - Prometheus

PrometheusStatLogger 指标发布器通过兼容 Prometheus 格式的 /metrics HTTP 端点提供指标。然后,可以将 Prometheus 实例配置为轮询该端点(例如,每秒一次)并将值记录在其时间序列数据库中。Prometheus 通常与 Grafana 配合使用,从而允许将这些指标随时间的变化绘制成图表。

Prometheus 支持以下指标类型:

  • Counter(计数器):一个会随时间增加且绝不减少的值,通常在 vLLM 实例重启时重置为零。例如,在实例生命周期内生成的 Token 数量。
  • Gauge(测量器):一个会上升和下降的值,例如当前调度执行的请求数量。
  • Histogram(直方图):在桶(bucket)中记录的指标样本计数。例如,TTFT <1ms、<5ms、<10ms、<20ms 等的请求数量。

Prometheus 指标还可以带有标签(label),从而允许根据匹配的标签对指标进行组合。在 vLLM 中,我们为每个指标添加了一个 model_name 标签,其中包含了该实例所服务的模型名称。

示例输出

$ curl http://0.0.0.0:8000/metrics
# HELP vllm:num_requests_running Number of requests in model execution batches.
# TYPE vllm:num_requests_running gauge
vllm:num_requests_running{model_name="meta-llama/Llama-3.1-8B-Instruct"} 8.0
...
# HELP vllm:generation_tokens_total Number of generation tokens processed.
# TYPE vllm:generation_tokens_total counter
vllm:generation_tokens_total{model_name="meta-llama/Llama-3.1-8B-Instruct"} 27453.0
...
# HELP vllm:request_success_total Count of successfully processed requests.
# TYPE vllm:request_success_total counter
vllm:request_success_total{finished_reason="stop",model_name="meta-llama/Llama-3.1-8B-Instruct"} 1.0
vllm:request_success_total{finished_reason="length",model_name="meta-llama/Llama-3.1-8B-Instruct"} 131.0
vllm:request_success_total{finished_reason="abort",model_name="meta-llama/Llama-3.1-8B-Instruct"} 0.0
...
# HELP vllm:time_to_first_token_seconds Histogram of time to first token in seconds.
# TYPE vllm:time_to_first_token_seconds histogram
vllm:time_to_first_token_seconds_bucket{le="0.001",model_name="meta-llama/Llama-3.1-8B-Instruct"} 0.0
vllm:time_to_first_token_seconds_bucket{le="0.005",model_name="meta-llama/Llama-3.1-8B-Instruct"} 0.0
vllm:time_to_first_token_seconds_bucket{le="0.01",model_name="meta-llama/Llama-3.1-8B-Instruct"} 0.0
vllm:time_to_first_token_seconds_bucket{le="0.02",model_name="meta-llama/Llama-3.1-8B-Instruct"} 13.0
vllm:time_to_first_token_seconds_bucket{le="0.04",model_name="meta-llama/Llama-3.1-8B-Instruct"} 97.0
vllm:time_to_first_token_seconds_bucket{le="0.06",model_name="meta-llama/Llama-3.1-8B-Instruct"} 123.0
vllm:time_to_first_token_seconds_bucket{le="0.08",model_name="meta-llama/Llama-3.1-8B-Instruct"} 138.0
vllm:time_to_first_token_seconds_bucket{le="0.1",model_name="meta-llama/Llama-3.1-8B-Instruct"} 140.0
vllm:time_to_first_token_seconds_count{model_name="meta-llama/Llama-3.1-8B-Instruct"} 140.0

注意

如何选择能在广泛的使用场景中对用户最实用的直方图桶(histogram buckets)并不是一件简单的事,需要随着时间的推移不断进行优化。

缓存配置信息

prometheus_client 支持 Info 指标,这相当于一个值永久设置为 1 的 Gauge,但会通过标签暴露有趣的关键/值对信息。这用于表示不会改变的实例信息 —— 因此只需在启动时进行观察 —— 并允许在 Prometheus 中对不同实例进行比较。

我们为 vllm:cache_config_info 指标采用了这一概念

# HELP vllm:cache_config_info Information of the LLMEngine CacheConfig
# TYPE vllm:cache_config_info gauge
vllm:cache_config_info{block_size="16",cache_dtype="auto",calculate_kv_scales="False",cpu_offload_gb="0",enable_prefix_caching="False",gpu_memory_utilization="0.9",...} 1.0

然而,出于不详的原因prometheus_client 在多进程模式下从未支持过 Info 指标。相反,我们只是简单地使用了一个值设置为 1 且 multiprocess_mode="mostrecent"Gauge 指标。

LoRA 指标

The vllm:lora_requests_info Gauge 与之有些类似,除了其值是当前墙上时钟时间,并且在每次迭代中都会更新。

使用的标签名称为:

  • running_lora_adapters:使用该适配器运行的请求数量的每个适配器计数,格式化为以逗号分隔的字符串。
  • waiting_lora_adapters:类似,只是计算等待调度的请求数量。
  • max_lora - 静态的“单批次中 LoRA 的最大数量”配置。

在逗号分隔的字符串中对多个适配器的运行/等待计数进行编码似乎相当不妥 —— 我们本可以使用标签来区分每个适配器的计数。这应该被重新评估。

请注意,这里使用了 multiprocess_mode="livemostrecent" —— 使用最新的指标,但仅来自当前正在运行的进程。

这是在 Pull Request #9477 中添加的,并且至少有一个已知用户。如果我们重新审视此设计并废弃旧指标,我们应该与下游用户进行协调,以便他们能在移除前进行迁移。

前缀缓存指标

在关于添加前缀缓存指标的 Issue #10582 中的讨论提出了一些有趣的点,这可能与我们处理未来指标的方式相关。

每次查询前缀缓存时,我们都会记录查询的 Token 数量以及缓存中存在的查询 Token 数量(即命中数)。

然而,人们真正感兴趣的指标是命中率 —— 即每次查询的命中次数。

在日志记录的情况下,我们认为最好是通过计算固定数量的最新查询的命中率来为用户服务(目前该间隔固定为最近的 1000 次查询)。

然而在 Prometheus 的情况下,我们应该利用 Prometheus 的时间序列特性,并允许用户计算他们选择的时间间隔内的命中率。例如,用于计算过去 5 分钟内命中率的 PromQL 查询。

rate(cache_query_hit[5m]) / rate(cache_query_total[5m])

为了实现这一目标,我们应该在 Prometheus 中将查询(queries)和命中(hits)记录为计数器(counter),而不是将命中率记录为测量器(gauge)。

已废弃指标

如何废弃指标

废弃指标不应该被轻率对待。用户可能不会注意到指标已被废弃,而当它突然(从他们的角度来看)被移除时,即使有同等的替代指标可供使用,也可能会给他们带来极大的不便。

作为一个例子,看看 vllm:avg_prompt_throughput_toks_per_s 是如何被废弃的(在代码中有注释),然后被移除,接着又被用户发现的。

总的来说:

  1. 我们在废弃指标时应该保持谨慎,特别是因为很难预测对用户的影响。
  2. 我们应该在 `/metrics` 输出中包含的帮助字符串里,加入醒目的废弃提示。
  3. 我们应该在面向用户的文档和版本发布说明(release notes)中列出已废弃的指标。
  4. 我们应该考虑将已废弃的指标隐藏在 CLI 参数后面,以便在删除它们之前,给管理员提供一段时间的缓冲方案(escape hatch)

有关项目范围内的废弃政策,请参阅 废弃政策

未实现 - vllm:tokens_total

Pull Request #4464 添加,但显然从未实现过。这可以直接删除。

重复 - 队列时间

vllm:time_in_queue_requests 这一直方图(Histogram)指标是由 Pull Request #9659 添加的,其计算方式为:

    self.metrics.first_scheduled_time = now
    self.metrics.time_in_queue = now - self.metrics.arrival_time

两周后, Pull Request #4464 添加了 vllm:request_queue_time_seconds,使我们留下了:

if seq_group.is_finished():
    if (seq_group.metrics.first_scheduled_time is not None and
            seq_group.metrics.first_token_time is not None):
        time_queue_requests.append(
            seq_group.metrics.first_scheduled_time -
            seq_group.metrics.arrival_time)
    ...
    if seq_group.metrics.time_in_queue is not None:
        time_in_queue_requests.append(
            seq_group.metrics.time_in_queue)

这似乎是重复的,其中一个应该被移除。后者正在被 Grafana 仪表板使用,所以我们应该废弃或移除前者。

前缀缓存命中率

见上文 —— 我们现在暴露的是 “queries”(查询)和 “hits”(命中)计数器,而不是 “hit rate”(命中率)测量器。

KV 缓存卸载

有两个遗留指标与“交换”(swapped)抢占模式有关,该模式在 v1 中已不再适用:

  • vllm:num_requests_swapped
  • vllm:cpu_cache_usage_perc

在这种模式下,当请求被抢占时(例如,为了在 KV 缓存中腾出空间以完成其他请求),KV 缓存块会被交换到 CPU 内存中。由于该特性在 V1 中不再使用,--swap-space 标志已被移除。

从历史上看,vLLM 长期以来一直支持束搜索(beam search)。SequenceGroup 封装了共享相同提示词 KV 块的 N 个序列(Sequence)的概念。这使得请求之间能够共享 KV 缓存块,并能够通过写时复制(copy-on-write)来进行分支。CPU 交换(CPU swapping)就是专门针对这些类似于束搜索的场景设计的。

后来,引入了前缀缓存(prefix caching)的概念,允许隐式共享 KV 缓存块。这被证明是一个比 CPU 交换更好的选择,因为这些块可以根据需要缓慢逐出,且被逐出的提示词部分可以重新计算。

虽然“并行采样”(n>1)仍需要替代方案,但 SequenceGroup 在 V1 中已被移除。束搜索已被移出核心引擎。为一个非常罕见的特性编写了大量复杂的代码。

在 V1 中,由于前缀缓存更好(零开销)且因此默认开启,抢占和重新计算策略应该能更好地发挥作用。

未来工作

并行采样

某些遗留指标仅在“并行采样”的语境下才有用。这是指在请求中使用 n 参数来请求针对相同提示词的多个生成结果。

作为在 Pull Request #10980 中添加并行采样支持的一部分,我们还应该添加这些指标。

  • vllm:request_params_n (Histogram)

观察每个已结束请求的 'n' 参数值。

  • vllm:request_max_num_generation_tokens (Histogram)

观察每个已结束序列组中所有序列的最大输出长度。在没有并行采样的情况下,这相当于 vllm:request_generation_tokens

投机解码(Speculative Decoding)

一些遗留指标是特定于“投机解码”的。在这种方法中,我们使用更快、近似的方法或模型生成候选 Token,然后使用较大的模型对这些 Token 进行验证。

  • vllm:spec_decode_draft_acceptance_rate (Gauge)
  • vllm:spec_decode_efficiency (Gauge)
  • vllm:spec_decode_num_accepted_tokens (Counter)
  • vllm:spec_decode_num_draft_tokens (Counter)
  • vllm:spec_decode_num_emitted_tokens (Counter)

目前有一个处于评审中的 PR( Pull Request #12193)旨在向 v1 添加“提示词查找 (ngram)”投机解码。其他技术将随后跟进。我们应该在此背景下重新审视这些指标。

注意

我们可能应该将接受率(acceptance rate)作为独立的 accepted(已接受)和 draft(草稿)计数器来暴露,就像我们对前缀缓存命中率所做的那样。效率指标(Efficiency)可能也需要类似的处理。

自动弹性伸缩与负载均衡

我们指标的一个常见用例是支持 vLLM 实例的自动化伸缩。

有关 Kubernetes Serving 工作组 的相关讨论,请参阅:

这是一个不简单的话题。思考一下 Rob 的这条评论:

我认为这个指标应该专注于尝试估计,导致平均请求长度 > 每秒查询数(queries per second)的最大并发量是多少……因为这才是真正会使服务器“饱和”的因素。

一个明确的目标是,我们应该暴露检测此饱和点所需的指标,以便管理员可以基于这些指标实施自动伸缩规则。然而,为了做到这一点,我们需要对管理员(以及自动化监控系统)如何判断一个实例正在接近饱和,有一个清晰的看法:

为了识别模型服务器计算的饱和点是什么(在此拐点,我们无法通过更高的请求率获得更多吞吐量,但开始引入额外的延迟),以便我们进行有效的自动伸缩?

指标命名

我们命名指标的方法可能值得重新审视

  1. 在指标名称中使用冒号似乎违反了 “冒号保留用于用户定义的记录规则” 的规定。
  2. 我们的大多数指标都遵循以单位结尾的约定,但并非全部如此。
  3. 我们的一些指标名称以 _total 结尾

    如果指标名称中包含 _total 后缀,它将被移除。当为计数器(counter)暴露时间序列时,会添加 _total 后缀。这是为了保证 OpenMetrics 与 Prometheus 文本格式之间的兼容性,因为 OpenMetrics 需要 _total 后缀。

添加更多指标

新指标的创意并不匮乏:

我们在添加新指标的方法上应该保持谨慎。虽然添加指标通常相对简单:

  1. 但它们可能很难被移除 —— 参见上面的废弃一节。
  2. 启用它们可能会对性能产生显著影响。而且除非指标可以默认启用且在生产环境中使用,否则它们的作用通常非常有限。
  3. 它们会对项目的开发和维护产生影响。随着时间的推移,每添加一个指标都会让这项工作变得更加耗时,也许并不是所有指标都值得在这种持续性的维护中进行投入。

追踪 - OpenTelemetry

指标提供了系统性能和健康状况随时间推移的聚合视图。另一方面,追踪在单个请求通过不同服务和组件时对其进行跟踪。两者都属于更广泛的“可观测性”范畴。

vLLM 支持 OpenTelemetry 追踪

OpenTelemetry 设有一个 生成式 AI 工作组

由于指标本身就是一个足够庞大的主题,我们认为追踪这一主题与指标是完全独立的。

OpenTelemetry 模型前向传播时间与执行时间

当前的实现暴露了以下两个指标:

  • vllm:model_forward_time_milliseconds (Histogram) - 当此请求在批次中时,在模型前向传播(forward pass)中花费的时间。
  • vllm:model_execute_time_milliseconds (Histogram) - 在模型执行函数中花费的时间。这将包括模型前向传播、worker 之间的阻断/同步、CPU-GPU 同步时间以及采样时间。

仅当启用 OpenTelemetry 追踪且使用 --collect-detailed-traces=all/model/worker时,才启用这些指标。该选项的文档指出:

收集指定模块的详细追踪。这涉及到可能开销昂贵和/或具有阻塞性的操作,因此可能会对性能产生影响。

这些指标是由 Pull Request #7089 添加的,并在 OpenTelemetry 追踪中显示为:

-> gen_ai.latency.time_in_scheduler: Double(0.017550230026245117)
-> gen_ai.latency.time_in_model_forward: Double(3.151565277099609)
-> gen_ai.latency.time_in_model_execute: Double(3.6468167304992676)

我们已经有了 inference_timedecode_time 指标,因此问题在于,是否存在足够常见的更高分辨率计时的使用场景,来证明其开销的合理性。

由于我们将单独讨论 OpenTelemetry 支持的问题,因此我们将把这些特定指标纳入该主题中。