训推同卡:colocate 的显存怎么分
单卡上训练和推理抢同一块 32GB。这一节把 colocate 模式调稳,并知道它比多卡慢在哪。
学完这节你能做到
- 说清 colocate 与 server 两种模式的区别,以及单卡为什么只能用前者
- 调出一组不 OOM 又不浪费的显存分配参数
- 解释 sleep mode 在省什么,以及为什么 colocate 必然比 server 慢
两种模式
RL 训练需要一个推理引擎持续产出数据。它和训练进程的关系有两种安排:
server 模式(多卡)
┌────────────┐ HTTP ┌────────────┐
│ 训练进程 │ ──────→ │ vLLM 服务 │
│ 卡 4-7 │ ←────── │ 卡 0-3 │
└────────────┘ └────────────┘
两边同时工作,采样和训练可以重叠
colocate 模式(单卡)
┌──────────────────────────────┐
│ 同一张卡,交替执行 │
│ 采样 ████ 训练 ████ │
│ ████ ████ │
└──────────────────────────────┘
一个时刻只有一件事在跑
| server | colocate | |
|---|---|---|
| 卡数要求 | ≥2 | 1 张就行 |
| 采样与训练 | 可以重叠 | 严格交替 |
| 通信 | HTTP / RPC | 显存内 |
| 权重同步 | 跨进程,要序列化 | 显存拷贝,快 |
| 速度 | 快 | 明显慢 |
单卡只能用 colocate。 这不是配置选择,是物理约束——一张卡不可能同时跑训练 kernel 和生成 kernel。
慢在两处:
- 不能重叠。server 模式下采样和训练并行,单步墙钟时间约等于
max(采样, 训练);colocate 是采样 + 训练。 - 切换开销。每轮要 sleep/wake,显存释放和重新申请要几秒。
按 L2 那次实测:采样 30s + 训练 12s + 切换 3s = 45s。如果能重叠,理想情况是 30s。colocate 的代价大约是 1.5 倍。
单卡上这不是取舍,是唯一选项。接受它,然后优化能优化的部分。
核心参数:显存怎么划
vllm_gpu_memory_utilization(slime 里叫 --sglang-mem-fraction-static)决定给 vLLM 多少。
关键认知:这是预先占用,不是按需申请。 vLLM 启动时就把这个比例的显存吃掉,用来放权重和 KV cache 池。
所以单卡上你必须手工划一条线:
32 GiB
├── vLLM util × 32 ← 你划的
└── 训练侧 剩下的
怎么定这条线?用 L0 的显存计算器:
- 算出训练侧需要多少(权重 + 梯度 + 优化器 + 激活 + logits)
- 留 1.5 GiB 给 CUDA context 和碎片
- 剩下的除以 32,就是 util 的上限
- 从这个上限往下减 5%,作为起始值
以 0.6B 全参为例:
训练侧 ≈ 10.4 GiB (权重1.1 + 梯度1.1 + AdamW6.7 + 激活0.2 + logits1.2)
context ≈ 1.5 GiB
────────────────────
可给 vLLM ≈ 32 − 10.4 − 1.5 = 20 GiB → util 上限 ≈ 0.62
起始值取 0.55
上面算出 0.55 是理论上限。实际先用 0.3 跑通,确认不 OOM,再往上加。
原因:算得再准也会漏掉一些东西(allocator 碎片、kernel workspace、临时张量)。先跑通再优化,比先优化再 debug OOM 省时间得多。
sleep mode:把显存借来借去
vLLM 的 sleep mode 是 colocate 能舒服工作的关键:
engine = LLM(..., enable_sleep_mode=True)
engine.wake_up() # 采样前:申请 KV cache
samples = rollout(...)
engine.sleep(level=1) # 采样后:归还 KV cache 显存
# ↓ 这段时间训练侧可以用这块显存
train_step(...)
两个 level:
| level | 释放什么 | 唤醒耗时 | 用在哪 |
|---|---|---|---|
| 1 | 只释放 KV cache,权重留着 | 快(1~3s) | ✅ 单卡 colocate 默认 |
| 2 | 权重也卸载到 CPU | 慢(10s+) | 显存极度紧张时 |
TRL 里对应 vllm_enable_sleep_mode=True(部分版本),Axolotl 的文档也推荐开启。
不开 sleep mode:vLLM 全程占着 util × 32,训练侧只能用剩下的。 开了 sleep mode:训练阶段那部分 KV cache 显存能借给训练侧用。
等价于让你把 util 设得比理论上限更高,因为两边不再同时峰值。这是单卡 colocate 最实用的一个开关。
OOM 排查顺序
colocate 下 OOM 有两种,先分清是哪一种:
报错发生在 采样阶段 → vLLM 侧不够 → 降 max_model_len / 降并发 / 提 util
报错发生在 训练阶段 → 训练侧不够 → 降 micro batch / 降 util / 开检查点
看 traceback 里出现的是 vLLM 的栈还是 transformers 的栈就能区分。
然后按这个顺序动,一次只动一个:
| 顺序 | 动作 | 影响效果吗 |
|---|---|---|
| 1 | micro batch 降到 1 + 梯度累积补回 | ❌ 不影响,数学等价 |
| 2 | 开 gradient_checkpointing | ❌ 不影响,只是慢 30% |
| 3 | 开 enable_sleep_mode | ❌ 不影响 |
| 4 | 降 max_model_len 到实际需要 | ❌ 只要不低于真实最大长度 |
| 5 | 调整 util(哪边不够给哪边) | ❌ 只影响速度 |
| 6 | 降 rollout 并发 | ⚠️ 只影响速度,但会明显变慢 |
| 7 | 降 max_completion_length | ⚠️ 可能影响效果 |
| 8 | 降 num_generations | ⚠️ 影响优势估计质量 |
| 9 | 关参考模型(beta=0) | ⚠️ 失去 KL 约束和监控 |
| 10 | 换 LoRA | ⚠️ 表达能力受限 |
num_generations 是新手最容易先降的,因为它看起来直接减少了工作量。
但 G 是 GRPO 的基线来源。G 从 8 降到 4,组内基线的估计方差显著增大,退化组(std=0)的比例也会上升。一般不要低于 4。
前六项都不影响效果,先把它们用完。
时间去哪了
记一次实测的分解(0.6B、8 prompt × 8 条、平均 152 token):
采样 28.4 s 62% ████████████████████████
前向 + 反向 12.1 s 26% ██████████
权重同步 2.2 s 5% ██
sleep/wake 3.1 s 7% ███
─────────────────────────────
单步 45.8 s
结论很清楚:优化的重点在采样。
按性价比排序的四个动作:
- 提高 util(如果显存允许)—— KV cache 池更大,能容纳更多并发,吞吐直接上去
- 降
max_completion_length—— 采样时间对长度基本是线性的。但要先确认截断率不高 - 确认 CUDA graph 开着 —— 关掉能损失几倍吞吐
- 减小固定开销的占比 —— 每步 batch 调大,让同步和切换的 5 秒摊薄
同步 + 切换固定 5.3 秒,占单步 12%。如果把每步的 prompt 数从 8 加到 16(采样时间翻倍到 57s),固定开销的占比就从 12% 降到 7%。
代价是每步显存峰值更高,而且更新频率降低。这是「大 batch 少步数」和「小 batch 多步数」的经典权衡,在 colocate 下前者更划算,因为固定开销更大。
- 1.看 util 给太高时 OOM 发生在哪一侧
- 2.拿到单步时间分解,确认瓶颈在采样
- 3.提高 util 并开 sleep mode,看单步时间下降多少
colocate 调优演练。目标是在不 OOM 的前提下把单步时间压下来。 goals 看目标,hint 要提示。
为什么单卡只能用 colocate 模式?
colocate 下 OOM,traceback 出现在 transformers 的 modeling 文件里。该先动什么?
sleep mode(level=1)的作用是什么?
这节课的落点
- 单卡只能 colocate,代价约 1.5 倍(不能重叠 + 切换开销),这是物理约束
vllm_gpu_memory_utilization是预占不是按需,必须手工划线- 理论上限用显存计算器算,但实践从 0.3 起步,跑通再往上加
- sleep mode level=1 归还 KV cache 显存,等价于让 util 能设得更高
- OOM 先看 traceback 在哪一侧,再按「不影响效果」的顺序动刀
- 不要先降
num_generations,它是 GRPO 的基线来源,一般不低于 4 - 采样占 60%+,优化重点:提 util、控长度、保住 CUDA graph、摊薄固定开销