Skip to content

platform#1223

Merged
helloyongyang merged 7 commits into
mainfrom
wan-sf_platform
Jul 4, 2026
Merged

platform#1223
helloyongyang merged 7 commits into
mainfrom
wan-sf_platform

Conversation

@Watebear

@Watebear Watebear commented Jul 3, 2026

Copy link
Copy Markdown
Collaborator

No description provided.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces sequence parallel support for the LongCat image model, optimizes precision for domestic hardware platforms (using float32 instead of float64 in Wan's pre-inference), and implements platform-specific padding workarounds for bfloat16 on NPUs/MLUs. It also refines attention modules and adds configuration options to disable distributed VAE decoding. The review feedback highlights several critical issues: a performance bottleneck in _infer_token_refiner_attn due to batch-looping and CPU-GPU synchronization, potential runtime crashes in wan_sf_runner.py on non-GPU platforms, a missing fallback check for flash_attn_func_v2 in the worldmirror attention layer, and an invalid value argument being passed to F.pad when using non-constant padding modes.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +168 to +196
def _infer_token_refiner_attn(self, attention_module, q, k, v, mask):
if mask is None:
mask = torch.ones(q.shape[:2], dtype=torch.bool, device=q.device)
else:
mask = mask.to(device=q.device, dtype=torch.bool)
if mask.ndim == 1:
mask = mask.unsqueeze(0)
if mask.shape[0] == 1 and q.shape[0] > 1:
mask = mask.expand(q.shape[0], -1)

batch, seqlen, heads, head_dim = q.shape
out = torch.zeros(batch, seqlen, heads * head_dim, dtype=q.dtype, device=q.device)
for batch_idx in range(batch):
valid_mask = mask[batch_idx]
valid_len = int(valid_mask.sum().item())
if valid_len == 0:
continue
cu_seqlens = torch.tensor([0, valid_len], dtype=torch.int32, device=q.device)
attn = attention_module.apply(
q=q[batch_idx, valid_mask].contiguous(),
k=k[batch_idx, valid_mask].contiguous(),
v=v[batch_idx, valid_mask].contiguous(),
cu_seqlens_q=cu_seqlens,
cu_seqlens_kv=cu_seqlens,
max_seqlen_q=valid_len,
max_seqlen_kv=valid_len,
)
out[batch_idx, valid_mask] = attn.reshape(valid_len, -1)
return out

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The current implementation of _infer_token_refiner_attn loops over the batch dimension and performs CPU-GPU synchronization (valid_mask.sum().item()) inside the loop. This can introduce significant performance overhead on GPU/NPU platforms.

Since we are using unpadded/varlen attention, we can vectorize this operation across the entire batch by concatenating the valid tokens and constructing a single cu_seqlens tensor for the whole batch. This avoids the Python loop and reduces CPU-GPU synchronization to a single .item() call.

    def _infer_token_refiner_attn(self, attention_module, q, k, v, mask):
        if mask is None:
            mask = torch.ones(q.shape[:2], dtype=torch.bool, device=q.device)
        else:
            mask = mask.to(device=q.device, dtype=torch.bool)
            if mask.ndim == 1:
                mask = mask.unsqueeze(0)
            if mask.shape[0] == 1 and q.shape[0] > 1:
                mask = mask.expand(q.shape[0], -1)

        batch, seqlen, heads, head_dim = q.shape
        out = torch.zeros(batch, seqlen, heads * head_dim, dtype=q.dtype, device=q.device)
        
        valid_lens = mask.sum(dim=-1)
        max_len = int(valid_lens.max().item())
        if max_len == 0:
            return out

        cu_seqlens = torch.zeros(batch + 1, dtype=torch.int32, device=q.device)
        cu_seqlens[1:] = valid_lens.cumsum(dim=0, dtype=torch.int32)

        q_valid = q[mask].contiguous()
        k_valid = k[mask].contiguous()
        v_valid = v[mask].contiguous()

        attn = attention_module.apply(
            q=q_valid,
            k=k_valid,
            v=v_valid,
            cu_seqlens_q=cu_seqlens,
            cu_seqlens_kv=cu_seqlens,
            max_seqlen_q=max_len,
            max_seqlen_kv=max_len,
        )
        out[mask] = attn.reshape(-1, heads * head_dim)
        return out

from lightx2v.utils.video_recorder import VideoRecorder
from lightx2v_platform.base.global_var import AI_DEVICE

torch_device_module = getattr(torch, AI_DEVICE)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Unconditionally calling getattr(torch, AI_DEVICE) can raise an AttributeError if AI_DEVICE is set to "cpu" or any other platform where the corresponding module does not exist in torch. Additionally, calling torch_device_module.empty_cache() will crash if the module does not have an empty_cache attribute. We should wrap this in a safe helper class to prevent runtime crashes on non-GPU/NPU platforms.

class _DeviceModule:
    @staticmethod
    def empty_cache():
        device_module = getattr(torch, AI_DEVICE, None)
        if device_module is not None and hasattr(device_module, "empty_cache"):
            device_module.empty_cache()

torch_device_module = _DeviceModule()

Comment on lines +14 to +17
try:
from flash_attn.flash_attn_interface import flash_attn_func as flash_attn_func_v2
except ImportError:
flash_attn_func_v2 = None

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Setting flash_attn_func_v2 = None when flash_attn is not installed will cause a runtime crash in _apply_attention if q.dtype is float16 or bfloat16, because _apply_attention unconditionally calls flash_attn_func_v2 in that branch. Please ensure that _apply_attention is updated to check if flash_attn_func_v2 is not None before calling it, falling back to F.scaled_dot_product_attention if it is unavailable.

Comment on lines +40 to +43
def _pad(x: torch.Tensor, pad, mode: str = "constant", value: float = 0.0) -> torch.Tensor:
if mode == "replicate" and PLATFORM in _REPLICATE_PAD_FLOAT32_PLATFORMS and x.dtype == torch.bfloat16:
return F.pad(x.float(), pad, mode=mode, value=value).to(dtype=x.dtype)
return F.pad(x, pad, mode=mode, value=value)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

In PyTorch, F.pad only supports the value argument when mode is "constant". Passing value when mode is "replicate" can raise a ValueError or warning depending on the PyTorch version. We should only pass value when mode == "constant".

Suggested change
def _pad(x: torch.Tensor, pad, mode: str = "constant", value: float = 0.0) -> torch.Tensor:
if mode == "replicate" and PLATFORM in _REPLICATE_PAD_FLOAT32_PLATFORMS and x.dtype == torch.bfloat16:
return F.pad(x.float(), pad, mode=mode, value=value).to(dtype=x.dtype)
return F.pad(x, pad, mode=mode, value=value)
def _pad(x: torch.Tensor, pad, mode: str = "constant", value: float = 0.0) -> torch.Tensor:
if mode == "replicate" and PLATFORM in _REPLICATE_PAD_FLOAT32_PLATFORMS and x.dtype == torch.bfloat16:
return F.pad(x.float(), pad, mode=mode).to(dtype=x.dtype)
if mode == "constant":
return F.pad(x, pad, mode=mode, value=value)
return F.pad(x, pad, mode=mode)

@helloyongyang
helloyongyang merged commit c0fb09a into main Jul 4, 2026
4 checks passed
@helloyongyang
helloyongyang deleted the wan-sf_platform branch July 4, 2026 06:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants