vLLM 的并行与分布式能力
vLLM 提供的并行和分布式能力,本质上是在解决三个核心问题:第一,大模型太大,单卡放不下;第二,请求太多,GPU 利用率太低;第三,多节点部署时通信和 KV cache 管理会变得极其复杂。所以它的整个 runtime 基本围绕模型切分、request 调度、KV cache 管理、distributed execution 四件事展开。
最基础的并行能力是 Tensor Parallel(TP)。它解决的是“模型太大放不下”的问题。比如一个 70B 模型,FP16 权重可能超过 140GB,单卡根本塞不进去,于是 vLLM 会把 attention 和 MLP 里的大矩阵切到多张 GPU 上。举例来说,一个 Linear projection 原本是一个巨大矩阵 W,现在 GPU0 只负责 W 的前一部分,GPU1 负责后一部分。每张 GPU 计算自己那部分结果,然后通过 NCCL 做 all-reduce 或 all-gather 合并。这样多张 GPU 就像共同组成“一个模型实例”。TP 的核心瓶颈其实不是计算,而是 GPU 通信,所以 NVLink 和 NVSwitch 很重要。如果只是 PCIe,通信延迟和带宽会很快成为瓶颈。很多 production inference 部署其实是 TP=2、4、8 这样的结构。
除了 TP,vLLM 也支持 Pipeline Parallel(PP)。PP 的逻辑不是切矩阵,而是切 layer。比如 GPU0 放 layer1–20,GPU1 放 layer21–40,GPU2 放 layer41–60。token 会像流水线一样逐层往后流。这种方式在训练里特别常见,但 inference 阶段没有那么理想,因为 decode 是 sequential 的,每生成一个 token 都必须等前一个 token 完成,于是 pipeline bubble 会很严重。不过对于极大模型,比如几百 B 参数以上,PP 还是必要的,因为单纯 TP 可能已经不够了。
Data Parallel(DP)则完全不同。DP 不是切模型,而是复制模型。比如 8 张 GPU,前 4 张组成一个完整 replica,后 4 张组成另一个 replica。不同用户请求分给不同 replica。这样做的好处是通信压力小,吞吐高,非常适合 API serving,但缺点是显存浪费,因为每个 replica 都要存完整模型。现实里很多系统其实是 TP+DP 混合,比如 TP=4、DP=2,也就是两个 replica,每个 replica 用四张卡 tensor parallel。
真正让 vLLM 出名的,其实不是 TP,而是 Continuous Batching。传统 inference batching 的问题在于:必须等一批 request 一起进入 batch,然后一起跑。但用户生成长度不同,有人输出 10 token 就结束,有人输出 2000 token,于是 batch 很快变得稀疏,GPU 出现大量 idle。vLLM 的 scheduler 会动态调整 batch,request 可以随时加入,也可以随时退出。GPU 不需要等整个 batch 结束,而是持续保持高 occupancy。这其实更像操作系统里的 process scheduling,而不是传统深度学习 batching。很多人第一次看 vLLM 时,以为重点是 attention kernel,但实际上 continuous batching 才是它对 serving 影响最大的东西之一。
Paged Attention 是另一个核心设计。传统 KV cache 分配方式是给每个 request 一块连续显存,但 request 长度不可预测,于是 GPU memory 会严重碎片化。vLLM 借鉴了操作系统虚拟内存 paging 的思想,把 KV cache 切成固定大小 block。request 不再需要连续显存,而是像 page table 一样映射到不同 block。这带来了几个巨大好处:显存利用率提高、碎片减少、KV cache 可以动态扩展、continuous batching 更容易实现。很多人误以为 paged attention 是 attention 数学优化,其实它更像一个 GPU memory management system。
长上下文时代,KV cache 本身已经成为主要瓶颈之一。比如 128k、1M context 时,KV cache 甚至可能比模型参数还大。所以 vLLM 现在越来越重视 distributed KV cache 和 KV offload,包括 block manager、hierarchical KV、CPU offload、甚至未来 NVMe tiering。因为很多 inference workload 已经从“算力问题”变成“memory system problem”。
vLLM 还支持 speculative decoding。正常 decode 最大的问题是 token dependency——一次只能生成一个 token。speculative decoding 会让一个小模型先生成多个 token,然后大模型一次性验证。如果验证通过,就能直接接受多个 token,而不是一步一步生成。这相当于在时间维度上做并行化,减少 sequential bottleneck。特别是长回答时,吞吐提升会非常明显。
Chunked Prefill 是近两年另一个重要优化。因为超长 prompt prefill 很重,比如 100k context,单个 request 就可能长时间占满 GPU,导致其他短 request 被饿死。于是 vLLM 会把 prefill 切成 chunk 分批执行,让 scheduler 能在长 request 和短 request 之间更公平地切换。这其实已经非常像操作系统里的 time slicing 和 fairness scheduling。
在分布式层面,vLLM 支持 multi-node inference。比如 Node0 放 GPU0–7,Node1 放 GPU8–15,通过 NCCL、RDMA、InfiniBand 做跨节点通信。这里真正困难的地方通常不是 forward 本身,而是 communication topology。因为 NVLink 带宽远高于网络,所以一旦跨节点,性能会迅速受到通信限制。尤其 MoE 模型会更夸张。
MoE inference 是现在 vLLM 越来越重要的方向。像 DeepSeek 这种模型,每个 token 并不会经过所有 FFN,而是 router 动态选择少数几个 expert。于是不同 expert 可能分布在不同 GPU 上,token 需要动态 routing。最麻烦的是 all-to-all communication——每张 GPU 都要和其他 GPU 交换 token。这种通信模式在 distributed system 里是最重的一类,所以很多 MoE serving 的瓶颈根本不是 FLOPS,而是 routing 和 communication overhead。
最近几年还有一个重要趋势叫 disaggregated serving。因为 prefill 和 decode 的特征完全不同:prefill compute-heavy、并行度高;decode memory-bound、延迟敏感。所以越来越多 inference architecture 开始把 prefill cluster 和 decode cluster 分离。也就是说,一部分 GPU 专门负责 prefill,另一部分专门负责 decode。vLLM 社区也在逐渐往这个方向发展,因为未来 ultra-long-context inference 基本一定会走 disaggregation。
你如果把这些功能整体看,会发现 vLLM 已经不像一个“PyTorch 推理 wrapper”,而更像一个 GPU-aware distributed runtime。它真正复杂的地方并不是 attention 数学,而是:如何调度 request、如何管理 KV cache、如何减少 GPU idle、如何做 memory paging、如何在 distributed 环境下减少通信、如何让多个用户共享同一个 runtime。这也是为什么很多人读到后面会发现,它越来越像操作系统、数据库 runtime 或 HPC scheduler,而不是传统 ML 项目。