Chapter 07

采样后端:vLLM / SGLang / TensorRT-LLM

📌 源码引用基于 verl/workers/rollout/ · commit 9c38b8bb1876 。

7.1 RLHF 里"采样"为什么单独一个后端

RLHF 训练循环每 step 都要做一次大规模生成(rollout):

训练框架(FSDP / Megatron)的 forward + generate 比专门的推理引擎慢 5-10×。所以 RLHF 系统几乎都把 rollout 分给专门的推理引擎

7.2 verl 支持的采样后端

后端Hydra 选项主战场
vLLM rollout.name=vllm 默认;最广泛
SGLang rollout.name=sglang 新一代,长上下文 / 多轮工具调用强
TensorRT-LLM rollout.name=trtllm NVIDIA 极致吞吐
HF Transformersrollout.name=hf 调试 / 小模型 / 验证数值等价
vexact 2026/05 新出 "零失配"HF rollout,权重布局与训练相同

7.3 vLLM 后端关键配置

字段含义
rollout.tensor_model_parallel_size vLLM 自家 TP 度
rollout.gpu_memory_utilization vLLM 抢用显存的上限,0.5-0.7 推荐
rollout.dtype bf16 / fp16 / fp8
rollout.max_num_batched_tokens vLLM 一次 batch 的最大 token 数
rollout.max_num_seqs 同时 in-flight 的 sequence 数
rollout.n 每个 prompt 采样几条(GRPO 必须 ≥ 4)
rollout.temperature 采样温度
rollout.top_p top-p 截断
rollout.enforce_eager 禁用 CUDA graph(调 bug 时打开)
rollout.enable_chunked_prefill chunk prefill 提升长 prompt 效率
rollout.disable_log_stats 关 vLLM 自己的 stats 输出

7.4 SGLang 后端的差异

能力vLLMSGLang
continuous batching
PagedAttention
多轮工具调用 (function call)支持但需自拼原生 RadixAttention 共享前缀
长上下文(>128K) 支持但较慢 较快
权重热更新 需要 cold restart支持 weight update API
verl 集成成熟度 最稳定 稳定

切换:

actor_rollout_ref.rollout.name=sglang
actor_rollout_ref.rollout.tensor_model_parallel_size=2
actor_rollout_ref.rollout.gpu_memory_utilization=0.6

7.5 rollout TP 度怎么选

核心矛盾:

常见经验值(H100/A100 80G):

模型推荐 rollout TP
≤ 4B 1
7B-13B 2
30B 2 或 4
70B 4 或 8
235B+ MoE 8

7.6 rollout.n:GRPO 组大小

GRPO 需要对同一 prompt 采样 $n$ 个 response 做组内归一化:

rollout.n说明
1 非 GRPO(PPO / REINFORCE)
4 GRPO 最小可行
5-8 常见推荐
16+ R1 / DAPO 风格"高 sample efficiency",开销大

7.7 权重 resharding:vLLM ↔ FSDP 的桥

训练 actor 用 FSDP 切,rollout 用 vLLM 自家 TP 切。切法不同,无法直接对接,verl 在 verl/workers/sharding_manager/fsdp_vllm.py 等文件里写了双向转换:

  1. FSDP 把分片参数 all-gather 到 rank 0;
  2. 按 vLLM 期望的布局把参数 dispatch 给每个 vLLM TP rank;
  3. rollout 完成,KV cache 清掉;
  4. FSDP 重新建 shard。

这一步是 hybrid engine 的命脉。新模型架构如果 sharding manager 不支持,rollout 阶段会报形状不匹配,需要按现有模板加适配。

7.8 rollout correction:把"模型实际生成"调成"真实数据"

问题:训练用的 model.forward 算的 logp,和 rollout 引擎实际采样时的 logp,可能因实现差异(kernel、数值精度、attention 实现)不完全一致。这种 mismatch 会让 PPO 的 importance ratio 偏离真实值。

verl 提供 rollout correction:

algorithm.rollout_correction.enable=true
algorithm.rollout_correction.method=is        # importance sampling

详细机制见 verl/trainer/config/algorithm/rollout_correction.yaml。新出的 vexact 就是为了彻底消除这个 mismatch 设计的。

7.9 router replay:MoE rollout 的特殊处理

MoE 模型的 expert 路由有随机性,训练和推理可能选不同 expert。verl 在 2026/04 PyTorch Conference Europe 上展示了 Router Replay:让 rollout 时记录每 token 的 expert 选择,训练 forward 重放相同选择,保证数值一致。

配置:

actor_rollout_ref.rollout.router_replay=true

7.10 调优清单

现象修复
rollout 阶段 OOM gpu_memory_utilization;升 TP
rollout 阶段慢 max_num_batched_tokens;用 chunked prefill
权重 resharding 慢 开 streaming sharding;用 vexact
训练后 logp 与 rollout logp 差距大 rollout_correction 或换 vexact
MoE 训练数值不稳 router_replay
SGLang init 慢 enforce_eager,重用 CUDA graph 缓存

7.12 ServerAdapter:rollout 端真正在做什么

verl 的 rollout 都是"客户端 + 独立 server 进程"架构。Server 是 Ray actor(vLLM AsyncLLMEngine / SGLang http_server),客户端是 worker 里的 ServerAdapter。两类后端的对照:

维度vLLMSGLang
客户端文件 workers/rollout/vllm_rollout/vllm_rollout.py:61 ServerAdapterworkers/rollout/sglang_rollout/sglang_rollout.py:95 ServerAdapter
weight 传输通道 CUDA IPC / 共享内存 / ZMQHTTP(POST /update_weights)
调用方式 server_handle.collective_rpc() 走 RayAsyncHttpServerAdapter 走 HTTP
sleep / wake Ray RPC wake_up(tags=...) / sleep(level=...)HTTP /resume_memory_occupation / /release_memory_occupation
LoRA 增量加载 原生支持,需要 VLLM_ALLOW_RUNTIME_LORA_UPDATING=true原生支持,单独 endpoint 上传 adapter
FP8 量化推理 需手动 quant 模型 客户端内联 wrap(sglang_rollout.py:329–339)
Prefill-Decode 分离 不支持 原生支持(见下节)

两者都做的事:

7.13 BucketedWeightSender:vLLM 的 IPC 优化

vLLM 这条路最快的原因之一是不走 HTTP,走 CUDA IPCvllm_rollout.py:178–182 调用的 BucketedWeightSender 做的事:

  1. 把训练侧 yield 的 (name, tensor) 累积成 ≈ 100 MB 的 bucket;
  2. 桶满了,通过 CUDA IPC handle(同节点)或 ZMQ(跨节点)发给 vLLM server;
  3. server 端 update_weights_from_ipc 在 GPU 上直接 rebuild_cuda_tensor()——零拷贝

同节点情况下整个 update_weights 的实际数据移动量是极小的——只传 IPC handle 而不传张量本身。这是 colocate 模式的性能基础。

7.14 SGLang Prefill-Decode 分离(PD disaggregation)

SGLang 在长 CoT 训练场景下有个独门能力:把 prefill 和 decode 分到不同的 server actor,各自调整资源。代码在 sglang_rollout.py:160–181

字段含义
_pd_role "prefill""decode"
_pd_server_index这一角色下第几个 server actor
server actor 名规则 sglang_server_{replica}_{node_rank}(colocated)
sglang_server_decode_{replica}_{idx}(disaggregated)

用途:long CoT 训练里 prefill / decode 计算特性差异巨大——prefill 算力受限,decode 带宽受限。把它们分到不同 GPU 池可以分别调参、各自达到最大吞吐。配置入口:actor_rollout_ref.rollout.disaggregation: { mode: pd, ... }

7.15 vexact:金标 rollout 后端

2026-05 引入的 verl-project/vexact 是个独立 Python 包,但 verl 提供了内置对接。它的存在理由:排除"训推 logits 不一致"作为 PPO 不收敛的可能

特性vexactvLLM / SGLang
训练侧 logits 与 rollout 完全一致 有 small numerical drift
底层实现 共享 HF transformer 定义,batch-invariant kernel各自的 fused kernel
吞吐 低(HF 级别)
典型用法 "调试一组 step 看 IS ratio 是不是 ≈ 1"生产训练

启用方式:YAML 加 actor_rollout_ref.rollout.name: vexact。一切 rollout 接口和 vLLM/SGLang 兼容,可以直接对照实验。

7.16 选型决策树(最终版)

flowchart TB Q1{多模态 / 扩散?} Q1 -- 是 --> Omni[verl-omni + 内嵌 backend] Q1 -- 否 --> Q2{长 CoT / 多轮工具调用 / 需要 PD 分离?} Q2 -- 是 --> SG[SGLang] Q2 -- 否 --> Q3{需要绝对训推一致 / 调试 IS ratio?} Q3 -- 是 --> Vexact[vexact] Q3 -- 否 --> Q4{NVIDIA TRT 集群 / 极致吞吐?} Q4 -- 是 --> TRT[TensorRT-LLM] Q4 -- 否 --> VLLM[vLLM 默认]

7.17 这章你需要带走什么