vLLM核心架构:从请求到Token的高效流水线
核心组件:vLLM的五大支柱
vLLM之所以能在众多推理引擎中脱颖而出,源于其围绕PagedAttention设计的组件化架构。理解这些组件,是后续调优调度参数的前提。下图展示了vLLM处理请求时的核心组件与数据流转关系。
vLLM的架构可拆解为五个核心组件,它们各司其职又紧密协作:
- LLM Engine(引擎层):对外提供OpenAI兼容的HTTP接口,负责请求校验、参数解析和结果返回。它是用户与系统交互的唯一入口。
- Scheduler(调度器):vLLM的“大脑”,决定哪些序列进入本轮迭代、以何种顺序执行。它实现了连续批处理(Continuous Batching),即每步迭代后动态调整批次,而非等待整个序列生成完毕。
- Worker / Model Runner(执行层):真正调用GPU执行Transformer前向传播的模块。在多卡场景下,它负责张量并行(Tensor Parallelism)和流水线并行(Pipeline Parallelism)的协调。
- KV Cache Manager(缓存管理器):管理GPU显存中的KV缓存块。它维护物理块与逻辑块的映射关系,并记录每个块的引用计数,以便及时释放不再使用的缓存。
- PagedAttention(分页注意力):vLLM的核心创新。它将连续的KV缓存拆分为固定大小的“块”(Block),允许这些块在显存中非连续存放,从而消除显存碎片,将显存利用率提升至接近90%。
推理流程:一次请求的完整旅程
理解vLLM的推理流程,是掌握调度参数作用点的关键。整个流程可划分为预填充(Prefill)和解码(Decode)两个阶段,调度参数在这两个阶段的作用截然不同。
- 请求接入与预处理:客户端发送请求后,LLM Engine将输入文本转换为Token ID序列,并记录序列的元信息(如长度、采样参数)。
- 调度决策(Prefill阶段):Scheduler根据当前等待队列、显存余量以及
max_num_seqs、max_seq_len等参数,决定将哪些新请求纳入本批次。此阶段需要为每个序列分配足够的KV块来缓存整个输入上下文的注意力结果。 - 预填充计算:Worker对批次内所有请求的输入Token并行执行前向传播,生成第一个输出Token,并填充KV Cache。此阶段计算密集,GPU利用率高。
- 调度决策(Decode阶段):每生成一个Token后,Scheduler会重新评估批次。已生成完毕的序列会被移出,新到达的请求可能被加入。此时
max_num_seqs决定了并发解码的上限。 - 解码迭代:对批次内每个序列,模型读取其最后一个Token的Embedding,结合KV Cache执行注意力计算,预测下一个Token的概率分布。随后根据
temperature、top_p等采样参数选出最终Token。 - 流式返回与资源释放:生成的Token通过流式接口逐步返回给客户端。当序列遇到结束符或达到
max_tokens限制时,其占用的KV块被释放,供新请求使用。
调度参数作用点速查
| 参数名 | 作用阶段 | 核心影响 |
|---|---|---|
max_num_seqs |
Prefill + Decode | 限制批次内最大序列数,直接影响吞吐与显存峰值 |
max_seq_len |
请求准入 | 超过该长度的请求直接报错,防止显存溢出 |
gpu_memory_utilization |
启动时 | 决定KV Cache可用显存上限,影响可并发序列总量 |
block_size |
KV Cache分配 | 每个物理块容纳的Token数,影响显存碎片程度 |
max_num_batched_tokens |
Prefill | 限制单次预填充的Token总数,防止长输入导致OOM |
对比分析:vLLM vs. TensorRT-LLM vs. HuggingFace TGI
在实际选型中,工程师常将vLLM与TensorRT-LLM、TGI(Text Generation Inference)对比。三者目标一致——提升推理性能,但设计哲学差异显著。
| 维度 | vLLM | TensorRT-LLM | HuggingFace TGI |
|---|---|---|---|
| 核心优化 | PagedAttention + 连续批处理 | 图优化 + 算子融合 + 量化 | 连续批处理 + 张量并行 |
| 显存管理 | 分块KV Cache,利用率高 | 静态显存池,需精确计算 | 动态分配,碎片较多 |
| 易用性 | Python原生,开箱即用 | 需编译引擎,学习曲线陡 | Python友好,部署简单 |
| 量化支持 | GPTQ / AWQ / FP8 | INT4/INT8/FP8,支持最全 | GPTQ / AWQ |
| 动态批处理 | 迭代级调度,粒度最细 | 请求级调度,粒度较粗 | 迭代级调度 |
| 适用场景 | 高并发在线服务 | 极致性能、生产环境 | 快速原型、中小规模 |
从实际测试数据看,在A100上使用Llama-2-7B模型、并发32路请求时,vLLM的吞吐量可达TGI的2.2倍左右,而TensorRT-LLM在静态batch下可能比vLLM再快10%~15%,但其代价是需要针对具体模型和GPU进行编译优化,且动态请求场景下优势会缩小。vLLM的优势在于动态调度灵活性和部署便捷性,特别适合请求长度波动大、并发数高的在线场景。
案例:从TGI迁移到vLLM的收益
某智能客服团队原先使用TGI部署Qwen-14B模型,线上峰值时平均首Token延迟(TTFT)达到1.8秒,GPU显存利用率仅62%。迁移到vLLM后,仅调整了gpu_memory_utilization=0.90和max_num_seqs=64,在相同硬件上取得了以下效果:
- TTFT降至0.9秒,降低50%
- 吞吐量从每秒120个Token提升至310个Token
- 显存利用率稳定在85%以上,未出现OOM
这一提升主要归功于PagedAttention消除了KV Cache碎片,以及迭代级调度让GPU始终在处理有效请求,而非等待慢序列完成。
误区与复盘:调度参数不是越大越好
在调优过程中,工程师常陷入以下误区:
误区一:盲目调大 max_num_seqs
增大并发序列数确实能提升吞吐,但会线性增加KV Cache占用。当显存不足时,vLLM会触发显存交换(Swap)到CPU,导致解码速度骤降。正确做法是监控vllm:num_requests_running和vllm:gpu_cache_usage_perc指标,将缓存使用率维持在90%~95%之间。
误区二:忽略 max_num_batched_tokens 对长输入的影响
当多个长文档同时到达时,Prefill阶段的Token总数可能瞬间超过GPU计算能力。若该参数设置过大,会导致单次前向传播时间过长,阻塞后续解码。建议根据模型最大序列长度和GPU算力,将max_num_batched_tokens设为max_num_seqs × 平均输入长度的1.2倍左右。
复盘建议:每次调整参数后,应同时观察TTFT、ITL(Token间延迟)和吞吐量三个指标。TTFT反映Prefill效率,ITL反映Decode稳定性,吞吐量反映整体利用率。三者不可兼得时,需根据业务优先级取舍——对话场景优先降低TTFT,离线批量生成则更看重吞吐。