27届大模型面试准备(七十八):大模型推理线上问题定位与根因分析工程——TTFT/TBT 抖动、OOM、吞吐跌零排查手册

27届大模型面试准备(七十八):大模型推理线上问题定位与根因分析工程------TTFT/TBT 抖动、OOM、吞吐跌零排查手册

引言

本文是系列第 78 篇。前两篇讲了高可用、容灾与多模态训练,这一篇聚焦一个所有上线模型都逃不掉的现实问题:线上推理出问题了,怎么在 10 分钟内定位根因。面试官非常喜欢拷问候选人的"故障排查肌肉记忆"------因为能背架构的人很多,能在凌晨三点对着监控曲线说出"这是 PD 分离后 KV 调度不均导致 TBT 长尾"的人很少。

一篇合格的推理工程简历,必须能讲清楚四类高频故障:TTFT(首 token 延迟)抖动、TBT(token 间延迟)长尾、OOM 显存溢出、吞吐跌零(throughput 突然掉到接近 0)。本篇给出可操作的排查链路与代码级诊断手段。

复制代码
线上推理问题定位总览
===========================================
           监控告警触发
                │
    ┌───────────┼───────────────┐
    ▼           ▼               ▼
 TTFT 抖动   TBT 长尾      吞吐跌零/OOM
    │           │               │
    ▼           ▼               ▼
 Prefill 侧   Decode 侧      资源/数据/调度
 排队/算子     KV/调度/批     瓶颈定位
    │           │               │
    └───────────┼───────────────┘
                ▼
         根因定位 → 止血 → 复盘

一、关键指标定义与采集

先把指标说清楚,否则排查无从谈起。

指标 含义 健康量级 用户感知
TTFT 首 token 延迟(请求到第一个 token) < 500ms(小模型) "响应快不快"
TBT token 间延迟(解码步间隔) < 50ms(实时) "打字流不流畅"
TPOT 每 output token 时间 同 TBT 同 TBT
E2E 延迟 整句完成时间 TTFT + n*TBT 总耗时
QPS/吞吐 每秒请求/生成 token 数 视资源 成本与容量
批大小 同批并发序列数 动态 显存占用

采集方式:在推理框架里对每个请求打点(request_id、enqueue_time、first_token_time、last_token_time),用 Prometheus 暴露 histogram,Grafana 看分位数(p50/p90/p99)。一定要看 p99 而不是均值------均值掩盖长尾。

复制代码
import time, prometheus_client as pc

HIST_TTFT = pc.Histogram("llm_ttft_ms", "Time to first token", buckets=[100,300,500,1000,2000])
HIST_TBT  = pc.Histogram("llm_tbt_ms", "Inter-token latency", buckets=[10,30,50,100,200])

class ReqTracer:
    def __init__(self, req_id):
        self.req_id = req_id
        self.t_enq = time.time()
        self.t_first = None
    def on_first(self):
        self.t_first = time.time()
        HIST_TTFT.observe((self.t_first - self.t_enq) * 1000)
    def on_step(self, t_prev, t_now):
        HIST_TBT.observe((t_now - t_prev) * 1000)

二、TTFT 抖动排查

TTFT 高通常卡在 Prefill 阶段。根因分布:

复制代码
TTFT 高
 ├─ 请求排队(并发>容量)
 │    看调度队列深度、排队时长直方图
 ├─ Prefill 算子慢(长 prompt、未切分)
 │    看 prompt token 数分布、attention 是否用 flash
 ├─ 冷启动 / 模型未预热
 │    看实例刚起/刚扩的时段
 └─ 批调度策略激进导致大请求阻塞小请求
      看 chunked prefill 是否开启

最容易被忽略的一点:长 prompt 的 Prefill 是计算密集且不可抢占的,一个 8k token 请求 Prefill 可能要几百毫秒,会直接拖高同窗口所有请求的 TTFT。解法:开启 chunked prefill(把 Prefill 切成小块与 Decode 交织),或用 PD 分离把 Prefill 和 Decode 放到不同实例池。

复制代码
# vLLM 开启 chunked prefill(示意)
# 启动参数
--enable-chunked-prefill \
--max-num-batched-tokens 8192 \
--scheduler-policy fcfs

三、TBT 长尾排查

TBT 高卡在 Decode 阶段 。Decode 每步只算一个新 token,计算量小但受 KV Cache 带宽和批大小支配。根因:

根因 信号 解法
批内混入超长序列,KV 挤占带宽 同批 max_len 远高于均值 按长度分组调度
KV Cache 碎片化 显存够但申请失败 换 paged/prefix cache
调度不均(PD 分离后 KV 传输慢) Decode 实例等 KV 优化 KV 传输/本地化
某卡降频/掉速 单卡 TBT 明显偏高 查 NVML 温度/功耗
采样 top-p 高导致核函数慢 高 top-p 时变慢 降 top-p/用投机解码
复制代码
# 用 pynvml 巡检单卡降频(TBT 长尾时逐个卡排查)
import pynvml
pynvml.nvmlInit()
for i in range(pynvml.nvmlDeviceGetCount()):
    h = pynvml.nvmlDeviceGetHandleByIndex(i)
    util = pynvml.nvmlDeviceGetUtilizationRates(h)
    clk = pynvml.nvmlDeviceGetClockInfo(h, pynvml.NVML_CLOCK_SM)
    pwr = pynvml.nvmlDeviceGetPowerUsage(h) / 1000.0
    print(f"gpu{i} util={util.gpu}% sm_clk={clk}MHz pwr={pwr}W")
# 某卡 sm_clk 远低于其他卡 => 降频/掉速,是 TBT 长尾元凶之一

四、OOM 显存溢出排查

OOM 是上线最常见的"硬故障"。多模态/长上下文尤其容易炸。定位清单:

复制代码
OOM 排查树
├─ 静态占用的 KV Cache 预留过大?
│    算:kv_bytes = 2 * n_layers * n_kv_heads * head_dim * seq * batch * dtype
│    下调 max_num_seqs / max_model_len
├─ 单请求超长(恶意/异常 prompt)?
│    设 max_input_len 上限 + 拒绝/截断
├─ 激活峰值(Prefill 大 batch)?
│    降 max_num_batched_tokens、开梯度无关的重算
├─ 碎片化导致"有总显存但无连续块"?
│    用 paged KV(vLLM/TSR 默认)、prefix cache 复用
└─ 多模型共卡?
      隔离部署或限制并发

def est_kv_bytes(n_layers, n_kv_heads, head_dim, seq, batch, dtype_bytes=2):
    per_tok = 2 * n_layers * n_kv_heads * head_dim * dtype_bytes  # K and V
    return per_tok * seq * batch

# 例:7B 模型 32层 32头 dim128, seq=4096, batch=64
print(est_kv_bytes(32, 32, 128, 4096, 64) / 1e9, "GB")
# 据此倒推 max_num_seqs,避免预留超显存

五、吞吐跌零排查

吞吐突然掉到接近 0,通常是"软死锁"而非硬件坏:

现象 根因 快速止血
请求全部卡住 调度死锁/某请求异常占批 超时踢出 + 重启 worker
QPS 为 0 但进程在 上游断流/健康检查误杀 查网关与探活配置
GPU 利用率 0 但显存满 KV 泄漏(请求未释放) 修请求生命周期,加 watchdog
吞吐阶梯下跌 扩缩容抖动/权重加载阻塞 预热 + 灰度发布

关键工程实践:给每个请求设硬超时与最大生成长度,并在请求结束时强制释放 KV 块,用 watchdog 监控"显存占用 vs 活跃请求数"的偏离度,偏离即告警。

复制代码
import asyncio

async def guarded_generate(req, max_tokens=2048, timeout=60):
    try:
        return await asyncio.wait_for(
            engine.generate(req, max_tokens=max_tokens), timeout=timeout)
    except asyncio.TimeoutError:
        engine.abort(req.req_id)          # 必须显式 abort 释放 KV
        raise TimeoutError(f"req {req.req_id} aborted after {timeout}s")

六、定位方法论总结

把上面串成一张"止血---定位---复盘"闭环:

复制代码
1. 看告警:哪个指标 p99 异常(TTFT/TBT/吞吐/OOM)
2. 切维度:按实例/卡/路由/用户维度下钻,找异常子集
3. 对变更:是否刚发布/扩容/改配置(时间相关性最强)
4. 抓现场:保留一个异常请求 trace + 对应卡监控快照
5. 止血:降级(限流/关特性/回滚)→ 根因 → 加防护(超时/配额)

面试速答

问:TTFT 高一般查什么?

答:先看是不是 Prefill 阶段排队(并发超容量)或长 prompt 未切分;再确认 chunked prefill / PD 分离是否开启;最后看实例冷启动。核心是 Prefill 计算密集且不可抢占,长请求会阻塞同窗口。

问:TBT 长尾但 GPU 利用率不低,为什么?

答:多半是批内混入超长序列挤占 KV 带宽,或某卡降频/掉速。用 pynvml 逐个卡看 SM 时钟和功耗,往往能抓到单卡异常。

问:OOM 但显存总量够,怎么回事?

答:KV Cache 碎片化------没有连续块可分配。用 paged KV(vLLM/TensorRT-LLM 默认)和 prefix cache 复用解决,并下调 max_num_seqs。

问:吞吐跌零怎么快速止血?

答:先确认是否请求级死锁/KV 泄漏,给请求加硬超时与 abort 释放;显存满但利用率 0 基本是 KV 泄漏,重启 worker 并修生命周期。

高频追问清单

  1. chunked prefill 和普通 prefill 在调度上有什么区别?对 TTFT/TBT 各有什么影响?
  2. PD 分离后 KV Cache 怎么跨节点传输?带宽瓶颈怎么估?
  3. paged KV 的碎片率怎么度量?什么负载下仍会碎片?
  4. 连续批处理(continuous batching)和静态批处理对长尾延迟影响?
  5. 投机解码引入后 TBT 抖动变大,可能是什么原因?
  6. 多租户下怎么给不同优先级请求做资源隔离与配额?
  7. 显存监控里"保留但无活跃请求"的 KV 怎么排查泄漏?
  8. 怎样设计请求级 trace,使一次慢请求可还原完整调度路径?
  9. 压测时怎么构造能暴露长尾的流量模型(泊松/突发)?
  10. 大促/峰值前怎么做容量评估与限流阈值设定?
相关推荐
ReleaseU2 小时前
数据库 MCP:让 Agent 自己查数据、写报表
人工智能·大模型
夏文强4 小时前
DeepSeek Harness 可观测性:用 OpenTelemetry 把会话遥测出去
人工智能·开源·大模型·agent·deepseek
夏文强5 小时前
DeepSeek Harness SDK 集成:把 Agent 嵌进你的应用
人工智能·开源·大模型·agent·deepseek
thesky12345614 小时前
27届大模型面试准备(七十一):多租户 GPU 虚拟化与推理隔离工程——MIG、MPS 与噪声邻居治理
大模型·mig·多租户·mps·gpu虚拟化·显存隔离·噪声邻居
虎虎(_ _)。゜zzZ1 天前
Qdrant向量数据库工程实战
数据库·人工智能·大模型·向量数据库·rag·qdrant
deepseek231 天前
GPT-6 Astra 发布拆解:Computer Use 产品化时刻、CoT 监控失守与 AGI 契约的终结
大模型·openai·ai agent
程序猿编码1 天前
基于GGML的C++17轻量化语音推理引擎:说话人识别与语音分析技术全解析
开发语言·c++·pytorch·深度学习·神经网络·大模型
夏文强1 天前
DeepSeek Harness 底层探秘:Cordis 元框架与「一切皆插件」的实现
人工智能·开源·大模型·agent·deepseek
夏文强1 天前
DeepSeek Harness 可追溯性实战:会话日志的 resume、fork 与 replay
人工智能·开源·大模型·agent·deepseek