4、吞吐优化概述:吞吐量定义、影响吞吐的关键因素、优化策略全景
4.1 吞吐量的定义:从“每秒请求数”到“有效生成Token数”
在vLLM的语境下,吞吐量(Throughput)并非一个单一的指标,而是衡量系统在单位时间内能够处理多少有效推理工作量的综合能力。业界最常用的两个口径是:
- 请求级吞吐(Requests Per Second, RPS):每秒完成的请求数量。该指标直观反映系统对用户请求的响应能力,但在输出长度差异巨大的场景下容易失真——一个生成512个Token的请求与一个生成32个Token的请求被等量齐观。
- Token级吞吐(Tokens Per Second, TPS):每秒生成的Token总数(含输入处理部分)。这是衡量算力利用效率更本质的指标,也是vLLM优化时最关注的数字。通常以
output_tokens_per_second或total_tokens_per_second(含输入)来度量。
在显存受限的推理场景中,我们真正要优化的吞吐是“在KV Cache不溢出的前提下,单位显存能够支撑的Token生成速率”。因此,vLLM的吞吐优化本质上是一场显存资源与计算流水线之间的精细博弈。下图展示了吞吐量在系统层面的构成关系:
4.2 影响吞吐的关键因素:显存墙与调度开销
在vLLM中,吞吐量并非单纯由GPU算力决定,而是被以下四个核心因素所钳制:
| 因素 | 影响机制 | vLLM中的对应优化 |
|---|---|---|
| KV Cache容量 | KV Cache占用显存比例可达30%~50%,容量不足导致批大小受限或触发抢占 | PagedAttention按页分配,减少内部碎片;gpu_memory_utilization控制预留比例 |
| 批大小(Batch Size) | 批大小越大,GPU算力利用率越高,但受限于KV Cache总量 | Continuous Batching动态调整批成员,而非静态等批满 |
| 调度粒度 | 请求级调度导致GPU空转等待;Token级调度才能填满流水线 | 迭代级调度(Iteration-level Scheduling) |
| 显存碎片 | 传统预分配方式产生大量碎片,实际可用KV Cache远小于理论值 | 按页分配 + 按需换入换出 |
核心原理:vLLM的吞吐优化遵循“显存利用率 → 批大小 → 算力利用率 → 吞吐量”的传导链。显存是起点,如果KV Cache无法容纳足够多的并发序列,再大的算力也无法转化为有效吞吐。因此,vLLM将显存管理从“连续大块预留”改为“分页按需分配”,使得KV Cache的利用率从传统方案的40%~60%提升至90%以上。
4.3 优化策略全景:从显存到调度再到执行
vLLM的吞吐优化策略可以归纳为三个层次,每一层解决不同维度的问题:
- 显存层:最大化KV Cache可用空间
- 通过
PagedAttention将KV Cache划分为固定大小的块(默认16个Token/块),按需分配,消除内部碎片与外部碎片。 - 通过
gpu_memory_utilization参数(默认0.9)控制预留显存比例,在模型加载后尽可能多地将剩余显存分配给KV Cache。 - 通过
max_num_seqs限制并发序列数,防止单序列过度占用显存导致其他序列被抢占。
- 通过
- 调度层:提高GPU计算流水线的填充度
- Continuous Batching:不再等待一个batch全部完成才回收资源,而是每步迭代后立即将已完成序列移出,并加入新序列。
- 抢占与恢复:当显存不足时,优先抢占最老序列的KV Cache,将其换出到CPU内存,待显存释放后再换回。
- Prefix Caching:对共享前缀(如System Prompt)的KV Cache进行复用,避免重复计算。
- 执行层:减少单步计算开销
- 使用
CUDA Graph捕获解码阶段的固定计算图,减少内核启动开销。 - 通过
--enforce-eager关闭Graph捕获(调试用),生产环境建议开启。 - 配合
--max-model-len合理设置序列最大长度,避免为极少数的超长序列预留过多显存。
- 使用
4.4 案例:从“显存不足”到“吞吐翻倍”的调优路径
假设我们在一张A100-80GB上部署Llama-3-8B模型,初始配置如下:
# 初始配置(吞吐不佳)
python -m vllm.entrypoints.openai.api_server \
--model meta-llama/Llama-3-8B \
--gpu-memory-utilization 0.7 \
--max-num-seqs 64 \
--max-model-len 4096
运行后观察吞吐指标,发现TPS仅为约1800 tokens/s,且GPU利用率波动明显。通过nvidia-smi观察显存分配,发现KV Cache仅占约20GB(总显存的25%),大量显存闲置。进一步分析,问题出在gpu_memory_utilization=0.7导致预留不足,且max-num-seqs=64限制了并发度。
优化后的配置:
# 优化后配置(吞吐显著提升)
python -m vllm.entrypoints.openai.api_server \
--model meta-llama/Llama-3-8B \
--gpu-memory-utilization 0.92 \
--max-num-seqs 256 \
--max-model-len 2048 \
--enable-prefix-caching
调整后,KV Cache可用空间从约20GB提升至约45GB,并发序列数从64提升至256,TPS从1800提升至4200 tokens/s,吞吐提升约2.3倍。关键变化在于:
- 显存利用率提升,批大小得以扩大,GPU算力被更充分地填充。
- 启用Prefix Caching后,共享System Prompt的请求不再重复计算前缀,进一步释放了计算资源。
- 将
max-model-len从4096降至2048,避免了为极少数的超长序列预留大量显存。
4.5 误区与复盘:吞吐优化中的常见陷阱
误区一:盲目调大 gpu_memory_utilization 到 0.99
虽然更大的显存利用率意味着更大的KV Cache,但过高的设置可能导致CUDA Context或激活值分配失败,引发OOM。建议从0.90~0.95起步,逐步上调并观察稳定性。
误区二:忽略 max-num-seqs 与KV Cache的匹配关系
如果max-num-seqs设置过大,而每个序列的KV Cache分配不足,会导致频繁的抢占与换入换出,反而降低吞吐。应通过vllm日志中的# GPU KV cache size和# Maximum concurrency信息来校准。
复盘要点:吞吐优化不是单一参数的调优,而是显存预算 → 并发上限 → 调度策略 → 执行效率的联动调整。每次修改参数后,应同时观察TPS、TTFT、GPU利用率以及KV Cache命中率四个指标,避免只盯TPS而牺牲延迟。