在大规模语言模型推理服务部署中,如何在保证延迟的前提下最大化 GPU 吞吐量是核心挑战。vLLM 作为当前最受关注的开源推理 Serving 框架,其核心技术正是连续批处理(Continuous Batching)与 PagedAttention 的协同设计。本文将从调度层与内存管理两个维度,系统性解析这套机制的工程实现细节,并为实际部署提供可操作的配置指导。

连续批处理调度器的工作原理

传统的静态批处理(Static Batching)要求整个批次的请求同时完成前向传播,这种模式在推理场景中存在严重的资源浪费问题。当批中某个短序列率先完成生成后,GPU 必须等待该批次中最慢的请求结束才能处理下一批请求,导致计算资源出现大量空闲窗口。连续批处理则从根本上改变了这一约束 —— 它允许在推理过程中动态地将新请求插入批次,同时将已完成请求从批次中移除,从而实现更高的 GPU 利用率。

vLLM 的连续批处理调度器将推理过程划分为两个关键阶段:预填充(Prefill)与解码(Decode)。预填充阶段负责处理请求的输入 token 序列,这一阶段计算密集且能有效利用 GPU 的并行计算能力;解码阶段则是自回归生成下一个 token 的过程,其计算量相对较小但对延迟极为敏感。调度器在每个调度周期内需要做出一个核心决策:当前 GPU 资源应该分配给哪些请求进行预填充,哪些请求继续解码。这个决策直接影响整体吞吐量和首 token 延迟(Time to First Token, TTFT)。

调度策略的实现细节体现在 vLLM 的代码结构中。在 arg_utils.py 中可以观察到,调度器支持通过 max_num_seqs 参数控制最大并发序列数,这个参数直接决定了同一时刻 GPU 上可处理的最大请求量。较大的 max_num_seqs 能够提升吞吐量但会增加调度开销和内存压力,较小的值则有助于降低延迟但牺牲了 GPU 利用率。根据实际测试经验,在 A100-80GB 显卡上运行 70B 参数的模型时,max_num_seqs 设置在 256 到 512 之间通常能取得较好的平衡。

PagedAttention 的内存管理革命

PagedAttention 是斯坦福大学团队提出的革命性注意力机制,其核心思想借鉴了操作系统中的虚拟内存分页管理。在传统的 PagedAttention 实现中,KV Cache 需要被连续存储在 GPU 内存中,每次为新序列分配缓存时都必须申请连续内存块。当序列长度增长或动态变化时,这种连续内存分配策略会导致严重的内存碎片化问题,最终使得实际可用内存远小于物理显存容量。

vLLM 将 PagedAttention 的内存分页管理能力发挥到了极致。在 vLLM 的架构中,KV Cache 被组织为固定大小的页面块(默认 block_size 为 16 个 token),每个序列的 KV Cache 可以非连续地分布在多个页面块中。当序列需要扩展时,只需分配新的页面块而无需寻找连续的内存区域。这种设计从根本上消除了内存碎片问题,使得 GPU 显存利用率可以稳定保持在较高水平。

分页机制的实际效果在长上下文场景下尤为显著。以往处理 32K 或更长的上下文时,传统的连续内存分配方式往往在序列长度达到十几 K 时就因内存不足而失败,而 vLLM 凭借分页管理可以轻松支持更长的上下文长度。值得注意的是,vLLM 还支持将不活跃的页面块交换到 CPU 内存(通过 KV Transfer 机制),这为在有限 GPU 显存条件下运行超大模型提供了弹性扩展能力。

关键配置参数与调优策略

针对连续批处理与分页内存的协同优化,有几个关键参数需要重点关注。gpu_memory_utilization 控制预留给 KV Cache 和模型权重的 GPU 显存比例,默认值通常为 0.9,但实际部署中需要根据模型规模和 batch 需求进行调整。当遇到 OOM 错误时,首先应该降低该参数而非直接增加硬件资源;反之,如果 GPU 显存利用率长期低于 0.7,则可以尝试提升该值以支持更大的并发量。

max_model_len 参数设定了单序列能够使用的最大 token 数,这个值直接影响页面块的分配策略。较大的 max_model_len 需要分配更多的页面块来存储 KV Cache,会减少可并发的序列数量。在实际部署中,应该根据业务场景的实际需求设置该值,避免设置过大的保守值导致资源浪费。以 - chat 模型为例,如果业务场景中最长的输入不超过 4K tokens,则将 max_model_len 设置为 4096 比使用 8192 更加合理。

调度器的预填充与解码资源分配比例也是调优的重点。vLLM 默认采用基于剩余步数的调度策略,优先处理快要完成的解码请求以最小化等待时间。但对于需要高吞吐量的离线批量推理场景,可以通过调整预填充批次的占比来提升整体效率。在 vLLM 的配置中,可以通过设置 specific 的调度策略参数来改变这一行为。

监控指标与性能瓶颈定位

生产环境中部署 vLLM 时,需要关注几个关键监控指标以确保系统运行在健康状态。GPU 利用率是最直接的指标,但需要区分是计算瓶颈还是内存瓶颈导致的利用率不足。如果 GPU Compute utilization 较低但 Memory utilization 接近满载,说明瓶颈在内存访问,应该优先优化 KV Cache 的分配策略或降低并发量;反之如果内存利用率较低但计算利用率高,则可以考虑增加并发序列数来提升吞吐量。

prefills_per_second 和 decodes_per_second 的比值能够反映调度器的工作状态。健康的系统应该保持适当的预填充与解码比例,预填充过多会导致新请求的 TTFT 增加,解码不足则会影响整体吞吐量。通过观察这个比值的变化趋势,可以及时发现调度策略是否需要调整。

另外需要监控的是页面块的分配失败率。当系统频繁出现页面块分配失败时,说明当前的 gpu_memory_utilization 设置已经接近上限,或者存在内存泄漏问题。vLLM 提供了详细的日志来追踪页面块的分配与释放情况,在调试阶段应该开启这些日志以便定位问题。

总结

vLLM 通过连续批处理调度器与 PagedAttention 分页内存管理的协同设计,实现了推理服务中吞吐量与延迟的优化平衡。连续批处理打破了传统静态批处理的资源闲置瓶颈,而 PagedAttention 则从根本上解决了 KV Cache 的内存碎片化问题。在实际部署中,通过合理配置 max_num_seqs、gpu_memory_utilization、max_model_len 等关键参数,并结合 GPU 利用率、预填充解码比例等监控指标进行动态调优,可以充分发挥 vLLM 的性能优势。理解这两个核心机制的工作原理与配置策略,是构建高效推理服务的必要基础。


参考资料

vLLM 项目 GitHub 仓库:https://github.com/vllm-project/vllm

vLLM 引擎参数配置源码:https://github.com/vllm-project/vllm/blob/main/vllm/engine/arg_utils.py