Prhub

#38020 [Diffusion] Port the Wan VAE decoder fast paths to the Qwen-Image VAE

原始 PR 作者 Dayuxiaoshui 合并时间 2026-09-05 18:02 文件变更 5 提交数 2 评论 1 代码增减 +446 / -127

执行摘要

将 Wan VAE 解码器快速路径(损失精确的融合卷积缓存、质量门控的 RMSNorm+SiLU 融合与通道最后布局优化)完整移植到 Qwen-Image VAE,消除大量低效算子启动,解码性能提升约 6.5%。

PR body 明确指出:'The Qwen-Image VAE is the Wan 2.1 VAE under other class names, but none of the Wan VAE decoder fast paths that already live in main reached it',并引用了在 H200 上的性能分析数据:'78 aten::copy_ and 30 fill_ launches per decode from the per-conv clone / cat / F.pad / relayout bookkeeping, plus a fully eager norm chain'。目标是消除这些已知的性能浪费,并修复发现的布局问题。

该 PR 值得精读。理由:

1) 设计质量高:清晰地区分了损失精确(无条件安装)和质量门控两条路径,权衡了性能与精度。将 Wan 特定逻辑重构为通用安装器是良好的工程实践。
2) 实现严谨:修复了一个隐蔽的布局问题(退化步长导致 NCHW 路径),并提供了详尽的精度/性能基准测试数据。
3) 测试充分:6 个新测试用例覆盖了关键功能点,且与源码变更紧密对应。值得关注的设计决策包括:使用 as_strided 修复布局而非复制数据(纯视图操作),以及将快速路径安装逻辑集中到一个通用函数以支持多模型。风险点主要在于质量门控路径引入的微小数值差异,但这在扩散模型视觉输出的可接受范围内。

讨论亮点

Review 仅有一条来自 BBuf 的 CI 链接评论并直接批准合并,表明审查重点在于功能正确性验证而非设计争议。PR body 中提供了详尽的精度和性能对比数据,包括位精确性验证(torch.equal)、PSNR/SSIM 对比、内核启动数统计和内存峰值测量,这些数据是决策的关键依据,减少了审查过程中的讨论需求。核心设计决策(损失精确路径 vs. 质量门控路径)已在 PR 描述中清晰阐述并基于测量结果支持。

实现拆解

  1. 重构快速路径安装器:在 wan_vae_cuda_opt.py 中,将原有的 maybe_optimize_wan_vae 拆分为 _maybe_optimize_wan_family_vae 通用安装函数和 maybe_optimize_wan_vae/maybe_optimize_qwen_image_vae 两个模型特定入口。_install_norm_silu 函数被参数化,接受 residual_block_cls、rms_norm_cls 和 label,使其可同时处理 Wan 和 Qwen-Image 两种不同的残差块和归一化层类型。新增了 _install_channels_last_upsample 函数用于包装上采样模块,以及 _install_module_gates 函数用于将门控传递给布局敏感模块(如注意力块)。
  2. 为 Qwen-Image VAE 添加损失精确的因果卷积融合:在 autoencoder_kl_qwenimage.py 中新增了 _fused_conv_cache_supported 检查函数和 _run_cached_causal_conv 辅助函数。后者统一处理所有因果卷积层的缓存逻辑,当条件满足(权重为 channels_last_3d、CUDA 平台、非 torch.compile)时,调用 cat_pad_channels_last_3d 内核将输入连接、填充和布局转换一步完成,并产出紧凑缓存。该函数被应用于 QwenImageResidualBlock 的 conv1/conv2、QwenImageDecoder3d 的 conv_in/conv_out 以及 QwenImageResample 的 time_conv。
  3. 应用质量门控的快速路径:maybe_optimize_qwen_image_vae 函数被注册到 platforms/cuda.py::optimize_vae。当门控开启时,安装 FusedWanRMSNormSiLU(复用现有 Triton 内核)替换所有 QwenImageRMS_norm -> SiLU 链;安装 GatedChannelsLastUpsample 解决上采样时的退化步长问题,确保 Conv2d 在 channels_last 格式下运行。QwenImageAttentionBlock 的残差连接也被门控,以保持 channels_last_3d 布局。
  4. 补充测试与文档:在 test_model_fast_paths.py 中新增 6 个测试用例,覆盖融合卷积的位精确性、缓存逻辑一致性、上采样优化、门控分发、布局保持以及安装后的整体行为。更新了 existing-fast-paths.md 技能文档。
文件 模块 状态 重要度
python/sglang/multimodal_gen/runtime/models/vaes/wan_vae_cuda_opt.py VAE 快速路径安装器 modified 8.95
python/sglang/multimodal_gen/runtime/models/vaes/autoencoder_kl_qwenimage.py Qwen-Image VAE 模型 modified 8.54
test/registered/kernels/ops/diffusion/test_model_fast_paths.py 快速路径测试 modified 7.86

关键符号

_maybe_optimize_wan_family_vae maybe_optimize_qwen_image_vae _install_norm_silu _install_channels_last_upsample _install_module_gates GatedChannelsLastUpsample.forward _run_cached_causal_conv _fused_conv_cache_supported

关键源码片段

python/sglang/multimodal_gen/runtime/models/vaes/wan_vae_cuda_opt.py core-logic

快速路径安装器的核心重构文件,新增通用安装函数 `GatedChannelsLastUpsample`、`_install_channels_last_upsample`、`maybe_optimize_qwen_image_vae`,并参数化了 `_install_norm_silu`,是本次架构调整的核心。

def _maybe_optimize_wan_family_vae(
    vae: nn.Module,
    *,
    decoder_cls: type,
    residual_block_cls: type,
    rms_norm_cls: type,
    label: str,
    upsample_cls: type | None = None,
    gated_module_classes: tuple[type, ...] = (),
) -> nn.Module:
    """通用 Wan 系列 VAE 安装器:安装质量门控的 CUDA 快速路径。
​
    通过类型参数支持不同模型(Wan 和 Qwen-Image),实现逻辑复用。
    """
    decoder = getattr(vae, "decoder", None)
    if type(decoder) is not decoder_cls:
        return vae
    if decoder.use_parallel_decode and decoder.world_size > 1:
        logger.info("%s: spatial-parallel decode; skipping fast path.", label)
        return vae
    if not _HAS_TRITON:
        logger.warning("%s: Triton unavailable; skipping fast path.", label)
        return vae
​
    gate = VaeFastPathGate()
    n_norm = _install_norm_silu(
        decoder,
        gate,
        residual_block_cls=residual_block_cls,
        rms_norm_cls=rms_norm_cls,
        label=label,
    )
    if n_norm is None:
        return vae
    n_up = 0
    if upsample_cls is not None:
        # 安装通道最后布局上采样包装器,修复退化步长问题
        n_up = _install_channels_last_upsample(decoder, gate, upsample_cls)
    if gated_module_classes:
        # 将门控传递给布局敏感模块(如注意力块),以保持 channels_last_3d 布局
        _install_module_gates(decoder, gate, gated_module_classes)
    register_vae_fast_path_gate(vae, gate)
    logger.info(
        "%s: installed quality-gated fast path (%d RMSNorm+SiLU fusions, "
        "%d channels_last upsamples).",
        label,
        n_norm,
        n_up,
    )
    return vae
​
​
class GatedChannelsLastUpsample(nn.Module):
    """质量门控的通道最后布局上采样包装器。
​
    解决因 `b * t == 1` 时退化步长导致 `Conv2d` 走 NCHW 路径的问题。
    门控开启时,使用 `as_strided` 重新标记视图以强制 NHWC 布局。
    """
​
    def __init__(self, upsample: nn.Upsample, gate: VaeFastPathGate) -> None:
        super().__init__()
        self._sgl_upsample = upsample
        self._sgl_gate = gate
​
    def forward(self, x: torch.Tensor) -> torch.Tensor:
        up = self._sgl_upsample
        if torch.compiler.is_compiling() or x.dim() != 4:
            return up(x)
        _, c, h, w = x.shape
        canonical = (h * w * c, 1, w * c, c)
        # 仅当门控开启、步长不规范且已是 channels_last 时,修正布局
        if (
            self._sgl_gate.enabled
            and x.stride() != canonical
            and x.is_contiguous(memory_format=torch.channels_last)
        ):
            x = x.as_strided(x.shape, canonical) # 纯视图,无拷贝
        return up(x)
python/sglang/multimodal_gen/runtime/models/vaes/autoencoder_kl_qwenimage.py data-contract

Qwen-Image VAE 模型定义文件,新增了损失精确的因果卷积融合逻辑(`_fused_conv_cache_supported`, `_run_cached_causal_conv`)和模型特定的优化应用(如 `QwenImageUpsample` 跳过 fp32 往返),是性能提升的直接来源。

def _run_cached_causal_conv(
    conv: nn.Module,
    x: torch.Tensor,
    cache_list: list,
    idx: int,
) -> torch.Tensor:
    """统一的因果卷积缓存辅助函数,替代重复的 clone/cat/pad 逻辑。
​
    当权重为 channels_last_3d 且在 CUDA 平台上时,调用 cat_pad_channels_last_3d 内核
    将输入连接、填充和布局转换一步完成,并产出紧凑缓存,实现损失精确的优化。
    """
    cache = cache_list[idx]
    is_rep = isinstance(cache, str) # "Rep" 标记来自 QwenImageResample
    payload = None if is_rep else cache
    if _fused_conv_cache_supported(conv, x) and (
        payload is None or (payload.device == x.device and payload.dtype == x.dtype)
    ):
        # 计算需要保留的缓存帧数:首帧片段仅保留自身尾部,后续片段和 Rep 槽保留 CACHE_T 帧
        keep_t = (
            CACHE_T if (payload is not None or is_rep) else min(CACHE_T, x.shape[2])
        )
        pair = cat_pad_channels_last_3d(x, payload, conv._padding, keep_cache_t=keep_t)
        if pair is not None:
            inp, cache_list[idx] = pair
            return nn.Conv3d.forward(conv, inp) # 使用融合输入执行卷积
    # 原始 aten 路径(位精确的逻辑副本)
    cache_x = x[:, :, -CACHE_T:, :, :].clone()
    if cache_x.shape[2] < 2 and payload is not None:
        cache_x = torch.cat(
            [payload[:, :, -1, :, :].unsqueeze(2).to(cache_x.device), cache_x], dim=2
        )
    elif cache_x.shape[2] < 2 and is_rep:
        cache_x = torch.cat(
            [torch.zeros_like(cache_x).to(cache_x.device), cache_x], dim=2
        )
    out = conv(x) if payload is None else conv(x, payload)
    cache_list[idx] = cache_x
    return out
​
​
class QwenImageUpsample(nn.Upsample):
    # ... 省略 ...
    def forward(self, x):
        if current_platform.is_amp_supported():
            # nearest-exact 是纯 gather 操作,bf16/fp16 输入与 fp32 往返结果位精确
            # 跳过 fp32 转换可消除三次全尺寸拷贝
            return super().forward(x)
        return super().forward(x.float()).type_as(x)

评论区精华

没有提炼出高价值讨论线程

当前评论区没有形成足够清晰的争议点或结论,后续有更多讨论时会体现在这里。

风险与影响

布局变化的质量影响:质量门控路径(quality="high")通过 as_strided 重新标记退化步长的张量以保持 channels_last 布局,这可能导致 cuDNN 选择不同的算法,造成非位精确的数值差异(测量显示与损失精确路径的 PSNR 为 37.58 dB)。静默回退机制:_run_cached_causal_conv 和 QwenImageCausalConv3d.forward 在内核不可用或条件不满足时会静默回退到原始 aten 链,这保证了正确性但可能隐藏性能问题。依赖特定内核:优化强依赖于 cat_pad_channels_last_3d Triton 内核的存在;如果内核未编译或导入失败,所有融合路径将静默失效。测试覆盖边界:测试主要针对小型解码器(dim=16),真实大模型(Qwen/Qwen-Image VAE 权重)的端到端行为由 PR 作者在外部验证,仓库内测试未覆盖。

用户影响:直接提升使用 Qwen-Image VAE 模型进行图像生成和编辑的推理性能,特别是在少步数(如 Lightning 变体)、批处理或高分辨率(2K)场景下。系统影响:建立了 Wan 系列 VAE(包括 Qwen-Image)的通用 CUDA 快速路径安装框架,降低了未来添加类似优化的门槛。在 H200 上,1024x1024 bf16 解码 GPU 内核时间从 56.14ms 降至 52.41ms(损失精确)或 44.61ms(高质量),内核数量减少 16% 至 58%。团队影响:提供了高质量的优化实现范例(清晰的路径分离、完善的测试、详尽的文档),可作为后续 VAE 性能优化的参考。

布局变化影响数值精度 依赖特定 Triton 内核 优化逻辑静默回退

关联 Issue

未识别关联 Issue

当前没有检测到明确关联的 Issue 链接,后续同步到相关引用后会出现在这里。

完整报告

参与讨论