夜雨聆风学习资料网

ARTICLE · 948076

vLLM-Omni 分布式逐层卸载:64 GB 显存跑 124 GB 的 DiT 视频生成模型

vLLM-Omni 分布式逐层卸载:64 GB 显存跑 124 GB 的 DiT 视频生成模型

124 GB 的 DiT 模型跑在 64 GB 的卡上。逐层卸载压住了显存,代价却转嫁到 host 侧:每个 DP rank 各存一整份权重,DP4 就要 496 GB 内存。vLLM-Omni 把 host 侧也切开——权重按 rank 分片,运行时用 AllGather 逐层重建,双缓冲预取保证设备上只驻留 2 层。实测冷启动 cgroup 峰值从 178 GB 降到 47 GB,4 张 B300 上吞吐是 HSDP+USP4 的 1.39 倍而每卡显存只用 30%,各策略输出逐字节一致。

TL;DR

开箱即用的版本组合: 下文 DLO + AllGather 的快速上手命令,请使用 vLLM 0.27.0 搭配 vLLM-Omni v0.27.0rc1。

vLLM-Omni 的 Distributed Layerwise Offload 让比单卡 HBM 还大的视频生成模型(例如 Cosmos3-Super,64B / 124 GB)能够跨多张 NPU 或 GPU 运行,同时把 host 内存开销压到很低。整套方案包含:

  • • meta device 初始化 + mmap 权重加载:权重以 mmap 视图的形式指向共享的 OS page cache,消除了建模阶段 O(dp_size × model_size) 的 RSS。冷启动时 cgroup 可见峰值下降 73%(Cosmos3-Nano DP4:178 GB → 47 GB)。
  • • 权重分片 + AllGather:每个 rank 只保存 1/dp_size 的模型权重,运行时通过 AllGather 在专用 stream 上重建完整的层权重,并与计算重叠。
  • • 固定双缓冲方案:任意时刻每个设备上只驻留 2 层权重,与模型总层数无关。缓冲区容量仍随模型中最大的 block 增长,总显存还包含随负载变化的激活值与通信缓冲。在实测的 720p 10s 负载下,从 17B 到 64B 模型,峰值显存增长约 22%(23.1 → 28.1 GB),空载显存增长约 27%(11.5 → 14.6 GB)。
  • • DP 多并发:每个 DP rank 并行处理不同的请求,相对单请求 HSDP 取得 3.3× 吞吐——约为理想 4× 扩展的 83%。
  • • 平台无关:借助 vLLM-Omni 的平台抽象层,NVIDIA GPU(CUDA/NCCL)与昇腾 NPU(CANN/HCCL)都能跑。
  • • 8× B300 上的拓扑相关性:在评估过的三条 MiniMax-H3 路线中,AllGather 在 DP1×SP8 的延迟场景和 DP4×SP2 的平衡点上表现最好,而在 DP8×SP1 上则是 rank-local DLO 胜出,达到 183.78 videos/h 与 43.97 Wh/video。

在昇腾 910B3 上实测的 DLO+AllGather 运行中(Cosmos3-Nano,33 GB;Cosmos3-Super,124 GB),所有配置都产出了正确的视频输出,且 cgroup 可见的 host 内存按 O(model_size + dp_size × 常数) 增长,而不是 O(dp_size × model_size)。关闭 AllGather 的模式下,纯 DP 配置中每个 rank 仍保留一整份 host 副本;而既有的 TP 分片本来就是 rank-local 的,直接复用即可。CUDA 侧的进程内存统计包含了 pinned 分片,下文单独列出。

快速上手

版本要求:下面两条 AllGather 命令需要 vLLM-Omni v0.27.0rc1 或更高版本,搭配 vLLM 0.27.0。在 v0.26.0 版本上,Cosmos3 的 DLO+DP 路径会拒绝所有请求,因为引擎要求 supports_request_batch=True 才允许多请求准入,而 Cosmos3OmniDiffusersPipeline 并未声明该属性[1]。

#5864[2] 修复了这一点:对 DLO+AllGather+DP 配置绕过 supports_request_batch 的要求——每个 DP rank 通过 pipeline 的单请求前向路径独立跑自己的请求,引擎再从各 rank 的队列收集结果。关闭 AllGather 的 DP 命令不在 #5864 的覆盖范围内;--dlo-no-use-allgather 下的独立请求分发由 #5911[3] 跟踪(仍未合并)。#5864 中的正确性修复不改变 DLO 的权重分片与卸载内存机制;下文每个测量小节都会各自说明所用环境。

# 4× NPU 或 GPU —— Cosmos3-Nano,DP=4vllm serve /path/to/Cosmos3-Nano --omni \    --enable-distributed-layerwise-offload \    --data-parallel-size 4# 2× 设备 —— Cosmos3-Super(124 GB),DP=2vllm serve /path/to/Cosmos3-Super --omni \    --enable-distributed-layerwise-offload \    --data-parallel-size 2# 关闭 AllGather(每个 rank 加载完整权重,不做分片)vllm serve /path/to/Cosmos3-Nano --omni \    --enable-distributed-layerwise-offload \    --data-parallel-size 4 \    --dlo-no-use-allgather

--dlo-use-allgather / --dlo-no-use-allgather 这组开关控制权重是否分片(默认分片)。关闭时,每个 rank 加载标准 loader 给出的 rank-local 张量——在纯 DP 配置下这就是一整份模型副本,而既有的 TP 分片本来就是 rank-local 的,直接复用。当 AllGather 的同步开销超过省下的内存时,这个模式就派得上用场。

问题:大扩散模型 vs. HBM 与 host 内存

Cosmos3-Super(64B 参数,BF16 下 124 GB)装不进单张 64 GB HBM 的卡。现有方案分为两类——卸载类,把权重从 host 内存流式搬入;以及并行类,把常驻的工作切分到多个设备上。两类各有局限:

为什么需要分布式逐层卸载

图 1:针对 Cosmos3-Super 的卸载类与并行类方案对比。HSDP 每卡约 31 GB 权重加上约 25 GB 的激活值与通信缓冲(合计约 56 GB),只剩 8 GB 余量;DLO 则只在 HBM 里保留两层,同时把 host 侧权重也切开。

方案
设备 HBM
每 rank host 内存
局限
HSDP(FSDP2)
model / N
0
HBM 会被撑满:64B → 56 GB/卡(只剩 8 GB 余量)
逐层卸载(纯 DP)
仅 2 层
完整模型
需要 N × model_size 的 host 内存(4 × 124 GB = 496 GB)
Tensor Parallel
model / N
0
激活值扩展性有改善,但有通信开销
分布式逐层卸载(本文)
仅 2 层
model / N
需要 AllGather 同步

对纯 DP 部署来说,host 内存才是致命瓶颈:传统逐层卸载会在每个 rank 的 host 内存里各存一整份模型。4 张卡就是 4 × 124 GB = 496 GB——比大多数服务器的内存还多。TP 部署可能本来就在用 rank-local 分片,每 rank 的 host 内存会按比例下降。

更糟的是,模型加载期间每个 rank 各自调用 param.data.copy_(loaded_weight),在 RSS 里造出 dp_size 份完整的私有副本。峰值 RSS 按 O(dp_size × model_size) 增长,200B 模型在 dp_size=4 时会到 2 TB。

方案总览

Distributed Layerwise Offload 用四项相互配合的技术同时解决 HBM 和 host 内存两个瓶颈:

技术
解决的问题
主要收益
meta device + mmap
加载期 O(dp_size × model) 的 RSS
冷启动 cgroup 可见峰值 −73%
权重分片 + AllGather
N × model_size 的 host 内存
总量降到 1× model_size(共享 page cache)
双缓冲预取
全部权重都要上设备
任意时刻只驻留 2 层
DP 多并发
请求串行处理
N 个并行请求带来 3.3× 吞吐

前三项让大模型部署在内存上变得可行;DP 多并发则是一项吞吐优化,建立在第二项技术本来就需要的 AllGather 同步之上。下面按我们实现的顺序逐项展开,每项回答三个问题:问题为什么存在、方法为什么有效、你能得到什么。

1. meta device + mmap 权重加载

为什么。 原先的加载路径是每个 rank 各自调用 load_model(load_device="cpu"),然后才 offload_backend.enable()。这导致 param.data.copy_(loaded_weight) 在 RSS 里造出 dp_size 份完整的私有模型副本。Cosmos3-Nano DP4 的 cgroup 可见峰值是 178 GB——而模型本身只有 33 GB。

为什么有效。 卸载后端用 to_empty(device="meta") 把已经建好的 DiT 模块转到 meta device,释放其参数存储而保留张量元信息;随后把这些 meta 参数替换成来自 safe_open().get_tensor() 的 mmap 视图——它们指向 OS page cache,而不是私有副本。

# distributed_layerwise_backend.py —— 释放已有的 DiT 参数存储dit_module.to_empty(device="meta")# 先解析 HF repo ID,再把 meta 参数替换成 mmap 视图model_path = download_weights_from_hf(...)tensor = safe_open(file_path, framework="pt", device="cpu").get_tensor(ckpt_key)parent._parameters[name] = Parameter(tensor)  # 指向共享 page cache

由于所有 rank mmap 的是同一批 safetensors 文件,操作系统在 page cache 里只维护每个文件页的一份副本——所有进程共享。没有任何一个 rank 会造出私有副本。

对于 Hugging Face repo ID(而非本地路径),我们先用 download_weights_from_hf() 解析出 snapshot 路径,与 vLLM 既有的 DiffusersPipelineLoader 的做法保持一致。

你能得到什么。 Cosmos3-Nano DP4 的冷启动 cgroup 可见峰值从 178 GB 降到 47 GB——下降 73%。178 GB 的基线由三部分构成:132 GB 的私有模型副本、33 GB 的共享 page cache,以及约 13 GB 的框架 / 瞬态开销。mmap 的 page cache(1× model_size)是共享且只读的,内存吃紧时操作系统还能部分回收。

meta device 与 mmap 加载的内存对比

图 2:实测 Cosmos3-Nano DP4 的冷启动峰值从 178 GB 降到 47 GB——把四份私有权重副本换成了由一份共享 mmap page cache 支撑的 meta 参数。

2. 权重分片与 AllGather 重建

为什么。 即便用上 mmap 加载,逐层卸载机制仍然会把完整模型拷进每个 rank 的 pinned CPU 内存里,用于 H2D 传输。在这里实测的纯 DP 基线中,4 张卡就意味着 4 × 33 GB = 132 GB 的 pinned 内存——而且随设备数线性增长。

为什么有效。 每个 rank 不再存完整模型,而是只存 1/dp_size 的权重。运行时在专用通信 stream 上通过 all_gather_into_tensor 重建出完整的层权重。

# _shard_and_pin:每个 rank 只保存自己的 1/dp_size 分片shard_size = (total_numel + dp_size - 1) // dp_size  # 向上取整shard = torch.zeros(shard_size, dtype=dtype, device="cpu")# 只拷贝落在 [rank * shard_size, (rank+1) * shard_size) 区间内的部分shard[dst_slice].copy_(mmap_view.flatten()[src_slice])shard = shard.pin_memory()  # 用于快速 H2D 的 DMA 缓冲

分片采用向上取整并补零,使所有分片等长——这是 all_gather_into_tensor 的要求。分片完成后,原来的 mmap 视图会被替换成零元素占位张量,释放对 page cache 的引用。

你能得到什么。 pinned 内存总量从 dp_size × model_size 降到 model_size(所有 rank 求和)。以 Cosmos3-Super DP4 为例:4 × 124 GB → 总计 124 GB,每 rank 31 GB。

权重分片与 AllGather 重建

图 3:host 侧常驻权重从"每 rank 一整份模型"缩到"每 rank 一个分片";AllGather 只在设备上重建当前这一层的完整权重。

3. 双缓冲预取:H2D 与 AllGather 重叠

为什么。 分片解决了内存问题,但每一层在计算时仍然需要完整权重在设备上。如果一次性加载所有层,HBM 又会被撑满——回到最初的问题。同步式加载(H2D → 等待 → AllGather → 等待 → 计算)也很浪费时间:数据搬运期间 GPU 是闲着的。

为什么有效。 我们只维护两个设备缓冲区(slot),每个按模型中最大的 block 大小分配。当计算 stream 在执行第 N 层(使用 slot 0)时,后台 stream 把第 N+1 层准备到 slot 1:

DLO 双缓冲预取流水线

图:完整的三 stream 时间线,展示计算(蓝)、H2D(橙)、AllGather(绿)如何通过双缓冲 slot 相互重叠。红色虚线箭头表示事件同步——计算要等 AllGather 完成后才切换 slot。

两阶段的准备工作跑在不同的 stream 上:

  1. 1. H2D(copy_stream):把 1/dp_size 分片从 pinned CPU 内存搬到设备
  2. 2. AllGather(comm_stream):从所有 rank 汇聚分片,拼成完整权重缓冲

两条 stream 都通过基于 event 的同步与计算 stream 重叠。AllGather 完成后,参数会依据缓存好的元信息重新指向输出缓冲区的对应切片。

缓冲区在所有 block 之间共享——按最大 block 大小一次性分配,每一层复用。这保证了显存占用被 2 × max_block_size 限住,与总层数无关。

在昇腾 NPU 上,pin_memory() 通过 /dev/davinci_manager(NPU 设备驱动)分配可做 DMA 的内存。这块内存位于 CPU 内核空间,不被 cgroup 统计——这也是 cgroup 峰值比预期低得多的关键原因。

你能得到什么。 HBM 里只放 2 层权重(Nano 约 2 GB,Super 约 3 GB),与总层数无关。所需的缓冲区容量仍随最大 block 增长,总显存还包含随负载变化的激活值与通信缓冲。在实测的 dist_offload+SP 720p 10s 负载下,从 Nano 到 Super 的峰值显存增长约 22%(23.1 → 28.1 GB),空载显存增长约 27%(11.5 → 14.6 GB)。模型大了 3.8 倍,但两项显存指标都远低于 64 GB。

Cosmos3-Nano 与 Cosmos3-Super 的显存占用

图 4:720p 10s 下实测的 dist_offload+SP 显存。峰值显存从 23.1 GB 升到 28.1 GB,约 +22%,而 124 GB 的模型体量是原来的 3.8 倍;HSDP+SP 在 Super 上则达到 56.3 GB。

4. DP 多并发:N 个请求并行

为什么。 AllGather 汇聚的只是权重分片——它完全与请求无关。这意味着所有 DP rank 在每次 AllGather 调用处同步,但它们可以并行计算不同的激活值(也就是不同的请求)。如果不利用这一点,DP rank 在两次 AllGather 之间就是闲着的,吞吐被限制在同时只处理 1 个请求。

为什么有效。 启用 dp_concurrent 后,调度器会把至多 dp_size 个请求打成一批。executor 用单次广播 RPC 把所有请求发出去:

DP 多并发的请求流

图 5:一次广播携带一个请求列表;各 DP rank 计算不同的请求,而同步的 AllGather 调用交换的是与请求无关的权重分片。

# Executor:一次性发出所有请求reqs_list = [nr.req for nr in new_reqs]results = collective_rpc("execute_model", args=(reqs_list, ...),                         unique_reply_rank=None, exec_all_ranks=True)

每个 worker 依据自己的 DP rank(而不是 global rank,以便正确处理 SP/TP)挑一个请求:

dp_rank = get_data_parallel_rank()req = reqs_list[dp_rank % len(reqs_list)]

每个 DP 副本内只有主 rank(SP=0、TP=0、CFG=0、PP=0)回复,并带上 dp_rank 标签用于结果匹配。executor 通过轮询收集响应,再按 dp_rank 排序把结果对回请求。

有一道校验会拒绝批次兼容性键不一致的并发请求。这个键覆盖空间 / 时间形状(height、width、num_frames、fps)、CFG / guidance 设置(guidance_scale、true_cfg_scale、cfg_normalize)、num_inference_steps、LoRA 身份(lora_int_id、lora_scale)、输出数量、质量模式,以及 pipeline 特有的 extra_args——因为 AllGather 是集合通信,这些共享字段一旦不一致,就会出现一个 rank 走偏、其他 rank 挂住的情况。extra_args 尤其可能改变前向调度,所以引擎要求同一波请求里它的 JSON 完全相同。而随机种子、generator 这类请求本地字段可以逐 rank 不同。自 #5864[2] 起,pipeline 不再需要声明 supports_request_batch=True;引擎让每个 DP rank 的请求各自走 pipeline 的单请求前向路径,再从各 rank 的结果队列收集。不兼容或空 prompt 的请求波会在派发到 worker 之前被拒绝,而部分完成的请求波会以超时失败告终,而不是把集合通信卡死。

你能得到什么。 4 个并发请求达到 3.22 生成视频帧/秒——是 HSDP 单请求基线的 3.3 倍,约为理想 4× 扩展的 83%。固定的 AllGather 开销(约 150 ms/step)被 4 个并发计算摊薄。

昇腾的内存统计:cgroup 可见 vs. 物理内存

朴素的分析会预期 host 内存占用是 2× model_size:page cache(1× 模型)+ 分片缓冲(合计 1× 模型)。但在昇腾 NPU 上,pin_memory() 是通过 /dev/davinci_manager 分配的,把分片放进了 CPU 内核态的 DMA 内存里——这部分对 cgroup 内存控制器不可见。物理内存 ≈ page cache + pinned 分片 + 框架开销;cgroup 看不到 pinned DMA 那一块,但服务器仍然需要这么多物理内存。

昇腾 host 与 HBM 内存统计

图 6:Cosmos3-Nano DP2 下的昇腾内存统计。cgroup 看到的是共享 page cache 与框架 RSS,而通过 /dev/davinci_manager 分配的 pinned 分片位于驱动管理的 CPU DMA 内存中,而非 NPU HBM。

用干净的测量(Cosmos3-Nano DP2,全新 cgroup)验证:

cgroup usage_in_bytes = 49 GB = cache(31) + rss(18)  ← 精确吻合,没有额外项cgroup kmem           = 0 GBdavinci_manager RSS   = 0 kB  (见 /proc/<pid>/smaps)每卡 NPU HBM          = 10 GB  (< 14.5 GB 的分片 → 分片不在 HBM 里)Slab                  = 3.3 GB  (装不下 29 GB 的分片)
组成部分
位置
大小
cgroup 是否统计?
safetensors page cache
系统内存(用户态,共享)
1× model_size
✓(cache)
框架(Python/torch/HCCL)
系统内存(用户态,每 rank)
约 3.5 GB × dp_size
✓(rss)
分片(pinned)
CPU 内核态 DMA(/dev/davinci_manager)
每 rank model_size / dp_size
✗
预取缓冲
NPU HBM
每 rank 2 × block_size
✗

也就是说,cgroup 可见内存按 O(model_size + dp_size × 常数) 增长,而不是 O(dp_size × model_size)——但总物理内存等于 cgroup 可见内存加上cgroup 看不见的那部分 pinned DMA 分片。以 200B 模型、dp_size=4 为例:约 423 GB cgroup + 约 400 GB 内核 DMA = 约 823 GB 总物理内存(2 TB 内存装得下),而不用 mmap 则是 2000 GB。

验证结果

全部测试在昇腾 910B3(每卡 64 GB HBM,2 TB 系统内存)上完成,模型为 Cosmos3-Nano(33 GB)与 Cosmos3-Super(124 GB)。

正确性

模型
配置
请求
HTTP
帧数
视频
Nano(33 GB)
DP2
2 并发,35 steps
2/2 × 200
29/29
OK
Nano(33 GB)
DP4
4 并发,35 steps
4/4 × 200
29/29
OK
Super(124 GB)
DP2
1 请求,5 steps
200
29
OK
Super(124 GB)
DP4
1 请求,5 steps
200
29
OK

host 内存(cgroup 峰值)

模型
配置
cgroup 峰值
page cache
RSS
单 worker HWM
vs. 基线
Nano(33 GB)
DP4(mmap)
47 GB
31 GB
14 GB
12.1 GB
—
Nano(33 GB)
DP4(无 mmap)
178 GB
—
—
36 GB
−73%
Super(124 GB)
DP2
157 GB
149 GB
7 GB
65.2 GB
—
Super(124 GB)
DP4
172 GB
149 GB
14 GB
35.5 GB
—

NPU 显存

模型
配置
每卡显存(空载)
每卡显存(推理中)
64 GB 余量
Nano(33 GB)
DP2
9.9 GB
10.4 GB
55 GB
Nano(33 GB)
DP4
9.4 GB
10.2 GB
55 GB
Super(124 GB)
DP2
约 15 GB
—
约 49 GB
Super(124 GB)
DP4
约 10 GB
—
约 54 GB

在实测的 dist_offload+SP 720p 10s 负载下,从 Nano 到 Super 峰值显存增长约 22%(23.1 → 28.1 GB),空载显存增长约 27%(11.5 → 14.6 GB)。设备上只驻留 2 层权重,所以体量大 3.8 倍的模型依然远低于 64 GB 上限。

性能

这批昇腾测量使用 Cosmos3-Nano,832×480,29 帧,35 步去噪。生成帧/秒指的是每墙钟秒产出的输出视频帧总数(29 帧 × 每波输出数 / 每波时延),不是视频的播放帧率。

策略
每步(ms)
生成帧/秒
每 rank CPU
每卡显存
vs. HSDP
HSDP+SP(基线)
870
0.967
0 GB
20.3 GB
—
dist_offload+AG(DP4,1 请求)
1,020
0.806
3.5 GB
12.4 GB
−17%
dist_offload+AG(DP4,4 请求)
1,020
3.22
3.5 GB
12.4 GB
3.3×
dist_offload 无 AG
1,877
0.439
28.3 GB
14.1 GB
−55%

AllGather 开销 = 150 ms/step(72 ms stream 切换 + 10 ms HCCL + 68 ms Python 分发),在 Cosmos3-Nano DP4 上测得。通信量随层维度、参与方数量与拓扑变化。有 4 个并发请求时,这项固定开销被摊薄 4 倍。

NVIDIA B300 GPU 结果

为验证平台无关性,我们把同一套 DLO 栈跑在 NVIDIA B300 SXM6 GPU 上。下面的 Cosmos3 测试使用 Cosmos3-Super BF16(124 GB)、4× NVIDIA B300(物理 GPU 1,5,6,7)、Python 3.12.3、PyTorch 2.11.0+cu130、CUDA 13.0、vLLM 0.23.0,以及 vLLM-Omni commit 9772bb32[4](PR #5397[5] 合并前的快照;最终合并的 head 还包含本次基准测试中没有的 loader 门控与 TP/mmap 校验改动)。紧随其后的 MiniMax-H3 小节会各自说明它用的 vLLM / vLLM-Omni 版本、enforce_eager=True 开关,以及一处本地 pipeline 补丁;那些细节只适用于 MiniMax-H3 研究,不能套用到 Cosmos3 的运行上。

正确性通过跨策略逐字节一致的输出哈希验证。例如 T2I 种子 42 在 DLO+AG、无 AG、DLO+USP4、传统逐层卸载+USP4、HSDP+USP4 之间产生了完全相同的 SHA256 6e7d2a8c63b88391...。T2V 832×480×29f 种子 17 在所有策略下产生了相同的 666,029 字节输出(SHA256 c5d38f5d21ca619e...)。

CUDA 侧的进程树 PSS 包含共享 page cache、pinned CPU 分片与框架内存。昇腾的 cgroup 测量则不含 /dev/davinci_manager 背书的 pinned 分片,因此 GPU 的 PSS 数字与昇腾的 cgroup 数字不能直接对比。

1024×1024 T2I,50 步

策略
并发
每波时延
吞吐
进程树 PSS
每卡峰值显存
DLO+AG DP4
4
43.69s(中位)
0.0915 outputs/s
198–202 GiB
12.62 GiB
DLO 无 AG DP4
4
112.96s
0.0354 outputs/s
532 GiB
11.43 GiB
HSDP+USP4
1
15.19s
0.0658 outputs/s
483 GiB
42.00 GiB
传统逐层卸载+USP4
1
105.22s
0.0095 outputs/s
533 GiB
13.99 GiB

4 个并发请求下,DLO+AG DP4 的吞吐达到 HSDP+USP4 的 1.39×,而每卡显存只用了 30%(12.6 GiB vs 42.0 GiB)。

832×480 T2V,29 帧,35 步

策略
每波输出数
每波时延
吞吐
输出 SHA
DLO+AG DP4
4
38.79s
0.1033 outputs/s
c5d38f5d...
HSDP+USP4
1
15.38s
0.0653 outputs/s
c5d38f5d...
DLO+AG+USP4
1
30.79s
0.0326 outputs/s
c5d38f5d...
传统逐层卸载+USP4
1
81.46s
0.0123 outputs/s
c5d38f5d...

各负载下的时延与显存(35 步,DLO+AG DP4 vs HSDP+USP4)

负载
DLO 策略
DLO 每波输出
DLO 每波时延
DLO 每卡峰值显存
HSDP 每波输出
HSDP 每波时延
HSDP 每卡峰值显存
480p,29f
DLO+AG DP4
4
38.79s
14.55 GiB
1
15.38s
43.77 GiB
480p,约 5s(121f)
DLO+AG DP4
4
102.58s
15.88 GiB
1
41.36s(125f)
53.73–62.65 GiB
480p,约 10s(241f)
DLO+AG DP4
4
226.70s
17.33 GiB
1
82.47s(245f)
53.74 GiB
720p,5s(121f)
DLO+AG DP4
4
288.29s
24.95 GiB
1
87.47s
52.19 GiB
720p,10s(241f)
DLO+AG+USP4
1
214.53s
24.99 GiB
1
210.05s
53.73 GiB

在 720p 10s(241f)上,DLO+AG+USP4 用时 214.53s——与 HSDP 的 210.05s 相差 2.13% 以内——且输出逐字节一致(SHA256 08cb679322996ea6...),同时显存只用了 HSDP 的 47%(24.99 GiB vs 53.73 GiB)。

MiniMax-H3 在 8× B300 上:DLO 模式取决于拓扑

Shunyang Li 另做了一份 MiniMax-H3 B300 研究[6],测试 DP、SP 与 DLO 执行模式在一个 8× NVIDIA B300 SXM6 AC 节点上如何相互影响。与上面的 Cosmos3 测量不同,这个负载同时生成视频和音频:768×1344,124 视频帧,立体声音频,BF16,每副本 batch size 1,请求 50 步(49 次调度器去噪更新)。该研究的 environment.json.txt 记录的是 vLLM 0.24.0 与 vLLM-Omni 0.26.0rc2.dev11+g6607f4a7f(源码 commit 9e73ee1[7]);运行脚本设置了 enforce_eager=True(关闭图编译),并对 pipeline_minimax_h3.py 打了一处本地 subgroup-broadcast 补丁[6]。这些结果并不代表未修改的正式发布版本,也不代表默认的编译图路径。下面每条被选中的 T2VA 路线,都包含跨两个引擎生命周期、每个生命周期完成一次完整预热之后的 20 次测量波。吞吐为输出数除以波时间;能耗按八卡板级功率求和后按每个输出积分,未扣除空载基线;外部 nvidia-smi 采样器以 0.758s 的中位间隔记录显存与功率。

MiniMax-H3 在八张 B300 上的拓扑感知 DLO 策略

图 7:三条被评估路线内实测出的服务前沿。提高 DP 是用每波时延换并发输出能力;到 DP8×SP1 时,更优的 DLO 模式从 AllGather 变成 rank-local。

服务目标
拓扑 / DLO 模式
波 P50
波 P95
稳态吞吐
实测每卡峰值
每视频板级能耗
最低时延
DP1×SP8 / AllGather
34.55s
35.25s
103.84 videos/h
26.37 GiB
68.08 Wh
平衡拐点
DP4×SP2 / AllGather
94.73s
95.31s
151.89 videos/h
25.11 GiB
51.76 Wh
最高吞吐 / 最低能耗
DP8×SP1 / rank-local
156.74s
157.03s
183.78 videos/h
20.05 GiB
43.97 Wh

配对的五波模式对比解释了为什么不存在一个全局统一的 DLO 策略。在 DP1×SP8 上,AllGather 使用 SP 组,吞吐提升 129.4%,P50 时延下降 56.6%。在 DP4×SP2 上,它的吞吐收益收窄到 2.2%。到 DP8×SP1 时,AllGather 反而让吞吐下降 4.1%、P50 时延上升 3.8%,并把实测的每卡峰值从 20.03 GiB 推到 94.03 GiB,因此这里更适合 rank-local DLO。FL2VA 首帧与 Ref2VA 图像+音频测试保持了同样的"时延—吞吐"排序。

MiniMax-H3 上 FL2VA 与 Ref2VA 的时延—吞吐 Pareto 前沿

图 8:在三条被评估的路线上(每条 n=5 次测量波),FL2VA 首帧 I2VA 与 Ref2VA 图像+音频改变了时延与吞吐的绝对值,但保持了 DP1×SP8 → DP4×SP2 → DP8×SP1 的前沿排序。数据来源:MiniMax-H3 B300 研究归档[6]。

这些结果是一份拓扑研究,不是普适的生产结论。DP2×SP4 未做测量;实验覆盖的是单节点、单一输入集、单一分辨率与帧数,且只做形状校验而非感知质量评估。它使用的是源码 commit 9e73ee1[7] 加上一处记录在案的本地 subgroup-broadcast 修复,运行时还警告所测的 vLLM-Omni 与 vLLM 版本并非发布对齐版本。该归档提供了 PDF、CSV、105 个波样本、环境哈希、本地 diff 与基准测试脚本[6],供独立复核。

外推到 400 GB

下表是基于上文内存模型做的 host 容量外推;并没有真正跑过 200B 级别的模型,该规模下的最大 block 大小、显存余量、带宽、时延与输出质量都尚未验证。

模型
dp_size
cgroup 峰值(估计)
总内存(估计)
2 TB 装得下吗?
33 GB
4
47 GB
约 80 GB
✓
124 GB
4
172 GB
约 296 GB
✓
185 GB
4
约 220 GB
约 405 GB
✓
400 GB
4
约 423 GB
约 823 GB
✓
400 GB
8
约 443 GB
约 843 GB
✓

致谢

感谢各位 vLLM-Omni 贡献者,包括 @hsliuustc0106 与 @yuanheng-zhao 细致的代码评审意见,感谢 Shunyang Li(GitHub @lishunyang12)贡献的 MiniMax-H3 B300 拓扑研究与可复现归档,也感谢昇腾 NPU 团队提供的硬件支持。

参考资料

源码:

  • • 分布式逐层卸载后端、meta 转换与 mmap 加载:distributed_layerwise_backend.py
  • • OffloadConfig 与策略选择:base.py
  • • 多队列 executor:multiproc_executor.py
  • • DP 多并发 worker:diffusion_worker.py
  • • 单元测试:test_distributed_layerwise_backend.py

RFC 与 PR:

  • • RFC:GitHub Issue #5396
  • • 实现 PR:vllm-omni#5397[5]
  • • DLO DP 并发请求修复:vllm-omni#5864[2]
  • • rank-local DLO DP 的独立请求支持:vllm-omni#5911[3]

模型与基准归档:

  • • Cosmos3-Nano:33 GB safetensors(17B 参数,72 个 block)
  • • Cosmos3-Super:124 GB safetensors(64B 参数,128 个 block)
  • • MiniMax-H3:B300 DLO 研究说明与可复现归档[6]

参考链接

  1. 1. vllm-omni Issue #5953 — github.com/vllm-project/vllm-omni/issues/5953
  2. 2. vllm-omni PR #5864 — github.com/vllm-project/vllm-omni/pull/5864
  3. 3. vllm-omni PR #5911 — github.com/vllm-project/vllm-omni/pull/5911
  4. 4. vllm-omni commit 9772bb32 — github.com/vllm-project/vllm-omni/commit/9772bb321f558a28c0dca1cb53b44aaf10e4ab69
  5. 5. vllm-omni PR #5397 — github.com/vllm-project/vllm-omni/pull/5397
  6. 6. MiniMax-H3 B300 DLO 研究说明与归档 — github.com/lishunyang12/vllm-omni-rankings/tree/main/scripts/minimax_h3_b300_dlo_industrial_report
  7. 7. vllm-omni commit 9e73ee1 — github.com/vllm-project/vllm-omni/commit/9e73ee1a50ce247c638052011914d8027d717f28

vLLM 官方博客

vllm.ai/blog/2026-08-17-distributed-layerwise-offload

相关学习资料