metrics、profiling 与 evidence#
这一节会解释 metrics、profiling 和更硬的证据面怎样彼此配合,避免把"有指标"误读成"已经能定位问题"。
metrics 和 profiling 为什么不能混看#
metrics 更适合回答"系统整体现在怎么样",profiling 更适合回答"这一段为什么慢"。这两者都属于证据,但粒度和代价完全不同。
如果把它们混看,最常见的后果就是:
- 只看到整体吞吐变化,却不知道是哪个阶段变慢了;
- 或者 profile 看得太细,却没有先确认问题到底是不是整体趋势。
哪些 metrics 值得先看#
SGLang 在 /metrics 路径(Prometheus 格式)暴露系统指标。以下是排障中最先需要盯的几个:
请求状态类
sglang:num_running_reqs # 当前正在执行(在 forward pass 里)的请求数
sglang:num_waiting_reqs # 在 waiting queue 里等待的请求数
sglang:num_queue_reqs # 包含 grammar queue 的等待请求总数这三个数字放在一起看,能立刻区分:
num_running低但num_waiting高 → scheduler admission 有瓶颈,或 batch 成不起来num_running饱和,num_waiting也高 → 正在正常工作,只是负载大- 两者都低但延迟高 → 问题在单请求执行侧,不是吞吐侧
资源占用类
sglang:token_usage # KV cache 占用率(0.0 ~ 1.0),接近 1.0 代表显存紧张
sglang:gpu_memory_usage_bytes # GPU 显存绝对用量
sglang:cache_hit_ratio # RadixCache 前缀命中率token_usage 接近 1.0 时,scheduler 会开始更激进地驱逐 KV,cache_hit_ratio 通常随之下降。这两个字段放在一起比单独看更有价值。
延迟类
sglang:e2e_req_latency_seconds_bucket # E2E 延迟直方图(histogram)
sglang:ttft_seconds_bucket # time-to-first-token 延迟直方图
sglang:inter_token_latency_seconds_bucket # token 间延迟直方图(decode 速度)这三个 histogram 能让你看到 P50、P90、P99 的分布,而不只是平均值。P99 偏高但 P50 正常,通常代表个别请求遭遇了调度或驱逐抖动。
吞吐类
sglang:gen_throughput # 生成吞吐(tokens/sec),全局累积
sglang:prefill_throughput # prefill 吞吐(tokens/sec)一个典型的 metrics 观察场景#
假设你看到服务端 P99 latency 升高。第一步先看:
curl http://localhost:30000/metrics | grep -E 'num_running|num_waiting|token_usage|cache_hit'输出示例:
sglang:num_running_reqs 8
sglang:num_waiting_reqs 47
sglang:token_usage 0.91
sglang:cache_hit_ratio 0.34num_waiting 是 num_running 的 5 倍多,token_usage 已经接近饱和,cache_hit_ratio 从正常的 0.7+ 掉到了 0.34。这组数字指向的方向很清楚:KV cache 压力大,驱逐频繁,导致 cache 命中率下降,batch 无法快速补充新请求,waiting queue 积压。
这是一个典型的"资源链条"问题,不是 GPU 计算变慢了,而是 memory pressure 导致调度速度下降。
profiling 真正补的是哪层证据#
当 metrics 已经告诉你"有问题",但还不知道"慢在代码里哪里",这时才需要 profiling。
SGLang 支持两种 profiling 路径:
1. 通过 server 参数开启 torch profiler
启动时加 --enable-torch-profiler,SGLang 会在执行时插入 PyTorch Profiler。产出的 trace 可以用 chrome://tracing 或 torch.profiler.tensorboard_trace_handler 查看。
这个路径适合回答:scheduler_step 里哪个内核最慢?
典型输出片段(伪结构):
scheduler_step 12.4ms
├─ prepare_input 0.8ms
├─ forward (attention) 8.3ms
│ ├─ FlashAttention 7.1ms
│ └─ AllReduce 1.2ms
├─ sample 0.6ms
└─ update_cache 2.7ms ← 如果这里异常高,通常是 RadixCache insert 太频繁2. 通过 nsight/nsys 进行 CUDA 级 profiling
nsys profile python -m sglang.launch_server ... 可以拿到 CUDA kernel 级时间线,用于排查 GPU 利用率低、bubble 多等问题。这个路径代价较重,适合确认已知性能问题的根因,不适合首次排障。
什么时候应该从 metrics 切到 profiling#
更稳的判断通常是:
- 如果你还没确认问题是不是整体趋势,先停在 metrics;
- 如果 metrics 已经指向了某一段(例如
inter_token_latency高,但num_running_reqs正常),再进入 profiling 看 forward 内核的时间分布; - 如果 profiling 仍然不够,再考虑 request dump 或离线模拟。
这层判断很重要,因为 profiling 是重工具。它应该出现在"已经知道问题在哪个阶段"之后,而不是第一步。
为什么这里还要强调 evidence#
性能问题特别容易掉进"感觉更慢了"这种口头判断里。对维护者来说,真正值钱的不是一张图,而是:
- 哪个 metric 在哪个时间点超过了阈值;
- 在什么输入条件下(batch size、序列长度、cache 命中率)问题出现;
- 和上一个版本相比,哪个 histogram 的 P99 升高了多少;
- 这个数字是否足以支撑继续优化或触发回滚。
也就是说,profiling 的目标不是多收集信息,而是形成足以支撑工程判断的证据。
小结#
这一节真正想提醒读者的是:
- metrics 先回答"是不是有问题,问题在哪一层";
- profiling 再回答"代码层面瓶颈在哪个内核";
- 二者一起才更像一条完整证据链。
如果把这一节读稳,后面的性能讨论就更不容易退化成"跑了个 benchmark,感觉有变化"这种不够严肃的判断。
叶王 © 2013-2026 版权所有。如果本文档对你有所帮助,可以请作者喝饮料。