3、基础概念:LLM推理流程、批处理、吞吐量、延迟、显存管理
3.1 LLM推理的核心流程:从输入到输出的完整链路
理解vLLM之前,必须先在大脑中建立LLM推理的基本流水线。一次完整的推理请求,并非简单地把文本塞进模型然后得到结果,而是经历多个阶段。以自回归生成模型(如Llama、Qwen)为例,推理过程可拆解为以下四个核心阶段:
- 输入预处理(Preprocessing):原始文本通过分词器(Tokenizer)转换为token ID序列,并填充(Padding)或截断(Truncation)到模型要求的长度。
- 模型前向计算(Prefill / 预填充):将输入token序列一次性送入Transformer,并行计算所有位置的Key/Value(KV)缓存。此阶段计算密集,GPU利用率高。
- 自回归解码(Decode / 逐token生成):模型逐个生成新token。每生成一个token,将其追加到序列末尾,并利用上一步的KV缓存进行增量计算。此阶段内存带宽受限,GPU利用率低。
- 输出后处理(Postprocessing):对生成的token序列进行解码,应用停止条件(如遇到EOS token或达到最大长度),返回最终文本。
下图展示了这一核心流程,并标注了每个阶段的关键特征:
3.2 批处理(Batching):提升吞吐量的核心手段
单个请求的推理效率很低,因为GPU在Decode阶段大部分时间处于“等待”状态。为了充分利用GPU资源,需要将多个请求合并成一批(Batch)同时处理。批处理的核心思想是:让GPU在同一个时间片内处理更多请求,从而提高硬件利用率。
传统静态批处理(Static Batching)存在明显缺陷:必须等待批次中最慢的请求完成才能释放资源,导致其他已完成请求的GPU资源被白白浪费。vLLM引入了连续批处理(Continuous Batching)机制,其核心逻辑如下:
- 请求级调度:不再以“批次”为单位,而是以“请求”为单位进行调度。
- 动态加入/退出:当一个请求生成完毕,立即从批次中移除,并让新请求加入,无需等待整个批次完成。
- 迭代级调度:在每个Decode迭代中,动态决定哪些请求参与计算,哪些请求暂停等待。
下表对比了两种批处理方式的关键差异:
| 对比维度 | 静态批处理 | 连续批处理(vLLM) |
|---|---|---|
| 调度粒度 | 批次(Batch) | 单个请求(Request) |
| 资源释放 | 等待最慢请求完成 | 请求完成立即释放 |
| GPU利用率 | 存在明显空闲窗口 | 持续保持高利用率 |
| 新请求加入 | 需等待下一批次 | 可随时加入当前迭代 |
| 吞吐量影响 | 较低,受“木桶效应”限制 | 显著提升,可达2-3倍 |
3.3 吞吐量与延迟:一对需要权衡的指标
在评估LLM推理服务时,吞吐量(Throughput)和延迟(Latency)是最核心的两个性能指标,它们之间存在天然的矛盾关系。
吞吐量衡量系统在单位时间内能处理的请求数或生成的token数,常用指标包括:
- Requests Per Second (RPS):每秒处理的请求数。
- Tokens Per Second (TPS):每秒生成的token总数。
延迟衡量单个请求从发出到完全返回所花费的时间,通常细分为:
- 首token延迟(TTFT, Time To First Token):从请求发出到收到第一个输出token的时间。用户感知最明显。
- 每token生成时间(TPOT, Time Per Output Token):生成每个输出token的平均耗时。
- 端到端延迟(E2E Latency):整个请求从开始到结束的总时间。
下图展示了批处理大小对吞吐量和延迟的影响关系:
3.4 显存管理:LLM推理的最大瓶颈
LLM推理的显存消耗主要来自三部分:模型权重、激活值(Activations)和KV缓存。其中,KV缓存是动态增长且最容易被忽视的部分。以7B模型为例,每个token的KV缓存大小约为 2 × 层数 × 注意力头数 × 头维度 × 2字节,对于Llama-2-7B(32层,32头,128维),每个token约消耗 1MB 显存。当并发请求数达到100时,仅KV缓存就需要 100 × 序列长度 × 1MB,轻松超过10GB。
传统显存管理方式存在严重的碎片化问题:
- 内部碎片:预分配的KV缓存块大小固定,但实际请求长度不一,导致部分块未被充分利用。
- 外部碎片:不同请求的KV缓存块大小不同,频繁分配/释放导致显存出现大量不连续的空洞。
vLLM的核心创新——PagedAttention——借鉴操作系统虚拟内存的分页思想,将KV缓存划分为固定大小的块(Block),每个块可存储固定数量token的KV数据。通过块表(Block Table)将不连续的物理块映射为连续的虚拟空间,从而:
# PagedAttention 核心思想示意
# 物理块:Block 0, Block 1, Block 2, ...(不连续)
# 逻辑视图:Request A 的 KV 缓存 = Block 5 → Block 1 → Block 8(通过块表映射)
Block Table (Request A):
Logical Block 0 → Physical Block 5
Logical Block 1 → Physical Block 1
Logical Block 2 → Physical Block 8
这种设计带来三个关键优势:
- 近乎零碎片:所有块大小一致,分配和释放不会产生碎片。
- 显存共享:多个请求可以共享相同的物理块(如并行采样时),进一步节省显存。
- 按需分配:只在需要时分配新块,避免预分配浪费。
3.5 综合案例:一个典型推理请求的完整旅程
假设一个在线客服机器人收到用户消息“请帮我查询订单状态”,模型为Qwen-7B-Chat,部署在单张A10 GPU(24GB显存)上。整个推理过程如下:
- 请求到达:vLLM调度器将请求加入等待队列,分配一个序列ID。
- 预处理:分词器将文本转换为
[151644, 8948, 8832, ...]等token ID序列,长度约15个token。 - Prefill阶段:15个token一次性输入模型,并行计算生成KV缓存,占用约15MB显存。此阶段耗时约20ms。
- Decode阶段:模型开始逐token生成回复。每生成一个token,vLLM通过PagedAttention分配新的KV缓存块。假设回复共120个token,总耗时约1.2秒(每token约10ms)。
- 动态调度:在Decode过程中,vLLM同时处理其他请求。当本请求生成完毕,立即释放其KV缓存块,供新请求使用。
- 返回结果:完整回复“您的订单已发货,预计3天内送达...”返回给用户,端到端延迟约1.3秒。
整个过程中,vLLM通过连续批处理同时服务了约20个类似请求,GPU利用率保持在85%以上,而传统静态批处理方式在相同负载下GPU利用率仅约50%。
--max-num-seqs 参数控制最大并发请求数,通过 --gpu-memory-utilization 控制显存使用比例(默认0.9),并根据实际业务延迟要求调整批处理策略。