性能调优指南

Last updated: 07/17/2025.

Author: Guangming Sheng, Jiali Zheng

在这一部分,我们将讨论如何调优 verl 中所有阶段的性能,包括以下方面:

  1. Rollout 生成吞吐量。

  2. 启用 use_remove_padding=True 以进行序列打包(即数据打包并移除填充)。

  3. 针对前向和后向计算的批大小调优

  4. 启用 use_dynamic_bsz=True 以获得更高的吞吐量。

  5. 利用 Ulysses 序列并行进行长上下文训练

  6. LigerKernel 用于 SFT 性能优化

  7. FSDP 训练后端的向前预取

  8. 对数几率熵计算的内存优化

Rollout 生成调优

verl 当前支持两种 rollout 后端:vLLM 和 TGI(SGLang 支持即将推出)。

以下是调优基于 vLLM 的 rollout 的关键因素。在调优之前,我们推荐设置 actor_rollout_ref.rollout.disable_log_stats=False 以便记录 rollout 统计信息。

  • 增加 gpu_memory_utilization

    • 对于 vLLM v0.7.0 及更高版本,vLLM 实例只会使用 内存的 gpu_memory_utilization 比例。

    • 对于 SGLang,这是用于 静态 内存的空闲 GPU 内存比例,如模型权重和 KV 缓存。然而,在推理过程中,剩余的(1-gpu_memory_utilization)也会被使用。

    但是,如果模型参数和优化器状态未被卸载,使用过高的比例可能会导致 OOM。 0.5 到 0.7 之间的值通常能很好地在高吞吐量和避免 OOM 之间取得平衡。

    注意:由于 gpu_memory_utilization 的定义在不同推理引擎之间有所不同,对一个引擎有效的值可能会对另一个引擎造成 OOM。

  • 调整 max_num_seqsmax_num_batched_tokens。 如果日志中 GPU 缓存利用率相对较低,增加 max_num_seqsmax_num_batched_tokens 可以放大解码阶段的有效批大小,从而允许更多并发请求每批。 我们推荐设置 max_num_batched_tokens > 2048 以获得更高的吞吐量。

  • 使用更小的 tensor_parallel_size。 当 GPU 资源允许时,更小的张量并行大小会生成更多 vLLM 副本。 数据并行(DP)可以提供比张量并行(TP)更高的吞吐量,但也会增加 KVCache 消耗。 仔细平衡更多副本与更高内存使用之间的权衡。 我们在 HybridFlow paper 的第 8.4 节中对此权进行了评估实验。

  • 使用 cudagraph_capture_sizes 来平衡性能和内存。 如果设置了 cudagraph_capture_sizes,vLLM 将尝试为不同批大小捕获模型执行图。 由于 cudagraph 内存无法卸载到 CPU,在更新 actor 时内存会留在 GPU 上。 使用更小的批大小可以避免 OOM,但会略微降低吞吐量。 必须设置 enforce_eager=False 来使用 cudagraph_capture_sizes

更多调优细节,如处理抢占和分块预填充, 可在 vLLM 官方调优指南 中找到。

为获得最佳性能,我们推荐使用 vLLM v0.8.3 或更高版本。详情请见 https://github.com/volcengine/verl/blob/main/docs/README_vllm0.8.md

启用移除填充(序列打包)

目前,对于基于 llama、mistral、gemma1 和 qwen 的模型,用户可以启用 use_remove_padding=True 来利用 transformers 库提供的序列打包实现。

对于其他模型,transformers 库可能也支持,但我们尚未测试。 用户可以将所需的模型配置添加到 test_transformer.py 文件。 并通过运行以下命令测试其功能:

pytest -s tests/models/test_transformer.py

如果测试通过,您可以将所需模型添加到模型 registry.py 文件中。 然后,您可以享受序列打包的性能提升, 并欢迎您向 verl 提交经过测试的模型 PR!

批大小调优

为了在经验准备(即模型前向)以及模型更新(即 actor/critic 前向/后向)中实现更高的吞吐量, 用户可能需要为不同计算调优 *micro_batch_size_per_gpu

在 verl 中,设置批大小的核心原则是:

  • **算法指标**(训练批大小、PPO 迷你批大小)是 全局 的(从单个控制器的角度来看), 并在每个 worker 中进行归一化。参见 归一化代码

  • **性能相关参数**(微批大小、动态批大小的最大 token 长度)是 本地 参数,用于定义每 GPU 的数据分配。 参见 归一化代码

Note

在您的训练脚本中,请使用 *micro_batch_size_per_gpu 而不是 *micro_batch_size。 这样您就不需要考虑 micro_batch_size 的归一化,并且 micro_batch_size 将被弃用。

批大小调优提示

因此,用户可能需要调优 *micro_batch_size_per_gpu 以加速训练。以下是一些提示:

  1. 启用梯度检查点: 设置 actor_rollout_ref.model.enable_gradient_checkpointing=Truecritic.model.enable_gradient_checkpointing=True。 这通常允许更大的微批大小,对大迷你批训练有益。

  2. 尽可能增加 *micro_batch_size_per_gpu 直到等于归一化的 mini_batch_size

  3. 使用更大的仅前向参数: 仅前向参数,如 actor_rollout_ref.ref.log_prob_micro_batch_size_per_gpuactor_rollout_ref.rollout.log_prob_micro_batch_size_per_gpucritic.forward_micro_batch_size_per_gpu 可以更大(例如 2 倍)于训练相关的微批大小, 如 actor_rollout_ref.actor.ppo_micro_batch_size_per_gpucritic.ppo_micro_batch_size_per_gpu

  4. 允许 Critic 和 Reward 模型使用更大的微批大小: Critic 和 Reward 模型的微批大小可以比 Actor 模型更大。这是因为 actor 模型在最后一层有更大的词汇表大小。

  5. 启用激活卸载: 设置 actor_rollout_ref.model.enable_activation_offload=Truecritic.model.enable_activation_offload=True。 这通常与梯度检查点结合使用,以获得更大的微批大小,目前仅在 FSDP 后端可用。

动态批大小调优

动态批大小是一种允许模型在单次前向传递中处理相似数量 token 的技术(实际批大小不同)。 这可以显著提高训练效率并减少内存使用。

要利用这项技术,用户可以在 actor、ref、critic 和 reward 模型中设置 use_dynamic_bsz=True。 启用 use_dynamic_bsz=True 后,用户无需调优 *micro_batch_size_per_gpu。 相反,用户应调优以下参数:

  • actor_rollout_ref.actor.ppo_max_token_len_per_gpucritic.ppo_max_token_len_per_gpu: 在 update_policyupdate_critic 的前向和后向中处理的 token 最大数量。

  • actor_rollout_ref.ref.log_prob_max_token_len_per_gpuactor_rollout_ref.rollout.log_prob_max_token_len_per_gpu: 在 compute_log_prob``compute_ref_log_prob``的前向计算中处理的 token 最大数量。

  • critic.forward_micro_batch_size_per_gpureward_model.forward_micro_batch_size_per_gpu: 在 compute_valuescompute_rm_score 前向计算中处理的 token 最大数量。

动态批大小调优提示

以下是调优上述参数的一些提示:

  1. 增加 actor_rollout_ref.actor.ppo_max_token_len_per_gpu 使其至少为 2 x (最大提示长度 + 最大响应长度)。我们在 run_qwen2-7b_rm_seq_balance.sh 中设置为 3 倍。 尝试增加它以获得更高的吞吐量。

  2. 仅前向参数可以更大: 与非动态批场景类似,仅前向的 token 限制可以超过前向/后向操作中使用的限制。

  3. 为 Critic 和 Reward 模型使用更大的限制: Critic 和 Reward 参数可以设置为 Actor 限制的至少 2 倍。例如,我们在此处设置为 4 倍: run_qwen2-7b_rm_seq_balance.sh

Ulysses 序列并行用于长上下文训练

要利用这项技术,用户可以在 actor、ref、critic 和 reward 模型中设置 ulysses_sequence_parallel_size>1

我们支持不同模型使用不同的 ulysses_sequence_parallel_size 大小。

要训练长序列(>32k),用户可能需要减少 *micro_batch_size_per_gpu*max_token_len_per_gpu 以避免 OOM。

LigerKernel 用于 SFT

LigerKernel 是一个用于监督微调(SFT)的高性能内核,可以提高训练效率。要在 SFT 训练中启用 LigerKernel:

  1. 通过 pip3 install liger-kernel 安装 liger-kernel。在您的 SFT 配置文件中(例如 verl/trainer/config/sft_trainer.yaml),设置 use_liger 参数:

    model:
      use_liger: True  # 启用 LigerKernel 用于 SFT
    
  2. 默认值为 False。仅在您想要使用 LigerKernel 优化时启用它。

  3. LigerKernel 对提高 SFT 场景中的训练性能特别有用。

FSDP 训练后端的向前预取

在训练阶段,用户可以通过设置 fsdp_config.forward_prefetch=True 在 FSDP 中启用向前预取。例如,actor_rollout_ref.actor.fsdp_config.forward_prefetch=True。此配置在完成当前前向计算之前预取下一个前向传递的全聚集操作,重叠通信与计算以提高效率。更多详情,请参考 FSDP forward_prefetch 文档。

Note

后向预取不受支持,因为 BACKWARD_POST 策略在嵌套模块情况下可能会错误预取。详情请见 FSDP 文档

迁移到 FSDP2

FSDP2 相比 FSDP1 提供了显著改进。根据 PyTorch TorchTitan 基准

  • 平均 GPU 内存使用降低 7%

  • BF16 训练吞吐量提高 1.5%

  • 与 DTensor 和每参数分片具有更好的可组合性

在 VERL 中启用 FSDP2:

# 在 actor 配置中启用 FSDP2
actor_rollout_ref.actor.strategy="fsdp2"

Note

FSDP2 需要 PyTorch 2.1+,推荐用于具有 transformer 架构的模型。

对数几率熵计算的内存优化

logits 张量(通常形状为 [bsz*seq_len, voc])可能会消耗大量内存。在使用 compute_entropy_from_logits 时,内存使用 приблиз为 [bsz*seq_len, voc] × (4 字节 (float32) + 2 字节 (针对 softmax+logsumexp 的自动转换) + 1 字节 (softmax 输出))

为降低此内存峰值,通过设置启用分块计算: actor_rollout_ref.ref.entropy_from_logits_with_chunking = True 这会以 [chunk_size, voc] (例如 2048)的块形状处理张量,而不是完整序列长度,仅在模型前向传递期间进行。

此外,在训练期间,标准梯度检查点(enable_gradient_checkpointing=True)不适用于熵计算。为在此上下文中降低内存峰值,设置: actor_rollout_ref.actor.entropy_checkpointing = True 这会专门为熵计算启用熵重计算,从而在训练期间降低内存使用。