利用 vLLM 睡眠模式实现零重载模型切换
简介
多模型服务难题:你有两个 LLM,它们都能塞进 GPU,但无法同时容纳。传统方案迫使你做出艰难的权衡:
- 保持两个模型常驻 → 需要 2 倍显存(昂贵,甚至不可能)
- 按需重新加载模型 → 每次切换需要 30-100+ 秒(缓慢,浪费)

vLLM 睡眠模式提供了第三种选择:模型能在几秒内休眠并快速唤醒——既实现了按需加载的效率,又保留了持久化服务的速度。
满足不同需求的两种睡眠级别
- 级别 1:将权重卸载到 CPU 内存(唤醒速度极快)
- 级别 2:彻底丢弃权重(唤醒速度接近级别 1,内存占用极低)
这两种级别都比完全重新加载快 18-200 倍,并且能与张量并行 (TP)、流水线并行 (PP) 和专家并行 (EP) 无缝兼容。
为什么睡眠模式优于快速权重加载器
即使权重加载速度极快,每次冷启动仍需支付隐藏成本,而睡眠模式则可避免这些成本。
| 成本 | 描述 | 快速权重加载器 | 睡眠模式 |
|---|---|---|---|
| 1. 显存加载时间 | 复制权重到 GPU | ✅ 已优化 | ✅ 已保留 |
| 2. 内存分配器设置 | CUDA 分配器初始化 | ❌ 每次都需要 | ✅ 已保留 |
| 3. CUDA 图捕获 | 记录执行图 | ❌ 每次都需要 | ✅ 已保留 |
| 4. GPU 内核 JIT 编译 | DeepGEMM, FlashInfer, TorchInductor | ❌ 每次都需要 | ✅ 已保留(经过初始预热) |
| 5. 缓存预热 | 首请求开销 | ❌ 每次都需要 | ⚡ 快速重预热 |
通过保持进程存活,睡眠模式避免了基础设施(#2-#4)的开销和昂贵的重新初始化。这就是为什么基准测试显示,睡眠模式下的推理速度比冷启动快 61-88%。
本文涵盖内容:
- 横跨多种模型尺寸(0.6B 到 235B)和 GPU(A4000 到 A100)的综合基准测试
- 深入的技术细节,解释性能提升的原理
- 关于预热影响和 FP8 量化的消融研究
- 选择合适的睡眠级别的决策指南
快速入门:使用睡眠模式
在线服务 API
启动两个启用了睡眠模式的 vLLM 服务
# Terminal 1: Start Phi-3-vision
export VLLM_SERVER_DEV_MODE=1
vllm serve microsoft/Phi-3-vision-128k-instruct --enable-sleep-mode --port 8001
# Terminal 2: Start Qwen3-0.6B
export VLLM_SERVER_DEV_MODE=1
vllm serve Qwen/Qwen3-0.6B --enable-sleep-mode --port 8002模型的休眠与唤醒
# Put Phi-3-vision to sleep (Level 2 - minimal RAM usage)
curl -X POST 'localhost:8001/sleep?level=2'
# Put Qwen3-0.6B to sleep (Level 2)
curl -X POST 'localhost:8002/sleep?level=2'
# Wake up Phi-3-vision for inference
curl -X POST 'localhost:8001/wake_up'
curl -X POST 'localhost:8001/collective_rpc' \
-H 'Content-Type: application/json' \
-d '{"method":"reload_weights"}'
# IMPORTANT: Reset prefix cache after waking (Level 2 only)
curl -X POST 'localhost:8001/reset_prefix_cache'
# Now run inference on Phi-3-vision...
# (your inference requests here)
# Put back to sleep when done
curl -X POST 'localhost:8001/sleep?level=2'
# Wake up Qwen3-0.6B
curl -X POST 'localhost:8002/wake_up'
# (Level 1 doesn't need reload_weights or reset_prefix_cache)
# Run inference on Qwen3-0.6B...注意:对于级别 2 睡眠,唤醒后必须调用
reload_weights和reset_prefix_cache。级别 1 睡眠不需要这些额外步骤。
警告: 安全性:
/sleep、/wake_up、/collective_rpc和/reset_prefix_cache端点需要设置VLLM_SERVER_DEV_MODE=1,且应仅在受信任的网络中公开。这些管理端点可能会中断服务,仅适用于训练集群或后端应用等受限环境。
性能概览
让我们看看睡眠模式与传统模型重新加载相比表现如何。
睡眠模式 L1 与无睡眠模式性能对比
下方的交互式图表展示了执行 5 次模型切换的总时间:运行模型 A 推理,切换到模型 B,运行模型 B 推理,然后重复此模式(A→B→A→B→A→B)。
使用睡眠模式:模型在切换间隙休眠/唤醒,保留了基础设施。不使用睡眠模式:每次切换都需要完全重启 vLLM 并重新加载。
GPU: A100 | vLLM 0.11.0 | 睡眠级别: 1 | 编译:
cudagraph_mode: FULL_AND_PIECEWISE推理性能提升
除了更快的模型切换外,睡眠模式还提供了更快的推理时间。由于模型在从睡眠中唤醒时已经过预热,它们跳过了影响新加载模型的冷启动开销。
推理时间 = 预填充 (prefill) + 解码 (decode)(唤醒/加载后的首个请求)。每个请求使用不同的问题以避免缓存,输出限制为 100 个 token。
误差线显示了多次运行中的最小/最大值变化。数值显示在柱状图上。
GPU: A100 | vLLM 0.11.0 | 睡眠级别: 1 | 编译:
cudagraph_mode: FULL_AND_PIECEWISE为什么睡眠模式能提高推理速度
61-88% 的推理加速并不是来自更快的权重加载——而是来自保留了冷启动必须从头重建的昂贵基础设施。
睡眠模式保留了什么
| 组件 | 是否保留? | 冷启动必须支付的成本 |
|---|---|---|
| 内存分配器 (CuMemAllocator) | ✅ 是 | ❌ 每次重新初始化 |
| CUDA 图 | ✅ 是 | ❌ 每次重新捕获 |
| 进程状态 (Python, CUDA 上下文) | ✅ 是 | ❌ 每次重启 |
| GPU 内核 JIT 缓存 | ✅ 是(首次预热后) | ❌ 每次重新编译 |
关键区别
-
无睡眠模式:进程卸载即死亡 → 无法从预热中获益
- 必须重启 Python 进程和 CUDA 上下文
- 必须重新初始化内存分配器
- 必须重新捕获 CUDA 图
- 必须重新进行 JIT 内核编译 (DeepGEMM, FlashInfer, TorchInductor)
- 结果:首次推理慢 4-7 倍(见基准:唤醒 0.92s vs 冷启动 3.72s)
-
使用睡眠模式:进程保持存活 → 预热产生回报
- ✅ 分配器、图、进程状态和 JIT 内核在初始预热后全部保留
- 结果:首次推理保持快速(约 1s),避免了 3-4s 的冷启动惩罚
注意:时间因模型大小、GPU 代际和配置而异。请参阅预热的影响部分,查看详细测量数据,显示未预热时存在 5-7 倍的减速。
模型切换性能
睡眠模式最显著的优势在于模型切换时间。唤醒睡眠中的模型比加载全新的 vLLM 实例快 18-20 倍。
误差线显示了多次运行中的最小/最大值变化。数值显示在柱状图上。
GPU: A100 | vLLM 0.11.0 | 睡眠级别: 1 | 编译:
cudagraph_mode: FULL_AND_PIECEWISE硬件可扩展性:A4000 GPU 测试结果
睡眠模式的益处不仅限于高端 GPU。这是在 A4000 GPU 上运行较小模型的工作负载,证明了这种性能提升可以在不同硬件层级和模型尺寸间扩展。
GPU: A4000 (TP=1) | vLLM 0.11.0 | 睡眠级别: 1 | 编译:
cudagraph_mode: FULL_AND_PIECEWISEA4000:推理性能
推理时间 = 预填充 (prefill) + 解码 (decode)(唤醒/加载后的首个请求)。每个请求使用不同的问题以避免缓存,输出限制为 100 个 token。
误差线显示了多次运行中的最小/最大值变化。数值显示在柱状图上。
GPU: A4000 (TP=1) | vLLM 0.11.0 | 睡眠级别: 1 | 编译:
cudagraph_mode: FULL_AND_PIECEWISEA4000:模型切换性能
误差线显示了多次运行中的最小/最大值变化。数值显示在柱状图上。
GPU: A4000 (TP=1) | vLLM 0.11.0 | 睡眠级别: 1 | 编译:
cudagraph_mode: FULL_AND_PIECEWISEA4000 关键观察
- 推理性能:唤醒模式使 Qwen3-0.6B 的推理速度提升 83%,Phi-3-vision 提升 81%
- 模型切换:唤醒时间极快(约 0.1-0.8s),对比冷启动实现 58-203 倍加速
- 总时间节省:62%(5 次切换中 85s vs 226s)
- 对于小型模型实现近乎即时的切换(0.1s 唤醒时间),使多模型服务体验极其顺滑
- 证明了睡眠模式在不同 GPU 等级和模型尺寸上均有效
睡眠级别:如何选择合适的模式
vLLM 睡眠模式提供两种级别,各有权衡
级别 1(默认):将模型权重卸载到 CPU 内存,丢弃 KV 缓存
- 唤醒速度最快(小型模型约 0.1-0.8s,大型模型约 3-6s)
- 需要足够的 CPU RAM 来存储模型权重
- 最适合:具有充足 CPU 内存、需要频繁切换模型的系统
级别 2:丢弃模型权重和 KV 缓存,仅在 CPU 中保留缓冲区(如 RoPE 缩放张量等)
- 唤醒速度较慢(小型模型约 0.8-2.6s),因为需从磁盘重新加载权重
- CPU RAM 占用极少 - 仅保留小缓冲区
- 最适合:CPU RAM 有限的系统,或者需要管理无法全部装入内存的大量模型时
性能比较:级别 1 vs 级别 2 vs 无睡眠
GPU: A100 (TP=1) | vLLM 0.11.0 | 编译:
cudagraph_mode: FULL_AND_PIECEWISE三种模式对比:级别 1(最快)、级别 2(内存占用最少)、无睡眠。鼠标悬停查看精确时间。
性能总结
| 模式 | 总时间 | 唤醒时间 (A/B) | CPU RAM | 最适合 |
|---|---|---|---|---|
| 无睡眠 | 357.1s | 不适用(完全重载) | 极低 | 单模型,无需切换 |
| 级别 1 | 112.6s | 0.26s / 0.82s | 高(每模型 GB 级别) | 频繁切换,内存充裕 |
| 级别 2 | 124.6s | 0.85s / 2.58s | 极低(每模型 MB 级别) | 内存有限,成本优化 |
关键洞察
- 级别 1 最快(比无睡眠快 68%),但需要大量 CPU RAM
- 级别 2 速度几乎同样快(比无睡眠快 65%),且对 RAM 要求极低
- 级别 2 的唤醒速度约为级别 1 的 3 倍(以 Qwen3-0.6B 为例,0.85s vs 0.26s),这是由于需要重新加载权重
- 两种睡眠模式都比无睡眠模式带来了巨大的改进
为什么级别 2 仍然比无睡眠模式快
乍一看这似乎违反直觉:级别 2 从 SSD 重新加载权重(就像“无睡眠模式”一样),那么为什么它整体快了 23-45 倍?
答案:权重加载只是五大成本之一
当你以无睡眠模式重新加载模型时,你需支付所有这些成本
| 成本 | 级别 2 | 无睡眠模式 |
|---|---|---|
| 1. 权重加载 (SSD → VRAM) | ❌ 必须支付 | ❌ 必须支付 |
| 2. 进程初始化 | ✅ 跳过 | ❌ 必须支付 |
| 3. 内存分配器设置 | ✅ 跳过 | ❌ 必须支付 |
| 4. CUDA 图捕获 | ✅ 跳过 | ❌ 必须支付 |
| 5. GPU 内核 JIT 编译 | ✅ 保留(已编译) | ❌ 全量编译 + 预热 |
级别 2 策略
- 从 SSD 重新加载权重(与无睡眠模式相同)
- 其他全部保留:进程状态、分配器实例、CUDA 图和编译好的 JIT 内核全都完好无损
- 无需重新编译:内核在初始预热期间已编译并保持在缓存中
- 每次切换平均耗时:约 2.6s(见上文基准数据)
无睡眠模式现实
- 从 SSD 重新加载权重(与级别 2 相同)
- 其他一切重建:进程重启 + 分配器初始化 + 图重新捕获
- JIT 内核:全量编译 + 显式预热例程 (
kernel_warmup()+ 虚拟运行) - 每次切换平均耗时:约 48s(见上文基准数据)
基准数据证明了一切:对于 5 次模型切换
- 级别 2:总计 124.6s(平均每次切换约 2.6s)
- 无睡眠:总计 357.1s(平均每次切换约 48s)
尽管两者都从 SSD 重载权重,但级别 2 整体快了 2.9 倍,因为它保留了昂贵的基础设施(进程状态、分配器、CUDA 图),而这些在无睡眠模式下每次都必须从头重建。
级别 2:推理性能
推理时间 = 预填充 (prefill) + 解码 (decode)(唤醒/加载后的首个请求)。每个请求使用不同的问题以避免缓存,输出限制为 100 个 token。
误差线显示了多次运行中的最小/最大值变化。数值显示在柱状图上。
GPU: A100 (TP=1) | vLLM 0.11.0 | 睡眠级别: 2 | 编译:
cudagraph_mode: FULL_AND_PIECEWISE级别 2:模型切换性能
误差线显示了多次运行中的最小/最大值变化。数值显示在柱状图上。
GPU: A100 (TP=1) | vLLM 0.11.0 | 睡眠级别: 2 | 编译:
cudagraph_mode: FULL_AND_PIECEWISE关键观察
| 指标 | 无睡眠 | 级别 2 | 改进 |
|---|---|---|---|
| 总时间 (5 次切换) | 357.1s | 124.6s | 快 65% |
| Qwen3-0.6B 切换时间 | 平均 37.6s | 平均 0.85s | 快 45 倍 |
| Phi-3-vision 切换时间 | 平均 58.1s | 平均 2.58s | 快 23 倍 |
| Qwen3-0.6B 推理 | 平均 3.67s | 平均 0.53s | 快 86% |
| Phi-3-vision 推理 | 平均 6.30s | 平均 0.76s | 快 88% |
| 唤醒时间 vs 级别 1 | - | 慢 3-10 倍 | 以 CPU RAM 换取速度 |
何时使用级别 2
- CPU RAM 有限:系统无法将所有模型权重保存在 CPU 内存中
- 成本优化:CPU RAM 较小的廉价云实例
- 管理多个模型:在内存受限的情况下管理大量模型
- 仍有显著收益:即使需要重新加载权重,级别 2 也比无睡眠模式快 23-45 倍
级别 1 与级别 2 比较
- 级别 1:唤醒时间约 0.1-0.8s,每个模型需要约 10-100GB+ CPU RAM
- 级别 2:唤醒时间约 0.8-2.6s,每个模型仅需约 MB 级别 CPU RAM
- 两者都比完全重载(约 20-100s)快得多
消融研究
预热对睡眠模式的影响
跳过预热阶段会影响性能吗?预热在初始加载期间预编译 CUDA 图,这可能耗时几秒。我们来对比一下有无预热的情况。
GPU: A100 (TP=1) | vLLM 0.11.0 | 睡眠级别: 1 | 编译:
cudagraph_mode: FULL_AND_PIECEWISE对比有预热(预编译)与无预热(延迟编译)。鼠标悬停查看精确时间。
关键发现
| 指标 | 有预热 | 无预热 | 差异 |
|---|---|---|---|
| 初始加载时间 | 108.7s(包含 8.4s 预热) | 101.1s(无预热) | 初始节省 7.6s |
| 首次推理 (A) | 0.45s | 2.59s | 无预热慢 5.8 倍 |
| 首次推理 (B) | 0.93s | 6.61s | 无预热慢 7.1 倍 |
| 后续推理 | 平均 0.43s | 平均 0.41s | 无差异 |
| 总时间 (5 次切换) | 119.5s | 119.0s | 几乎相同 |
洞察
- 预热仅编译内核一次,所有唤醒周期均受益:通过初始预热,JIT 编译和 CUDA 图捕获在加载时执行一次,并保留在后续所有的休眠/唤醒周期中
- 无预热时,每次唤醒都要支付编译成本:5-7 倍的减速发生在每次唤醒后的首次推理上,而不仅仅是一次
- 已编译内核在休眠/唤醒间保留:在初始加载期间预热(8.4s)后,所有后续唤醒都有快速的首次推理(0.45s, 0.93s),证明内核已缓存
- 只需极少预热:单次 1-token 推理足以触发完整 JIT 编译和 CUDA 图捕获,使预热非常廉价
- 用初始加载时间换取一致性能:8.4s 的预热成本支付一次,即可分摊到所有模型切换中
- 建议:在生产环境中始终使用预热,以期获得持续、快速的推理体验
量化对睡眠模式的影响
量化 (FP8) 会影响睡眠模式性能吗?我们在 A100 GPU 上使用相同工作负载测试了开启和未开启 FP8 量化的情况。
GPU: A100 (TP=1) | vLLM 0.11.0 | 睡眠级别: 1 | 编译:
cudagraph_mode: FULL_AND_PIECEWISE对比 BF16(基准)与 FP8 量化。鼠标悬停查看精确时间。
消融研究:推理性能(BF16 vs FP8)
推理时间 = 预填充 (prefill) + 解码 (decode)(唤醒/加载后的首个请求)。每个请求使用不同的问题以避免缓存,输出限制为 100 个 token。
误差线显示了多次运行中的最小/最大值变化。数值显示在柱状图上。
GPU: A100 (TP=1) | vLLM 0.11.0 | 睡眠级别: 1 | 编译:
cudagraph_mode: FULL_AND_PIECEWISE消融研究:模型切换(BF16 vs FP8)
误差线显示了多次运行中的最小/最大值变化。数值显示在柱状图上。
GPU: A100 (TP=1) | vLLM 0.11.0 | 睡眠级别: 1 | 编译:
cudagraph_mode: FULL_AND_PIECEWISE关键发现
| 指标 | BF16 | FP8 | 改进 |
|---|---|---|---|
| 总时间 (5 次切换) | 108.2s | 113.6s | -5%(稍慢) |
| Qwen3-0.6B 唤醒时间 | 平均 0.27s | 平均 0.18s | 快 33% |
| Phi-3-vision 唤醒时间 | 平均 0.90s | 平均 0.78s | 快 13% |
| Qwen3-0.6B 推理 | 平均 0.41s | 平均 0.44s | -7%(稍慢) |
| Phi-3-vision 推理 | 平均 0.81s | 平均 0.57s | 快 30% |
| 初始加载时间 | 90.5s | 96.9s | -7%(预热时间更长) |
洞察
- FP8 唤醒操作更快(快 13-33%),这是因为内存移动量减少
- FP8 改善了较大模型的推理(对于 Phi-3-vision 快 30%),但对微型模型影响微乎其微
- 由于预热期间的量化开销,FP8 的初始加载时间较长
- 初始加载后,FP8 通过更快的唤醒周期提供更顺滑的切换
- 对于频繁切换的工作负载,FP8 更快的唤醒时间可以抵消较长的初始加载时间
决策指南:该使用哪个睡眠级别?
什么情况下使用睡眠级别 1
- 你有足够的 CPU RAM 来存放所有模型权重
- 你需要最快的唤醒时间(0.1-6s)
- 你频繁切换模型(每几秒/分钟)
- 推理延迟的一致性至关重要
什么情况下使用睡眠级别 2
- CPU RAM 有限(无法容纳所有模型权重)
- 你正在优化云成本(RAM 较小的更廉价实例)
- 你有许多模型需要管理(10 个以上)
什么情况下跳过睡眠模式
- 你只使用单模型(无需切换)
- 模型切换极其罕见(每天/每周一次)
- 两个模型都能同时装入 GPU 内存
总结
vLLM 睡眠模式将多模型 GPU 服务从 30-100 秒的重新加载惩罚缩减为亚秒级切换。基准测试结果足以说明问题。
- 模型切换快 18-200 倍,具体取决于模型大小和硬件
- 推理快 61-88%(预热模型 vs 冷启动)
- 总时间节省 65-68%
- 适用于各种规模:0.6B 到 235B 参数,无论小型还是大型 GPU
LLM 服务的未来在于多模型化,而睡眠模式使这一切在今天成为现实。
致谢
特别感谢 Vensen Mu, Jeff Aw, Jun Kang Chow, Tun Jian Tan, Pin Siang Tan, Amir Balwel, Ye Hur Cheong, Zhiyao Cen 和 Kaichao You 开发了睡眠模式功能并撰写此博客。