vLLM V1 性能调优全解:优化级别、抢占、分块预填充、并行策略与多模态缓存

vLLM V1 性能调优全解:优化级别、抢占、分块预填充、并行策略与多模态缓存 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枚举O0O3与OPTIMIZATION_LEVEL_TO_CONFIG映射表为每个级别配置了具体的编译开关。以默认级别为例O0 关闭全部融合 passfuse_norm_quant、fuse_act_quant、fuse_allreduce_rms等cudagraph_mode为NONE编译模式为NONEO1 开启 norm/act 融合与 PIECEWISE CUDA 图并启用 FlashInfer 自动调优O2/O3 在 O1 基础上追加 allreduceRMSNorm 融合、RoPEKV 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_LOAD1可以让缓存在未命中时大声失败而不是静默重编译——任何对模型、配置、相关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 性能为代价换取最快启动适用于开发调试循环也可以用来量化启动时间中有多少是编译/捕获贡献的。三、抢占PreemptionKV 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_cnt1该机制保障了系统鲁棒性但抢占重算会损害端到端延迟。如果你频繁遇到抢占可考虑以下手段调大gpu_memory_utilizationvLLM 按该百分比预分配 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_statsFalse在日志中打印累计抢占请求数。另外注意在 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_lenvLLM 可能在服务器启动时崩溃。from vllm import LLM # 设置 max_num_batched_tokens 来调优性能 llm LLM(modelmeta-llama/Llama-3.1-8B-Instruct, max_num_batched_tokens16384)更深入的原理可参考相关论文arXiv: 2401.08671、arXiv: 2308.16369即 Sarathi 与 SplitFuse 系列工作。五、并行策略vLLM 支持多种可组合的并行策略用于在不同硬件配置下优化性能。5.1 张量并行TP张量并行把模型参数切分到单节点内的多张 GPU 上每层内切分是单节点大模型推理最常用的策略。适用场景模型大到无法放入单张 GPU需要降低每张卡的显存压力从而腾出更多 KV cache 空间以提高吞吐。from vllm import LLM # 把模型切分到 4 张 GPU llm LLM(modelmeta-llama/Llama-3.3-70B-Instruct, tensor_parallel_size4)对于单卡放不下的模型如 70B 参数级张量并行是必需的。5.2 流水线并行PP流水线并行把模型的不同层分布到不同 GPU每张卡按顺序处理模型的不同部分。适用场景已经用满了高效的张量并行但仍需进一步分布模型或跨节点对于非常深且窄的模型按层分布比按张量切分更高效。PP 可与 TP 组合用于超大模型from vllm import LLM # 组合流水线并行与张量并行 llm LLM( modelmeta-llama/Llama-3.3-70B-Instruct, tensor_parallel_size4, pipeline_parallel_size2, )5.3 专家并行EP专家并行是面向 MoEMixture of Experts模型的专门并行方式把不同的专家网络分布到不同 GPU 上。适用场景专门用于 MoE 模型如 DeepSeekV3、Qwen3MoE、Llama-4希望在各 GPU 间均衡专家计算负载。通过enable_expert_parallelTrue启用此时 MoE 层使用专家并行而非张量并行并行度与tensor_parallel_size设置的值相同。5.4 数据并行DP数据并行把整个模型复制在多个 GPU 组上并行处理不同的请求批次。适用场景GPU 数量足以完整复制模型需要扩展的是吞吐而非模型容量多用户环境下请求批次间的隔离有利于稳定性。DP 与其他并行策略可组合通过data_parallel_sizeN设置。注意 MoE 层会按照张量并行度与数据并行度的乘积来切分。5.5 多路 GPU 服务器的 NUMA 绑定在多 socket GPU 服务器上如果 GPU worker 进程的 CPU 执行与内存分配远离 GPU 所在的 NUMA 节点性能会下降。vLLM 可以在 Python 子进程启动前用numactl为每个 worker 固定绑定让解释器、模块导入和早期分配器状态从一开始就带着期望的 NUMA 策略创建。使用--numa-bind启用该特性。默认情况下 vLLM 自动检测 GPU 到 NUMA 的映射并对每个 worker 使用--cpunodebindnode --membindnode需要自定义 CPU 策略时加--numa-bind-cpusvLLM 会切换为--physcpubindcpu-list --membindnode。这些--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_METHODspawn自动检测依赖宿主机 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_BINDauto会根据每个 CPU worker 可用的 CPU 与 NUMA 拓扑推导 OpenMP 放置策略显式设置该变量可覆盖自动策略使用nobind可禁用。GPU 专属的--numa-bind、--numa-bind-nodes、--numa-bind-cpus不会配置 CPU worker 亲和性。当前 CPU 后端的设置与调优参考CPU 运行时环境变量如何决定VLLM_CPU_OMP_THREADS_BIND5.7 多模态编码器的 Batch 级 DP默认情况下多模态编码器与语言解码器一样使用 TP 切分权重以降低每卡的显存与计算负担。但由于多模态编码器相对语言解码器很小TP 收益有限而 TP 因每层后的 all-reduce 带来显著通信开销。因此改为按 TP 切分批量输入数据本质上是 batch 级 DP往往更有利在tensor_parallel_size8时该方式被验证可提升约 10% 的吞吐与 TTFT对于使用硬件未优化 Conv3D 的视觉编码器batch 级 DP 相比常规 TP 还能再提升约 40%。代价是多模态编码器权重在 TP rank 间复制显存占用会有小幅上升如果模型本来就勉强放得下可能引发 OOM。设置mm_encoder_tp_modedata即可启用例如from vllm import LLM llm LLM( modelQwen/Qwen2.5-VL-72B-Instruct, tensor_parallel_size4, # 当 mm_encoder_tp_modedata 时 # 视觉编码器用 TP4而非 DP1来切分输入数据 # 即 TP 度成为其有效 DP 度。 # 注意这与专家并行场景下语言解码器的 DP 度相互独立。 mm_encoder_tp_modedata, # 语言解码器无论 mm_encoder_tp_mode 如何设置 # 始终用 TP4 切分权重。 )重要batch 级 DP 不要与 API 请求级 DP 混淆——后者由data_parallel_size控制。batch 级 DP 需要逐模型实现模型类中需设置supports_encoder_tp_data True无论模型是否支持你都需要在引擎参数中设置mm_encoder_tp_modedata才会启用该特性。已知支持的模型包括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_FASTOKENS1 vllm serve Qwen/Qwen3-8B离线 API 等价写法import os os.environ[VLLM_USE_FASTOKENS] 1 from vllm import LLM llm LLM(modelQwen/Qwen3-8B)前提是安装fastokensPython 包 0.2.0若未安装vLLM 会在加载 tokenizer 时抛出清晰的ImportError。该开关对任何最终加载 HF fast tokenizer 的--tokenizer-modehf、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 缓存当 APIP0与 engine coreP1进程一一对应时IPC 缓存自动启用避免两者间重复传输相同的多模态输入。键复制缓存Key-Replicated Cache默认 IPC 缓存采用键复制模式——缓存键同时存在于P0与P1进程中但实际缓存数据只驻留在P1。共享内存缓存Shared Memory Cache当存在多个 worker 进程例如 TP 1时共享内存缓存更高效。设置mm_processor_cache_typeshm启用该模式下缓存键存放在P0缓存数据本身放在所有进程可访问的共享内存中。从源码看这些配置字段定义在 vllm/config/multimodal.pymm_processor_cache_gb默认 4 GiB 且最小为 0mm_processor_cache_type默认lru可选shmshm 模式另有mm_shm_cache_max_object_size_mb默认 128 MiB限制单个对象大小且仅在mm_processor_cache_typeshm时允许设置配置校验器会拒绝其他组合。7.3 配置示例mm_processor_cache_gb默认 4 GiB控制缓存大小。单个处理后的条目超过该预算时会带告警地不缓存地服务而不是让引擎启动失败若希望缓存这类条目请调大该值。若缓存收益不大可用mm_processor_cache_gb0彻底禁用 IPC 与 processor 缓存# 使用更大的缓存 llm LLM( modelQwen/Qwen2.5-VL-3B-Instruct, mm_processor_cache_gb8, ) # 使用基于共享内存的 IPC 缓存 llm LLM( modelQwen/Qwen2.5-VL-3B-Instruct, tensor_parallel_size2, mm_processor_cache_typeshm, mm_processor_cache_gb8, ) # 禁用缓存 llm LLM( modelQwen/Qwen2.5-VL-3B-Instruct, mm_processor_cache_gb0, )7.4 缓存内容放置根据配置P0与P1上的多模态缓存内容如下mm_processor_cache_type缓存类型P0缓存P1引擎缓存P1Worker 缓存最大内存lruProcessor 缓存K VN/AN/Amm_processor_cache_gb * data_parallel_sizelru键复制缓存KK VN/Amm_processor_cache_gb * api_server_countshm共享内存缓存KN/AVmm_processor_cache_gb * api_server_countN/A已禁用N/AN/AN/A0其中 K 存多模态项的哈希V 存多模态项处理后的张量数据。八、GPU 部署中的 CPU 资源规划vLLM V1 采用多进程架构见 V1 进程架构每个进程都需要 CPU 资源。CPU 核配置不足是性能下降的常见根源在虚拟化环境中尤其如此。8.1 最少 CPU 需求对于N张 GPU 的部署至少存在1 个 API server 进程——处理 HTTP 请求、分词和输入处理1 个 engine core 进程——运行调度器并协调 GPU workerN 个 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 数默认为DPDP是数据并行度N是 GPU 总数。例如 8 张 GPU 上DP4, TP24 个 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),仅供参考