06. 线上延迟高时你通常怎么优化?

整理线上延迟高时你通常怎么优化?的面试回答思路与拆解方式。

简单回答

线上延迟优化要先定位瓶颈在哪个环节——是 Prefill 太慢(Prompt 太长)、Decode 太慢(模型太大或带宽不够)、检索太慢(向量库或 Rerank 耗时高)、还是系统层面的问题(排队、网络 IO、CPU 预处理)。然后对症下药:Prompt 太长可以裁剪上下文或用 Prompt Caching;Decode 慢可以量化模型或换更小的模型;检索慢可以优化索引或减少 Rerank 候选集;系统层面可以优化调度、加缓存、做异步处理。

详细解释

先分解延迟

线上延迟是端到端的总时间,它是多个环节的叠加。不做分解就优化等于瞎调。

一个典型的 RAG + LLM 请求的延迟分解:

Query 预处理(tokenization、Query Rewrite)→ Embedding 编码(query 向量化)→ 向量检索 → Rerank → Prompt 组装 → LLM Prefill(TTFT)→ LLM Decode(生成 N 个 token)→ 后处理和返回。

要在每个环节打时间戳,算出各环节的耗时占比。通常 LLM 推理(Prefill + Decode)是大头,但不一定——有些系统 Rerank 就占了 500ms,有些系统 Query Rewrite 用了一次 LLM 调用花了 1 秒。

LLM 推理延迟优化

TTFT 高(Prefill 慢)。 根因是 Prompt 太长。裁剪上下文——减少送给模型的 Chunk 数量,只保留 Rerank 分数最高的几个。用 Prompt Caching 避免重复 Prefill——System Prompt 部分缓存起来,后续请求只 Prefill 变化的部分。换支持 FlashAttention 的推理框架减少 Prefill 计算量。

Decode 慢(per-token latency 高)。 根因通常是模型太大、访存瓶颈。量化模型——INT8 或 INT4 直接减少参数读取量。如果用 API 调用,考虑换更小的模型(如果效果可以接受的话)。增加 GPU 数量做张量并行(但通信开销可能抵消部分收益)。

输出太长。 如果模型输出了 500 个 token 但用户只需要 100 个,可以在 Prompt 中加"请简洁回答"的指令,或者设置 max_tokens 限制。Streaming 输出可以让用户更早开始看到内容,虽然总时间不变但感知体验好很多。

检索链路延迟优化

向量检索慢。 检查向量索引类型——HNSW 比 IVF 快但内存占用大。检查是否有不必要的过滤条件拖慢了检索。如果数据量大考虑分片或预过滤。

Rerank 慢。 Rerank 用的 Cross-Encoder 模型可能比较大。减少 Rerank 的候选集大小(从 Top-100 减到 Top-30)。换更轻量的 Rerank 模型。如果对精度要求不极致,可以跳过 Rerank 直接用向量检索的排序。

Embedding 编码慢。 在线 query 的 Embedding 编码通常很快(单条文本几毫秒),如果慢了检查是不是用了太大的 Embedding 模型或没有用 GPU 加速。

系统层面优化

排队延迟。 请求到达时 GPU 在忙,需要等待。增加 GPU 数量扩容。优化 Continuous Batching 的调度效率。如果有明显的流量高峰可以做预热和弹性扩缩容。

Query Rewrite 延迟。 如果用 LLM 做 Query Rewrite,这本身就是一次完整的 LLM 调用,可能花 0.5~1 秒。考虑用更小的模型做 Rewrite,或者只在多轮对话场景下做 Rewrite(单轮对话通常不需要)。

网络延迟。 客户端和服务端之间的网络 RTT。如果是跨区域调用(比如客户端在国内、API 在海外)延迟可能几百毫秒。考虑就近部署或用 CDN 代理。

串行改并行。 向量检索和 BM25 检索如果是串行执行的,可以改成并行——两路同时发请求然后合并结果。Query Rewrite 和 Embedding 编码如果有依赖关系就只能串行,如果没有就并行。

缓存

结果缓存——相同或相似的 query 直接返回缓存的回答。简单有效但要注意缓存失效(知识库更新后旧缓存可能不再准确)。用 query 的 Embedding 向量做相似度匹配来判断是否命中缓存(语义缓存),比用字符串精确匹配更灵活。

KV Cache / Prompt Caching 前面已经讲过。

降级策略

如果优化后延迟还是不满足要求,可以做降级——当延迟超过阈值时自动切换到更快但效果略差的模式。比如跳过 Rerank、减少检索的 Chunk 数量、换更小的模型、或者直接返回"系统繁忙请稍后再试"。

面试时可以这样答

线上延迟高先分解——在每个环节打时间戳看哪里慢。通常 LLM 推理是大头,但不一定——有些系统 Rerank 或 Query Rewrite 占了大量时间。

LLM 推理延迟方面,TTFT 高就裁剪上下文或用 Prompt Caching,Decode 慢就量化模型或换更小的模型。检索链路方面,Rerank 慢就减小候选集或换轻量模型。系统层面看有没有串行可以改并行的、有没有排队延迟需要扩容的。

一个实用的优化是 Streaming 输出——虽然总时间不变但用户更早看到内容,感知体验好很多。另一个是语义缓存——相似 query 直接返回缓存结果。

如果优化后还不够,就做降级策略——延迟超阈值时自动切换到更快的模式,保证可用性。

常见追问

  1. 你实际项目中延迟最大的瓶颈在哪个环节?
  2. 语义缓存具体怎么实现?命中率能到多少?
  3. Streaming 输出在工程上有什么需要注意的?