Cosmos3端侧部署优化(一) ------- Wan VAE
Cosmos3 Wan VAE — Engine 文件大小 vs 运行时 Context Memory
本文档解释 Cosmos3 Policy-DROID TRT 部署 中 vae_encode.engine 的:
- 磁盘体积 vs execution context memory(§1–§5)
- trtexec layer profile 延迟热点(§8)
- 数值格式与热点算子原理(Conv3D / RMS Norm / GEMM)(§9–§10)
以及如何估算、验证与优化。
1. 现象(Jetson Thor 实测)
Policy-DROID 固定 profile 下,四 stage engine 磁盘大小与 vae_encode profiling 日志如下:
engines/
dit.engine 28G
text_embed.engine 1.2G
vae_decode.engine 1.1G
vae_encode.engine 297M
trtexec --loadEngine=.../vae_encode.engine --exportProfile=./vae_profile.json ...
Input: video 1×3×17×480×832
Output: vision_latent 1×48×5×30×52
Created execution context with device memory size: 2358.28 MiB
疑问:engine 文件只有 ~300MB,为何 context memory 需要 ~2.3GB?
2. 两个数字分别是什么
| 指标 | vae_encode 实测 | 含义 |
|---|---|---|
| Engine 文件 ~297MB | 磁盘序列化产物 | 主要是 BF16 权重 + TensorRT 编译后的 layer / kernel 元数据 |
| Context memory ~2358 MiB | IExecutionContext 分配大小 | GPU 上 单次 forward 所需运行时显存:中间 激活(activation) + conv scratch workspace + 部分持久 buffer |
TensorRT 日志中的 2358 MiB 通常不含 engine 权重本身(权重在 deserializeCudaEngine 时另外占用 GPU)。
实际跑 vae_encode 时,峰值显存粗算为:
总显存 ≈ engine 权重 (~300MB) + context (~2.3GB) + I/O binding (~40MB) ≈ 2.6GB+
结论:297MB vs 2.3GB 并不矛盾——前者是「模型有多重」,后者是「一次 forward 中间要开多大的临时空间」。
3. 为什么 vae_encode 的激活远大于权重
3.1 输入 / 输出体积极不对称
Policy-DROID profile(shapes.POLICY_DROID):
video: [1, 3, 17, 480, 832] ≈ 40 MB (BF16)
vision_latent: [1, 48, 5, 30, 52] ≈ 0.7 MB (BF16)
输出 latent 极小,但 encoder 必须在全分辨率、多帧的 3D 特征图上做因果卷积。中间激活随 T × H × W × C 增长,与最终输出体积无关。
3.2 Wan VAE Encoder 结构
AutoencoderKLWan / WanEncoder3d(diffusers)典型配置:
base_dim=96,dim_mult=[1, 2, 4, 4]→ 通道 96 → 192 → 384 → 384- 空间下采样 ×4(
scale_factor_spatial=16),时间下采样(temperal_downsample) - 残差块 + 因果 3D 卷积(
WanCausalConv3d)
前几层仍在 17 帧 × 480 × 832 上计算。单层 BF16 激活粗算:
96 × 17 × 480 × 832 × 2B ≈ 1.2 GB # 单张 feature map
384 × 17 × 240 × 416 × 2B ≈ 1.2 GB # 下采样后仍很大
384 × 17 × 120 × 208 × 2B ≈ 0.3 GB
3.3 峰值显存的叠加因素
- 残差连接:同一时刻需保留
x与x + f(x),峰值接近 2×。 - TRT 层融合有限:部分中间 tensor 并行存活,峰值 = 同时存活 tensor 之和,而非单 tensor 大小。
- Conv3D workspace:build 时
workspace_mb=8192(见 build_cfg)允许 TRT 选择更耗显存、更快的 cuDNN 算法;runtime scratch 被烘焙进 context size。 - Profiler 开销:
trtexec --exportProfile会引入额外同步与 profiler buffer(日志会提示 e2e timing 不准确)。
因此 输入 40MB → 中间激活 1~2GB+ 对 3D CNN 是预期行为,不是 leak 或 build 错误。
4. 与其他 stage 对比
| Stage | Engine 文件 | 主导因素 | Context 特点 |
|---|---|---|---|
vae_encode | ~297MB | 权小、激活大 | ~2.3GB(CNN 典型) |
vae_decode | ~1.1GB | 权重 + 上采样激活 | 通常 ≥ encode |
text_embed | ~1.2GB | 主要是权重 | 相对可控 |
dit | ~28GB | 16B MoT 权重 | 权重主导;context 另计 |
dit 28GB 主要是 MoT DiT 参数量;vae_encode 权重小,但 spatial-temporal 卷积激活随 H×W×T 超线性增长,故 context 相对 engine 文件显得「不成比例地大」。
5. Cosmos3 四 stage 显存预算
Chamleon 串行调度下,context 可复用同一块 device buffer;权重须分别加载:
峰值显存 ≈ Σ(各 engine 权重) + max(各 stage context) + I/O buffer + host 开销
Policy-DROID 在 Thor 上的主要瓶颈通常是 dit 28GB 权重;vae_encode 的 ~2.6GB 总量在单次 policy infer 中只出现 1 次(去噪环内不再调用 vae_encode)。
内存类型划分(见 edge-llm-flow §3.6.2):
| 类型 | vae_encode 对应物 | 生命周期 |
|---|---|---|
| 权重 | engine 内 constant | 进程级(deserialize 后常驻) |
| Context / scratch | 2.3GB device memory | 单次 enqueue;多 stage 可串行共享 |
| I/O binding | video / vision_latent buffer | stage 间 handoff |
6. 验证方法
# Layer 信息与 context size
trtexec --loadEngine=output/cosmos3_policy_droid_trt/engines/vae_encode.engine --dumpLayerInfo
# 不带 profiler 的纯推理(更接近生产显存)
trtexec --loadEngine=output/cosmos3_policy_droid_trt/engines/vae_encode.engine \
--warmUp=10 --iterations=10
# 另开终端: nvidia-smi 观察实际占用
Chamleon workflow 的 trt_profile action 会导出 layer timing;若只关心显存,优先用无 --exportProfile 的 trtexec 或 nvidia-smi。
Layer 级延迟 breakdown 与优化优先级见 §8。
7. 优化方向(v2)
| 手段 | 效果 | 代价 |
|---|---|---|
降低 workspace_mb(如 8192→2048) | 减小 runtime scratch | 可能变慢;需 rebuild |
| 降低 canvas / 帧数 | 激活近似按 H×W×T 缩放 | 需 re-export + rebuild 全 stage |
| VAE INT8 / NVFP4 量化 | 权重与激活均下降 | ModelOpt 路径;需校准与精度验证 |
| 串行 stage + 共享 context memory | 峰值不叠加各 stage context | 已实现于编排设计;需 runtime 侧 setDeviceMemory |
| Gradient checkpointing(训练侧) | 不适用 TRT 推理 | — |
当前 v1 部署为 bf16 固定 profile;VAE 量化见 Model-Optimizer 与 docs/quantize/nvfp4.md 中的 Wan Conv3D 量化说明。
延迟热点与 layer 级 breakdown 见下文 §8(Thor 上 trtexec --exportProfile 实测)。
8. trtexec Layer Profile 分析(Jetson Thor)
8.1 数据来源
trtexec --loadEngine=output/cosmos3_policy_droid_trt/engines/vae_encode.engine \
--exportProfile=./vae_profile.json --warmUp=10 --iterations=10
- Profile 文件:
vae_profile.json(358 个 layer/kernel 条目) - Engine:
vae_encode.engine(BF16,build_cfgworkspace_mb=8192) - 输入 / 输出:
video [1,3,17,480,832]→vision_latent [1,48,5,30,52] - 迭代次数:11(JSON 首条
{ "count": 11 })
--exportProfile会引入额外 GPU 同步,e2e 计时略偏高。精确 latency 请用
--separateProfileRun或去掉--exportProfile单独测(见 §8.7)。
8.2 总体性能
| 指标 | 数值 |
|---|---|
| Layer 时间总和(11 iter) | 5060 ms |
| 估算 e2e / iter(layer 求和) | ~460 ms |
| Profile 层 / kernel 数 | 358(≈358 次 launch/iter) |
| Conv 条目 | 149 |
| 平均单次 Conv launch | ~1.65 ms |
Policy 链路中 vae_encode 只跑 1 次;dit 去噪环(~30 步)通常是算力主瓶颈,但单次 encode ~460ms 仍不可忽视。
8.3 按算子类型
| 类型 | 占比 | ms/iter | 识别特征 |
|---|---|---|---|
| Conv3D | 53.6% | ~247 | /encoder/.../Conv_myl* |
| RMS Norm | 27.8% | ~128 | MulSumSqrtMax、TranReshMoveMean(WanRMS_norm) |
| Elemwise 融合 | 18.0% | ~83 | ReplDivMulMulAddSilu...(SiLU + 残差 + scale) |
| Layout | 0.6% | ~3 | Resh / Tran / Move / Slic |
| Head / quant | <0.1% | <1 | conv_out、quant_conv、SlicAddMul(归一化已 bake 进 export) |
Head 与 latent 归一化几乎不占时间;热点在 encoder 主体。
8.4 按分辨率阶段
| 阶段 | 占比 | ms/iter | 空间尺度(约) |
|---|---|---|---|
L0 down_blocks_0 | 23.6% | ~109 | 全分辨率 480×832 |
| Downsample RMS 融合 | 24.2% | ~111 | 下采样路径上的 WanRMS_norm |
L1 down_blocks_1 | 17.8% | ~82 | 1/2 分辨率 |
| ResBlock 融合(L0/L1) | 16.8% | ~77 | SiLU + RMS + 残差 |
L2 down_blocks_2 | 7.4% | ~34 | 1/4 分辨率 |
| L3 / mid / conv_in | <5% | 已充分下采样 |
约 48% 时间耗在全分辨率阶段(L0 Conv + 下采样 RMS),与 §3 中 GB 级 activation 分析一致。
Top RMS 层(各 ~23–28 ms/iter,合计 ~112 ms/iter ≈ 22%):
__myl_TranReshMoveMean_myl0_56 avg ≈ 28.1 ms
__myl_TranReshMoveMean_myl0_64 avg ≈ 26.7 ms
__myl_TranReshMoveMean_myl0_52 avg ≈ 24.5 ms
__myl_TranReshMoveMean_myl0_60 avg ≈ 22.9 ms
Top Conv 层(L0 resnet,每块 4 份 tiled conv 各 ~6 ms/iter):
/encoder/down_blocks_0/resnets_0/conv2_* ~6.4 ms/iter × 4 ≈ 25.6 ms/iter(该 resblock)
/encoder/down_blocks_0/resnets_0/conv1_* ~6.0 ms/iter × 4 ≈ 24.0 ms/iter
8.5 因果卷积时间展开(kernel 碎片化)
Wan VAE 使用 因果 3D 卷积 + feat_cache(WanCausalConv3d,CACHE_T=2,见 diffusers
autoencoder_kl_wan.py)。ONNX/TRT 导出时将 cache 逻辑展开,同一逻辑层对应多份 Conv:
conv1/Conv ← 主路径
conv1_1/Conv … conv1_4/Conv ← 时间维分块(17 帧 → 4–5 块)
Profile 统计:
- 28 组 Conv 各有 4+ 个 tiled 变体
- 例:
down_blocks_0/resnets_0/conv2四份合计 ~282 ms / 11 iter ≈ 25.6 ms/iter - 带来 358 次 kernel launch/iter 的开销与算子碎片化
这是 TRT 处理动态 cache 的通用代价;自定义 CausalConv3d Plugin 是长期优化方向。
8.6 瓶颈性质:Memory-bound
L0 单层 feature map(C=96, T=17, H=480, W=832, BF16):
输入 video: 1×3×17×480×832×2B ≈ 40 MB
单层 L0 feature map: 1×96×17×480×832×2B ≈ 1.3 GB
- 运算强度低:相对 Thor 算力,Conv3D + RMS 偏 DRAM 带宽受限(算子级分析见 §10)
- RMS 占 ~28%:逐通道 reduce + scale,访存密集、算术少
- 与 §2–§3 中 2.3GB context memory 相互印证:带宽型 workload 需要大 activation buffer
8.7 延迟优化方案(按预期收益)
P0 — 输入侧(需 re-export,收益最大)
| 方案 | 预期 | 说明 |
|---|---|---|
| 降低 canvas 分辨率 | L0 近似按 H×W;边长 ÷2 → 时间约 ÷4 | 换 resolution_tier;需与训练分布对齐 |
减少 num_frames | 随 T 近似线性 | Policy 主要用 frame0 条件;17 帧为 MoT 对齐;改 profile 需质量验证 |
| Crop 替代 full canvas | 显著减 activation | v1 为 TRT shape 恒定用 full-canvas;v2 可评估 |
P1 — TRT build / runtime(不改语义)
| 方案 | 预期 | 说明 |
|---|---|---|
| CUDA Graph | launch 开销,约 5–15% | deploy.use_cudagraph: true 后 rebuild;358 kernel 场景适用 |
| 重跑 build tactic | 不定 | 对比 workspace_mb 4096/8192;或 builder optimization level |
| FP16 engine A/B | 通常不会比 BF16 更快 | 两者均为 16 bit/元素,DRAM 流量相同;详见 §9 |
| FP32 → FP16/BF16 | 激活+权重字节数 ÷2 | 相对 FP32 才有「带宽减半」;当前 v1 已是 16-bit |
| 纯 TRT layer fusion | 有限 | 已有大量 ReplDivMulMulAddSilu 融合;边际收益小 |
P2 — 模型 / 导出(工程量大)
| 方案 | 预期 | 说明 |
|---|---|---|
| CausalConv3d TRT Plugin | 消除 conv_1…4 展开 | 合并时间块,减 kernel 数与 buffer |
| NVFP4 / INT8 量化 Conv3D | 权重 4–8×、激活 2–4× 字节下降 | ModelOpt;需 calibration;这才是相对 BF16 的真正降带宽手段 |
| 简化 VAE 结构 | 最大 | 改语义,仅研究方向 |
P3 — 部署策略
| 方案 | 说明 |
|---|---|
| Cache z0 | 观测不变时跳过重复 vae_encode |
| vae_encode ∥ text_embed | 多 stream 并行(显存允许时) |
| 共享 context memory | 不加速单次 encode,降峰值显存(§5) |
8.8 建议实验命令
# 1) 纯 latency(无 profiler 同步开销)
trtexec --loadEngine=output/cosmos3_policy_droid_trt/engines/vae_encode.engine \
--shapes=video:1x3x17x480x832 --warmUp=20 --iterations=100
# 2) Layer profile 与 e2e 分离(推荐)
trtexec --loadEngine=.../vae_encode.engine \
--exportProfile=./vae_profile.json --separateProfileRun \
--warmUp=10 --iterations=10
# 3) CUDA Graph:deploy.yaml use_cudagraph: true → chameleon compile → 重复 (1)
# 4) 带宽验证(memory-bound 假设)
nsys profile trtexec --loadEngine=.../vae_encode.engine --iterations=10
优先实验:相同 shape 下对比 CUDA Graph on/off 的 e2e ms。若收益 <5%,主要矛盾在 L0 全分辨率 Conv3D 带宽,应转向 降分辨率 / INT8·NVFP4 量化(P0/P2),而非 BF16↔FP16 互换。
9. 数值格式与 DRAM 带宽
9.1 各 dtype 存储宽度
| 格式 | bit / 元素 | 字节 / 元素 | 相对 BF16 的激活+权重流量 |
|---|---|---|---|
| FP32 | 32 | 4 B | 2× |
| BF16 | 16 | 2 B | 1×(基准) |
| FP16 | 16 | 2 B | 1×(与 BF16 相同) |
| FP16/BF16 混合 | 16 | 2 B | 1× |
| INT8 | 8 | 1 B | 0.5× |
| NVFP4 | 4 | 0.5 B | 0.25× |
BF16 与 FP16 都是 16 bit 浮点,区别在 指数/尾数分配(BF16:8 bit 指数对齐 FP32,4 bit 尾数;FP16:5 bit 指数,10 bit 尾数)。磁盘上的 engine 体积、DRAM 读写字节数、activation tensor 大小完全一致。
因此文档 §8.7 中「BF16 → FP16 带宽减半」是错误表述(已修正):不存在因位宽变化带来的 2× 加速。
9.2 BF16 ↔ FP16 A/B 还可能有什么差别?
在字节流量相同的前提下,换 precision flag Rebuild 后,最多出现小幅、不确定差异:
| 维度 | 说明 |
|---|---|
| TRT / cuDNN kernel 选型 | 不同 dtype 标签可能走到不同 tactic(是否用 Tensor Core、tiling 策略) |
| 硬件 峰值算力表 | 部分 GPU 对 FP16/BF16 Tensor Core 峰值不同;但 memory-bound 时算力常不是瓶颈 |
| 数值行为 | VAE 深链路上 BF16 动态范围更大;FP16 可能精度更差或偶然更快,需质量+A/B |
| WanRMS_norm | 源码中 BF16/FP16 输入会 先 cast 到 FP32 做 normalize(见 §10.2),两者内部 reduce 路径一样 |
结论:FP16 A/B 属于低优先级实验;真正降带宽靠 INT8/NVFP4(位宽变小)或减小 H×W×T。
9.3 当前 v1(BF16)与量化路径对照
v1 bf16 engine → 2 B/elem,profile ~460 ms/iter,context ~2.3 GB
FP16 rebuild → 仍 2 B/elem,不应期待显著加速
INT8 Conv3D → ~1 B/elem 权重+激活,理论 DRAM ~÷2(含 scale 开销)
NVFP4 → ~0.5 B/elem 权重,激活 8bit/4bit 混合,理论 DRAM 更低
10. 热点算子原理:Conv3D 与 RMS Norm
本节对应 §8 profile 中 53.6% Conv3D 与 27.8% RMS Norm,源码见 diffusers
autoencoder_kl_wan.py(WanCausalConv3d、WanRMS_norm、WanResidualBlock)。
10.1 张量约定(Policy-DROID encode)
全程 layout:NCTHW = [B, C, T, H, W](batch, channel, time, height, width)。
| 位置 | 典型 shape(L0) | 说明 |
|---|---|---|
输入 video | [1, 3, 17, 480, 832] | RGB,17 帧 canvas |
conv_in 后 | [1, 96, 17, 480, 832] | base_dim=96 |
| L0 resblock 内 | [1, 96→384, 17, 480, 832] | 通道渐增,空间仍全分辨率 |
| L1 下采样后 | [1, 384, 17, 240, 416] | 空间 ÷2 |
| 最终 latent | [1, 48, 5, 30, 52] | 时间+空间均下采样 |
通道阶梯(dim_mult=[1,2,4,4],base_dim=96):96 → 192 → 384 → 384。
10.2 WanRMS_norm:计算过程(非 GEMM)
数学定义
对 channel-first 视频特征 x ∈ R^{B×C×T×H×W},WanRMS_norm 在 通道维 C 上归一化(dim=1),再乘可学习缩放:
# 对每个固定 (b, t, h, w),令 v = x[b, :, t, h, w] ∈ R^C
v_norm = v / ||v||_2 # F.normalize,L2 归一化(非 LayerNorm 的减均值)
y[b,c,t,h,w] = v_norm[c] * sqrt(C) * gamma[c] + bias[c]
gamma shape 为 (C, 1, 1, 1)(images=False 时),在 T,H,W 上 broadcast。
与 LayerNorm 不同:RMS/L2 norm 不减均值,只做范数缩放;没有全连接 GEMM。
源码路径
# autoencoder_kl_wan.py — WanRMS_norm.forward
needs_fp32_normalize = x.dtype in (float16, bfloat16, ...)
normalized = F.normalize(x.float() if needs_fp32_normalize else x, dim=1).to(x.dtype)
return normalized * self.scale * self.gamma + self.bias # scale = sqrt(dim)
BF16/FP16 推理的关键点:normalize 在 FP32 中完成,再 cast 回 BF16。一次 RMS 的 DRAM 流量粗算:
读 x (BF16) C×T×H×W × 2B
写/读 FP32 临时 C×T×H×W × 4B ← 隐式扩容
reduce/sqrt/div 遍历 C 维
写 y (BF16) C×T×H×W × 2B
算术强度极低(每元素 O(1) FLOP,多次内存 pass),故 profile 中 RMS 占 ~28% 且与 Conv 一样是 memory-bound。
TRT profile 中的名字
| Profile 子串 | 对应操作 |
|---|---|
MulSumSqrtMax / MulSumSqrtMaxMin | 平方 → 沿 C 求和 → sqrt → 归一化除法 |
TranReshMoveMean | layout 变换 + reduce(大 tensor 上尤其慢,§8.4 Top 4 各 ~24–28 ms/iter) |
ReplDivMulMulAddSilu | 常与 norm + SiLU + 残差 融合在同一 mega-kernel |
每个 WanResidualBlock 含 norm1、norm2 两次 RMS;encoder 堆叠数十次,RMS 总成本显著。
10.3 WanCausalConv3d:计算过程
标准 Conv3D
给定输入 x[B,C_in,T,H,W]、权重 W[C_out,C_in,kT,kH,kW]、偏置 b[C_out]:
y[b, c_out, t', h', w'] = b[c_out]
+ Σ_{c_in, kt, kh, kw}
W[c_out, c_in, kt, kh, kw] · x[b, c_in, t'+·, h'+·, w'+·]
其中 · 由 stride / padding / dilation 决定输出坐标。Wan 主体 kernel 为 3×3×3(kT=kH=kW=3),padding=1(空间对称),时间维因果。
因果(Causal)padding
WanCausalConv3d 将时间维 padding 改为 只看过去帧:
标准 symmetric pad (T) → 因果 pad:仅在 t 负方向补零 / 补 cache
self._padding = (W左, W右, H上, H下, 2×T_pad, 0) # 过去侧 2×pad,未来侧 0
若提供 cache_x(上一时间块的末帧特征),则 concat 到 t 维前面 再 pad,保证 streaming 推理与整段 17 帧一次 encode 数学等价。
Policy TRT 静态一次 encode 时,export 仍把 cache 逻辑 展开 为 conv_1 … conv_4 多个固定 Conv 节点(§8.5),产生额外 launch,不是算法多算,而是 图展开代价。
Conv3D 与 GEMM 的关系
cuDNN / TensorRT 内部常把 Conv 化为 GEMM(通用矩阵乘):
im2col(或 implicit GEMM):
输入 patch 矩阵 A : M × K
M = B · T' · H' · W' (每个输出时空点一行)
K = C_in · kT · kH · kW (每个 patch 拉成向量)
权重矩阵 B_w : K × N
N = C_out
输出 : C = A · B_w (M × N,即 y reshape 后)
对 L0 一个 conv1(96→96,3³,T=17,H=480,W=832)粗算:
M ≈ 1 × 17 × 480 × 832 ≈ 6.8×10^6
K ≈ 96 × 27 ≈ 2.6×10^3
N ≈ 96
GEMM FLOPs ≈ 2·M·K·N ≈ 3.3×10^11 (~330 GFLOP)
激活读写 ≈ 2 × 1.3 GB(输入+输出 feature map,BF16)
运算强度 AI = FLOPs / Bytes ≈ 330e9 / (2.6e9) ≈ 130 FLOP/B(仍偏低,Jetson 带宽顶约数百 FLOP/B 量级)→ memory-bound。
Tensor Core 路径会把 GEMM 放在 16×16×16 tile 上;但 im2col 展开本身 会增大临时 buffer(计入 context memory §2)。
输入 / 输出小结(单个 Conv3D 层)
| Shape(L0 例) | 字节 (BF16) | |
|---|---|---|
| 输入 x | [1, 96, 17, 480, 832] | ~1.3 GB |
| 权重 W | [96, 96, 3, 3, 3] | ~0.5 MB |
| 偏置 | [96] | 可忽略 |
| 输出 y | [1, 96, 17, 480, 832] | ~1.3 GB |
| workspace | cuDNN 算法相关 | 可达数百 MB(build 时 workspace_mb 上限) |
权重很小、激活巨大——这与 engine 297MB vs context 2.3GB(§1)一致。
10.4 WanResidualBlock:Conv 与 RMS 如何串联
输入 x [B,C_in,T,H,W]
│
├─ conv_shortcut (1×1×1 CausalConv3d) ──────────────────────────────┐
│ │
└─ norm1 (RMS) → SiLU → conv1 (3³ CausalConv3d) │
→ norm2 (RMS) → SiLU → Dropout → conv2 (3³) │
(+) → 输出
- 2× RMS + 2× Conv3D(3³)+ 1× shortcut Conv(可能 1³) 每个 resblock
- SiLU:
x * sigmoid(x),elementwise,profile 中并入ReplDivMulMulAddSilu融合 kernel - 残差
x + h需 同时持有 主路径与 shortcut 输出 → 峰值 activation ≈ 2×(§3.3)
L0 每个 resblock 4 份 tiled conv(时间展开)× 2 个 conv 层 ≈ profile 中看到的 conv1_1…conv1_4、conv2_1…conv2_4。
10.5 下采样路径(Downsample RMS 为何特别慢)
WanResample(mode="downsample3d"):
x [B,C,T,H,W]
→ permute+reshape → [B·T, C, H, W]
→ ZeroPad2d + Conv2d stride 2 # 空间 ÷2
→ reshape 回 [B,C,T,H/2,W/2]
→ time_conv: CausalConv3d stride (2,1,1) # 时间维可选 ÷2
→ 前后各有 WanRMS_norm
空间下采样前后 feature map 仍大(例如 480×832→240×416,T 仍为 17),RMS 在 大 H×W 上全通道 reduce → §8.4 中 4 个 TranReshMoveMean 各 ~25 ms/iter,合计 ~22% 总时间。
10.6 算子 ↔ Profile ↔ 优化映射
| 算子 | 是否 GEMM | Profile 占比 | 主要瓶颈 | 有效优化 |
|---|---|---|---|---|
| CausalConv3d 3³ | 内部 im2col+GEMM | ~54% | 大 T×H×W activation 读写 | 降分辨率/帧数;INT8/NVFP4;CausalConv plugin 减 launch |
| WanRMS_norm | 否(reduce+scale) | ~28% | FP32 normalize 双遍读写 | 融合已较多;量化;降 spatial size |
| SiLU + 残差 | 否 | ~18% | elementwise 带宽 | TRT 已融合;边际有限 |
| Layout | 否 | <1% | — | — |
mid_block 含 WanAttentionBlock:reshape 为 [B·T, C, H, W] 后 Conv2d 1×1 做 QKV(小 GEMM)+ attention;Policy profile 中 mid 仅 ~1.7%,不是热点。
10.7 与 Roofline 的直觉对照
参见 docs/optimizer/thor_roofline.md:
Conv3D L0: 中等 FLOPs、超大 Bytes → 落在 memory-roof 斜线下方
RMS Norm: 极低 FLOPs、超大 Bytes → 更偏 memory-bound
因此 降 H×W×T 同时缩小 Conv 与 RMS 成本(近似线性~超线性);BF16→FP16 不改变 Bytes,Roofline 横轴不变。
11. 小结
- Engine 文件大小 ≈ 权重 + TRT plan;Context memory ≈ 激活 + scratch。
- Wan 3D VAE encode 在
[1,3,17,480,832]上,中间 feature map 可达 GB 级,远大于 ~300MB 权重。 - Policy infer 中 vae_encode 只跑 1 次;dit 去噪环占 算力与权重 主导,vae 占 激活峰值 主导。
- Thor 上
vae_encode~2.3GB context + ~300MB 权重 ≈ 2.6GB+ 为正常现象。 - Layer profile(§8):单次 encode ~460 ms/iter;~48% 在全分辨率 L0 + 下采样 RMS;358 kernel/iter;memory-bound。
- BF16 与 FP16 同为 16 bit(§9),互换不会减半带宽;降带宽靠 INT8/NVFP4 或 缩小输入 canvas。
- Conv3D(§10.3) 内部是 im2col+GEMM,但受 GB 级 activation 限制;RMS(§10.2) 非 GEMM,FP32 reduce 访存密集,两者共占 profile ~81%。
更多推荐
所有评论(0)