观测层级:
一条指标属于请求,还是整台机器
指标名相近,描述的实体可能截然不同。区分指标是刻画单个请求还是整机环境,是建立有效因果链的前提。此处 level 指原始观测对象,而非 Prometheus 汇总存储后的形态。
样本进入直方图后被折叠进固定边界的分桶,不再保留请求标识。这是为了长期存储与低开销聚合所做的设计取舍;若需排查单条慢请求,应接入 Distributed Trace,不要试图从 _bucket 逆推单个请求。
1.1四种容易混淆的计量单位
同一个请求可能触发多次缓存操作;同一个 batch 又同时服务多个请求;设备指标则以固定周期采样。 这三类“多对多”关系,是指标误读的主要来源。
| 计量单位 | 典型对象 | 例子 | 能回答 | 不能回答 |
|---|---|---|---|---|
| 每请求 | 一次推理的完整生命周期 | time_to_first_token_seconds |
用户感知的排队、首字与总耗时分布 | GPU 是否被该请求独占 |
| 每操作 | 一次 KV 传输、一次缓存 Get/Put | kv_transfer_latency_ms、nixl_xfer_time_seconds |
搬运本身的耗时与带宽 | 请求端到端增量(一次请求可能触发多次操作) |
| 每批次 | 一次调度 step / 一次 forward | iteration_tokens_total、FPM wall_time |
单步计算规模、batch 内长度均衡度 | 某个 token 的归属与单请求延迟 |
| 每设备 | GPU、HBM、PCIe、网卡端口 | DCGM_FI_PROF_DRAM_ACTIVE、port_xmit_wait |
资源是否饱和、链路是否拥塞 | 慢请求的具体身份与调用链 |
遇到名称含 total 或 num_ 的指标,先核实统计实体是请求、token、block 还是物理字节;直方图则需确认样本粒度是一次请求、一次 I/O 还是单步迭代。单位不同,跨实例聚合的数学方法也截然不同。
Python prometheus_client 中,Counter 若注册名未以 _total 结尾,导出时会自动追加 _total,
同时生成 _created;Histogram 会展开为 _bucket、_sum、_count。
OTel 仪表经 Prometheus Exporter 导出时,点号会替换为下划线,例如 lmcache_mp.l1_read 变成
lmcache_mp_l1_read_total。
因此上游文档或源码里的注册名与端点实际暴露的名字常有细微差异。配置告警与 Grafana 仪表盘前,先直接抓取一次 /metrics,以其实际输出的 HELP 与 TYPE 为准。
1.2原生、条件原生与自建
下文的指标分三类,混在一起会导致“文档里有、线上抓不到”的排查成本:
kv_cache_usage_perc。--kv-cache-metrics、MC_STORE_CLIENT_METRIC=1)。时间口径:
TTFT、ITL、TPOT 各自从哪一刻算起
“首 Token 时间 300ms”在客户端、网关和引擎中往往对应不同的时间区间。必须先统一物理起止点再对比数值,避免将统计口径的差异误判为性能波动。
流式模式下客户端收到首块有效内容即为 TTFT 终点;非流式下客户端必须等待完整响应返回,而引擎内部仍按首步完成时间上报,两者在数值上会产生数量级脱节。而在 PD 分离架构中,KV 传输耗时可能前移至首包等待,也可能隐藏在第一个生成 token 的间隔中。
2.1六个概念,三套边界
| 概念 | 层级 | 起止 / 含义 | 判读边界 |
|---|---|---|---|
| 客户端 TTFT | R | 客户端发起请求,到收到首次非空有效输出 | 含外部网络、网关鉴权、路由与流式缓冲;不是引擎 /metrics 的 TTFT |
| 网关 TTFT | R | 请求进入网关,到网关发出首个响应分片 | 含网关排队和路由开销,不含客户端侧公网 RTT |
| 引擎 TTFT | R | 框架定义的引擎请求起点到首 token / 首输出 | 流式与非流式计时终点不同,跨框架不可直接比较 |
| TTFB / 首 SSE 事件 | R | HTTP 首字节或首个 SSE 事件到达客户端 | 可能只是 role: assistant 或空文本帧,不等于首个有效 token |
| ITL | R token 样本 | 请求内相邻输出 token 的间隔 | 与 SSE chunk 间隔不等价(一个 chunk 可含多个 token) |
| TPOT | R 请求样本 | (E2E − TTFT) / (output_len − 1) | 每请求只有一个样本,不能用 ITL P99 代替请求级 TPOT P99 |
| E2E latency | R | 请求起点到生成完成 | 客户端、网关、引擎各自观测的边界不同 |
各链路阶段的 P99 既不能简单相加,也不能相减以反推端到端指标。不同指标的 P99 对应完全不同的长尾请求集合,描述的是不同随机变量的统计分布边缘,而非同一批请求的各阶段累加。分析单次请求的真实耗时构成,只能依赖完整的 Trace 上下文。
网关与引擎各自的 P99 分别为 210ms 与 190ms,但端到端 P99 达到 520ms。分位点描述的是整个分布的长尾边界,而非同一批具体请求。在网关遭遇排队的请求,在引擎内部可能执行顺利;反之亦然。拆解单次请求的真实耗时构成,必须依赖携带同一 Trace ID 的调用链,无法通过多组直方图加减求得。
2.2ITL 的样本加权:长输出会放大自己的权重
ITL 是 token 级样本,每个请求贡献 output_len − 1 个样本;TPOT 是请求级样本,每个请求只贡献一个。
只要流量里存在少量超长输出,它们就会在 ITL 分布里占据远超其请求占比的权重。
这会让“ITL P99 变差”与“单请求平均速度变差”指向不同的结论。
仅占 5% 数量的长文本请求,即可在 ITL 样本池中占据逾半数比重。因此 ITL 反映的是整体 Token 流的平稳程度,容易被极端长文本主导;评估终端用户感知的单请求生成速度时,应以请求级样本 request_time_per_output_token_seconds 为准。
2.3四个常见的口径陷阱
| 陷阱 | 表现 | 正确做法 |
|---|---|---|
| SGLang 流式 / 非流式 TTFT 不同 | 同一服务切换 stream 后,观测到的 TTFT 量级变化 |
流式请求在首段 detokenize 输出准备发送时结束;非流式客户端只能看到完整响应。比较前先对齐请求模式。 |
| SGLang 请求级 TPOT 有过滤条件 | 短输出请求找不到 TPOT 样本 | request_time_per_output_token_seconds 仅记录正常完成、output_len > 1 且非 PD Prefill-only worker 的请求。 |
| PD 分离下首 token 落点漂移 | 引擎 TTFT 正常,但客户端仍感觉慢 | KV 交接与 D 端预分配可能落在首包前,也可能落在首个 ITL。原生引擎 TTFT 正常不能单独排除 PD 传输慢。 |
| 把 SSE chunk 间隔当 ITL | 网关侧统计的输出间隔比引擎 ITL 更平滑 | 一个 SSE chunk 可能合并多个 token。要比较推理节奏,必须使用引擎侧 token 级样本。 |
网关与 KV 路由:
把“入口慢”和“引擎慢”分开
多实例与前缀感知架构中,请求需经网关与路由器分发。厘清网关层排队与路由耗时,才能避免将入口选路开销误判为引擎算力瓶颈。
请求经前端网关排队、前缀哈希与 Radix Tree 匹配完成选路,随后分发至工作节点。其中 router_overhead_seconds 仅度量选路本身的算法耗时;请求在网关层的等待时间需结合 queued_requests 观测,不可笼统归入单一的“网关延迟”。
| 指标 | 层级 | 类型 / 单位 | 作用与边界 |
|---|---|---|---|
dynamo_frontend_time_to_first_token_seconds | R | H s | 网关侧 TTFT;与引擎 TTFT 对照可剥离网关排队与跨节点转发 |
dynamo_frontend_inter_token_latency_seconds | R | H s | 网关侧流式输出间隔;受 SSE 合并影响 |
dynamo_frontend_request_duration_seconds | R | H s | 网关侧端到端总耗时 |
dynamo_frontend_queued_requests / dynamo_frontend_inflight_requests | S | G 请求 | 网关排队与在途量;判断洪峰是否卡在限流或路由前 |
dynamo_frontend_disconnected_clients | S | C 连接 | 客户端超时断连累计;配合引擎 abort 排查重试雪崩 |
dynamo_router_overhead_seconds | R | H s | 前缀哈希、Radix Tree 查询与 worker 选路耗时 |
dynamo_component_router_requests_total | S | C 请求 | 路由器分发总请求数,用作流量分母 |
dynamo_component_router_prefix_hit_rate | S | G 0–1 | 路由层预测的前缀命中率;属于 Gauge,不可跨实例算术平均 |
3.1路由视图漂移:预测命中与真实命中的差
路由器的 prefix_hit_rate 是“按本地 Radix Tree 视图预测”的结果,而引擎的
prefix_cache_hits / prefix_cache_queries 才是实际命中的 token 数。
当 worker 的缓存被淘汰、Radix Tree 尚未同步,或新实例尚未建立前缀索引时,两者会出现持续偏差。
偏差本身不是故障,但如果它长期扩大,路由决策就会把请求送到并不命中缓存的 worker,
表现为 Prefill 计算量上升,而路由层仍显示“命中良好”。
两组曲线背离往往预示着路由器的前缀树状态与后端 Worker 发生脱节:例如 Worker 端已因显存压力淘汰了热点 KV 块,而网关尚未收到更新。此时表面上前缀命中预测维持高位,但引擎端的实际 Prefill 计算量已开始攀升。
以“网关 TTFT P99 − 引擎 TTFT P99”估算网关自身耗时是常见的统计陷阱。两组分位样本并非同一批请求,所得差值缺乏物理意义。可靠的做法是对同批请求注入统一的 trace_id,在 Trace 系统中计算各层 Span 的单请求耗时差;或直接采集网关自身排队、选路与转发的细分 Span。
vLLM V1:
用调度状态串起主链路指标
围绕请求的主状态流转——等待入队、调度分配、Prefill 计算、Decode 迭代直至完成。顺着状态迁移看指标,调度瓶颈一目了然。
等待队列细分为 capacity 与 deferred,用以区分单纯显存不足与等待异步依赖(如 KV 连接器拉取)。进入运行态后,Prefill 与 Decode 各自记录耗时分布;若触发抢占(Preemption),正在解码的请求会被重置回等待队列并面临重算,直接反映 KV 池容量耗尽。
4.1主链路必看指标
指标(前缀 vllm:) | 层级 | 类型 / 单位 | 作用与判读边界 |
|---|---|---|---|
time_to_first_token_seconds | R | H s | 引擎 TTFT;与网关 / 客户端 TTFT 分开看 |
inter_token_latency_seconds | R | H s | 输出节奏与 Decode 步间抖动;token 级样本 |
request_time_per_output_token_seconds | R | H s | 请求级平均 TPOT;每请求一个样本 |
e2e_request_latency_seconds | R | H s | 引擎内部请求总耗时 |
request_queue_time_seconds | R | H s | WAITING 队列耗时;判断调度拥塞或 KV 分配等待 |
request_prefill_time_seconds | R | H s | Prefill 阶段框架耗时 |
request_decode_time_seconds | R | H s | Decode 阶段框架总耗时 |
request_inference_time_seconds | R | H s | RUNNING 阶段(Prefill + Decode)总耗时 |
num_requests_running / num_requests_waiting | S | G 请求 | 当前执行与排队规模;只反映瞬时水位 |
num_preemptions | S | C 次 | 抢占累计;需结合 KV 使用率判断是否发生重算 |
kv_cache_usage_perc | S | G 0–1 | 引擎 KV Block Pool 使用比例,不是整卡显存占比 |
prefix_cache_hits / prefix_cache_queries | S | C token | 本地 APC 前缀缓存命中与查询量 |
external_prefix_cache_hits / external_prefix_cache_queries | S | C token | 外部 KV Connector 的命中与查询 token 数 |
prompt_tokens / generation_tokens | S | C token | 输入 / 输出 token 累计量,导出带 _total |
request_prompt_tokens / request_generation_tokens | R | H token | 单请求输入 / 输出长度分布;解释 E2E 变化的重要旁证 |
request_success | S | C 请求 | 按 finish_reason 细分;不能作为全部入口请求分母 |
iteration_tokens_total | S | H token/step | 每个调度 step 的总 token 分布;名字带 total,但类型是 Histogram |
Request level 指标均以服务器直方图形式暴露,不保留请求 ID。定位单次慢请求细节需启用 OpenTelemetry Trace。
4.2KV 水位与抢占:高水位不等于故障
kv_cache_usage_perc 长期停在 0.9 以上不一定是问题。高水位意味着缓存被充分利用,
只要没有发生抢占与重算,延迟可以是稳定的。真正需要警惕的是水位触顶后,
num_preemptions 开始增长,同时等待队列与 ITL 长尾一起抬升。
在健康复用状态下,KV 池即便稳定维持在 0.9 以上,抢占速率也保持为 0,吞吐平稳;而在抖动场景中,水位一旦触及 0.99,抢占脉冲随之爆发并导致排队反弹。评判显存压力不能单看使用率均值,关键在于是否引发了请求重算。
num_requests_waiting_by_reason{reason="capacity"} 指向 KV / 算力槽位不足;
reason="deferred" 指向等待异步外部条件。
如果只看 num_requests_waiting,两种完全不同的瓶颈会被合并成同一个“队列变长”。
进一步可用 num_requests_kv_fetch_by_stage{stage} 区分
waiting_to_start、in_progress、completed_waiting 三个阶段。
4.3条件指标:KV 生命周期、缓存来源与 MFU
| 指标 | 层级 | 开启条件 | 能解释的问题 |
|---|---|---|---|
request_prefill_kv_computed_tokens | R | 默认 | 本次请求实际新算的 Prefill KV token 数(已扣除命中部分) |
prompt_tokens_cached_by_source{source} | S | 默认 | 按 device / host / disk / p2p / external 分解缓存命中来源 |
admission_rejections | S | 默认 | 并发上限或队列满导致的直接拒绝 |
request_num_preemptions | R | 默认 | 单请求被抢占次数分布;识别是否集中伤害少数长文本请求 |
kv_block_lifetime_seconds | S | --kv-cache-metrics | KV Block 从分配到淘汰的驻留寿命 |
kv_block_idle_before_evict_seconds | S | --kv-cache-metrics | 淘汰前空闲时长;判断缓存池是否过小导致热块被冲刷 |
kv_block_reuse_gap_seconds | S | --kv-cache-metrics | 同一 Block 相邻两次访问的间隔 |
estimated_flops_per_gpu_total | S | --enable-mfu-metrics | 每 GPU 估算有效模型算力累计 |
estimated_read_bytes_per_gpu_total / estimated_write_bytes_per_gpu_total | S | --enable-mfu-metrics | 每 GPU 估算理论显存读写字节数 |
4.4投机解码:接受率要按位置看
投机解码的收益取决于“提出的候选 token 有多少被 target 模型接受”。
全局接受率 rate(accepted) / rate(draft_tokens) 会掩盖位置差异:
越靠后的候选越难被接受,而每一步验证的计算成本并不会同比下降。
各位置的命中率逐级递减属正常衰减。若业务流量特征变化导致位置 2 之后命中率断崖式下滑,而验证开销固定发生,过大的投机步长反而会拖慢单 Token 生成速度。评估投机收益需细分到具体位置分布,而非仅看单一的整体接受率。
相关指标:spec_decode_num_drafts、spec_decode_num_draft_tokens、
spec_decode_num_accepted_tokens、
spec_decode_num_accepted_tokens_per_pos{position}。
另外 model_forward_time_milliseconds 与 model_execute_time_milliseconds
可区分“单次 forward 计算”与“Worker execute_model 全过程”,后者含输入准备、通信与采样。
通过 --otlp-traces-endpoint 启动后,vLLM 会导出请求级 llm_request Span,
并在属性中携带 gen_ai.usage.prompt_tokens、gen_ai.usage.completion_tokens、
gen_ai.response.finish_reasons 以及排队与执行时间戳。
它弥补的正是 Prometheus Histogram 无法关联单个慢请求的限制。
SGLang:请求 Trace
把“慢在哪一段”变成可枚举的阶段
SGLang 将文本分词、调度分派、Prefill 与 Decode 循环乃至跨节点交接拆分为明确的 Span。根据排查场景选择合适的 Trace Level,在观测粒度与运行时开销之间取得平衡。
Level 2 覆盖核心分期,适合生产环境常态采样;Level 3 展开 Decode 循环;Level 4 则为单次 Forward 记录 Span,会带来可观的 CPU 额外开销与 Trace 导出量。在线开启高粒度追踪时,务必配置 SGLANG_TRACE_ASYNC=1,防止 OTLP 导出阻塞调度主线程。
上游 SGLang 将 SGLANG_TRACE_LEVEL 定义为 0–3;
在 NVIDIA Dynamo 的 SGLang 集成中取值为 1–4,默认 2(粗粒度),
3 增加 decode_loop,4 开启逐次 Forward Span。
排查前先确认所用发行版的编号体系,否则“调到 3”在两种体系下对应的粒度不同。
5.1主链路指标
与 vLLM 类似,SGLang 的主链路指标也围绕“排队、Prefill、Decode、结束”组织。
差别主要在命名与过滤条件:token_usage 对应 KV Token Pool 水位,
num_retracted_reqs 对应回退,cache_hit_rate 则是 RadixCache 的瞬时快照。
指标(前缀 sglang:) | 层级 | 类型 / 单位 | 作用与判读边界 |
|---|---|---|---|
time_to_first_token_seconds | R | H s | 服务端 TTFT;流式在首段 detokenize 输出时结束,非流式按内部 first_token_time |
inter_token_latency_seconds | R | H s | 流式输出块 / token 间延迟 |
request_time_per_output_token_seconds | R | H s | 请求级 TPOT;仅成功完成、output_len > 1 且非 PD prefill-only worker 时计入 |
e2e_request_latency_seconds | R | H s | 引擎侧请求端到端总耗时 |
queue_time_seconds | R | H s | 请求在 Scheduler 队列中的等待耗时 |
per_stage_req_latency_seconds{stage} | R | H s | 开启请求级阶段统计后,按 stage 标签拆分的阶段延迟 |
num_running_reqs / num_queue_reqs | S | G 请求 | 当前运行中与等待队列中的请求数 |
num_used_tokens / token_usage | S | G token / 0–1 | KV Token Pool 已用 token 与占用比例;混合架构另有 full_token_usage、swa_token_usage、mamba_usage |
cache_hit_rate | S | G 0–1 | RadixCache 近期命中率快照;Gauge 不可跨实例直接求算术平均 |
cached_tokens_total{cache_source} | S | C token | 按 device、host、storage 统计的命中缓存 token 累计 |
gen_throughput | S | G token/s | 引擎统计窗口内的生成吞吐速率 |
num_retracted_reqs / num_retracted_requests_total | S | G 请求 / C 次 | 当前回退请求数与累计回退;另有 input / output token 维度 |
spec_accept_rate / spec_accept_length | S | G 0–1 / token | 投机解码接受率与平均每轮接受 token 长度(含 1 个 target bonus token) |
is_cuda_graph / cuda_graph_passes | S | G 布尔 / C 次 | 当前是否命中 CUDA Graph 及累计执行步数;排查跌出 Graph 导致的抖动 |
num_grammar_queue_reqs / grammar_compilation_time_seconds | S / R | G 请求 / H s | 结构化输出(JSON Schema / Regex)语法编译排队数与耗时 |
forward_execution_seconds_total | S | C s | 模型 Forward 执行累计秒数 |
estimated_flops_per_gpu_total / estimated_read_bytes_per_gpu_total | S | C FLOPs / bytes | 每 GPU 算力与显存读写估算累计 |
cache_hit_rate 是瞬时 Gauge,不能跨实例直接求算术平均。计算集群整体命中率时,应分别对各实例的命中 Token 与总 Token 求和,再做除法。
5.2原生阶段与链路对应
| 阶段名(含版本演进) | 对应链路 | 典型判读 |
|---|---|---|
tokenize | Tokenizer 文本切词 | 变长而队列不涨,指向 CPU 切词或分词器竞争 |
api_server_dispatch(旧名 dispatch) | API Server 向 Tokenizer / Engine 分派 | 与 tokenize 同升,多为 Host 侧瓶颈 |
dpc_dispatch(旧名 dc_dispatch) | Data Parallel Controller 路由分派 | DP 场景下定位跨控制器转发开销 |
mm_encode | 多模态输入编码 | 图像 / 视频请求专属,常表现为个别请求的长尾 |
request_process | 调度器请求预处理 | 与调度队列状态一起看 |
prefill_waiting | Prefill 队列等待 | 与 num_queue_reqs 同升说明排队 |
prefill_forward | Prefill Forward 执行 | 变长才下钻到 batch 与 GPU |
chunked_prefill | 分块 Prefill 复合阶段 | 长输入被拆分时的组合耗时 |
decode_forward / decode_loop | 单步 / 多步 Decode | 高粒度 Trace 下逐次输出,注意采集开销 |
spec_draft / spec_verify | 投机解码 Draft 与验证 | 与接受率指标对照,判断投机是否负优化 |
prefill_prepare / prefill_bootstrap | PD 下 P 端准备与握手 | 堆积在握手阶段而非数据搬运 |
prefill_transfer_kv_cache | PD 下 P 端发起 KV 传输 | 与 kv_transfer_latency_ms 对照 |
decode_prepare / decode_bootstrap | PD 下 D 端预分配与握手 | 预分配失败会反压 P 端 |
decode_waiting / decode_transferred | D 端等待传输与接管 | waiting 长说明传输或调度未就绪 |
5.3Retract:Token Pool 触顶后的回退链
SGLang 在 KV Token Pool 不足时会把已运行的请求回退(retract),释放其占用的 token,
后续再重新计算。它和 vLLM 的 preemption 类似,但指标名不同:
num_retracted_reqs、num_retracted_requests_total、
num_retracted_input_tokens_total / output_tokens_total。
token_usage 逼近 1.0 上限。num_retracted_reqs 递增,被回退请求释放 Token 并重新排队 Prefill。inter_token_latency_seconds 长尾显著抬升。max_running_requests,或扩充单卡 KV 容量与部署规模。
Dynamo / SGLang 还通过 ZMQ(默认端口 20300)发布逐 iteration 的结构化事件。
它不是按请求导出的 Prometheus Histogram,而是批次级事件:
wall_time、scheduled_requests.sum_prefill_tokens、
var_prefill_length、sum_decode_kv_tokens、var_decode_kv_tokens 等。
这些字段解释的是“这一步 batch 长什么样”,包括长度方差导致的 Attention 负载不均。
KV 分层与卸载:先统一命名,再谈命中率
前缀复用、分层卸载与 PD 交接属于三种不同性质的数据流转。各框架对 L0 / L1 / L2 缓存层级的定义互有出入,推导命中率前须先对齐物理存储介质与分子分母口径。
Prefix reuse 避免 Prefill 重复计算,命中发生于单引擎前缀缓存; Offload / Reload 在 GPU HBM、Host DRAM、远端存储或本地磁盘间迁移动态数据; PD handoff 则负责在 Prefill 与 Decode 节点间移交上下文(见 PD 分离)。 三类动作均属于操作级(Server level)度量,不应直接等同于请求端到端延迟。
6.1同一层,不同名字
| 物理层级 | SGLang HiCache | LMCache MP | vLLM Connector / Offload | Mooncake |
|---|---|---|---|---|
| GPU HBM | L1(GPU KV Pool) | L0 | GPU KV Cache | 通常不驻留(作为传输源 / 目的) |
| 本地 Host DRAM | L2(Host Pool) | L1 | Simple CPU Offload Pool / HiSparse Host Cache | Store Client 的本地缓冲(非持久层) |
| 本地磁盘 / 远端存储 | L3(外部存储) | L2(外部 / 远端插件) | 外部 KV Connector(NIXL 等) | Store 集群 + SSD 层 |
| 跨节点 KV | PD handoff / P2P 传输,不属于本地缓存层级 | NIXL / P2P Connector | Transfer Engine(RDMA / TCP / NVLink) | |
HiCache 的 L1 对应 GPU、L2 对应 Host;而 LMCache MP 的 L0 对应 GPU、L1 对应 Host、L2 对应外部存储。跨组件交流或配置监控时需统一词汇,避免单提“L1 命中率”引发歧义。
6.2分层缓存与搬运路径
分层缓存以 PCIe / NVLink 带宽为代价换取更大的前缀留存窗口。每次回载(Load Back)都会与模型推理争用总线与显存带宽;若命中带来的计算减免无法覆盖搬运延时,卸载反倒会拖累整体吞吐。
Lookup 命中仅证明索引记录存在,但在异步发起的读取执行前,目标块可能已被驱逐。LMCache Legacy 的 lookup_hit_rate 与 retrieve_hit_rate 统计分母并不相同;在 MP 架构中,还需结合 l1_read_failure(并发竞态驱逐)与 l2_prefetch_failure 的具体原因定位损耗。
6.3HiCache:三段迁移与丢弃
指标(前缀 sglang:) | 方向 | 类型 / 单位 | 判读 |
|---|---|---|---|
hicache_host_used_tokens / hicache_host_total_tokens | 容量 | G token | Host 池水位;触顶后会出现丢弃 |
evicted_tokens_total / eviction_duration_seconds | GPU → 淘汰 | C token / H s | GPU 侧淘汰量与耗时 |
hicache_backup_tokens_total / hicache_backup_duration_seconds | GPU → Host | C token / H s | 备份速度是否跟得上淘汰速度 |
load_back_tokens_total / load_back_duration_seconds | Host → GPU | C token / H s | 回载量与时延;仅覆盖 Stream Copy |
load_back_bytes_total / hicache_backup_bytes_total | 物理字节 | C bytes | 各 TP Rank 自身的 shard 流量 |
prefetched_tokens_total / prefetch_bandwidth | L3 → Host | C token / H GB/s | 预取完成量与带宽 |
storage_prefetch_hit_tokens_total | L3 查询 | C token | 查询命中,尚不等于已加载可用 |
storage_prefetch_unfulfilled_tokens_total{reason} | L3 未完成 | C token-attempt | deadline / host_capacity / transfer |
hicache_dropped_tokens_total | 备份失败丢弃 | C token | L1 淘汰时尚未完成 L2 备份即被覆盖 |
逻辑 token 指标(如 load_back_tokens_total)在同一组 TP Rank 上记录相同数值,
跨实例聚合时必须限定 tp_rank="0"。
物理字节指标(如 load_back_bytes_total)记录各 Rank 自身的 KV shard 流量,
求整机带宽时应跨全部 TP Rank 求和。
混用两种口径,会得到整数倍的偏差。
6.4LMCache Legacy:In-Process 的操作级指标
Legacy 模式在推理进程内运行,指标前缀为 lmcache:;
MP 模式把缓存拆成独立进程,改用 OTel 命名 lmcache_mp.*。
两者都区分 Lookup、Retrieve、Store 三类操作,但命中率的分母不同,看板不能混用。
指标(前缀 lmcache:) | 类型 / 单位 | 作用与判读边界 |
|---|---|---|
num_lookup_requests / num_retrieve_requests / num_store_requests | C 次 | Lookup、Retrieve、Store 三类缓存 API 调用次数 |
num_lookup_tokens / num_lookup_hits / lookup_hit_rate | C token / G 0–1 | Lookup 查询与命中的 token 数及瞬时命中率 |
num_requested_tokens / num_hit_tokens / retrieve_hit_rate | C token / G 0–1 | Retrieve 阶段实际拉取命中的 token 数;分母与 Lookup 不同 |
num_stored_tokens | C token | 实际写入缓存的 token 总数 |
time_to_lookup / time_to_retrieve / time_to_store | H s | 三类操作的耗时分布 |
retrieve_speed / store_speed | H token/s | Retrieve 与 Store 的速率分布 |
retrieve_to_gpu_time / store_from_gpu_time / store_put_time | H s | CPU / Buffer → GPU 拷贝、GPU → CPU 拷贝及后端 Put 耗时 |
remote_time_to_get / remote_time_to_put / remote_time_to_get_sync | H s | 远端后端(Mooncake / Redis / InfiniStore)Get / Put 与同步等待 |
remote_backend_batched_get_blocking_time | H s | 远端批量 Get 阻塞主路径的时间 |
local_cache_usage / remote_cache_usage / local_storage_usage | G bytes | 本地 CPU 缓存、远端缓存与本地磁盘已用字节数 |
local_cpu_evict_count / local_cpu_evict_failed_count / forced_unpin_count | C 次 | 淘汰次数、淘汰失败与超时强制 Unpin;排查内存泄漏与死锁 |
num_p2p_requests / num_p2p_transferred_tokens / p2p_time_to_transfer / p2p_transfer_speed | C 次 / token, H s, token/s | 跨实例 P2P KV 传输次数、token 数、耗时与速率 |
Legacy 采集进程内的缓存调用;MP 则聚焦独立进程中的共享池与异步任务队列。Legacy 下命中率下滑重点排查 time_to_retrieve 与 CPU 驱逐;MP 下则重点分析 l1_read_failure 与 l2_prefetch_failure 的错误标签。
6.5LMCache MP:L0 / L1 / L2 与在途任务
| OTel 注册名 | 类型 / 单位 | 作用与边界 |
|---|---|---|
lmcache_mp.lookup_requested / .lookup_hit | C key | 查询与命中的 chunk key 数 |
lmcache_mp.lookup_hit_l1 / .lookup_hit_l2 | C key | L1 命中,以及 L1 未命中但 L2 命中的条件互斥计数 |
lmcache_mp.l1_read / .l1_write / .l1_evicted | C chunk | L1 内存池读写与淘汰 chunk 数 |
lmcache_mp.l1_memory_usage_bytes / .l1_usage_ratio | G bytes / 0–1 | L1 池水位 |
lmcache_mp.l1_chunk_lifetime / .l1_chunk_idle_before_evict / .l1_chunk_evict_reuse_gap | H s | 驻留寿命、淘汰前空闲、淘汰后再次被请求的“遗憾间隔” |
lmcache_mp.l2_store_submitted / .l2_store_completed | C 请求 | L2 异步写提交与完成;单位是请求,不是 chunk |
lmcache_mp.l2_prefetch_load_completed / .l2_load_completed | C chunk | 成功加载 chunk 数;不能直接除以提交数算成功率 |
lmcache_mp.active_prefetch_jobs / .num_inflight_l2_stores / .num_inflight_l2_loads | G 任务 | 在途任务与队列压力 |
lmcache_mp.l0_l1_store_throughput / .l0_l1_load_throughput / .l2_load_throughput | H GB/s | 各段吞吐分布 |
lmcache_mp.transfer_phase_bytes{phase} / .transfer_phase_elapsed{phase} | C bytes / H s | 按内部阶段拆分的传输量与时延 |
lmcache_mp.l1_allocation_failure / .l1_read_failure / .l2_prefetch_failure{reason} | C 次 | L1 容量满、查后读前被淘汰、L2 预取失败 |
6.6vLLM Connector:NIXL、Simple Offload 与 HiSparse
指标(前缀 vllm:) | 类型 / 单位 | 作用与边界 |
|---|---|---|
nixl_xfer_time_seconds / nixl_post_time_seconds | H s | 数据搬运耗时与描述符提交耗时分开看 |
nixl_bytes_transferred / nixl_num_descriptors | H bytes / 个 | 单次传输规模与散列描述符数量;累计流量取 _sum |
nixl_num_failed_transfers / nixl_num_failed_notifications | C 次 | 数据传输与握手通知失败 |
nixl_num_kv_expired_reqs / nixl_num_notifications_after_expiry | C 请求 / 次 | KV 租约过期,以及过期后才到达的 D 端通知 |
simple_kv_offload_load_blocks_total | C block | 从 CPU Offload Pool 恢复到 GPU 的 Block 数 |
simple_kv_offload_save_outcomes_total{outcome} | C 次 | 保存结果:写入、跳过或池满丢弃 |
simple_kv_offload_pending_store_blocks / .used_blocks | G block | 待写出积压与 CPU 池已用 |
hisparse_cache_hits / .hisparse_cache_misses | C 次 | 稀疏缓存命中 / 未命中 |
hisparse_host_cache_usage_perc / .hisparse_pending_page_transfers | G 0–1 / 次 | Host 缓存水位与在途页迁移 |
hisparse_host_block_lifetime_seconds / .hisparse_host_block_reuse_gap_seconds | H s | Host Block 寿命与复用间隔 |
HiCache H2D 回载耗时拉长通常对应三类不同根因:
(1) PCIe / NVLink 总线被计算算子或其他数据拷贝争用;
(2) 前置 Fence 与 Stream 同步阻塞;
(3) 缓存块已就绪但调度器尚未批复 GPU 槽位。
排查时,第一类比对 load_back_bytes_total 与耗时比值,并对照 DCGM_FI_PROF_PCIE_RX_BYTES;
第二类观察同步等待并结合请求排队;
第三类则直接核对 num_requests_kv_fetch_by_stage{stage="completed_waiting"}。
PD 分离:
一次请求,两个节点上的两段时间
PD 分离解耦了计算特征截然不同的 Prefill 与 Decode 节点,通过网络搬运 KV。新的队列、握手与租约状态使首 Token 延迟的构成跨越了节点边界。
Prefill 完成后,D 端需先行预分配 KV 空间并完成 Bootstrap 握手,再发起网络拉取。各阶段操作常与计算重叠执行,因此 bootstrap_ms、alloc_ms 与 latency_ms 无法通过简单的标量累加换算为客户端的 TTFT 增量。
指标(前缀 sglang:) | 端 | 类型 / 单位 | 作用与边界 |
|---|---|---|---|
num_prefill_bootstrap_queue_reqs | P | G 请求 | 等待与 D 端完成 Bootstrap 握手 |
num_prefill_inflight_queue_reqs | P | G 请求 | 已完成计算、传输中或等待确认释放 KV |
num_decode_prealloc_queue_reqs | D | G 请求 | 等待本地 KV Token Pool 预分配空间 |
num_decode_transfer_queue_reqs | D | G 请求 | 已分配空间,等待 KV 传输完成 |
pending_prealloc_token_usage | D | G 0–1 | 已预分配但尚未 Decode 的 token 占比;识别虚占 |
kv_transfer_bootstrap_ms | P/D | H ms | 握手耗时;单位是毫秒 |
kv_transfer_alloc_ms | D | H ms | D 端 KV 空间分配等待 |
kv_transfer_latency_ms | P/D | H ms | 跨节点数据搬运 |
kv_transfer_speed_gb_s / kv_transfer_total_mb | P/D | H GB/s / MB | 单次传输有效带宽与数据量 |
num_bootstrap_failed_reqs_total / num_transfer_failed_reqs_total | P/D | C 请求 | 握手失败与传输失败累计 |
num_prefill_retries_total | P | C 次 | 因握手或传输失败触发的 Prefill 重试 |
SGLang 的 kv_transfer_* 系列以毫秒导出,与秒级 TTFT 同图展示前必须先除以 1000 换算量纲。
7.1D 端预分配反压:一个典型的级联死锁前兆
D 端显存水位过高会导致预分配等待,kv_transfer_alloc_ms 攀升;这会直接导致 P 端已完成计算的请求无法交接,已算好的 KV 显存被锁定在 inflight 队列中。当 P 端也耗尽空闲块时,两端将形成互等停滞。排查此类卡顿应优先从 D 端回收或限流切入,而非盲目扩容 P 端。
7.2NIXL:租约、通知与失败路径
vLLM 的 PD 传输通常经 NIXL 完成。除了 nixl_xfer_time_seconds 这类搬运耗时,
还需要关注控制面的失败语义:KV 租约超时、过期后才到达的通知、以及传输失败计数。
| 现象 | 先看 | 对照 | 边界 |
|---|---|---|---|
| KV 在 D 端拉取前已过期 | vllm:nixl_num_kv_expired_reqs |
nixl_num_notifications_after_expiry;num_requests_kv_fetch_by_stage |
D 端排期过晚,P 端已按超时回收;不是网络慢 |
| 握手通知失败 | vllm:nixl_num_failed_notifications |
NIC rnr_nak_retry_err |
接收队列未就绪或内存注册不足 |
| 传输耗时长但带宽正常 | nixl_xfer_time_seconds、nixl_num_descriptors |
nixl_post_time_seconds |
KV 块碎片化导致小包 Scatter-Gather 开销大,而非链路拥塞 |
| 传输失败后重试 | nixl_num_failed_transfers |
P 端 num_prefill_retries_total |
重试会重复消耗 Prefill 算力,需与排队指标一起判断 |
由 D 节点直回首包时,KV 传输与预分配开销会计入客户端 TTFT;
若架构选择由 P 节点直回首包,该段等待将平移至首个生成 Token 的 ITL 样本中。
因此在 PD 分离场景中,引擎端上报的 TTFT 正常并不能直接排除跨节点传输慢,需结合首步 ITL 与 kv_transfer_latency_ms 综合判断。
GPU:
利用率接近 100%,并不代表算力被用满
GPU_UTIL 仅反映采样窗口内是否存在活跃的 Kernel,无法衡量 Tensor Core 的实际负载。理清采样机制,方能解释“利用率虚高而吞吐受阻”的反常现象。
GPU_UTIL 表示采样周期内任意 SM 处于活跃状态的时间比例(窗口通常在 1/6 秒至 1 秒)。短促的算子执行与周期性空闲易被统计窗口抹平。
即使整张 GPU 仅有单个 SM 运行微小 Kernel,NVML 的 GPU_UTIL 也能直接显示为 100%;此时 SM_ACTIVE 与 PIPE_TENSOR_ACTIVE 则暴露了真实的算力闲置。评估硬件饱和度必须依赖更深层的 Profiling 计数器,不能轻信宿主机层面的设备利用率。跨节点传输证据见 网络与存储后端。
8.1四种典型场景的指标组合
虽然四种工况下的表面利用率均接近打满,背后的物理制约却截然不同:长上下文 Decode 受限于显存带宽(DRAM 高、Tensor 低);通信或 Launch 开销受制于同步等待(SM 与 Tensor 双低);唯有大 Batch Prefill 才能同时将计算流水线与显存带宽推至高效区间。
8.2DCGM 指标与默认采集边界
| 导出名 | 默认 CSV | 类型 / 单位 | 含义与判读边界 |
|---|---|---|---|
DCGM_FI_DEV_GPU_UTIL | 开启 | G 0–100% | 采样周期内任意 SM 有 kernel 的比例;不代表算力饱和 |
DCGM_FI_PROF_GR_ENGINE_ACTIVE | 开启 | G 0–1 | 图形 / 计算引擎活动时间比例 |
DCGM_FI_PROF_SM_ACTIVE | 需取消注释 | G 0–1 | 全部 SM 存在驻留 warp 的周期比例均值;等 HBM 的 warp 也计入 |
DCGM_FI_PROF_SM_OCCUPANCY | 需取消注释 | G 0–1 | 平均驻留 warp 数与硬件上限之比 |
DCGM_FI_PROF_PIPE_TENSOR_ACTIVE | 开启 | G 0–1 | Tensor Core 流水线活动比例;不等于有效 MFU |
DCGM_FI_PROF_DRAM_ACTIVE | 开启 | G 0–1 | HBM 总线有读写事务的比例;不是显存容量占比 |
DCGM_FI_DEV_FB_USED / FB_FREE | 开启 | G MiB | 整卡 HBM 已用 / 空闲;含权重、CUDA Context、缓存池与 KV Pool |
DCGM_FI_DEV_MEM_COPY_UTIL | 开启 | G 0–100% | 显存控制器忙碌比例;不是 PCIe DMA 引擎独占利用率 |
DCGM_FI_PROF_PCIE_TX_BYTES / RX_BYTES | 开启 | G bytes/s | 已是速率 Gauge,不要套 rate() |
DCGM_FI_PROF_NVLINK_TX_BYTES / RX_BYTES | 需取消注释 | G bytes/s | 已是速率 Gauge,不要套 rate() |
DCGM_FI_DEV_SM_CLOCK / MEM_CLOCK | 开启 | G MHz | 实时频率;与降频原因一起看 |
DCGM_FI_DEV_CLOCK_THROTTLE_REASONS | 开启 | G 位掩码 | 功率墙、温度墙等降频原因 |
DCGM_FI_DEV_POWER_USAGE / TOTAL_ENERGY_CONSUMPTION | 开启 | G W / mJ | 实时功耗与累计能耗;能量需除以 1000 转为焦耳 |
DCGM_FI_DEV_XID_ERRORS | 开启 | G 错误码 | 值是最后一次触发的 XID 编号,不是次数,严禁求 rate() |
DCGM_EXP_XID_ERRORS_TOTAL | 开启 | C 次 | XID 事件计数,统计频次应使用它 |
DCGM_FI_DEV_PCIE_REPLAY_COUNTER | 开启 | C 次 | PCIe 链路重放;增量大于 0 指向物理链路问题 |
官方 dcgm-exporter 容器默认 CSV 仅启用基础指标。SM_ACTIVE、SM_OCCUPANCY、NVLINK_TX/RX_BYTES 默认被注释,需通过挂载自定义字段配置文件显式导出。
8.3单位与聚合陷阱
| 陷阱 | 原因 | 正确做法 |
|---|---|---|
对 PCIe / NVLink 速率套 rate() | 这些指标本身已是 bytes/s Gauge | 直接取值或做平均;累计流量用对应的 *_BYTES_TOTAL |
把 FB_USED 当作 KV 使用率 | 整卡显存包含权重、上下文、缓存池与碎片 | 引擎 KV 压力看 kv_cache_usage_perc 或 token_usage |
对 XID 指标求 rate() | 值是错误码编号,不是计数 | 用 DCGM_EXP_XID_ERRORS_TOTAL 统计频次 |
把 GPU_UTIL 当作 MFU | 只表示有 kernel 在跑 | 结合 PIPE_TENSOR_ACTIVE、DRAM_ACTIVE 与 Nsight |
跨卡平均 SM_ACTIVE 判断 TP 瓶颈 | 慢卡会被平均值掩盖 | 逐卡对比,找 Straggler |
8.4Profile 的层级与代价
| 工具 | 层级 | 能看到什么 | 限制 |
|---|---|---|---|
Nsight Systems nsys | S 线程 / Stream / Kernel | CPU 发射延迟、Stream 排队、Kernel 宽度、Memcpy 方向、GPU Bubble | CPU 侧 NVTX Range 宽度不等于设备端 Kernel 执行时间 |
Nsight Compute ncu | S 单 Kernel | Warp Stall、SM / DRAM / L2 吞吐、寄存器与共享内存限制、Roofline 位置 | 触发 Kernel Replay 并锁频,严重改变线上延迟;仅限离线或单步采样 |
| CUDA Event Elapsed | S 设备 Stream 区间 | 同一或同步 Stream 上的精确起止耗时(如 FPM wall_time) | 不计入其他并发 Stream 的资源争用 |
| NCCL Profiler Plugin | S Rank / Collective | AllReduce / AllGather / SendRecv 起止、Channel / Proxy 活动、多 Rank 到达偏斜 | 需 NCCL 2.23+ 并加载插件;导出为 Trace,不是默认 Prometheus 指标 |
网络与存储后端:
当软件层说“传输慢”,硬件发生了什么
软件层的传输耗时仅能说明存在停顿,无法解释停顿根因。下钻到物理网卡与驱动计数器,才能精准定性链路拥塞、丢包重传、接收缓冲耗尽或对端挂起。
RDMA 传输抖动的根因可能沿途分布在发送队列、交换机缓冲或对端 WQE 深度。定位不能只关注吞吐速率:port_xmit_wait 标识发送侧受阻(如流控反压),而 out_of_buffer 与 rnr_nak_retry_err 则是接收侧未能及时挂入缓冲的信号,两者的归因与处置路径完全相反。
9.1网卡计数器与单位
| 计数器 | 目录 | 单位 | 物理含义与判读 |
|---|---|---|---|
port_rcv_data / port_xmit_data | counters/ | C 4-byte words | 收发累计数据量;原始值以 4 字节 word 为单位,换算字节需乘 4 |
port_xmit_wait | counters/ | C ticks | 因 PFC Pause 或 IB Credit 不足导致发送停顿;增量飙升指向下游拥塞 |
np_cnp_sent / rp_cnp_handled | hw_counters/ | C 包 | RoCEv2 DCQCN:发出与处理 CNP 的次数 |
roce_adp_retrans / duplicate_request | hw_counters/ | C 次 | 自适应重传与重复请求;大于 0 直接证明丢包或乱序 |
rnr_nak_retry_err | hw_counters/ | C 次 | Receiver Not Ready 重试耗尽;接收端未及时挂入 Recv WQE 或内存不足 |
local_ack_timeout_err | hw_counters/ | C 次 | 等待 ACK 超时;交换机丢包、光纤闪断或对端假死 |
out_of_buffer | hw_counters/ | C 次 | 接收侧 FIFO 溢出丢包;常见于 PCIe 拥塞或 Host 内存带宽饱和 |
直接读取 sysfs 的 port_rcv_data 时,必须先乘 4 才是字节数。
node_exporter 的 node_infiniband_port_data_received_bytes_total 已完成换算,
直接对 bytes 求 rate() 即可。两种来源混用会得到 4 倍偏差。
9.2从“传输慢”到具体原因的检查顺序
nixl_bytes_transferred 与描述符数量。总字节小但描述符多,往往是内存碎片导致大量小包 Scatter-Gather。port_xmit_wait 飙升指向发送端受阻;out_of_buffer 与 rnr_nak_retry_err 则指示对端接收缓冲耗尽。roce_adp_retrans 与 local_ack_timeout_err 递增代表物理链路丢包、光模块闪断或对端宕机。np_cnp_sent 激增说明已触发拥塞通知,需检查交换机队列水位与 PFC/ECN 阈值匹配。9.3Mooncake:控制面与数据面分开看
mooncake_client_rpc_latency 仅度量客户端与 Master 节点间的控制元数据交互,不能代表实际数据搬运耗时。实际传输表现需观察 mooncake_transfer_get_latency 与 SSD I/O 指标;此外,单 Key 操作与 Batch 操作的数据体量差异悬殊,看板中必须按操作类型独立聚合。
| 指标 | 平面 | 单位 | 判读 |
|---|---|---|---|
mooncake_transfer_read_bytes / write_bytes | 数据面 | C bytes | 名字无 _total,但为单调 Counter,用 rate() 求吞吐 |
mooncake_transfer_get_latency / put_latency | 数据面 | H μs | 单 key 操作;单位是微秒 |
mooncake_transfer_batch_get_latency / batch_put_latency | 数据面 | H μs | 批量操作,数据量远大于单 key,必须单独看板 |
mooncake_client_rpc_count / rpc_latency | 控制面 | C 次 / H μs | Client → Master 的元数据 RPC;不是数据搬运耗时 |
mooncake_ssd_read_latency_us / write_latency_us | SSD | H μs | 与传输延迟可能嵌套包含,不可直接相加 |
mooncake_process_rss_bytes / swap_bytes | 进程 | G bytes | Swap 大于 0 会让微秒级 RDMA / RPC 退化为毫秒级长尾 |
mooncake_master_allocated_bytes / total_capacity_bytes | Master | G bytes | 全局分配与总容量;含多租户配额水位 |
mooncake_master_eviction_failed_total | Master | C 次 | 所有副本均处于租约锁定,无法淘汰释放空间 |
mooncake_master_snapshot_duration_ms / snapshot_restore_fail_total | Master HA | H ms / C 次 | 快照耗时过长会阻塞控制面 RPC |
Master 的 GET :9003/metrics 是标准 Prometheus 文本格式;
GET :9003/metrics/summary 是单行人类可读摘要,不能配置进 Scrape Job。
Store Client 端点(默认 :9300)需要 MC_STORE_CLIENT_METRIC=1 才启用。
Transfer Engine 的 MC_TE_METRIC=1 打印周期性日志统计,但新一代 TENT 引擎不支持该开关。
排障证据链:
先看什么,再对照什么,哪里必须停
按异常表象组织排查链路:先看第一响应指标,再交叉印证底层证据,并厘清推论成立的前提边界。
10.1核心观测集
全量指标会带来额外的高基数存储与抓取开销。覆盖主链路的最简指标组合如下,各层保留关键指标用于快速定界与证伪:
| 层 | 最小集合 | 它能证伪什么 | 通常需要的开关 |
|---|---|---|---|
| 入口 | dynamo_frontend_time_to_first_token_seconds、dynamo_router_overhead_seconds、dynamo_frontend_queued_requests | “引擎慢”其实发生在网关排队或路由 | 网关 / Dynamo 默认 |
| 引擎 | time_to_first_token_seconds、inter_token_latency_seconds、request_queue_time_seconds、num_requests_waiting | 延迟是排队、Prefill 还是 Decode 引起 | vLLM / SGLang 默认 |
| KV | kv_cache_usage_perc、num_preemptions、num_requests_waiting_by_reason | 高水位是否已经导致驱逐重算 | vLLM 默认;block 级需 --kv-cache-metrics |
| 缓存 / PD | kv_transfer_latency_ms、num_decode_prealloc_queue_reqs、prefetched_tokens_total | 跨节点交接或分层回载是否成为瓶颈 | PD / HiCache / Connector 开启后 |
| GPU | GPU_UTIL、SM_ACTIVE、PIPE_TENSOR_ACTIVE、DRAM_ACTIVE、FB_USED | “卡忙”是否等于“算力被用满” | DCGM;SM / NVLink 需自定义 CSV |
| 网络 | port_xmit_wait、roce_adp_retrans、rnr_nak_retry_err | 传输慢是否来自链路或接收端 | node_exporter / rdma_exporter |
PromQL:聚合顺序与统计语义
跨实例分位数必须先按 le 归并直方图分桶,再调用 histogram_quantile()。直接对多实例的 P99 取平均会抹平真实长尾,是监控统计中最隐蔽的误区。
实例 A 与 B 的 P99 分别为 120ms 与 400ms。若先在各实例计算分位再取算术平均,会得出虚假的 260ms;而先合并各桶样本再计算分位数,真实的全局 P99 依然是 400ms。对分位数求平均会掩盖单节点的严重长尾,给容量与 SLA 评估带来误导。
11.1四条聚合规则
histogram_quantile(q, sum by (le) (rate(..._bucket[5m])))。跨实例与跨容器聚合的通用基准。tp_rank="0" 或按 dp_rank 归并,避免被卡数成倍放大。NaN,应在分母添加 > 0 或使用 clamp_min()。11.2可直接使用的查询
histogram_quantile(0.99, sum by (model_name, le) (rate(vllm:time_to_first_token_seconds_bucket[5m])))
model_name 与 le 归并同口径桶后再取分位数,自动合并多实例与不同 priority 的样本。sum by (model_name) (rate(vllm:generation_tokens_total{tp_rank=~"0|"}[5m]))
tp_rank=~"0|" 既兼容未打 TP 标签的单卡环境,也避免在多卡张量并行下把吞吐重复累加。sum by (model_name) (rate(vllm:prefix_cache_hits_total[5m])) / (sum by (model_name) (rate(vllm:prefix_cache_queries_total[5m])) > 0)
> 0,确保在无流量时返回空集而非 NaN。sum(rate(lmcache_mp_lookup_hit_l1_total[5m])) / (sum(rate(lmcache_mp_lookup_requested_total[5m])) > 0)
lookup_hit_l2 仅统计 L1 未命中但命中 L2 的样本;总命中率可直接累加两层命中量,但分母始终使用总查询 Key 数。histogram_quantile(0.99, sum by (model_name, le) (rate(sglang:kv_transfer_latency_ms_bucket[5m]))) / 1000
sum by (instance) (rate(vllm:nixl_bytes_transferred_sum[5m])) / 1048576
nixl_bytes_transferred 为直方图,累计字节流量应提取 _sum 后求 rate();除以 1048576 转换为 MiB/s。sum by (instance) (rate(mooncake_transfer_read_bytes[5m])) / 1048576
_total 后缀,但底层为单调递增 Counter,需通过 rate() 计算瞬时带宽。sum by (instance, device) (rate(node_infiniband_port_data_received_bytes_total[1m])) / 1e9
node_exporter 已将原始 4 字节 Word 转换为字节;若直接从内核 sysfs 采集,需手动乘以 4。
vLLM V1 指标可能携带 priority、model_name 等维度。
用 sum by 时显式归并,避免同一指标因为标签组合不同被重复计算。
对于 cache_hit_rate 这类 Gauge,不要跨实例求算术平均:先取各实例的分子分母,再合并计算。
来源与版本:本文结论对应的上游坐标
各框架指标实现与命名演进活跃。下列链接与 Commit 为本文梳理时的核查坐标;生产环境部署前应以对应版本的 /metrics 输出为准。
客户端基准与网关
- NVIDIA GenAI-Perf Metrics客户端 TTFT、ITL、TPOT 与首个有效输出口径
- Dynamo Observability MetricsFrontend 网关与 KV Router 指标
- Dynamo SGLang ObservabilityFPM、CUDA Event 计时、Trace Level 1–4
vLLM
- vLLM Production Metrics核心、NIXL、HiSparse、Simple Offload、KV 生命周期、MFU
- vLLM v1/metrics/loggers.py注册名、标签、投机解码与 KV Fetch Stage
- vLLM OpenTelemetry 示例OTLP Trace Exporter 与 Span 属性
- vLLM Metrics Design指标设计与多进程聚合背景
SGLang
- SGLang Production Metrics流式 / 非流式 TTFT、TPOT 过滤条件、启用方式
- metrics_collector.py调度、投机解码、PD、HiCache 指标定义
- SGLang Request TracingTrace 启用、上下文传播、
SGLANG_TRACE_ASYNC - req_time_stats.pyRequestStage 枚举与打点位置
- SGLang PD DisaggregationPD 架构、Bootstrap 与 KV 传输数据流
KV Cache / LMCache / Mooncake
- LMCache Legacy vLLM EndpointIn-Process 导出与
lmcache:注册名 - LMCache Legacy MetricsIn-Process 完整指标清单
- LMCache MP Metrics
lmcache_mp.*、L0 / L1 / L2、条件命中语义 - Mooncake ObservabilityMaster 端点、进程内存 / Swap、Quota 与 Snapshot
- Mooncake Store DeploymentClient 指标端口与 TE 开关
- client_metric.h传输、Batch、RPC、SSD 指标与微秒单位
GPU / 通信 / 统计
- DCGM Exporter Metrics导出名、默认 CSV 启用状态、硬件兼容性
- DCGM Profiling ModuleProfiling 字段定义与计数器冲突
- DCGM 3.x Field IDs字段单位、XID 错误码、Throttle 位掩码
- NVML Utilization 定义(R550)GPU_UTIL 与 MEM_COPY_UTIL 的采样周期
- Nsight SystemsCPU / CUDA / NVTX 时间线
- Nsight ComputeKernel 级剖析与 Replay 开销
- NCCL Environment Variables
NCCL_PROFILER_PLUGIN等配置 - Prometheus Histograms跨实例聚合与
histogram_quantile
12.1核查基准
| 项目 | 锁定 Commit | 核查日期 |
|---|---|---|
| vLLM | ace90aeb247a757d32fd8e37e02eb60ba8fd0a46 | 2026-10-27 |
| SGLang | e4d4a9a7de5454cd8b854b21af723c2155a55a3d | 2026-10-27 |
| Mooncake | b03155412e7d41782b24558548b952fd7f2d6ee3 | 2026-10-27 |
指标命名常随版本迭代更替,但其背后的三维坐标——观察层级(Request / Server)、物理单位(s / ms / μs / bytes / token / block)以及导出形态(Counter / Gauge / Histogram / Span)始终稳定。接入新指标时,先对齐其所在坐标,即可快速明确其在监控体系中的定位。