1. vllm 的核心优势——PagedAttention
PagedAttention是vLLM最核心的技术创新,它解决了大型语言模型推理过程中的内存管理难题。
1.1 传统 attention 遇到的挑战
在标准Transformer推理中,存在两个关键阶段:
- 预填充阶段(Prefill):处理输入提示,生成所有token的KV缓存
- 解码阶段(Decode):生成新token,每次只处理一个新token但需要访问所有之前的KV缓存
传统方法中,KV缓存以连续内存块分配,导致以下问题: - 内存浪费:预先为最大序列长度分配内存,即使实际序列更短
- 内存碎片:不同长度的请求会导致严重的内存碎片
- 批处理限制:不同长度序列很难在一个批次中高效处理
- 并发瓶颈:同时处理的序列数量受到严格限制
1.2 PagedAttention 工作原理
PagedAttention引入了"分块存储"的概念:
- 物理块(Physical Blocks):将GPU内存分成固定大小的物理块(如16个token)
- 逻辑块(Logical Blocks):每个序列的KV缓存被分割成逻辑块
- 块表(Block Table):维护逻辑块到物理块的映射关系,类似操作系统页表
核心数据结构
- 块表:每个激活序列维护一个块表,记录其逻辑块映射到哪些物理块
- 物理块池:所有可用物理块的集合,由内存管理器统一分配
- 块缓存(Block Cache):通过索引快速访问KV缓存值
注意力计算流程
- 将注意力查询(Q)与多个物理块中的键(K)计算相似度
- 通过块表找到当前序列对应的所有物理块
- 计算注意力权重并与对应的值(V)加权求和
- 生成新token并更新块表
内存管理策略
- 按需分配:只为实际序列长度分配物理块,不再预分配最大长度
- 细粒度控制:以块为单位分配内存,减少碎片
- 共享物理块:不同请求可以共享相同的物理块(前缀相同时)
- 内存回收:序列完成后立即回收物理块,供其他序列使用
2. 分布式执行逻辑
在 vllm/config.py 的 ParallelConfig
的__post_init__
方法中,vLLM会根据以下条件自动选择使用Ray还是其他执行后端:
if self.distributed_executor_backend is None and self.world_size > 1:
from vllm.executor import ray_utils
backend = "mp" # 默认使用多进程
ray_found = ray_utils.ray_is_available()
if current_platform.is_neuron():
backend = "uni" # neuron使用单进程控制多设备
elif (current_platform.is_cuda()
and cuda_device_count_stateless() < self.world_size):
# 如果本地GPU数量小于所需的world_size,必须使用Ray进行多节点部署
if not ray_found:
raise ValueError("Unable to load Ray which is "
"required for multi-node inference, "
"please install Ray with `pip install "
"ray`.")
backend = "ray"
elif ray_found:
# 如果Ray已初始化且存在placement group,使用Ray
if self.placement_group:
backend = "ray"
else:
from ray import is_initialized as ray_is_initialized
if ray_is_initialized():
from ray.util import get_current_placement_group
if get_current_placement_group():
backend = "ray"
self.distributed_executor_backend = backend
执行器的选择逻辑:
在 vllm/executor 文件夹下,vllm 一共有四种执行器
执行器 | 描述 |
---|---|
单进程执行器(UniProcExecutor) | 用途: 单GPU/单设备场景,所有计算在同一进程中 何时使用: 当模型适合单个GPU且world_size=1时 代码标识: distributed_executor_backend="uni" 特点: 最简单的执行器,无分布式开销 |
多进程执行器(MultiprocessingDistributedExecutor) | 用途: 单节点多GPU场景 何时使用: 当模型需要多个GPU但都在同一台机器上时 代码标识: distributed_executor_backend="mp" 特点: 使用Python原生的多进程通信;适用于单机器多GPU场景;每个GPU一个Python进程;相比Ray开销更小 |
Ray分布式执行器(RayDistributedExecutor) | 用途: 多节点分布式场景 何时使用: 当模型太大不适合单节点,或需要跨多节点部署时 代码标识: distributed_executor_backend="ray" 特点: 基于Ray分布式计算框架 支持跨节点部署 提供高级资源管理和容错特性 支持任务队列和弹性扩展 |
外部启动器执行器(External Launcher) | 用途: 与外部分布式框架集成的场景 何时使用: 当使用torchrun等外部工具管理分布式环境时 代码标识: distributed_executor_backend="external_launcher" 特点: 允许外部工具(如torchrun)来管理进程创建 vLLM只负责特定设备上的执行 适用于与现有分布式训练基础设施集成的场景 |
vLLM有一套智能的自动选择逻辑,基于以下因素选择合适的执行器:
- 硬件平台: 在不同平台上有不同的默认行为
- 在Neuron硬件上默认使用uni
- 在Intel XPU或TPU上强制使用ray
- GPU可用性:
- 如果本地可用GPU数量小于所需的world_size,使用ray
- 否则默认使用mp
- Ray环境:
- 如果已存在Ray初始化环境或placement group,使用ray
- 如果设置了placement_group参数,使用ray
3. Ray在vLLM中的核心价值
-
分布式资源管理
- Ray提供了统一的资源管理层,自动处理GPU、CPU、内存等资源的分配
- 支持跨节点动态调度,不需要手动指定哪个进程在哪个节点上运行
-
更高层次的抽象
- 相比直接使用NCCL仅提供通信原语,Ray提供了任务、角色(Actor)的概念
- 使vLLM能够更容易地表达复杂的工作流,如张量并行和流水线并行的组合
-
统一的有状态和无状态计算模型
- Ray Actor模型适合管理持久化状态(如KV缓存、模型权重)
- Ray任务模型适合处理无状态的操作(如请求调度、张量操作)
- 这正好匹配了LLM推理中有状态(模型)和无状态(请求)的混合特性
-
内置容错机制
- Ray提供自动的工作节点故障检测和恢复
- 对于长时间运行的推理服务至关重要
-
通信层优化
- Ray确实简化了NCCL的使用,但不仅限于此
- vLLM通过Ray可以更容易地实现自定义的通信模式,如PagedAttention中的数据传输
-
部署灵活性
- 从单机扩展到多机无需修改代码
- 支持异构集群(不同GPU类型混合部署)
- 与云平台和Kubernetes集成更简单
-
调度优化
- Ray的调度器针对异步和并行任务进行了优化
- 有助于vLLM实现连续批处理(continuous batching)和预填充(prefill)优化
如果vLLM直接使用NCCL而不是Ray:
- 需要自行管理进程创建、终止和故障处理
- 需要手动实现资源分配和节点间协调
- 缺乏高级调度功能来优化推理请求处理
- 扩展到新硬件平台(TPU、NeuronCore等)更困难
本质上,Ray让vLLM能够专注于模型推理的核心逻辑,而不必处理低级分布式系统的复杂性。这使得vLLM能够更高效地实现其PagedAttention和连续批处理等创新,同时保持对各种部署环境的适应性。