vLLM 官方调优方案

以下是 vLLM 官网性能优化文档(Optimization and Tuning)的中文翻译:


优化与调优

本指南涵盖了 vLLM V1 的优化策略和性能调优方法。

提示

内存不足?请参阅本指南中关于如何节省内存的部分。

优化级别

vLLM 提供了 4 个优化级别(-O0, -O1, -O2, -O3),允许用户在启动时间和运行性能之间进行权衡:

  • -O0:无优化。启动时间最快,但性能最低。
  • -O1:快速优化。包含简单编译、快速算子融合以及分段(PIECEWISE)CUDA Graphs。
  • -O2:默认优化。包含额外的编译范围、额外的算子融合以及完整与分段(FULL_AND_PIECEWISE)CUDA Graphs。
  • -O3:激进优化。目前等同于 -O2,但未来可能会包含更多耗时或实验性的优化。

以下是 vLLM 官网"优化级别"详细文档的中文翻译:


优化级别

概述

vLLM 提供了 4 个优化级别(-O0, -O1, -O2, -O3),允许用户在启动时间和运行性能之间进行权衡:

  • -O0:无优化。启动时间最快,但性能最低。
  • -O1:快速优化。包含简单编译、快速算子融合以及分段(PIECEWISE)CUDA Graphs。
  • -O2:默认优化。包含额外的编译范围、额外的算子融合以及完整与分段(FULL_AND_PIECEWISE)CUDA Graphs。
  • -O3:激进优化。目前等同于 -O2,但未来可能会包含更多耗时或实验性的优化。

所有优化级别的默认配置都可以通过手动设置底层标志来实现。用户手动设置的标志优先级高于优化级别的默认值

级别摘要与使用示例

bash 复制代码
# CLI 用法
vllm serve RedHatAI/Llama-3.2-1B-FP8 -O1
python 复制代码
# Python API 用法
from vllm.entrypoints.llm import LLM

llm = LLM(
    model="RedHatAI/Llama-3.2-1B-FP8",
    optimization_level=2 # 等同于 -O2
)

-O0: 无优化

以尽可能快的速度启动------不进行自动调优、不进行编译、不使用 CUDA Graphs。此级别适用于开发和调试的初始阶段。

配置项:

  • -cc.cudagraph_mode=NONE
  • -cc.mode=NONE(同时导致 -cc.custom_ops=["none"]
  • -cc.pass_config.fuse_...=False(禁用所有融合)
  • --kernel-config.enable_flashinfer_autotune=False

-O1: 快速优化

优先保证快速启动,但仍启用基本优化(如编译和 CUDA Graphs)。对于大多数希望启动更快、同时确保代码不会破坏 CUDA Graphs 或编译流程的开发场景,此级别是一个良好的平衡选择。

配置项:

  • -cc.cudagraph_mode=PIECEWISE
  • -cc.mode=VLLM_COMPILE
  • --kernel-config.enable_flashinfer_autotune=True

算子融合:

  • -cc.pass_config.fuse_norm_quant=True*
  • -cc.pass_config.fuse_act_quant=True*
  • -cc.pass_config.fuse_act_padding=True
  • -cc.pass_config.fuse_mla_dual_rms_norm=True

*注:仅当任一算子使用自定义内核时才启用这些融合,否则 Inductor 融合效果更好。

†注:这些融合仅限 ROCm 平台,且需要 AITER。

-O2: 完整优化(默认)

以牺牲额外启动时间为代价优先考虑性能。推荐用于生产环境工作负载,因此也是默认级别。由于额外的编译范围,此级别的融合过程可能需要更长时间。

配置项(在 -O1 基础上增加):

  • -cc.cudagraph_mode=FULL_AND_PIECEWISE
  • -cc.pass_config.fuse_allreduce_rms=True
  • -cc.pass_config.fuse_rope_kvcache=True

†注:这些融合仅限 ROCm 平台,且需要 AITER。

-O3: 激进优化

此级别目前与 -O2 相同,但未来可能会包含更耗时或更具实验性的额外优化。

故障排除

常见问题

  • 启动时间过长 :使用 -O0-O1 以加快启动速度。
  • 编译错误 :使用 debug_dump_path 获取额外的调试信息。
  • 性能问题 :确保在生产环境中使用 -O2

加速启动

除了优化级别外,还有三种机制可以减少相同(模型、配置、硬件)组合在重复启动时的首字延迟(Time-to-First-Token):

  1. 复用编译缓存 :vLLM 会将 torch.compile 的产物持久化存储在 VLLM_CACHE_ROOT(默认为 ~/.cache/vllm)下。该缓存目录可以在机器间复制,也可以烘焙到容器镜像中;详见 torch.compile 设计文档。设置 VLLM_FORCE_AOT_LOAD=1 可以在缓存未命中时直接报错,而不是静默地重新编译(任何对模型、配置、相关 VLLM_* 环境变量、torch 构建版本或 GPU 型号的更改都会使缓存失效)。
  2. 使用 --kv-cache-memory 跳过内存分析 :在启动时,vLLM 会在日志中记录能够复现当前分配的确切 --kv-cache-memory 值。在下次启动时传入该值,可以跳过内存分析测量和 CUDA-graph 内存估算过程。注意:这会对性能产生影响:KV Cache 的大小将被精确设定为给定值而非通过测量得出,因此保守的值会限制批次并发数(从而限制吞吐量),而过于乐观的值则会导致分配时失败。该值仅在同一 GPU 且初始空闲内存相同的情况下有效;如果在硬件变更或共存任务变更后启动时出现 OOM,请移除该标志以重新进行分析。
  3. 使用 --enforce-eager 禁用 CUDA Graphs 服务:跳过编译和 CUDA-graph 捕获以实现尽可能快的启动,代价是稳态解码性能下降。这对开发迭代循环以及衡量启动过程中编译/捕获所占的时间非常有用。

抢占(Preemption)

由于 Transformer 架构的自回归特性,有时 KV Cache 空间不足以处理所有批处理请求。在这种情况下,vLLM 可以抢占某些请求以释放 KV Cache 空间供其他请求使用。被抢占的请求会在有足够的 KV Cache 空间可用时重新计算。当这种情况发生时,您可能会看到以下警告:

text 复制代码
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_seqsmax_num_batched_tokens:这会减少批次中的并发请求数量,从而降低对 KV Cache 空间的需求。
  • 增加 tensor_parallel_size:这会将模型权重分片到多个 GPU 上,使每个 GPU 有更多内存可用于 KV Cache。但是,增加此值可能会导致过多的同步开销。
  • 增加 pipeline_parallel_size:这会将模型层分布到多个 GPU 上,减少每个 GPU 上模型权重所需的内存,间接留出更多内存用于 KV Cache。但是,增加此值可能会导致延迟惩罚。

您可以通过 vLLM 暴露的 Prometheus 指标监控抢占请求的数量。此外,通过设置 disable_log_stats=False,您可以在日志中记录累计抢占请求数。

在 vLLM V1 中,默认的抢占模式是 RECOMPUTE(重计算)而非 SWAP(交换),因为在 V1 架构中重计算的开销更低。

分块预填充(Chunked Prefill)

分块预填充允许 vLLM 将大型预填充任务分成较小的块进行处理,并将其与解码请求一起批处理。此功能通过更好地平衡计算密集型(预填充)和内存密集型(解码)操作,有助于同时提高吞吐量和降低延迟。

在 V1 中,只要条件允许,默认启用分块预填充。启用后,调度策略会优先处理解码请求。它会在调度任何预填充操作之前,将所有待处理的解码请求打包。当 max_num_batched_tokens 预算中有可用 token 时,才会调度待处理的预填充。如果某个待处理的预填充请求无法完全放入 max_num_batched_tokens 预算中,系统会自动对其进行分块。

该策略有两个好处:

  1. 由于解码请求被优先处理,改善了词间延迟(ITL)和生成解码速度。
  2. 通过将计算密集型(预填充)和内存密集型(解码)请求放在同一个批次中,有助于实现更好的 GPU 利用率。

使用分块预填充进行性能调优

您可以通过调整 max_num_batched_tokens 来调优性能:

  • 较小的值(例如 2048)可获得更好的 ITL,因为较少的预填充会减慢解码速度。
  • 较高的值可获得更好的首字延迟(TTFT),因为可以在一个批次中处理更多的预填充 token。
  • 为了获得最佳吞吐量,建议设置 max_num_batched_tokens > 8192,尤其是在大显存 GPU 上运行较小模型时。
  • 如果 max_num_batched_tokensmax_model_len 相同,则几乎等同于 V0 的默认调度策略(区别在于它仍然优先处理解码)。

警告

当禁用分块预填充时,max_num_batched_tokens 必须大于 max_model_len

在这种情况下,如果 max_num_batched_tokens < max_model_len,vLLM 可能会在服务启动时崩溃。

python 复制代码
from vllm import LLM

# 设置 max_num_batched_tokens 以调优性能
llm = LLM(model="meta-llama/Llama-3.1-8B-Instruct", max_num_batched_tokens=16384)

更多详情请参阅相关论文 (https://arxiv.org/pdf/2401.08671https://arxiv.org/pdf/2308.16369)。

并行策略

vLLM 支持多种并行策略,可以组合使用以针对不同硬件配置优化性能。

张量并行(Tensor Parallelism, TP)

张量并行在每个模型层内将模型参数分片到多个 GPU 上。这是单节点内大模型推理最常用的策略。

适用场景:

  • 模型太大,无法装入单个 GPU
  • 需要减轻单个 GPU 的显存压力,以便留出更多 KV Cache 空间来提高吞吐量
python 复制代码
from vllm import LLM

# 将模型拆分到 4 个 GPU 上
llm = LLM(model="meta-llama/Llama-3.3-70B-Instruct", tensor_parallel_size=4)

对于无法装入单个 GPU 的大模型(如 70B 参数模型),张量并行是必不可少的。

流水线并行(Pipeline Parallelism, PP)

流水线并行将模型层分布到多个 GPU 上。每个 GPU 按顺序处理模型的不同部分。

适用场景:

  • 已经用尽了高效的张量并行但仍需进一步分发模型,或者需要跨节点分发
  • 对于非常深且窄的模型,层级分布比张量分片更高效

流水线并行可以与张量并行结合用于超大模型:

python 复制代码
from vllm import LLM

# 结合流水线和张量并行
llm = LLM(
    model="meta-llama/Llama-3.3-70B-Instruct",
    tensor_parallel_size=4,
    pipeline_parallel_size=2,
)

专家并行(Expert Parallelism, EP)

专家并行是针对混合专家模型(MoE)的一种专用并行形式,其中不同的专家网络被分布到各个 GPU 上。

适用场景:

  • 专门针对 MoE 模型(如 DeepSeekV3, Qwen3MoE, Llama-4)
  • 希望在 GPU 之间平衡专家计算负载

通过设置 enable_expert_parallel=True 启用专家并行,这将使 MoE 层使用专家并行代替张量并行。它将使用与您为张量并行设置的相同并行度。

数据并行(Data Parallelism, DP)

数据并行在多个 GPU 组上复制整个模型,并并行处理不同的请求批次。

适用场景:

  • 拥有足够的 GPU 来复制整个模型
  • 需要扩展吞吐量而非模型规模
  • 在多用户环境中,请求批次之间的隔离是有益的

数据并行可以与其他并行策略结合使用,通过 data_parallel_size=N 设置。请注意,MoE 层将根据张量并行大小和数据并行大小的乘积进行分片。

多路 GPU 节点的 NUMA 绑定

在多路 CPU 插槽的 GPU 服务器上,如果 GPU 工作进程的 CPU 执行和内存分配偏离了离 GPU 最近的 NUMA 节点,性能可能会下降。vLLM 可以在 Python 子进程启动前使用 numactl 固定每个工作进程,确保解释器、导入和早期分配器状态从一开始就遵循所需的 NUMA 策略。

使用 --numa-bind 启用该功能。默认情况下,vLLM 会自动检测 GPU 到 NUMA 的映射,并为每个工作进程使用 --cpunodebind=<node> --membind=<node>。当您需要自定义 CPU 策略时,添加 --numa-bind-cpus,vLLM 将切换为 --physcpubind=<cpu-list> --membind=<node>

这些 --numa-bind* 选项仅适用于 GPU 执行进程。它们不会配置 CPU 后端单独的线程亲和性控制。自动 GPU 到 NUMA 检测目前已针对基于 CUDA/NVML 以及 ROCM 的平台实现;其他 GPU 后端如果使用这些选项,必须提供显式的绑定列表。

--numa-bind-nodes 按照 GPU 索引的顺序,为每个可见 GPU 接受一个非负 NUMA 节点索引。--numa-bind-cpus 按照 GPU 索引的顺序,为每个可见 GPU 接受一个 numactl CPU 列表。每个 CPU 列表必须使用 numactl --physcpubind 语法,例如 0-3, 0,2,4-716-31,48-63

bash 复制代码
# 自动检测可见 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 决定。
  • 当前实现绑定了 GPU 执行进程(如 EngineCore 和多进程 worker)。它不对前端 API 服务器进程或 DP 协调器应用 NUMA 绑定。
  • 在容器化环境中,NUMA 策略系统调用可能需要额外权限,例如通过 docker run 运行时添加 --cap-add SYS_NICE

CPU 后端线程亲和性

CPU 后端使用与 --numa-bind 不同的机制。CPU 执行通过特定于 CPU 的环境变量进行配置,如 VLLM_CPU_OMP_THREADS_BINDVLLM_CPU_NUM_OF_RESERVED_CPUCPU_VISIBLE_MEMORY_NODES,而不是面向 GPU 的 --numa-bind* CLI 选项。

默认情况下,VLLM_CPU_OMP_THREADS_BIND=auto 会根据每个 CPU worker 可用的 CPU 和 NUMA 拓扑推导 OpenMP 放置策略。要覆盖自动策略,请使用 CPU 后端文档中记录的 CPU 列表格式显式设置 VLLM_CPU_OMP_THREADS_BIND,或使用 nobind 禁用此行为。

有关当前 CPU 后端设置和调优指南,请参阅:

  • 相关运行时环境变量
  • 如何决定 VLLM_CPU_OMP_THREADS_BIND

仅限 GPU 的 --numa-bind--numa-bind-nodes--numa-bind-cpus 选项不会配置 CPU worker 亲和性。

多模态编码器的批次级数据并行

默认情况下,TP 用于对多模态编码器的权重进行分片,就像语言解码器一样,以减少每个 GPU 上的内存和计算负载。

然而,由于多模态编码器的大小远小于语言解码器,TP 带来的收益相对较小。另一方面,由于每一层之后都要执行 all-reduce,TP 会产生显著的通信开销。

鉴于此,改用 TP 来分片批量输入数据(本质上是执行批次级数据并行)可能更有利。测试表明,当 tensor_parallel_size=8 时,这可以将吞吐量和 TTFT 提高约 10%。对于使用硬件未优化的 Conv3D 操作的视觉编码器,批次级 DP 相比常规 TP 还能再提升 40%。

尽管如此,由于多模态编码器的权重在每个 TP rank 上都有副本,内存消耗会有轻微增加,如果您的显存本就勉强够用,可能会导致 OOM。

您可以通过设置 mm_encoder_tp_mode="data" 来启用批次级 DP,例如:

python 复制代码
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 来分片权重
)

重要提示

不要将批次级 DP 与 API 请求级 DP(由 data_parallel_size 控制)混淆。

批次级 DP 需要针对每个模型单独实现,并通过在模型类中设置 supports_encoder_tp_data = True 来启用。无论如何,您都需要在引擎参数中设置 mm_encoder_tp_mode="data" 才能使用此功能。

已知支持的模型(及相应基准测试):

  • dots_ocr (Pull Request #25466)
  • GLM-4.1V 及以上 (Pull Request #23168)
  • InternVL (Pull Request #23909)
  • Kimi-VL (Pull Request #23817)
  • Llama4 (Pull Request #18368)
  • MiniCPM-V-2.5 及以上 (Pull Request #23327, Pull Request #23948)
  • Qwen2-VL 及以上 (Pull Request #22742, Pull Request #24955, Pull Request #25445)
  • Step3 (Pull Request #22697)

输入处理

fastokens 后端

默认情况下,vLLM 使用标准的 Hugging Face tokenizers 库来驱动快速分词器。对于 BPE 分词器(Qwen, Llama, DeepSeek, GPT-OSS 等),您可以切换到 fastokens Rust 后端 ,这是一个即插即用的替代品,在编码/解码和流式反分词方面速度明显更快。VLLM_USE_FASTOKENS 在 vLLM v0.23.0 及更高版本中可用。如果您安装的 vLLM 版本不识别该环境变量,请在启用覆盖前升级 vLLM:

bash 复制代码
VLLM_USE_FASTOKENS=1 vllm serve Qwen/Qwen3-8B

离线 API 中的等效写法:

python 复制代码
import os
os.environ["VLLM_USE_FASTOKENS"] = "1"

from vllm import LLM
llm = LLM(model="Qwen/Qwen3-8B")

必须安装 fastokens Python 包(>= 0.2.0);如果未安装,vLLM 在加载分词器时会抛出明确的 ImportError。该覆盖适用于任何最终加载 HF 快速分词器的 --tokenizer-mode(hf, deepseek_v32, deepseek_v4 等)。不使用 HF 快速分词器的模型(mistral, kimi_audio)会忽略该标志。

受分词器限制的工作负载(长共享前缀、突发性短提示、批量反分词)获益最大。如果您的瓶颈是 GPU 预填充/解码,更换分词器不太可能在端到端表现上显现出来。

并行处理

您可以通过 API 服务器横向扩展来并行运行输入处理。当输入处理(在 API 服务器内运行)相比于模型执行(在引擎核心内运行)成为瓶颈,并且您有富余的 CPU 容量时,这很有用。

bash 复制代码
# 运行 4 个 API 进程和 1 个引擎核心进程
vllm serve Qwen/Qwen2.5-VL-3B-Instruct --api-server-count 4

# 运行 4 个 API 进程和 2 个引擎核心进程
vllm serve Qwen/Qwen2.5-VL-3B-Instruct --api-server-count 4 -dp 2

注意

API 服务器横向扩展仅适用于在线推理。
警告

默认情况下,每个 API 服务器使用 8 个 CPU 线程从请求数据中加载媒体项(如图像)。

如果您应用 API 服务器横向扩展,请考虑调整 VLLM_MEDIA_LOADING_THREAD_COUNT 以避免 CPU 资源耗尽。
注意

API 服务器横向扩展会禁用多模态 IPC 缓存,因为它要求 API 和引擎核心进程之间一一对应。

这不会影响多模态处理器缓存。

多模态缓存

多模态缓存避免了对相同多模态数据的重复传输或处理,这在多轮对话中很常见。

处理器缓存

多模态处理器缓存自动启用,以避免在 BaseMultiModalProcessor 中重复处理相同的多模态输入。

IPC 缓存

当 API (P0) 和引擎核心 (P1) 进程之间存在一一对应关系时,多模态 IPC 缓存自动启用,以避免在它们之间重复传输相同的多模态输入。

键复制缓存(Key-Replicated Cache)

默认情况下,IPC 缓存使用键复制缓存,其中缓存键存在于 API (P0) 和引擎核心 (P1) 进程中,但实际缓存数据仅驻留在 P1 中。

共享内存缓存

当涉及多个工作进程时(例如 TP > 1),共享内存缓存更高效。可以通过设置 mm_processor_cache_type="shm" 启用。在此模式下,缓存键存储在 P0 上,而缓存数据本身位于所有进程可访问的共享内存中。

配置

您可以通过设置 mm_processor_cache_gb 的值(默认 4 GiB)来调整缓存大小。超过此预算的已处理项目将以未缓存方式提供服务(并附带警告),而不是导致引擎启动失败;如果您希望缓存这些项目,请增大 mm_processor_cache_gb

如果您从缓存中获益不多,可以通过 mm_processor_cache_gb=0 完全禁用 IPC 和处理器缓存。

示例:

python 复制代码
# 使用更大的缓存
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,
)

缓存布局

根据配置,P0 和 P1 上的多模态缓存内容如下:

mm_processor_cache_type 缓存类型 P0 缓存 P1 引擎缓存 P1 Worker 缓存 最大内存占用
lru 处理器缓存 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 核心配置不足是性能下降的常见原因,尤其是在虚拟化环境中。

最低 CPU 要求

对于拥有 N 个 GPU 的部署,至少需要:

  • 1 个 API 服务器进程 ------ 处理 HTTP 请求、分词和输入处理
  • 1 个引擎核心进程 ------ 运行调度器并协调 GPU worker
  • N 个 GPU worker 进程 ------ 每个 GPU 一个,执行模型前向传播

这意味着始终至少有 2 + N 个进程竞争 CPU 时间。

警告

使用的物理 CPU 核心数少于进程数会导致争用,并显著降低吞吐量和延迟。引擎核心进程运行忙循环,对 CPU 饥饿特别敏感。

最低要求是 2 + N 个物理核心(API 服务器 1 个,引擎核心 1 个,每个 GPU worker 1 个)。实际上,分配更多核心可以提高性能,因为操作系统、PyTorch 后台线程和其他系统进程也需要 CPU 时间。

重要提示

请注意这里指的是物理 CPU 核心 。如果您的系统启用了超线程,那么 1 vCPU = 1 超线程 = 1/2 物理 CPU 核心,因此您至少需要 2 x (2 + N) 个 vCPU。

数据并行和多 API 服务器部署

当使用数据并行或多个 API 服务器时,CPU 需求会增加:

text 复制代码
最小物理核心数 = A + DP + N + (如果 DP > 1 则为 1,否则为 0)

其中 A 是 API 服务器数量(默认为 DP),DP 是数据并行大小,N 是 GPU 总数。例如,在 8 个 GPU 上使用 DP=4, TP=2:

4 个 API 服务器 + 4 个引擎核心 + 8 个 GPU worker + 1 个 DP 协调器 = 17 个进程

性能影响

CPU 配置不足尤其会影响:

  • 输入处理吞吐量 ------ 分词、聊天模板渲染和多模态数据加载都在 CPU 上运行
  • 调度延迟 ------ 引擎核心调度器在 CPU 上运行,直接影响新 token 分发给 GPU worker 的速度
  • 输出处理 ------ 反分词、网络传输,尤其是流式 token 响应都会消耗 CPU 周期

如果您观察到 GPU 利用率低于预期,CPU 争用可能是瓶颈。增加可用 CPU 核心数甚至时钟频率可以显著改善端到端性能。

Attention 后端选择

vLLM 支持多种针对不同类型硬件和使用场景优化的 Attention 后端。后端会根据您的 GPU 架构、模型类型和配置自动选择,但您也可以手动指定以获得最佳性能。

L20显卡实战策略

针对 L20 显卡(48GB GDDR6显存、PCIe接口、无NVLink)在高并发下"一上量就延迟飙升"的问题,核心痛点通常不是算力不够,而是 显存带宽瓶颈KV Cache 碎片化/抢占。L20 是 GDDR6 显存,带宽远低于 A100/H100 的 HBM,因此对调度策略极其敏感。

结合你刚才翻译的 vLLM V1 文档,以下是专门针对 L20 + 高并发 场景的"榨干硬件"实战调优清单,按优先级排序:

1. 开启分块预填充(Chunked Prefill)------ 解决"一上量就卡"的关键

这是 V1 架构中解决高并发延迟抖动最重要的特性。默认情况下,长文本预填充会阻塞整个批次,导致正在解码的请求被挂起,用户体感就是"突然卡住"。

  • 动作 :确保使用 -O2(默认已开启),并调整 max_num_batched_tokens
  • L20 推荐值 :不要设太大!L20 带宽有限,建议从 40968192 开始测试。
    • 设太大(如 32k):单个 prefill chunk 占用时间过长,解码请求 ITL(词间延迟)飙升。
    • 设太小(如 1k):吞吐量上不去,GPU 利用率低。
  • 原理:将大 prefill 切碎,与 decode 请求混合执行,用计算换带宽平滑度。

2. 严防 KV Cache 抢占(Preemption)------ 延迟杀手

你在文档中看到的 WARNING ... preempted by RECOMPUTE 是 L20 高并发下的头号敌人。L20 只有 48GB 显存,一旦 KV Cache 爆满触发重计算,延迟会瞬间增加数倍。

  • 监控指标 :紧盯 Prometheus 中的 vllm:num_preemptions_total。只要这个值在涨,体验就一定差。
  • 调优手段
    • 降低 gpu_memory_utilization :L20 建议设为 0.90~0.92(默认 0.95 在 PCIe 卡上容易因系统开销误判 OOM 或触发激进抢占)。宁可少塞几个请求,也不要触发重算。
    • 限制 max_num_seqs:根据模型大小硬限并发数。例如 7B 模型在 L20 上,如果平均上下文 4k,建议 max_num_seqs 不超过 32-48。
    • 启用 Prefix Caching :如果业务有大量相同 system prompt 或 RAG 上下文,必须开启 --enable-prefix-caching。这能节省 30%-70% 的 KV Cache,直接减少抢占概率。

3. 针对 L20 的并行策略选择

L20 没有 NVLink,多卡通信走 PCIe,带宽极低。千万不要盲目堆 TP!

场景 推荐策略 原因
7B/8B 模型 TP=1 + DP=N L20 单卡完全够放 7B+KV Cache。DP 线性扩展吞吐,零通信开销。
14B 模型 TP=1 (量化) 或 TP=2 FP8/AWQ 量化后单卡可放;若必须 FP16,TP=2 是 L20 PCIe 上限。
70B 模型 PP > TP 优先流水线并行。TP=4 在 PCIe 上 all-reduce 开销可能吃掉 30%+ 性能。建议 PP=2, TP=2 或 PP=4, TP=1。
MoE 模型 EP + DP 专家并行天然适合 PCIe 卡,通信量远小于 TP。

⚠️ L20 铁律:能用 DP 就不用 TP,能用 PP 就不用 TP。TP=2 以上在 L20 上收益急剧递减。

4. CPU 资源别被忽视

vLLM V1 是多进程架构,API Server + Engine Core + GPU Worker 都需要 CPU。高并发时 tokenizer、scheduler、detokenizer 全在 CPU 上跑。

  • 检查top 看 CPU 是否跑满。如果 GPU 利用率不满但 CPU 满了,瓶颈在 CPU。
  • 配置:确保物理核心数 ≥ 2 + N(N=GPU数)。超线程不算!
  • NUMA 绑定 :如果是双路 CPU 服务器,必须--numa-bind。L20 走 PCIe,跨 NUMA 访存延迟翻倍,直接影响 token 生成速度。
  • fastokens :设置 VLLM_USE_FASTOKENS=1,Rust 后端在高并发 tokenize/detokenize 时比 HF 快 2-5 倍,释放 CPU 给调度器。

5. 输入处理优化(多模态/长前缀场景)

如果你的业务涉及图片或多轮对话:

  • 多模态缓存 :确保 mm_processor_cache_gb 足够(默认 4G,图片多就调到 8-16G),避免重复处理。
  • API Server 横向扩展 :如果输入预处理是瓶颈,加 --api-server-count N,把 tokenize 压力分摊到多个进程。注意同步调低 VLLM_MEDIA_LOADING_THREAD_COUNT 防 CPU 过载。

📌 落地执行清单

bash 复制代码
vllm serve your-model \
  -O2 \                          # 默认完整优化
  --gpu-memory-utilization 0.90 \ # L20 留余量防抢占
  --max-num-batched-tokens 8192 \ # 分块预填充,平衡 ITL 和吞吐
  --max-num-seqs 48 \             # 硬限并发,根据实际 KV 占用调整
  --enable-prefix-caching \       # 有共享前缀必开
  --numa-bind \                   # 双路 CPU 必开
  --tensor-parallel-size 1 \      # 7B/8B 用 DP 不用 TP
  --data-parallel-size 4          # 4 张 L20 做 DP

验证方法:压测时同时观察三个指标:

  1. gpu_memory_utilization 实际值(不应长期顶到上限)
  2. num_preemptions_total(应为 0 或极低)
  3. ITL P99(应平稳,不应有尖刺)

如果 ITL P99 仍有尖刺 → 降低 max_num_batched_tokens

如果吞吐量不够且无抢占 → 提高 max_num_seqs 或加 DP

如果出现 OOM → 降低 gpu_memory_utilization 或减小 max_model_len

这套组合拳下来,L20 的有效吞吐通常能提升 30%-50%,延迟稳定性会有质的改善。软件层面榨干 L20 的核心思想是:尊重 GDDR6 带宽上限,用调度换平滑,用 DP 换规模,绝不硬刚 TP。

相关推荐
qqww1552 小时前
Win11更新后Edge卡顿掉帧?隐私窗口流畅解决办法
windows·edge·优化·卡顿
政企项目老覃18 小时前
边缘 AI 推理部署:安防零售场景下的模型裁剪与端侧落地实践
人工智能·程序人生·算法·性能优化·vllm
赋创小助手1 天前
Qwen3.8-27B 本地推理 Benchmark 解析:llama.cpp、vLLM、SGLang 与长 Context 的性能差异
服务器·人工智能·大模型·qwen·vllm·sglang·context长度
论文复现现场1 天前
Llama/Qwen 70B 部署需要几张 RTX 4090?2卡、4卡、8卡显存、量化与 vLLM 选型
llama·qwen·vllm·大模型推理·大模型部署·rtx4090
鬓戈4 天前
Qwen3.8-27B + vLLM 性能优化
人工智能·性能优化·vllm
昇腾知识体系4 天前
vLLM-Ascend 支持矩阵:supported_models 模型列表、BF16/ACLGraph 支持项核对方法
人工智能·华为·知识图谱·vllm