[diffusion] feat: support efficient sequence shard (#18161)
This commit is contained in:
@@ -343,6 +343,8 @@ class PipelineConfig:
|
|||||||
|
|
||||||
def shard_latents_for_sp(self, batch, latents):
|
def shard_latents_for_sp(self, batch, latents):
|
||||||
# general logic for video models
|
# general logic for video models
|
||||||
|
if batch.enable_sequence_shard:
|
||||||
|
return latents, False
|
||||||
sp_world_size, rank_in_sp_group = get_sp_world_size(), get_sp_parallel_rank()
|
sp_world_size, rank_in_sp_group = get_sp_world_size(), get_sp_parallel_rank()
|
||||||
if latents.dim() != 5:
|
if latents.dim() != 5:
|
||||||
return latents, False
|
return latents, False
|
||||||
|
|||||||
@@ -154,6 +154,7 @@ class SamplingParams:
|
|||||||
suppress_logs: bool = False
|
suppress_logs: bool = False
|
||||||
|
|
||||||
return_file_paths_only: bool = True
|
return_file_paths_only: bool = True
|
||||||
|
enable_sequence_shard: bool = False
|
||||||
|
|
||||||
def _set_output_file_ext(self):
|
def _set_output_file_ext(self):
|
||||||
# add extension if needed
|
# add extension if needed
|
||||||
@@ -367,6 +368,12 @@ class SamplingParams:
|
|||||||
)
|
)
|
||||||
logger.warning(error_msg)
|
logger.warning(error_msg)
|
||||||
|
|
||||||
|
if self.enable_sequence_shard:
|
||||||
|
self.adjust_frames = False
|
||||||
|
logger.info(
|
||||||
|
f"Sequence dimension shard is enabled, disabling frame adjustment"
|
||||||
|
)
|
||||||
|
|
||||||
if pipeline_config.task_type.is_image_gen():
|
if pipeline_config.task_type.is_image_gen():
|
||||||
# settle num_frames
|
# settle num_frames
|
||||||
if not server_args.pipeline_config.allow_set_num_frames():
|
if not server_args.pipeline_config.allow_set_num_frames():
|
||||||
@@ -746,6 +753,12 @@ class SamplingParams:
|
|||||||
default=SamplingParams.return_file_paths_only,
|
default=SamplingParams.return_file_paths_only,
|
||||||
help="If set, output file will be saved early to get a performance boost, while output tensors will not be returned.",
|
help="If set, output file will be saved early to get a performance boost, while output tensors will not be returned.",
|
||||||
)
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--enable-sequence-shard",
|
||||||
|
action=StoreBoolean,
|
||||||
|
default=SamplingParams.enable_sequence_shard,
|
||||||
|
help="Enable sequence dimension shard with sequence parallelism.",
|
||||||
|
)
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
# SPDX-License-Identifier: Apache-2.0
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
import math
|
import math
|
||||||
|
from functools import lru_cache
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
import torch
|
import torch
|
||||||
@@ -12,8 +13,10 @@ from sglang.multimodal_gen.configs.models.dits import WanVideoConfig
|
|||||||
from sglang.multimodal_gen.configs.sample.wan import WanTeaCacheParams
|
from sglang.multimodal_gen.configs.sample.wan import WanTeaCacheParams
|
||||||
from sglang.multimodal_gen.runtime.distributed import (
|
from sglang.multimodal_gen.runtime.distributed import (
|
||||||
divide,
|
divide,
|
||||||
|
get_sp_group,
|
||||||
get_sp_world_size,
|
get_sp_world_size,
|
||||||
get_tp_world_size,
|
get_tp_world_size,
|
||||||
|
sequence_model_parallel_all_gather,
|
||||||
)
|
)
|
||||||
from sglang.multimodal_gen.runtime.layers.attention import (
|
from sglang.multimodal_gen.runtime.layers.attention import (
|
||||||
MinimalA2AAttnOp,
|
MinimalA2AAttnOp,
|
||||||
@@ -773,6 +776,29 @@ class WanTransformer3DModel(CachableDiT, OffloadableDiTMixin):
|
|||||||
|
|
||||||
self.layer_names = ["blocks"]
|
self.layer_names = ["blocks"]
|
||||||
|
|
||||||
|
@lru_cache(maxsize=1)
|
||||||
|
def _compute_rope_for_sequence_shard(
|
||||||
|
self,
|
||||||
|
local_len: int,
|
||||||
|
rank: int,
|
||||||
|
frame_stride_local: int,
|
||||||
|
width_local: int,
|
||||||
|
device: torch.device,
|
||||||
|
) -> tuple[torch.Tensor, torch.Tensor]:
|
||||||
|
token_start = rank * local_len
|
||||||
|
token_indices = torch.arange(
|
||||||
|
token_start,
|
||||||
|
token_start + local_len,
|
||||||
|
device=device,
|
||||||
|
dtype=torch.long,
|
||||||
|
)
|
||||||
|
t_idx = token_indices // frame_stride_local
|
||||||
|
rem = token_indices % frame_stride_local
|
||||||
|
h_idx = rem // width_local
|
||||||
|
w_idx = rem % width_local
|
||||||
|
positions = torch.stack((t_idx, h_idx, w_idx), dim=1)
|
||||||
|
return self.rotary_emb.forward_uncached(positions)
|
||||||
|
|
||||||
def forward(
|
def forward(
|
||||||
self,
|
self,
|
||||||
hidden_states: torch.Tensor,
|
hidden_states: torch.Tensor,
|
||||||
@@ -783,6 +809,12 @@ class WanTransformer3DModel(CachableDiT, OffloadableDiTMixin):
|
|||||||
**kwargs,
|
**kwargs,
|
||||||
) -> torch.Tensor:
|
) -> torch.Tensor:
|
||||||
forward_batch = get_forward_context().forward_batch
|
forward_batch = get_forward_context().forward_batch
|
||||||
|
if forward_batch is not None:
|
||||||
|
sequence_shard_enabled = (
|
||||||
|
forward_batch.enable_sequence_shard and self.sp_size > 1
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
sequence_shard_enabled = False
|
||||||
self.enable_teacache = (
|
self.enable_teacache = (
|
||||||
forward_batch is not None and forward_batch.enable_teacache
|
forward_batch is not None and forward_batch.enable_teacache
|
||||||
)
|
)
|
||||||
@@ -805,6 +837,7 @@ class WanTransformer3DModel(CachableDiT, OffloadableDiTMixin):
|
|||||||
post_patch_height = height // p_h
|
post_patch_height = height // p_h
|
||||||
post_patch_width = width // p_w
|
post_patch_width = width // p_w
|
||||||
|
|
||||||
|
if not sequence_shard_enabled:
|
||||||
# The rotary embedding layer correctly handles SP offsets internally.
|
# The rotary embedding layer correctly handles SP offsets internally.
|
||||||
freqs_cos, freqs_sin = self.rotary_emb.forward_from_grid(
|
freqs_cos, freqs_sin = self.rotary_emb.forward_from_grid(
|
||||||
(
|
(
|
||||||
@@ -819,11 +852,43 @@ class WanTransformer3DModel(CachableDiT, OffloadableDiTMixin):
|
|||||||
assert freqs_cos.dtype == torch.float32
|
assert freqs_cos.dtype == torch.float32
|
||||||
assert freqs_cos.device == hidden_states.device
|
assert freqs_cos.device == hidden_states.device
|
||||||
freqs_cis = (
|
freqs_cis = (
|
||||||
(freqs_cos.float(), freqs_sin.float()) if freqs_cos is not None else None
|
(freqs_cos.float(), freqs_sin.float())
|
||||||
|
if freqs_cos is not None
|
||||||
|
else None
|
||||||
)
|
)
|
||||||
|
|
||||||
hidden_states = self.patch_embedding(hidden_states)
|
hidden_states = self.patch_embedding(hidden_states)
|
||||||
hidden_states = hidden_states.flatten(2).transpose(1, 2)
|
hidden_states = hidden_states.flatten(2).transpose(1, 2)
|
||||||
|
|
||||||
|
# shape is [B, T' * H' * W', C]
|
||||||
|
seq_len_orig = hidden_states.shape[1]
|
||||||
|
seq_shard_pad = 0
|
||||||
|
if sequence_shard_enabled:
|
||||||
|
if seq_len_orig % self.sp_size != 0:
|
||||||
|
seq_shard_pad = self.sp_size - (seq_len_orig % self.sp_size)
|
||||||
|
pad = torch.zeros(
|
||||||
|
(batch_size, seq_shard_pad, hidden_states.shape[2]),
|
||||||
|
dtype=hidden_states.dtype,
|
||||||
|
device=hidden_states.device,
|
||||||
|
)
|
||||||
|
hidden_states = torch.cat([hidden_states, pad], dim=1)
|
||||||
|
sp_rank = get_sp_group().rank_in_group
|
||||||
|
local_seq_len = hidden_states.shape[1] // self.sp_size
|
||||||
|
hidden_states = hidden_states.view(
|
||||||
|
batch_size, self.sp_size, local_seq_len, hidden_states.shape[2]
|
||||||
|
)
|
||||||
|
hidden_states = hidden_states[:, sp_rank, :, :]
|
||||||
|
|
||||||
|
frame_stride = post_patch_height * post_patch_width
|
||||||
|
freqs_cos, freqs_sin = self._compute_rope_for_sequence_shard(
|
||||||
|
local_seq_len,
|
||||||
|
sp_rank,
|
||||||
|
frame_stride,
|
||||||
|
post_patch_width,
|
||||||
|
hidden_states.device,
|
||||||
|
)
|
||||||
|
freqs_cis = (freqs_cos.float(), freqs_sin.float())
|
||||||
|
|
||||||
# timestep shape: batch_size, or batch_size, seq_len (wan 2.2 ti2v)
|
# timestep shape: batch_size, or batch_size, seq_len (wan 2.2 ti2v)
|
||||||
if timestep.dim() == 2:
|
if timestep.dim() == 2:
|
||||||
# ti2v
|
# ti2v
|
||||||
@@ -881,6 +946,13 @@ class WanTransformer3DModel(CachableDiT, OffloadableDiTMixin):
|
|||||||
if self.enable_teacache:
|
if self.enable_teacache:
|
||||||
self.maybe_cache_states(hidden_states, original_hidden_states)
|
self.maybe_cache_states(hidden_states, original_hidden_states)
|
||||||
self.cnt += 1
|
self.cnt += 1
|
||||||
|
|
||||||
|
if sequence_shard_enabled:
|
||||||
|
hidden_states = hidden_states.contiguous()
|
||||||
|
hidden_states = sequence_model_parallel_all_gather(hidden_states, dim=1)
|
||||||
|
if seq_shard_pad > 0:
|
||||||
|
hidden_states = hidden_states[:, :seq_len_orig, :]
|
||||||
|
|
||||||
# 5. Output norm, projection & unpatchify
|
# 5. Output norm, projection & unpatchify
|
||||||
if temb.dim() == 3:
|
if temb.dim() == 3:
|
||||||
# batch_size, seq_len, inner_dim (wan 2.2 ti2v)
|
# batch_size, seq_len, inner_dim (wan 2.2 ti2v)
|
||||||
|
|||||||
Reference in New Issue
Block a user