vLLM V1 性能调优全解:优化级别、抢占、分块预填充、并行策略与多模态缓存
【免费下载链接】vllmA high-throughput and memory-efficient inference and serving engine for LLMs项目地址: https://gitcode.com/GitHub_Trending/vl/vllm
本文为 vLLM V1 引擎的系统化性能调优指南,覆盖优化级别(-O0~-O3)、快速启动、KV 缓存抢占、分块预填充(Chunked Prefill)、TP/PP/EP/DP 四种并行策略、NUMA 绑定、输入处理加速、多模态缓存以及 CPU 资源规划等主题。读完本文,你将掌握针对具体部署场景(显存不足、启动慢、抢占频繁、多卡扩展、多模态吞吐)选择正确参数组合的方法,并能对照仓库源码理解每项配置背后的实现机制。
如果你还遇到显存不足的问题,可先参考 显存节省指南 了解如何压缩内存占用。
一、优化级别:用启动时间换峰值性能
vLLM 提供 4 个优化级别(-O0、-O1、-O2、-O3),允许用户在启动时间与稳态性能之间做权衡:
-O0:不做任何优化。启动最快,但运行性能最低;-O1:快速优化。简单编译、快速算子融合,使用 PIECEWISE CUDA 图;-O2:默认优化。更多的编译范围、更多融合,FULL_AND_PIECEWISE CUDA 图;-O3:激进优化。目前与-O2等价,未来可能包含额外耗时或实验性的优化项。
从源码结构看,这些级别在 vllm/config/vllm.py 中定义:OptimizationLevel枚举(O0~O3)与OPTIMIZATION_LEVEL_TO_CONFIG映射表为每个级别配置了具体的编译开关。以默认级别为例:
- O0 关闭全部融合 pass(
fuse_norm_quant、fuse_act_quant、fuse_allreduce_rms等),cudagraph_mode为NONE,编译模式为NONE; - O1 开启 norm/act 融合与 PIECEWISE CUDA 图,并启用 FlashInfer 自动调优;
- O2/O3 在 O1 基础上追加 allreduce+RMSNorm 融合、RoPE+KV cache 写入融合、MLA 相关融合等,CUDA 图模式升级为
FULL_AND_PIECEWISE。
引擎初始化时会调用VllmConfig._apply_optimization_level_defaults()把这些默认值递归写入编译配置与内核配置,且默认optimization_level = OptimizationLevel.O2(见 vllm/config/vllm.py)。更多细节见 优化级别设计文档。
二、更快的启动:三个降低 Time-to-First-Token 的机制
除了优化级别之外,还有三个机制可以缩短同一(模型、配置、硬件)组合重复启动时的首 token 延迟:
2.1 复用编译缓存
vLLM 会把torch.compile的产物持久化到VLLM_CACHE_ROOT(默认~/.cache/vllm)。该缓存目录可以在机器之间拷贝,也可以直接烘焙进容器镜像。详见 torch.compile 设计文档。设置VLLM_FORCE_AOT_LOAD=1可以让缓存在未命中时"大声失败"而不是静默重编译——任何对模型、配置、相关VLLM_*环境变量、torch 构建或 GPU 型号的改动都会使缓存失效。
2.2 用--kv-cache-memory跳过显存 profiling
启动时 vLLM 会在日志中打印一个精确的--kv-cache-memory取值,该值可复现当前的显存分配方案。下次启动时把它传回去,即可跳过显存 profiling 测量和 CUDA 图显存估算两个阶段。
对应的实现字段是CacheConfig.kv_cache_memory_bytes(见 vllm/config/cache.py):当它不为 None 时,会忽略gpu_memory_utilization,对 KV cache 大小做更精细的控制。
需要注意其性能含义:KV cache 会被精确地设置为给定值,而不是实测值。因此:
- 取值保守会限制批并发量(进而限制吞吐);
- 取值激进会直接在分配阶段失败(OOM);
- 该值仅在相同 GPU、相同初始空闲显存下有效。如果硬件或同租户进程发生变化导致启动 OOM,请移除该参数重新 profiling。
2.3 用--enforce-eager跳过编译与 CUDA 图捕获
该参数同时跳过编译和 CUDA 图捕获,以牺牲稳态 decode 性能为代价换取最快启动,适用于开发调试循环,也可以用来量化启动时间中有多少是编译/捕获贡献的。
三、抢占(Preemption):KV cache 不足时的自我保护
由于 Transformer 的自回归特性,有时 KV cache 空间不足以处理所有已批处理请求。此时 vLLM 会抢占(preempt)部分请求来释放空间,被抢占的请求会在 KV cache 空间再次充足时被重新计算。出现这种情况时你可能看到类似如下告警:
WARNING 05-09 00:49:33 scheduler.py:1057 Sequence group 0 is preempted by PreemptionMode.RECOMPUTE mode because there is not enough KV cache space. This can affect the end-to-end performance. Increase gpu_memory_utilization or tensor_parallel_size to provide more KV cache memory. total_cumulative_preemption_cnt=1该机制保障了系统鲁棒性,但抢占+重算会损害端到端延迟。如果你频繁遇到抢占,可考虑以下手段:
- 调大
gpu_memory_utilization:vLLM 按该百分比预分配 GPU 缓存,调高可为 KV cache 提供更多空间; - 调小
max_num_seqs或max_num_batched_tokens:减少批内并发请求数,从而降低 KV cache 需求; - 调大
tensor_parallel_size:把模型权重切分到多张 GPU,让每张卡有更多显存留给 KV cache,但过高的 TP 会带来过重的同步开销; - 调大
pipeline_parallel_size:把模型层分布到多张 GPU,降低每张卡的权重大小,间接腾出 KV cache 空间,但会增加延迟。
监控方面,可通过 vLLM 暴露的 Prometheus 指标观察抢占次数;也可以设置disable_log_stats=False在日志中打印累计抢占请求数。
另外注意:在 vLLM V1 中,默认抢占模式是RECOMPUTE而非SWAP,因为在 V1 架构下重算的开销更低。
四、分块预填充(Chunked Prefill)
分块预填充允许 vLLM 把大 prefill 切成小片,与 decode 请求组批一起处理。这一特性通过更均衡地搭配计算受限(prefill)与访存受限(decode)的操作,同时改善吞吐和延迟。
在 V1 中,只要可能就会默认启用 chunked prefill。启用后,调度策略优先处理 decode 请求:先调度所有待处理的 decode 请求,当max_num_batched_tokens预算中还有剩余 token 时再调度 pending 的 prefill;若某个 pending prefill 请求放不进该预算,会自动切块。
该策略有两个好处:
- decode 请求被优先调度,改善了逐 token 延迟(ITL)与生成速度;
- 把计算受限(prefill)与访存受限(decode)的请求放入同一批,提高了 GPU 利用率。
4.1 通过max_num_batched_tokens调优性能
- 较小的值(如 2048)能取得更好的 ITL,因为拖慢 decode 的 prefill 更少;
- 较大的值能取得更好的 TTFT,因为单批可处理更多 prefill token;
- 追求最优吞吐时,建议
max_num_batched_tokens > 8192,尤其是小模型部署在大 GPU 上; - 如果
max_num_batched_tokens与max_model_len相等,那几乎就等价于 V0 的默认调度策略(区别仅是仍然优先 decode)。
警告:当 chunked prefill 被禁用时,
max_num_batched_tokens必须大于max_model_len;否则若max_num_batched_tokens < max_model_len,vLLM 可能在服务器启动时崩溃。
from vllm import LLM # 设置 max_num_batched_tokens 来调优性能 llm = LLM(model="meta-llama/Llama-3.1-8B-Instruct", max_num_batched_tokens=16384)更深入的原理可参考相关论文(arXiv: 2401.08671、arXiv: 2308.16369,即 Sarathi 与 SplitFuse 系列工作)。
五、并行策略
vLLM 支持多种可组合的并行策略,用于在不同硬件配置下优化性能。
5.1 张量并行(TP)
张量并行把模型参数切分到单节点内的多张 GPU 上(每层内切分),是单节点大模型推理最常用的策略。
适用场景:
- 模型大到无法放入单张 GPU;
- 需要降低每张卡的显存压力,从而腾出更多 KV cache 空间以提高吞吐。
from vllm import LLM # 把模型切分到 4 张 GPU llm = LLM(model="meta-llama/Llama-3.3-70B-Instruct", tensor_parallel_size=4)对于单卡放不下的模型(如 70B 参数级),张量并行是必需的。
5.2 流水线并行(PP)
流水线并行把模型的不同层分布到不同 GPU,每张卡按顺序处理模型的不同部分。
适用场景:
- 已经用满了高效的张量并行,但仍需进一步分布模型(或跨节点);
- 对于非常"深且窄"的模型,按层分布比按张量切分更高效。
PP 可与 TP 组合用于超大模型:
from vllm import LLM # 组合流水线并行与张量并行 llm = LLM( model="meta-llama/Llama-3.3-70B-Instruct", tensor_parallel_size=4, pipeline_parallel_size=2, )5.3 专家并行(EP)
专家并行是面向 MoE(Mixture of Experts)模型的专门并行方式:把不同的专家网络分布到不同 GPU 上。
适用场景:
- 专门用于 MoE 模型(如 DeepSeekV3、Qwen3MoE、Llama-4);
- 希望在各 GPU 间均衡专家计算负载。
通过enable_expert_parallel=True启用,此时 MoE 层使用专家并行而非张量并行,并行度与tensor_parallel_size设置的值相同。
5.4 数据并行(DP)
数据并行把整个模型复制在多个 GPU 组上,并行处理不同的请求批次。
适用场景:
- GPU 数量足以完整复制模型;
- 需要扩展的是吞吐而非模型容量;
- 多用户环境下请求批次间的隔离有利于稳定性。
DP 与其他并行策略可组合,通过data_parallel_size=N设置。注意 MoE 层会按照张量并行度与数据并行度的乘积来切分。
5.5 多路 GPU 服务器的 NUMA 绑定
在多 socket GPU 服务器上,如果 GPU worker 进程的 CPU 执行与内存分配远离 GPU 所在的 NUMA 节点,性能会下降。vLLM 可以在 Python 子进程启动前用numactl为每个 worker 固定绑定,让解释器、模块导入和早期分配器状态从一开始就带着期望的 NUMA 策略创建。
使用--numa-bind启用该特性。默认情况下 vLLM 自动检测 GPU 到 NUMA 的映射,并对每个 worker 使用--cpunodebind=<node> --membind=<node>;需要自定义 CPU 策略时加--numa-bind-cpus,vLLM 会切换为--physcpubind=<cpu-list> --membind=<node>。
这些--numa-bind*选项只作用于 GPU 执行进程,不影响 CPU 后端的线程亲和性控制。自动 GPU-to-NUMA 检测目前实现了 CUDA/NVML 与 ROCm 平台;其他 GPU 后端若使用这些选项必须显式给出绑定列表。
--numa-bind-nodes:按可见 GPU 顺序、每个 GPU 一个非负 NUMA 节点索引;--numa-bind-cpus:按可见 GPU 顺序、每个 GPU 一个numactlCPU 列表,必须使用numactl --physcpubind语法,如0-3、0,2,4-7、16-31,48-63。
# 自动检测可见 GPU 的 NUMA 节点 vllm serve meta-llama/Llama-3.1-8B-Instruct \ --tensor-parallel-size 4 \ --numa-bind # 显式指定 NUMA 节点映射 vllm serve meta-llama/Llama-3.1-8B-Instruct \ --tensor-parallel-size 4 \ --numa-bind \ --numa-bind-nodes 0 0 1 1 # 显式 CPU 固定,适合 PCT 或其他高频核布局 vllm serve meta-llama/Llama-3.1-8B-Instruct \ --tensor-parallel-size 4 \ --numa-bind \ --numa-bind-nodes 0 0 1 1 \ --numa-bind-cpus 0-3 4-7 48-51 52-55注意事项:
- CLI 使用会强制多进程改用
spawn方式;若通过 Python API 启用 NUMA 绑定,请同时设置VLLM_WORKER_MULTIPROC_METHOD=spawn; - 自动检测依赖宿主机 NVML 与 NUMA 支持,若无法可靠判定映射,请显式传
--numa-bind-nodes; - 显式的
--numa-bind-nodes/--numa-bind-cpus必须是合法的numactl输入。vLLM 会做少量校验,但实际绑定语义仍由numactl决定; - 当前实现对
EngineCore与多进程 worker 这类 GPU 执行进程做绑定,不应用于前端 API server 进程或 DP coordinator; - 容器化环境中,NUMA 策略系统调用可能需要额外权限,例如
docker run时加--cap-add SYS_NICE。
从源码看,--numa-bind、--numa-bind-nodes、--numa-bind-cpus三个 CLI 参数定义在 vllm/engine/arg_utils.py,字段透传到ParallelConfig的numa_bind/numa_bind_nodes/numa_bind_cpus。
5.6 CPU 后端线程亲和性
CPU 后端使用与--numa-bind不同的机制:CPU 执行通过VLLM_CPU_OMP_THREADS_BIND、VLLM_CPU_NUM_OF_RESERVED_CPU、CPU_VISIBLE_MEMORY_NODES等 CPU 专用环境变量配置,而不是 GPU 向的--numa-bind*CLI 选项。
默认VLLM_CPU_OMP_THREADS_BIND=auto,会根据每个 CPU worker 可用的 CPU 与 NUMA 拓扑推导 OpenMP 放置策略;显式设置该变量可覆盖自动策略,使用nobind可禁用。GPU 专属的--numa-bind、--numa-bind-nodes、--numa-bind-cpus不会配置 CPU worker 亲和性。当前 CPU 后端的设置与调优参考:
- CPU 运行时环境变量
- 如何决定
VLLM_CPU_OMP_THREADS_BIND
5.7 多模态编码器的 Batch 级 DP
默认情况下,多模态编码器与语言解码器一样使用 TP 切分权重,以降低每卡的显存与计算负担。但由于多模态编码器相对语言解码器很小,TP 收益有限;而 TP 因每层后的 all-reduce 带来显著通信开销。
因此改为按 TP 切分批量输入数据(本质上是 batch 级 DP)往往更有利:在tensor_parallel_size=8时该方式被验证可提升约 10% 的吞吐与 TTFT;对于使用硬件未优化 Conv3D 的视觉编码器,batch 级 DP 相比常规 TP 还能再提升约 40%。
代价是:多模态编码器权重在 TP rank 间复制,显存占用会有小幅上升,如果模型本来就勉强放得下,可能引发 OOM。
设置mm_encoder_tp_mode="data"即可启用,例如:
from vllm import LLM llm = LLM( model="Qwen/Qwen2.5-VL-72B-Instruct", tensor_parallel_size=4, # 当 mm_encoder_tp_mode="data" 时, # 视觉编码器用 TP=4(而非 DP=1)来切分输入数据, # 即 TP 度成为其有效 DP 度。 # 注意:这与专家并行场景下语言解码器的 DP 度相互独立。 mm_encoder_tp_mode="data", # 语言解码器无论 mm_encoder_tp_mode 如何设置, # 始终用 TP=4 切分权重。 )重要:batch 级 DP 不要与 API 请求级 DP 混淆——后者由data_parallel_size控制。
batch 级 DP 需要逐模型实现,模型类中需设置supports_encoder_tp_data = True;无论模型是否支持,你都需要在引擎参数中设置mm_encoder_tp_mode="data"才会启用该特性。已知支持的模型包括:dots_ocr、GLM-4.1V 及以上、InternVL、Kimi-VL、Llama4、MiniCPM-V-2.5 及以上、Qwen2-VL 及以上、Step3。
六、输入处理
6.1 fastokens 后端
默认 vLLM 使用 Hugging Face 标准tokenizers库驱动 fast tokenizer。对 BPE 类分词器(Qwen、Llama、DeepSeek、GPT-OSS 等),可切换到 Rust 实现的fastokens后端——一个即插即用的替换,在 encode/decode 和流式 detokenization 上明显更快。VLLM_USE_FASTOKENS环境变量自 vLLM v0.23.0 起可用;如果你安装的版本不识别该变量,请先升级 vLLM 再启用:
VLLM_USE_FASTOKENS=1 vllm serve Qwen/Qwen3-8B离线 API 等价写法:
import os os.environ["VLLM_USE_FASTOKENS"] = "1" from vllm import LLM llm = LLM(model="Qwen/Qwen3-8B")前提是安装fastokensPython 包(>= 0.2.0);若未安装,vLLM 会在加载 tokenizer 时抛出清晰的ImportError。该开关对任何最终加载 HF fast tokenizer 的--tokenizer-mode(hf、deepseek_v32、deepseek_v4等)生效;不使用 HF fast tokenizer 的模型(mistral、kimi_audio)会忽略该标志。
从源码看,该环境变量在 vllm/envs.py 中注册,默认值为 False,仅在显式设置为真时启用。
token 处理成为瓶颈的负载——长共享前缀、突发短 prompt、批量 detokenization——收益最大;如果你的瓶颈在 GPU prefill/decode,端到端可能看不出分词器的变化。
6.2 并行化输入处理(API server 扩容)
可以通过 API server 扩容 并行运行输入处理。当输入处理(在 API server 内运行)相对模型执行(在 engine core 内运行)成为瓶颈、且你有富余 CPU 能力时很有用:
# 4 个 API 进程 + 1 个 engine core 进程 vllm serve Qwen/Qwen2.5-VL-3B-Instruct --api-server-count 4 # 4 个 API 进程 + 2 个 engine core 进程 vllm serve Qwen/Qwen2.5-VL-3B-Instruct --api-server-count 4 -dp 2注意与限制:
- API server 扩容仅在线推理可用;
- 默认每个 API server 使用 8 个 CPU 线程从请求数据加载媒体项(如图片)。扩容后建议调整
VLLM_MEDIA_LOADING_THREAD_COUNT,避免 CPU 资源耗尽; - 扩容会禁用 多模态 IPC 缓存,因为它要求 API 与 engine core 进程一一对应。不影响 多模态 processor 缓存。
七、多模态缓存
多模态缓存避免对相同多模态数据做重复传输或处理,这在多轮对话中非常常见。
7.1 多模态 Processor 缓存
processor 缓存自动启用,避免在BaseMultiModalProcessor中重复处理相同的多模态输入。
7.2 多模态 IPC 缓存
当 API(P0)与 engine core(P1)进程一一对应时,IPC 缓存自动启用,避免两者间重复传输相同的多模态输入。
键复制缓存(Key-Replicated Cache):默认 IPC 缓存采用键复制模式——缓存键同时存在于P0与P1进程中,但实际缓存数据只驻留在P1。
共享内存缓存(Shared Memory Cache):当存在多个 worker 进程(例如 TP > 1)时,共享内存缓存更高效。设置mm_processor_cache_type="shm"启用:该模式下缓存键存放在P0,缓存数据本身放在所有进程可访问的共享内存中。
从源码看,这些配置字段定义在 vllm/config/multimodal.py:mm_processor_cache_gb默认 4 GiB 且最小为 0;mm_processor_cache_type默认"lru",可选"shm";shm 模式另有mm_shm_cache_max_object_size_mb(默认 128 MiB)限制单个对象大小,且仅在mm_processor_cache_type="shm"时允许设置(配置校验器会拒绝其他组合)。
7.3 配置示例
mm_processor_cache_gb(默认 4 GiB)控制缓存大小。单个处理后的条目超过该预算时会带告警地"不缓存地服务",而不是让引擎启动失败;若希望缓存这类条目,请调大该值。若缓存收益不大,可用mm_processor_cache_gb=0彻底禁用 IPC 与 processor 缓存:
# 使用更大的缓存 llm = LLM( model="Qwen/Qwen2.5-VL-3B-Instruct", mm_processor_cache_gb=8, ) # 使用基于共享内存的 IPC 缓存 llm = LLM( model="Qwen/Qwen2.5-VL-3B-Instruct", tensor_parallel_size=2, mm_processor_cache_type="shm", mm_processor_cache_gb=8, ) # 禁用缓存 llm = LLM( model="Qwen/Qwen2.5-VL-3B-Instruct", mm_processor_cache_gb=0, )7.4 缓存内容放置
根据配置,P0与P1上的多模态缓存内容如下:
| mm_processor_cache_type | 缓存类型 | P0缓存 | P1引擎缓存 | P1Worker 缓存 | 最大内存 |
|---|---|---|---|---|---|
| lru | Processor 缓存 | K + V | N/A | N/A | mm_processor_cache_gb * data_parallel_size |
| lru | 键复制缓存 | K | K + V | N/A | mm_processor_cache_gb * api_server_count |
| shm | 共享内存缓存 | K | N/A | V | mm_processor_cache_gb * api_server_count |
| N/A | 已禁用 | N/A | N/A | N/A | 0 |
其中 K 存多模态项的哈希,V 存多模态项处理后的张量数据。
八、GPU 部署中的 CPU 资源规划
vLLM V1 采用多进程架构(见 V1 进程架构),每个进程都需要 CPU 资源。CPU 核配置不足是性能下降的常见根源,在虚拟化环境中尤其如此。
8.1 最少 CPU 需求
对于N张 GPU 的部署,至少存在:
- 1 个 API server 进程——处理 HTTP 请求、分词和输入处理;
- 1 个 engine core 进程——运行调度器并协调 GPU worker;
- N 个 GPU worker 进程——每 GPU 一个,执行模型前向。
也就是说始终至少有2 + N个进程在争抢 CPU 时间。
警告:物理 CPU 核数少于进程数会造成争抢,显著拉低吞吐与延迟。engine core 进程运行忙循环,对 CPU 饥饿特别敏感。
最低要求是2 + N个物理核(API server 1 个、engine core 1 个、每个 GPU worker 1 个)。实践中分配更多核会有帮助,因为操作系统、PyTorch 后台线程和其他系统进程也需要 CPU 时间。
重要:这里指的是物理 CPU 核。若系统开启超线程,1 vCPU = 1 超线程 = 1/2 物理核,因此最低需要2 x (2 + N)个 vCPU。
8.2 数据并行与多 API server 部署
使用数据并行或多个 API server 时,CPU 需求随之上升:
最少物理核数 = A + DP + N + (1 if DP > 1 else 0)其中A是 API server 数(默认为DP),DP是数据并行度,N是 GPU 总数。例如 8 张 GPU 上DP=4, TP=2:
4 个 API server + 4 个 engine core + 8 个 GPU worker + 1 个 DP coordinator = 17 个进程8.3 性能影响
CPU 资源不足特别影响:
- 输入处理吞吐——分词、chat 模板渲染、多模态数据加载都跑在 CPU 上;
- 调度延迟——engine core 调度器在 CPU 上运行,直接影响新 token 派发到 GPU worker 的速度;
- 输出处理——detokenization、网络、尤其是流式 token 响应消耗 CPU 周期。
如果观察到 GPU 利用率低于预期,CPU 争抢可能就是瓶颈。增加可用 CPU 核数乃至主频,都能显著提升端到端性能。
九、注意力后端选择
vLLM 支持多种针对不同硬件和用例优化的注意力后端。后端会根据 GPU 架构、模型类型和配置自动选择,你也可以手动指定以获得最优性能。关于可用后端、其特性支持与配置方式的详细信息,参见 注意力后端特性支持文档。
小结
vLLM V1 的调优思路可以概括为四层:先用优化级别(-O0~-O3)和启动加速手段(编译缓存复用、--kv-cache-memory、--enforce-eager)解决"起得快不快";再用max_num_batched_tokens平衡 ITL 与 TTFT,用gpu_memory_utilization、max_num_seqs等抑制抢占;然后按硬件规模选择 TP/PP/EP/DP 组合,并用 NUMA 绑定与足量物理核消除 CPU 侧瓶颈;最后针对多模态负载调mm_encoder_tp_mode与多模态缓存参数。每个参数都可在仓库源码(vllm/config/vllm.py、vllm/config/cache.py、vllm/config/multimodal.py、vllm/engine/arg_utils.py)中对照验证其解析逻辑与默认值。
【免费下载链接】vllmA high-throughput and memory-efficient inference and serving engine for LLMs项目地址: https://gitcode.com/GitHub_Trending/vl/vllm
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考