跳到内容

并行加速指南

本指南介绍了如何在 vLLM-Omni 中使用并行方法来加速扩散模型推理,以及减少每个设备的内存需求。

概述

vLLM-Omni 目前支持以下并行方法:

  1. DeepSpeed Ulysses 序列并行 (Ulysses-SP) (论文): Ulysses-SP 沿序列维度分割输入,并使用 all-to-all 通信,让每个设备只计算一部分注意力头。

下表显示了并行方法目前支持的模型

模型 模型标识符 Ulysses-SP
Qwen-Image Qwen/Qwen-Image
Z-Image Tongyi-MAI/Z-Image-Turbo
Qwen-Image-Edit Qwen/Qwen-Image-Edit

序列并行

Ulysses-SP

快速入门

下面展示了一个使用 Ulysses-SP 的示例

from vllm_omni import Omni
from vllm_omni.diffusion.data import DiffusionParallelConfig
ulysses_degree = 2

omni = Omni(
    model="Qwen/Qwen-Image",
    parallel_config=DiffusionParallelConfig(ulysses_degree=2)
)

outputs = omni.generate(prompt="A cat sitting on a windowsill", num_inference_steps=50, width=2048, height=2048)

请参阅 examples/offline_inference/text_to_image/text_to_image.py 获取完整的可运行示例。

在线服务

您可以通过 --usp 在线服务中启用 Ulysses-SP 来进行扩散模型推理。

# Text-to-image (requires >= 2 GPUs)
vllm serve Qwen/Qwen-Image --omni --port 8091 --usp 2
基准测试

基准测试免责声明

这些基准测试仅供**一般参考**。所示配置使用了默认或常用的参数设置,并未经过详尽优化以达到最佳性能。实际性能可能因以下因素而异:

  • 特定的模型和用例
  • 硬件配置
  • 仔细的参数调优
  • 不同的推理设置(例如,步数、图像分辨率)

为了衡量并行方法的效果,我们使用 **Qwen/Qwen-Image** 模型生成图像(**2048x2048** 作为长序列输入),进行 50 次推理步。硬件设备为 NVIDIA H800 GPU。sdpa 是注意力后端。

配置 Ulysses 度 生成时间 加速比
基线 (diffusers) - 112.5s 1.0x
Ulysses-SP 2 65.2s 1.73x
Ulysses-SP 4 39.6s 2.84x
Ulysses-SP 8 30.8s 3.65x
如何并行化新模型

如果某个扩散模型已部署在 vLLM-Omni 中并支持单卡推理,您可以参考以下说明,了解如何使用 Ulysses-SP 对该模型进行并行化。

首先,请编辑 TransformerModelforward 函数,位于 xxx_model_transformer.py 文件中,使输入(图像隐藏状态、位置嵌入等)成为沿序列维度分割的块。以 qwen_image_transformer.py 为例:

class QwenImageTransformer2DModel(nn.Module):
    def forward(
        self,
        hidden_states: torch.Tensor,
        encoder_hidden_states: torch.Tensor = None,
        ...
    ):
+   if self.parallel_config.sequence_parallel_size > 1:
+       hidden_states = torch.chunk(hidden_states, get_sequence_parallel_world_size(), dim=-2)[
+           get_sequence_parallel_rank()
+      ]

    hidden_states = self.img_in(hidden_states)

    ...
    image_rotary_emb = self.pos_embed(img_shapes, txt_seq_lens, device=hidden_states.device)

+   def get_rotary_emb_chunk(freqs):
+       freqs = torch.chunk(freqs, get_sequence_parallel_world_size(), dim=0)[get_sequence_parallel_rank()]
+       return freqs

+   if self.parallel_config.sequence_parallel_size > 1:
+       img_freqs, txt_freqs = image_rotary_emb
+       img_freqs = get_rotary_emb_chunk(img_freqs)
+       image_rotary_emb = (img_freqs, txt_freqs)

接下来,在 forward 函数的末尾,请调用 get_sp_group().all_gather 来聚合跨设备的块状输出,并在序列维度上将它们连接起来。

class QwenImageTransformer2DModel(nn.Module):
    def forward(
        self,
        hidden_states: torch.Tensor,
        encoder_hidden_states: torch.Tensor = None,
        ...
    ):
    # Use only the image part (hidden_states) from the dual-stream blocks
    hidden_states = self.norm_out(hidden_states, temb)
    output = self.proj_out(hidden_states)

+   if self.parallel_config.sequence_parallel_size > 1:
+       output = get_sp_group().all_gather(output, dim=-2)
    return Transformer2DModelOutput(sample=output)

最后,您可以设置并行配置并将其传递给 Omni,然后开始并行推理:

from vllm_omni import Omni
+from vllm_omni.diffusion.data import DiffusionParallelConfig
ulysses_degree = 2

omni = Omni(
    model="Qwen/Qwen-Image",
+    parallel_config=DiffusionParallelConfig(ulysses_degree=2)
)

outputs = omni.generate(prompt="A cat sitting on a windowsill", num_inference_steps=50)