Chapter 09

观测与性能调优:看懂 /metrics

生产上线后,延迟 p99 突然飙到 5s 怎么排查?本章把 vLLM 的 /metrics 每个指标拆解,配一套 Grafana 看板,讲常见瓶颈的识别路径和修复手段。

/metrics 全家桶

vLLM 启动后访问 http://host:8000/metrics(Prometheus 格式)。核心指标分四类:

1. 吞吐与延迟

指标含义健康区间
vllm:time_to_first_token_secondsTTFT 直方图聊天 p95 < 500ms
vllm:time_per_output_token_secondsTPOT 直方图bf16 70B < 30ms
vllm:e2e_request_latency_seconds端到端请求延迟因输出长度而异
vllm:prompt_tokens_total累计 prompt token 数
vllm:generation_tokens_total累计生成 token 数

2. 调度状态

指标含义告警阈值
vllm:num_requests_running正在 decode 的序列数持续 = max-num-seqs 说明满载
vllm:num_requests_waiting排队等 prefill 的请求数> 10 就要加容量
vllm:num_requests_swapped被 swap 到 CPU 的序列数> 0 持续就说明池子小
vllm:num_preemptions_total累计 preemption 次数增长快 = 要加 GPU / 减并发

3. KV cache 使用率

指标含义告警阈值
vllm:gpu_cache_usage_percGPU KV 池使用率持续 > 90% 要扩容
vllm:cpu_cache_usage_percCPU swap 池使用率> 0 = 有溢出
vllm:gpu_prefix_cache_hit_rate前缀缓存命中率多轮对话 > 40% 算正常

4. 投机解码 / LoRA(按特性)

指标含义
vllm:spec_decode_draft_acceptance_rate草稿接受率
vllm:spec_decode_system_efficiency投机解码系统效率
vllm:lora_requests_total按 lora_name 分组的请求数

Grafana 看板最小集合

用 Prometheus scrape vLLM,Grafana 导入以下 4 个 Panel 就能覆盖 80% 排障场景:

  1. Panel 1 - 延迟直方图:histogram_quantile(0.95, rate(vllm:time_to_first_token_seconds_bucket[5m])) 和同样的 TPOT 画两条线
  2. Panel 2 - 吞吐:rate(vllm:generation_tokens_total[1m]) 看每秒输出 token 数
  3. Panel 3 - 队列深度:vllm:num_requests_waiting + vllm:num_requests_running 叠加堆积图
  4. Panel 4 - KV 压力:vllm:gpu_cache_usage_perc 折线 + rate(vllm:num_preemptions_total[5m]) 柱状图
# prometheus.yml 片段
scrape_configs:
  - job_name: 'vllm'
    scrape_interval: 5s
    static_configs:
      - targets: ['vllm-host:8000']
    metrics_path: '/metrics'

benchmark 压测:用官方脚本

vLLM 自带 benchmark_serving.py,能生成 Poisson 到达的合成负载:

# 准备 ShareGPT 数据集
wget https://huggingface.co/datasets/anon8231489123/ShareGPT_Vicuna_unfiltered/resolve/main/ShareGPT_V3_unfiltered_cleaned_split.json

# 起服务(另一终端)
vllm serve meta-llama/Llama-3-8B-Instruct

# 压测
python -m vllm.entrypoints.openai.api_server.benchmark \
  --backend openai \
  --base-url http://localhost:8000 \
  --model meta-llama/Llama-3-8B-Instruct \
  --dataset ShareGPT_V3_unfiltered_cleaned_split.json \
  --num-prompts 1000 \
  --request-rate 20      # QPS

输出长这样:

Successful requests:     1000
Benchmark duration:       50.12 s
Total input tokens:       234567
Total generated tokens:   145234
Request throughput:       19.95 req/s
Input token throughput:   4680 tok/s
Output token throughput:  2897 tok/s
─── Time to First Token ───────────────
Mean TTFT:       120 ms
Median TTFT:      95 ms
P99 TTFT:        480 ms
─── Time per Output Token ─────────────
Mean TPOT:        15 ms
Median TPOT:      13 ms
P99 TPOT:         45 ms

常见性能问题速查

症状:TTFT 高、TPOT 正常
通常是 prefill 排队或 prompt 太长。查 num_requests_waiting,开 --enable-chunked-prefill,必要时减 --max-model-len
症状:TTFT 正常、TPOT 飙升
decode batch 太大或 KV 压力爆。查 num_requests_running,降 --max-num-seqs,或开 KV FP8 腾空间。
症状:偶发 p99 抖动几百 ms
多半是 prefill 打断 decode。开 --enable-chunked-prefill(0.6+ 默认开),调大 --max-num-batched-tokens
症状:QPS 一冲就 OOM
KV 池不够。降 --gpu-memory-utilization 以外先量化(AWQ),再开 --kv-cache-dtype fp8
症状:多卡推理比单卡还慢
拓扑问题,看 nvidia-smi topo -m,没 NVLink 不要 TP;或通信环境变量没配,设 NCCL_IB_DISABLE=0 NCCL_SOCKET_IFNAME=...
症状:同 prompt 重复请求也慢
前缀缓存没开。加 --enable-prefix-caching,多轮对话 TTFT 立刻降。

几个被忽视的调优杠杆

1. --scheduler-delay-factor

默认 0,调度器来一个请求立刻处理。设成 0.5 让它等一小段(约当前步时间的 50%)聚合更多 prefill 一起做,吞吐↑ TTFT↑。夜间批量任务很香。

2. --swap-space

默认 4GB(CPU swap)。如果你的机器 RAM 富裕(256GB+),设成 64 能扛住更大并发波峰。但 swap 回来要重算 attention,看 num_requests_swapped 长期 > 0 说明你真正缺的是 GPU 不是 swap。

3. --enforce-eager

关掉 CUDA Graph 捕获。默认 vLLM 会在启动时 warmup 一堆 graph 加速 decode,首启动慢 30 秒。调试时加 --enforce-eager 更快但运行时慢 5-10%,生产一定关掉(不加这个参数)。

4. VLLM_USE_V1 环境变量

vLLM 0.6.3+ 有新调度器 V1(重写过,吞吐更高,尤其大 batch)。设 VLLM_USE_V1=1 启用。

日志怎么读

INFO Avg prompt throughput: 1234.5 tokens/s,
     Avg generation throughput: 567.8 tokens/s,
     Running: 42 reqs,
     Swapped: 0 reqs,
     Pending: 3 reqs,
     GPU KV cache usage: 67.3%,
     CPU KV cache usage: 0.0%

这是 vLLM 每 10 秒打一次的摘要行,生产上拿它做 alerting:

Profiling:深挖单请求

Prometheus 看的是聚合,想看单请求里每个 layer 耗时多少,用 Nsight Systems:

# 启动前 export
export VLLM_TORCH_PROFILER_DIR=/tmp/vllm-prof
vllm serve meta-llama/Llama-3-8B-Instruct

# 触发 profile(其他终端)
curl -X POST http://localhost:8000/start_profile
# 发几个测试请求
curl -X POST http://localhost:8000/stop_profile

# 用 Nsight 打开 /tmp/vllm-prof/*.json

能看到每次 forward 的 attention kernel / FFN / all-reduce 各占多少时间,针对性优化。

容量规划经验公式

并发上限 ≈ (KV 池 token 数) / (平均单请求 KV token 数)

例:A100-80G, Llama-3-8B, max-model-len=8192
  KV 池 = 56GB → ~11 万 token
  假设平均 prompt 500 + output 300 = 800 token/请求
  理论并发 = 110000 / 800 ≈ 137 并发

再留 30% buffer 应对突发:
  实际 --max-num-seqs 建议设 100

上线前用这公式估容量,压测对齐,就不会上线第一天就炸。

本章小结