跳到内容

优化与调优

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

抢占

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

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 缓存空间。
  • 减少 max_num_seqsmax_num_batched_tokens。这会减少批处理中并发请求的数量,从而需要更少的 KV 缓存空间。
  • 增加 tensor_parallel_size。这将模型权重分片到不同的 GPU 上,使得每个 GPU 可以有更多的内存用于 KV 缓存。然而,增加此值可能会导致过多的同步开销。
  • 增加 pipeline_parallel_size。这将模型层分布到不同的 GPU 上,减少每个 GPU 上模型权重所需的内存,间接留下更多内存用于 KV 缓存。然而,增加此值可能会导致延迟损失。

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

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

分块预填充

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

在 vLLM V1 中,分块预填充默认始终启用。这与 vLLM V0 不同,V0 中是根据模型特性有条件地启用。

启用分块预填充后,调度策略会优先处理解码请求。它会在调度任何预填充操作之前,批处理所有待处理的解码请求。当 max_num_batched_tokens 预算中有可用 token 时,它会调度待处理的预填充。如果待处理的预填充请求无法适应 max_num_batched_tokens,它会自动将其分块。

此策略有两个优点:

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

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

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

  • 较小的值(例如 2048)可以实现更好的内部 Token 延迟(ITL),因为预填充较少,不会减慢解码速度。
  • 较大的值可以实现更好的首个 Token 生成时间(TTFT),因为您可以在一个批次中处理更多的预填充 token。
  • 为了获得最佳吞吐量,我们建议将 max_num_batched_tokens > 8096,特别是对于大型 GPU 上的小型模型。
  • 如果 max_num_batched_tokensmax_model_len 相同,这几乎等同于 V0 的默认调度策略(除了它仍然优先处理解码)。
from vllm import LLM

# Set max_num_batched_tokens to tune performance
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 支持多种并行策略,可以组合使用以优化不同硬件配置下的性能。

张量并行 (TP)

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

何时使用:

  • 当模型太大无法适应单个 GPU 时
  • 当您需要减少每个 GPU 的内存压力以提供更多 KV 缓存空间来提高吞吐量时
from vllm import LLM

# Split model across 4 GPUs
llm = LLM(model="meta-llama/Llama-3.3-70B-Instruct", tensor_parallel_size=4)

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

流水线并行 (PP)

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

何时使用:

  • 当您已经最大限度地利用了高效的张量并行,但仍需要进一步分布模型,或跨节点分布时
  • 对于非常深且窄的模型,层分布比张量分片更高效时

流水线并行可以与张量并行结合使用,适用于超大型模型。

from vllm import LLM

# Combine pipeline and tensor parallelism
llm = LLM(
    model="meta-llama/Llama-3.3-70B-Instruct,
    tensor_parallel_size=4,
    pipeline_parallel_size=2
)

专家并行 (EP)

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

何时使用:

  • 专门用于 MoE 模型(如 DeepSeekV3、Qwen3MoE、Llama-4)
  • 当您希望平衡 GPU 之间的专家计算负载时

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

数据并行 (DP)

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

何时使用:

  • 当您有足够的 GPU 来复制整个模型时
  • 当您需要扩展吞吐量而不是模型大小时
  • 在多用户环境中,请求批次之间的隔离有利时

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

减少内存使用

如果您遇到内存不足问题,请考虑以下策略:

上下文长度和批处理大小

您可以通过限制上下文长度和批处理大小来减少内存使用。

from vllm import LLM

llm = LLM(
    model="meta-llama/Llama-3.1-8B-Instruct",
    max_model_len=2048,  # Limit context window
    max_num_seqs=4       # Limit batch size
)

调整 CUDA Graph 编译

V1 中的 CUDA graph 编译比 V0 中使用更多的内存。您可以通过调整编译级别来减少内存使用:

from vllm import LLM
from vllm.config import CompilationConfig, CompilationLevel

llm = LLM(
    model="meta-llama/Llama-3.1-8B-Instruct",
    compilation_config=CompilationConfig(
        level=CompilationLevel.PIECEWISE,
        cudagraph_capture_sizes=[1, 2, 4, 8]  # Capture fewer batch sizes
    )
)

或者,如果您不关心延迟或整体性能,可以使用 enforce_eager=True 完全禁用 CUDA graph 编译。

from vllm import LLM

llm = LLM(
    model="meta-llama/Llama-3.1-8B-Instruct",
    enforce_eager=True  # Disable CUDA graph compilation
)

多模态模型

对于多模态模型,您可以通过限制每个请求的图像/视频数量来减少内存使用。

from vllm import LLM

# Accept up to 2 images per prompt
llm = LLM(
    model="Qwen/Qwen2.5-VL-3B-Instruct",
    limit_mm_per_prompt={"image": 2}
)