缓存策略、批推理(batching)、异步 /并发机制

你好!很高兴能以同行的身份和你交流。作为一名顶级大模型(LLM)部署工程师,我们的核心KPI通常是三个维度的"不可能三角":低延迟(Low Latency)、高吞吐(High Throughput)和 低成本(Low Cost/High GPU Utilization)

你提到的缓存、批处理、异步并发正是打破这个三角、压榨硬件性能的三把核心"手术刀"。

下面我将结合主流推理框架(如 vLLM, TGI, Triton Inference Server)和实际业务场景(如 RAG、高并发 Chatbot),深入剖析这三者是如何落地的。


一、 缓存策略 (Caching Strategies)

核心目标:用显存换计算,降低 TTFT(首字延迟)。

在 LLM 中,缓存不仅仅是 Redis 里的 Key-Value,更多是指 GPU 显存中的 KV Cache

1. KV Cache 的内存管理(PagedAttention)
  • 痛点:Transformer 的自回归生成过程中,每生成一个 token 都需要复用前面所有 token 的 Key-Value 矩阵。传统做法是预分配连续显存,导致大量显存碎片(Internal Fragmentation),限制了 Batch Size。

  • 落地应用

    • 技术选型 :我们会在生产环境使用基于 PagedAttention(如 vLLM)的技术。它像操作系统管理虚拟内存一样,将 KV Cache 切分成块(Block),非连续存储。
    • 效果:显存利用率接近 100%,允许我们在同一张卡上塞入更大的 Batch Size,吞吐量直接翻倍。
2. 前缀缓存 (Prefix Caching / RadixAttention)
  • 场景:在 RAG(检索增强生成)或多轮对话中,System Prompt(系统提示词)或检索到的长文档 Context 往往是不变的,只有用户最后的问题在变。

  • 落地应用

    • 机制:我们会在推理引擎中维护以此前 Prompt Token 序列的 Hash 为键的各种 KV Block 树。
    • 实战:当一个 Request 进来,系统先去"树"上找有没有匹配的前缀。如果 System Prompt + Context 有 2000 token,且命中缓存,GPU 就不需要重新计算这 2000 token 的 KV 值,直接从显存 copy 或映射即可。
    • 收益TTFT(首字延迟)降低 80% 以上,且大幅减少计算负载。
3. 语义缓存 (Semantic Caching)
  • 场景:高频重复查询(如电商客服:"怎么退款?"和"退款流程是什么?")。

  • 落地应用

    • 在进入 GPU 之前,在网关层(API Gateway)引入向量数据库(Milvus/Qdrant)+ Redis。
    • 将用户 Query 向量化,如果相似度极高(>0.95),直接返回之前的缓存结果,完全不消耗 GPU 资源

二、 批推理 (Continuous Batching / Iteration-level Batching)

核心目标:填满 GPU 计算单元,提升 Throughput(吞吐量)。

传统的 Static Batching(静态批处理)要求"等齐 N 个请求再出发,所有人等最慢的那个人说完才结束",这在 LLM 场景(输入输出长度不一)是灾难性的。

1. 连续批处理 (Continuous Batching)
  • 原理 :也叫 In-flight batching。不再等待整个 Batch 结束。当 Batch 中某个请求生成了 [EOS](结束符),立即将其移出,并从等待队列(Waiting Queue)中插入一个新的请求进来填补空位。

  • 落地应用

    • 调度策略 :作为工程师,我需要配置调度器的 max_num_seqs(最大序列数)和 max_num_batched_tokens(最大 token 总数)。
    • 实战:在流量高峰期,GPU 始终处于"满载"状态,没有任何计算气泡(Bubbles)。
    • 收益 :相比静态批处理,吞吐量通常提升 10-20 倍
2. 动态 Batch 大小的权衡
  • 实战经验:Batch Size 不是越大越好。

    • Batch 过大 -> KV Cache 占用过多 -> 只能把部分 Layer 卸载到 CPU 或 OOM -> 性能崩塌。
    • Batch 过大 -> 计算密度过高 -> TPOT(每 token 生成时间)增加 -> 用户感觉生成变慢。
    • 策略 :通过压测(Locust/K6)找到特定显卡(如 H800 vs 4090)上的最佳甜点(Sweet Spot),通常我们会限制最大 Batch Size 以保证 TPOT 在 50ms 以内。

三、 异步与并发机制 (Async / Concurrency)

核心目标:解耦 I/O 与计算,处理高并发连接,提升用户体验。

LLM 推理是计算密集型,但 Web 服务是 I/O 密集型。

1. 全链路异步 (AsyncIO + FastAPI)
  • 场景:Python 的 GIL 锁限制了多线程能力,但在等待 GPU 推理、等待数据库检索时,CPU 是空闲的。

  • 落地应用

    • 入口层 :使用 FastAPI + uvicorn (ASGI)。所有 Controller 全部定义为 async def
    • 推理通信 :Web Server 不直接调用 Model,而是通过异步队列(如 Python asyncio.Queue 或 Redis Stream)将请求发送给独立的推理引擎(Engine Process)。
    • 效果:单核 CPU 可以维持数千个 HTTP 连接不阻塞,等待 GPU 返回 token。
2. 流式响应 (Streaming / SSE)
  • 原理:利用 HTTP Server-Sent Events (SSE)。

  • 落地应用

    • Generator 模式 :推理引擎内部使用 Python Generator (yield) 吐出 token。
    • 异步迭代 :API 层使用 async for 消费这个 Generator,并实时以此 stream 形式推给前端。
    • 收益:用户不需要等整个答案生成完(可能需要 10秒),而是 200ms 内就能看到第一个字,心理等待时间大幅缩短。
3. 分布式推理并发 (Tensor Parallelism)
  • 场景:单卡显存放不下 70B 模型,或者单卡推理太慢。

  • 落地应用

    • 利用 NCCL 库进行多卡通信。我们通常设置 Tensor Parallelism (TP)
    • 并发计算:将矩阵乘法切分到 2 张或 4 张卡上同时计算,然后同步结果。
    • 注意:TP 会带来通信开销,所以作为部署工程师,我会尽量把 TP 限制在单机内部(NVLink 连接),避免跨机 TP。

四、 综合实战:构建一个企业级 RAG 系统

如果我将上述三者结合,搭建一个处理百万级文档的问答系统,架构是这样的:

  1. 请求入口 (Async) : FastAPI 接收 HTTP 请求,await 挂起,将 Task 丢入队列。

  2. 语义缓存层: 检查 Redis,如果有高相似度问答,直接返回(Cache Hit)。

  3. 调度层 (Batching):

    • 推理引擎(如 vLLM 实例)从队列拉取请求。
    • 利用 Continuous Batching,将新请求强行插入正在计算的 Batch 中。
  4. 显存管理 (KV Cache):

    • 系统识别到该请求带有一个巨大的公司手册(System Prompt)。
    • 利用 Prefix Caching,发现这本手册的 KV Cache 已经在显存里了,跳过预计算(Prefill),直接开始生成(Decode)。
  5. 流式输出:

    • 每生成一个 token,通过异步队列回传给 API 层。
    • API 层通过 SSE 实时推送到用户浏览器。

总结

  • 缓存 是为了省显存跳过重复计算
  • Batching 是为了在单位时间内处理更多请求
  • 异步 是为了让 CPU 在等待 GPU 时不闲着 ,同时支持流式体验

这就是一名大模型部署工程师如何将理论转化为高性能服务的过程。希望这个视角的解答对你有所启发!

相关推荐
梁bk1 小时前
Redis 数据结构(下)ZSet, Hash
数据库·redis·缓存
q***04631 小时前
Spring Cloud Alibaba 组件版本选择
android·前端·后端
w***48821 小时前
解决报错net.sf.jsqlparser.statement.select.SelectBody
android·前端·后端
Violet_YSWY1 小时前
git清理缓存
git·elasticsearch·缓存
k***3881 小时前
MySQL 字符串日期格式转换
android·数据库·mysql
v***91301 小时前
数据库高安全—openGauss安全整体架构&安全认证
android·前端·后端
h***01541 小时前
图解缓存淘汰算法 LRU、LFU | 最近最少使用、最不经常使用算法 | go语言实现
算法·缓存·golang
沐雨风栉1 小时前
被局域网困住的文件?cpolar让 Go File 随时随地能用
运维·服务器·开发语言·数据库·后端·缓存·golang
e***87701 小时前
【Redis】centos7 systemctl 启动 Redis 失败
数据库·redis·缓存