vLLM 凭什么成为主流:推理架构的设计抉择与生态博弈

vLLM 凭什么成为主流:推理架构的设计抉择与生态博弈

问题背景

2023 年初,LLM 推理服务化还是个混乱地带。FasterTransformer 需要手写 C++ 组装模型,DeepSpeed-MII 勉强能用但社区不温不火,HuggingFace 的model.generate()离生产可用差了十万八千里。每家公司都在内部搞一套推理方案——有的基于 ONNX Runtime 手动拼图,有的给 PyTorch 套一层 FastAPI 直接上线,有的干脆买更多的 GPU 硬抗。

这里最核心的矛盾不是一个工程问题,而是一个架构问题:LLM 推理框架应该如何设计,才能让研究者专注模型、让工程师专注部署、让运维专注扩缩容?换言之,需要有人把 KV Cache 管理、批处理调度、显存分配、量化压缩这些"脏活"做进一个统一的系统里,并给出一个干净的 API 边界。

vLLM 就是在这样的背景下出现的。它的目标很明确:做 LLM 推理的"操作系统",而不是另一个推理库

整体架构:Centralized Scheduler + Block-Level Memory Manager

vLLM 的架构可以归结为两层:下层的Block-Level KV Cache Manager(以 PagedAttention 为核心),上层的Centralized Scheduler(统一调度器)。两层之间的关系就像操作系统的内存管理与进程调度——Manager 管"货架",Scheduler 管"排班"。

请求入口 ──► Centralized Scheduler ──► Block Manager (KV Cache 分配/回收) │ │ ▼ ▼ Worker Pool GPU Memory (Block Pool) │ ▼ Model Runner (Prefill / Decode)

调度器每次迭代的核心流程:

  1. 检查 KV Cache Block 池的空闲状况,从等待队列拉入尽可能多的新请求,为每个新请求分配 Block
  2. 新请求的 Prompt 做 Prefill(长 Prompt 拆成多个 Chunk,分多次迭代完成)
  3. 将所有进行中请求的当前 Token 拼成 Batch,执行一次前向
  4. 采样,更新序列状态和 KV Cache
  5. 已完成请求立即释放 Block,回收到空闲池

这里的关键是把所有请求的 KV Cache 放在一个全局物理池子里,请求来的时候按需分配 Block,结束的时候立即归还。没有"预留",没有"批次边界",GPU 显存被用到了它能被用到的最满状态。

核心创新:PagedAttention 的设计逻辑

PagedAttention 是 vLLM 最核心的单点贡献,它的思路是借用了操作系统的分页机制来解决 KV Cache 的内存碎片问题。

问题:KV Cache 的内存困境

LLM 推理中 KV Cache 是显存占用的主要来源。对于一个 7B 模型,每 Token 大约产生 0.5MB 的 KV Cache(FP16)。问题出在分配策略上:

  • 静态预分配:按 max_seq_len × batch_size 预留,大多数空间闲置。一个 2048 个槽位的批次,如果请求平均只需要 256 Token 输出,就有约 87% 的 KV Cache 空间是在做慈善。
  • 动态连续分配:按需分配、用完回收,但不同请求的 KV Cache 生命周期不同——短请求释放小块空间、长请求需要大块空间——在分配/释放交替中产生外部碎片。某一个时刻总空闲空间够一个新请求用,但没有一块连续的够大,分配失败。

方案:页式内存管理

PagedAttention 把 KV Cache 逻辑上拆成一个个固定大小的Block(例如每个 Block 容纳 16 个 Token 的 KV Cache)。一个请求的 KV Cache 不是一个连续大块,而是由若干个不连续的 Block 组成,通过Block Table做逻辑地址到物理地址的映射:

请求 A 的 Block Table: [Block₃, Block₇, Block₁₂] 请求 B 的 Block Table: [Block₁, Block₅, Block₉, Block₁₄] ... 空闲 Block 池: [Block₂, Block₄, Block₆, Block₈, ...]

每次请求增长一个 Token,如果当前的最后一个 Block 还有位置(不满 16 个 Token),就追加进去;如果满了,从空闲池里分配一个新的 Block,在 Block Table 中追加索引。请求完成,整个 Block Table 的所有 Block 归还到空闲池。

Attention 计算时,Kernel 根据每个请求的 Block Table 读取离散的 Block 数据,拼成逻辑上连续的 Key/Value 序列。这里有额外开销——Block Table 查找和跨 Block 拼接——但这个开销远小于碎片整理或显存浪费带来的损失。

收益分析

  • 零外部碎片:所有 Block 大小相同,任何空闲 Block 都能分配给任何请求,不存在"有足够空间但没有足够大的连续块"的问题
  • 按需增长:不需要预估输出长度,Token 逐个生成,Block 逐个分配
  • 即时回收:请求完成,Block 立刻归还,不需要 GC 或整理
  • 内存共享:这是 PagedAttention 的一个副产品——不同请求的 System Prompt 如果相同,它们可以共享同一组物理 Block(只读),通过 Copy-on-Write 处理后续的分叉。这在多轮对话、Few-shot Prompting 场景下节省的显存非常可观

PagedAttention 本质上是把"KV Cache 的物理存储"与"序列的逻辑视图"解耦了——操作系统的虚拟内存干了同样的事,只不过 OS 管的是 CPU 内存页,PagedAttention 管的是 GPU 显存块。

为什么成为主流:不是技术最强,而是架构决策最准

vLLM 成为主流推理框架,技术上并非每个维度都领先于所有对手,但它在几个关键的架构决策上押对了方向。

决策一:做推理引擎,不做推理服务全栈

vLLM 的 API 边界画得很克制:它提供一个OpenAI-compatible API Server,也提供一个Python APILLM类),但不试图做负载均衡、模型管理、多节点编排。这些事情留给 Kubernetes、Ray Serve、或者各家的 MLOps 平台去解决。

对比 HuggingFace TGI——它试图包揽从调度到路由到鉴权到监控的全栈职责,这在大团队里有优势,但灵活性被牺牲了。vLLM 选择"做好一件事,其他交给生态"的策略,让它在不同部署环境(裸机、K8s、云平台)中的适配成本极低。

决策二:优先保证通用性,在通用性上追求极致性能

很多推理框架的性能优化与特定模型或特定硬件深度耦合——FastTransformer 每个模型手写 Kernel,TensorRT-LLM 深度依赖 NVIDIA GPU 的软件栈。这种做法在 benchmark 上好看,但在一个模型结构每周都在变动的世界里,维护成本是指数级的。

vLLM 的选择是:先适配尽可能多的模型(当前已支持 50+ 种架构),然后用系统的架构优势(PagedAttention + Centralized Scheduler)来提升全局吞吐,而不是用逐模型的 Kernel 优化来追求单次延迟。它的ModelRunner抽象允许新模型通过相对简单的适配就能接入整套推理流水线,而且自动获得 Continuous Batching 和 PagedAttention 的收益。

这个决策在 2023-2024 年的模型爆发期被证明是正确的——当 Llama 3、Mistral、Mixtral、Qwen、DeepSeek 接二连三出现时,vLLM 的适配速度远超竞品。

决策三:开源的激进性与社区的飞轮效应

vLLM 的开源策略比 TGI 更激进、比 TensorRT-LLM 更开放。Apache 2.0 协议 + UC Berkeley 背书,吸引了大量贡献者。自 2023 年 6 月发布以来,贡献者数量迅速超越 TGI,成为 LLM 推理框架中最大的开源社区。

这形成了一个正向循环:更多用户 → 更多 Issue 和 Feature Request → 更多贡献者 → 支持更多模型和硬件 → 更多用户。在推理框架这个领域,"支持你的模型"往往比"比你快 5%"更有说服力。

决策四:显存效率带来的单卡优势

PagedAttention 使 vLLM 在相同 GPU 上可以容纳比 TGI(早期版本)和静态批处理方案多得多的并发请求。对于一个 7B 模型在 A100-80GB 上的部署,vLLM 可以同时处理约 2-3 倍于静态批处理的请求量。显存是怎么省出来的?不是压缩精度(那是量化的活),而是避免了静态预分配的浪费通过 Block 共享消除了相同前缀的冗余存储

在 GPU 按小时计费的云环境中,单卡能承载的并发量直接对应了单位推理成本。省显存就是省钱,这在商业决策中比任何技术指标都直观。

竞品对比:各自的选择与取舍

HuggingFace TGI:全栈优先,但包袱太重

TGI 是最早支持 Continuous Batching 的推理框架之一(比 vLLM 更早)。它的优势在于 HuggingFace 生态的原生支持——模型 Hub 到推理服务的链路最短。Transformer 模型下载后可以直接通过 TGI 拉起。

但 TGI 的包袱也来自此:它的架构必须兼容 HuggingFace Transformers 的全部特性,包括大量不常用于推理的代码路径;它的后端在 Rust(高性能 HTTP 层)和 Python(模型逻辑)之间来回切换,引入了通信开销。加之早期 TGI 的 KV Cache 管理不如 PagedAttention 高效,同等硬件下的并发能力落后于 vLLM。

TGI 的核心用户是已经在 HuggingFace 生态中深度投入的团队,对他们来说,"改一行代码就能上线"的价值大于 10% 的吞吐提升。

TensorRT-LLM:性能天花板,通用性的代价

TensorRT-LLM(简称 TRT-LLM)是 NVIDIA 官方的推理方案。它的性能在 NVIDIA GPU 上是天花板级别的——CUDA Graph 消除 CPU-GPU 交互开销、FP8/INT8 KV Cache 量化、Multi-Node Tensor+Pipeline Parallelism 的调度协同,每一项都做到了极致。

但 TRT-LLM 的代价也很明显:

  • 模型添加需要手写转换脚本:每个新模型架构都需要被映射到 TRT-LLM 的内部表示,这不是加一个配置文件的事,是需要理解 TRT 内部表示再写适配代码的事
  • 构建过程复杂:模型需要经过 ONNX → TensorRT Engine 的编译链,大型模型编译时间长,且在每次更新 TensorRT 版本后都需要重新构建
  • 闭源且硬锁定 NVIDIA:无法在 AMD、Intel GPU 或 Apple Silicon 上运行

TRT-LLM 的定位很清晰:追求极致性能的 NVIDIA GPU 用户。如果你的推理服务跑在 NVIDIA H100/H200 集群上,并且模型架构相对固定(例如只用 Llama),TRT-LLM 意味着最高的吞吐和最低的延迟。但如果你需要快速迭代模型、或者混合使用不同模型,它的维护负担会很快超过性能收益。

SGLang:结构化生成为核,调度更为激进

SGLang 是 2024 年最值得关注的推理框架之一。它没有试图做一个"通用推理引擎",而是围绕结构化生成(Structured Generation)重新设计了推理的编程模型。它的 RadixAttention 机制利用 Radix Tree 来管理 KV Cache 前缀的共享——当多个请求共享相同的 Prompt 前缀(例如 System Prompt + 多轮对话的历史)时,这个前缀的 KV Cache 只算一次,后续请求直接复用。

SGLang 的另一个创新是RadixAttention 的 Prefill 预热:在检测到某个前缀被高频命中时,将其 KV Cache 保留在显存中不回收,让新请求的 Prefill 可以跳过前缀部分。

SGLang 的定位和 vLLM 不完全重叠。如果 vLLM 是"通用推理的操作系统",SGLang 更像是"结构化生成场景的专用加速器"。但 SGLang 团队正在将 RadixAttention 和 Prefix Cache 的能力泛化到更多场景,它与 vLLM 的竞争将集中在 KV Cache 管理的效率上。2024 年底,SGLang 已经支持了与 vLLM 相当的模型列表,两者的边界正在模糊。

LMDeploy:国内部署的事实标准

LMDeploy 由上海人工智能实验室(InternLM 团队)开发,是国内部署量最大的推理框架之一。它的 TurboMind 引擎在 KV Cache 管理上采用了与 PagedAttention 类似的分块策略,但实现上更倾向于连续分配的优化路径——通过减少 Block 粒度开销来追求更高的 Kernel 效率。

LMDeploy 的核心优势在于对国产模型(InternLM、Qwen、Baichuan 等)的原生优化,以及对量化方案(W4A16、KV Cache INT8/INT4)的深度集成。如果 vLLM 的生态优势是在 HuggingFace Hub 上,LMDeploy 的优势是在中国云厂商和模型团队的深度绑定上。

Ollama / llama.cpp / MLX:不是竞品,是另一种场景

Ollama(底层基于 llama.cpp)代表的是本地推理和边缘部署的场景——MacBook 上跑 7B 模型、个人工作站上做实验。它们在显存效率(通过 MMAP 和量化)上做到了极致,但不追求高并发下的服务化吞吐。vLLM 的用户从来不是想在本地跑一个 Chatbot 的人,而是需要起一个 API Server 给几百个用户提供服务的人。两个世界的设计约束完全不同,不是竞争关系。

性能关键路径与瓶颈

理解 vLLM 的性能,需要区分两个场景:

Prefill 阶段的瓶颈在计算:处理一个长 Prompt 时,GPU 的 Tensor Core 接近满载,显存带宽不是制约因素。vLLM 的 Chunked Prefill 将长 Prompt 的 Prefill 拆入多次迭代,目的是不让一次 Prefill 拖慢同一批 Decode 请求的调度。但 Prefill 本身的延迟没有降低——它只是被"分期付款"了。

Decode 阶段的瓶颈在显存带宽:Decode 每次只处理一个 Token,矩阵乘法维度很小([batch, 1, hidden]),Tensor Core 远未吃饱。此时的主要耗时在从显存中读取 KV Cache——几十层的 Attention,每层几百个头的 KV,都要从全局显存搬到计算单元。Decode 的延迟 = KV Cache 的总字节 / 显存带宽

这个特性意味着:

  • 增大 Batch Size 对 Decode 吞吐的边际收益会快速递减——因为显存带宽是共享资源,更多请求意味着更多的 KV Cache 读取,带宽更快饱和
  • FlashAttention 对 Decode 的直接加速有限——FlashAttention 主要优化的是 Prefill 阶段的 Attention 计算(O(n²) → O(n) 显存读写,但 Decode 本身就只算一个 Token,显存读写量不大;而 Decode 需要加载的 KV Cache 总量,FlashAttention 无法消除)
  • KV Cache 量化(FP8/INT8)是 Decode 阶段最有效的优化——它将显存带宽需求直接减半,对 Decode 延迟的影响几乎是线性的

vLLM 在 Decode 阶段的主要优化手段是通过 PagedAttention Kernel 减少 Block Table 查找的开销,以及在可能的情况下做 Batch 内的 Prefix Caching。但这些是"减损"而非"加速"——Decode 的物理上限是显存带宽,而这一点所有框架都一样。

总结

vLLM 成为主流,不是因为它在每个单项指标上都最强——单卡极限性能不如 TensorRT-LLM,HuggingFace 模型即开即用的便利性不如 TGI,长前缀复用不如 SGLang 的 RadixAttention 激进。它的决定性问题在于做出了正确的架构选择:把推理框架定位为"引擎"而非"平台",用 PagedAttention 解决最痛的显存问题,以通用性换取生态飞轮

在一个模型架构碎片化、硬件选型多样化、部署环境复杂化的时代,架构的灵活性比单点性能优化更有长期价值。vLLM 恰好是这个判断的最好注脚。

而从更宏观的角度看,推理架构的演进还远未结束。Disaggregated Prefill/Decode 的分离式部署、Multi-LoRA 的细粒度共享、Speculative Decoding 与调度的协同——这些方向正在重新定义"推理引擎的职责边界"。vLLM 的下一阶段挑战,是在保持通用性的同时,将这些新技术纳入现有架构,而不让系统变成一个臃肿的特性拼盘。


参考

  • Kwon, Woosuk, et al. “Efficient Memory Management for Large Language Model Serving with PagedAttention.” SOSP 2023.
  • Zheng, Lianmin, et al. “SGLang: Efficient Execution of Structured Language Model Programs.” NeurIPS 2024.
  • NVIDIA TensorRT-LLM Documentation. https://github.com/NVIDIA/TensorRT-LLM
  • vLLM Documentation. https://docs.vllm.ai
  • HuggingFace Text Generation Inference. https://github.com/huggingface/text-generation-inference
  • LMDeploy. https://github.com/InternLM/lmdeploy