Files
wehub-resource-sync 94057c3d3e
PR Test (NPU) / check-changes (push) Has been cancelled
PR Test (NPU) / pr-gate (push) Has been cancelled
PR Test (NPU) / set-image-config (push) Has been cancelled
PR Test (NPU) / stage-b-test-1-npu-a2 (0) (push) Has been cancelled
PR Test (NPU) / stage-b-test-1-npu-a2 (1) (push) Has been cancelled
PR Test (NPU) / stage-b-test-2-npu-a2 (0) (push) Has been cancelled
PR Test (NPU) / stage-b-test-2-npu-a2 (1) (push) Has been cancelled
PR Test (NPU) / stage-b-test-4-npu-a3 (push) Has been cancelled
PR Test (NPU) / stage-b-test-16-npu-a3 (push) Has been cancelled
PR Test (NPU) / multimodal-gen-test-1-npu-a3 (push) Has been cancelled
PR Test (NPU) / multimodal-gen-test-2-npu-a3 (push) Has been cancelled
PR Test (Arm64) / pr-gate (push) Has been cancelled
PR Test (Arm64) / check-changes (push) Has been cancelled
PR Test (Arm64) / build-test (push) Has been cancelled
PR Test (sgl-router) / gate (push) Has been cancelled
PR Test (sgl-router) / tier-1 — lint (push) Has been cancelled
PR Test (sgl-router) / tier-2 — build + test (push) Has been cancelled
PR Test (sgl-router) / tier-3 — docker (placeholder) (push) Has been cancelled
PR Test (sgl-router) / tier-3 — k8s integration (push) Has been cancelled
PR Test (sgl-router) / tier-3 — e2e (push) Has been cancelled
PR Test (sgl-router) / finish (push) Has been cancelled
PR Test (NPU) / single-node-poc (map[name:qwen3_6_27b_w8a8_1p_in64k_out1k_50ms runner:linux-aarch64-a3-2 test_case:test/registered/ascend/performance/qwen3_6_27b/test_npu_qwen3_6_27b_w8a8_1p_in64k_out1k_50ms.py test_type:perf]) (push) Has been cancelled
PR Test (NPU) / pr-test-npu-finish (push) Has been cancelled
PR Test (Xeon) / pr-gate (push) Has been cancelled
PR Test (Xeon) / check-changes (push) Has been cancelled
PR Test (Xeon) / build-test (, xeon-gnr, base-b-test-cpu) (push) Has been cancelled
PR Test (XPU) / check-changes (push) Has been cancelled
PR Test (XPU) / pr-gate (push) Has been cancelled
PR Test (XPU) / stage-a-test-1-gpu-xpu (push) Has been cancelled
PR Test (XPU) / wait-for-stage-a (push) Has been cancelled
PR Test (XPU) / stage-b-test-1-gpu-xpu (push) Has been cancelled
PR Test (XPU) / finish (push) Has been cancelled
CI Model Inventory / build-inventory (push) Has been cancelled
Lint / lint (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark Compilation Check (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark - Manual Policy (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark - Request Processing (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark Summary (push) Has been cancelled
PR Test (SMG) / build-wheel (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on windows (x86_64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on macos (x86_64 - auto) (push) Has been cancelled
PR Test (SMG) / python-unit-tests (push) Has been cancelled
PR Test (SMG) / unit-tests (push) Has been cancelled
PR Test (SMG) / benchmarks (push) Has been cancelled
PR Test (SMG) / chat-completions (push) Has been cancelled
PR Test (SMG) / chat-completions-4gpu (push) Has been cancelled
PR Test (SMG) / e2e (push) Has been cancelled
PR Test (SMG) / docker-build-test (push) Has been cancelled
PR Test (SMG) / k8s-integration (push) Has been cancelled
PR Test (SMG) / finish (push) Has been cancelled
PR Test (SMG) / summarize-benchmarks (push) Has been cancelled
Release SGLang Model Gateway Docker Image / publish (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on macos (aarch64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (aarch64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (x86_64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (aarch64 - musllinux_1_1) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (x86_64 - musllinux_1_1) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / Build SDist (push) Has been cancelled
Release SGLang Model Gateway to PyPI / Upload to PyPI (push) Has been cancelled
Release SGLang Kernels / build-cu129-matrix (aarch64, 12.9, 3.10, arm-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / build-cu129-matrix (x86_64, 12.9, 3.10, x64-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / release-cu129 (push) Has been cancelled
Release SGLang Kernels / build-cu130-matrix (aarch64, 13.0, 3.10, arm-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / build-cu130-matrix (x86_64, 13.0, 3.10, x64-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / release-cu130 (push) Has been cancelled
Release SGLang Kernels / build-rocm-matrix (3.10, 700) (push) Has been cancelled
Release SGLang Kernels / build-rocm-matrix (3.10, 720) (push) Has been cancelled
Release SGLang Kernels / release-rocm700 (push) Has been cancelled
Release SGLang Kernels / release-rocm720 (push) Has been cancelled
Release SGLang Kernels / build-musa43 (43, 3.10) (push) Has been cancelled
Release SGLang Kernels / release-musa43 (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:38:16 +08:00

802 lines
25 KiB
Python

import contextvars
import math
from contextlib import contextmanager
import torch
import torch.distributed as dist
import torch.nn as nn
import torch.nn.functional as F
from sglang.multimodal_gen.runtime.distributed.parallel_state import (
get_decode_parallel_group_coordinator,
get_decode_parallel_rank,
get_decode_parallel_world_size,
)
from sglang.multimodal_gen.runtime.platforms import current_platform
from sglang.multimodal_gen.runtime.utils.logging_utils import init_logger
logger = init_logger(__name__)
if current_platform.is_cuda():
from sglang.jit_kernel.diffusion.causal_conv3d_cat_pad import (
can_use_fused_causal_conv3d_cat_pad_cuda,
fused_causal_conv3d_cat_pad_cuda,
)
from sglang.jit_kernel.diffusion.triton.causal_conv3d_pad import (
fused_causal_conv3d_cat_pad as fused_causal_conv3d_cat_pad_triton,
)
else:
can_use_fused_causal_conv3d_cat_pad_cuda = None
fused_causal_conv3d_cat_pad_cuda = None
fused_causal_conv3d_cat_pad_triton = None
_causal_conv3d_cat_pad_cuda_failed = False
def fused_causal_conv3d_cat_pad(
x: torch.Tensor,
cache_x: torch.Tensor,
padding: list[int],
) -> torch.Tensor:
global _causal_conv3d_cat_pad_cuda_failed
if (
fused_causal_conv3d_cat_pad_cuda is not None
and can_use_fused_causal_conv3d_cat_pad_cuda(x, cache_x, padding)
and not _causal_conv3d_cat_pad_cuda_failed
):
try:
return fused_causal_conv3d_cat_pad_cuda(x, cache_x, padding)
except Exception:
logger.warning(
"fused_causal_conv3d_cat_pad_cuda failed, falling back to Triton",
exc_info=True,
)
_causal_conv3d_cat_pad_cuda_failed = True
if fused_causal_conv3d_cat_pad_triton is None:
raise RuntimeError("causal Conv3D cat/pad fusion is only available on CUDA")
return fused_causal_conv3d_cat_pad_triton(x, cache_x, padding)
_SPATIAL_PARALLEL_DECODE_DISABLED = contextvars.ContextVar(
"spatial_parallel_decode_disabled", default=False
)
@contextmanager
def disable_spatial_parallel_decode():
token = _SPATIAL_PARALLEL_DECODE_DISABLED.set(True)
try:
yield
finally:
_SPATIAL_PARALLEL_DECODE_DISABLED.reset(token)
def spatial_parallel_decode_disabled() -> bool:
return _SPATIAL_PARALLEL_DECODE_DISABLED.get()
def _tensor_pad(x: torch.Tensor, len_to_pad: int, dim: int = -2):
return torch.cat(
[
x,
torch.zeros(
*x.shape[:dim],
len_to_pad,
*x.shape[dim + 1 :],
dtype=x.dtype,
device=x.device,
),
],
dim=dim,
)
def _tensor_chunk(x: torch.Tensor, dim: int = -2, world_size: int = 1, rank: int = 0):
if x is None:
return x
if world_size <= 1:
return x
return torch.tensor_split(x, world_size, dim=dim)[rank].contiguous(
memory_format=_halo_memory_format(x)
)
def _can_fuse_causal_conv3d_cat_pad(
x: torch.Tensor,
cache_x: torch.Tensor | None,
padding: list[int],
) -> bool:
if cache_x is None or fused_causal_conv3d_cat_pad is None:
return False
if not current_platform.is_cuda():
return False
if not x.is_cuda or not x.is_contiguous() or not cache_x.is_contiguous():
return False
if x.dim() != 5 or cache_x.dim() != 5 or x.dtype != cache_x.dtype:
return False
if x.shape[0] != cache_x.shape[0] or x.shape[1] != cache_x.shape[1]:
return False
if x.shape[3:] != cache_x.shape[3:]:
return False
width_left, width_right, height_top, height_bottom, depth_left, depth_right = (
padding
)
if width_left != width_right or height_top != height_bottom or depth_right != 0:
return False
if depth_left < cache_x.shape[2]:
return False
return bool(width_left or height_top)
def causal_conv3d_cat_pad(
x: torch.Tensor,
cache_x: torch.Tensor | None,
padding: list[int],
) -> torch.Tensor:
if cache_x is not None and padding[4] > 0:
if cache_x.device != x.device:
cache_x = cache_x.to(x.device)
if _can_fuse_causal_conv3d_cat_pad(x, cache_x, padding):
return fused_causal_conv3d_cat_pad(x, cache_x, padding)
x = torch.cat([cache_x, x], dim=2)
padding[4] -= cache_x.shape[2]
if any(padding):
x = F.pad(x, padding)
return x
def split_for_parallel_decode(
x: torch.Tensor, upsample_count: int, world_size: int, rank: int
):
return split_height_for_parallel_decode(
x,
expected_height=x.shape[-2] * (2**upsample_count),
world_size=world_size,
rank=rank,
)
def split_height_for_parallel_decode(
x: torch.Tensor, expected_height: int, world_size: int, rank: int
):
if spatial_parallel_decode_disabled():
return x, None
x = _tensor_chunk(x, dim=-2, world_size=world_size, rank=rank)
return x, expected_height
def _maybe_contiguous_for_sp_gather(x: torch.Tensor) -> torch.Tensor:
if (
x.dim() == 5
and hasattr(torch, "channels_last_3d")
and x.is_contiguous(memory_format=torch.channels_last_3d)
and not x.is_contiguous()
):
return x.contiguous()
if (
x.dim() == 4
and x.is_contiguous(memory_format=torch.channels_last)
and not x.is_contiguous()
):
return x.contiguous()
return x
def gather_and_trim_height(x: torch.Tensor, expected_height: int | None):
if spatial_parallel_decode_disabled():
return x
if expected_height is None:
return x
x, _ = gather_variable_height(x)
if x.shape[-2] != expected_height:
x = x[..., :expected_height, :].contiguous()
return x
def gather_height_for_global_op(x: torch.Tensor) -> torch.Tensor:
if spatial_parallel_decode_disabled():
return x
return gather_variable_height(x)[0]
def chunk_height_for_parallel_decode(x: torch.Tensor) -> torch.Tensor:
if spatial_parallel_decode_disabled():
return x
return _tensor_chunk(
x,
dim=-2,
world_size=get_decode_parallel_world_size(),
rank=get_decode_parallel_rank(),
)
def chunk_height_by_sizes(x: torch.Tensor, heights: list[int]) -> torch.Tensor:
if spatial_parallel_decode_disabled():
return x
rank = get_decode_parallel_rank()
start = sum(heights[:rank])
return x[..., start : start + heights[rank], :].contiguous(
memory_format=_halo_memory_format(x)
)
def gather_height_sizes(x: torch.Tensor) -> list[int]:
"""gather heights of sharded feature_maps from peers"""
if spatial_parallel_decode_disabled():
return [x.shape[-2]]
world_size = get_decode_parallel_world_size()
if world_size <= 1:
return [x.shape[-2]]
local_height = torch.tensor([x.shape[-2]], device=x.device, dtype=torch.int64)
gathered = [torch.empty_like(local_height) for _ in range(world_size)]
dist.all_gather(
gathered,
local_height,
group=get_decode_parallel_group_coordinator().device_group,
)
return [int(height.item()) for height in gathered]
def gather_variable_height(x: torch.Tensor) -> tuple[torch.Tensor, list[int]]:
if spatial_parallel_decode_disabled():
return x, [x.shape[-2]]
world_size = get_decode_parallel_world_size()
if world_size <= 1:
return x, [x.shape[-2]]
heights = gather_height_sizes(x)
max_height = max(heights)
if x.shape[-2] < max_height:
x = _tensor_pad(x, max_height - x.shape[-2], dim=-2)
gathered = get_decode_parallel_group_coordinator().all_gather(
_maybe_contiguous_for_sp_gather(x), dim=-2
)
chunks = torch.split(gathered, max_height, dim=-2)
return (
torch.cat(
[chunk[..., :height, :] for chunk, height in zip(chunks, heights)], dim=-2
),
heights,
)
def _halo_memory_format(reference: torch.Tensor) -> torch.memory_format:
if reference.dim() > 1 and reference.stride(1) == 1:
if reference.dim() == 5 and hasattr(torch, "channels_last_3d"):
return torch.channels_last_3d
if reference.dim() == 4:
return torch.channels_last
return torch.contiguous_format
def _ensure_recv_buf(
recv_buf: torch.Tensor | None, reference: torch.Tensor
) -> torch.Tensor:
memory_format = _halo_memory_format(reference)
if (
recv_buf is None
or recv_buf.shape != reference.shape
or recv_buf.dtype != reference.dtype
or recv_buf.device != reference.device
or not recv_buf.is_contiguous(memory_format=memory_format)
):
return torch.empty(
reference.shape,
dtype=reference.dtype,
device=reference.device,
memory_format=memory_format,
)
return recv_buf
def halo_exchange(
x: torch.Tensor,
height_halo_size: int = 1,
recv_top_buf: torch.Tensor | None = None,
recv_bottom_buf: torch.Tensor | None = None,
height_pad_mode: str = "zeros",
) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
"""exchange(send and recv) top/bottom conv-input halos with adjacent spatial ranks"""
if spatial_parallel_decode_disabled():
return x, recv_top_buf, recv_bottom_buf
if height_halo_size == 0:
return x, recv_top_buf, recv_bottom_buf
decode_group = get_decode_parallel_group_coordinator()
rank = get_decode_parallel_rank()
world_size = get_decode_parallel_world_size()
group = decode_group.device_group
group_ranks = decode_group.ranks
top_row_ref = x[..., :height_halo_size, :]
bottom_row_ref = x[..., -height_halo_size:, :]
recv_top_buf = _ensure_recv_buf(recv_top_buf, top_row_ref)
recv_bottom_buf = _ensure_recv_buf(recv_bottom_buf, bottom_row_ref)
p2p_ops = []
if rank > 0:
prev_rank = group_ranks[rank - 1]
top_row = top_row_ref.contiguous(memory_format=_halo_memory_format(top_row_ref))
p2p_ops.append(dist.P2POp(dist.irecv, recv_top_buf, prev_rank, group))
p2p_ops.append(dist.P2POp(dist.isend, top_row, prev_rank, group))
if rank < world_size - 1:
next_rank = group_ranks[rank + 1]
bottom_row = bottom_row_ref.contiguous(
memory_format=_halo_memory_format(bottom_row_ref)
)
p2p_ops.append(dist.P2POp(dist.isend, bottom_row, next_rank, group))
p2p_ops.append(dist.P2POp(dist.irecv, recv_bottom_buf, next_rank, group))
if p2p_ops:
reqs = dist.batch_isend_irecv(p2p_ops)
for req in reqs:
req.wait()
if rank == 0:
recv_top_buf.copy_(
_make_boundary_halo(
x,
recv_bottom_buf if world_size > 1 else None,
height_halo_size=height_halo_size,
is_top=True,
mode=height_pad_mode,
)
)
if rank == world_size - 1:
recv_bottom_buf.copy_(
_make_boundary_halo(
x,
recv_top_buf if world_size > 1 else None,
height_halo_size=height_halo_size,
is_top=False,
mode=height_pad_mode,
)
)
return (
torch.concat([recv_top_buf, x, recv_bottom_buf], dim=-2),
recv_top_buf,
recv_bottom_buf,
)
def _make_boundary_halo(
x: torch.Tensor,
neighbor: torch.Tensor | None,
*,
height_halo_size: int,
is_top: bool,
mode: str,
) -> torch.Tensor:
if mode == "zeros":
shape = list(x.shape)
shape[-2] = height_halo_size
return torch.zeros(shape, dtype=x.dtype, device=x.device)
if mode == "replicate":
edge = x[..., :1, :] if is_top else x[..., -1:, :]
return edge.expand(*edge.shape[:-2], height_halo_size, edge.shape[-1])
if mode == "reflect":
source = x
if is_top and neighbor is not None:
source = torch.cat([x, neighbor], dim=-2)
elif not is_top and neighbor is not None:
source = torch.cat([neighbor, x], dim=-2)
if is_top:
index = torch.arange(
height_halo_size, 0, -1, device=x.device, dtype=torch.long
)
else:
index = torch.arange(
source.shape[-2] - 2,
source.shape[-2] - 2 - height_halo_size,
-1,
device=x.device,
dtype=torch.long,
)
return source.index_select(-2, index)
raise ValueError(f"Unsupported spatial padding mode for parallel decode: {mode}")
def _pad_with_mode(
x: torch.Tensor, padding: tuple[int, ...], mode: str
) -> torch.Tensor:
if mode == "zeros":
return F.pad(x, padding)
return F.pad(x, padding, mode=mode)
def _set_conv_padding(module: nn.Module, padding: tuple[int, ...]) -> None:
module.padding = padding
module._reversed_padding_repeated_twice = tuple(
value for pad in reversed(padding) for value in (pad, pad)
)
def _conv_preserves_local_height(
*,
height_halo_size: int,
height_pad_top: int,
height_pad_bottom: int,
kernel_height: int,
dilation_height: int,
stride_height: int,
) -> bool:
kernel_span = dilation_height * (kernel_height - 1)
return (
stride_height == 1
and 2 * height_halo_size == kernel_span
and height_pad_top == height_halo_size
and height_pad_bottom == height_halo_size
)
def _conv3d_weight_is_channels_last_3d(weight: torch.Tensor) -> bool:
return (
weight.dim() == 5
and hasattr(torch, "channels_last_3d")
and (current_platform.is_cuda() or current_platform.is_rocm())
and weight.is_contiguous(memory_format=torch.channels_last_3d)
)
def _match_conv3d_input_format(x: torch.Tensor, weight: torch.Tensor) -> torch.Tensor:
if x.dim() == 5 and _conv3d_weight_is_channels_last_3d(weight):
return x.contiguous(memory_format=torch.channels_last_3d)
return x
def _spatial_parallel_conv_forward(
module: nn.Module,
x: torch.Tensor,
conv_forward,
*,
height_pad_mode: str,
match_conv3d_format: bool = False,
) -> torch.Tensor:
# send and recv halo
# x_padded: concatenated input
x_padded, module._halo_recv_top_buf, module._halo_recv_bottom_buf = halo_exchange(
x,
height_halo_size=module.height_halo_size,
recv_top_buf=module._halo_recv_top_buf,
recv_bottom_buf=module._halo_recv_bottom_buf,
height_pad_mode=height_pad_mode,
)
if match_conv3d_format:
x_padded = _match_conv3d_input_format(x_padded, module.weight)
if module.height_halo_size == 0:
return conv_forward(x_padded)
stride = module.stride[-2]
if _conv_preserves_local_height(
height_halo_size=module.height_halo_size,
height_pad_top=module.height_pad_top,
height_pad_bottom=module.height_pad_bottom,
kernel_height=module.kernel_size[-2],
dilation_height=module.dilation[-2],
stride_height=stride,
):
return conv_forward(x_padded)
heights = gather_height_sizes(x)
global_start = sum(heights[: module.rank])
global_height = sum(heights)
if stride > 1:
shift = (
global_start - module.height_halo_size + module.height_pad_top
) % stride
if shift:
x_padded = x_padded[..., shift:, :]
global_start += shift
if match_conv3d_format:
x_padded = _match_conv3d_input_format(x_padded, module.weight)
out = conv_forward(x_padded)
# trim the output to original shape
return _trim_conv_output_height(
out,
local_height=x.shape[-2],
global_height=global_height,
global_start=global_start,
height_halo_size=module.height_halo_size,
height_pad_top=module.height_pad_top,
height_pad_bottom=module.height_pad_bottom,
kernel_height=module.kernel_size[-2],
dilation_height=module.dilation[-2],
stride_height=stride,
)
class SpatialParallelConv2d(nn.Conv2d):
def __init__(
self,
in_channels: int,
out_channels: int,
kernel_size: int | tuple[int, int],
stride: int | tuple[int, int] = 1,
padding: int | tuple[int, int] = 0,
dilation: int | tuple[int, int] = 1,
groups: int = 1,
bias: bool = True,
padding_mode: str = "zeros",
height_padding: tuple[int, int] | None = None,
):
super().__init__(
in_channels=in_channels,
out_channels=out_channels,
kernel_size=kernel_size,
stride=stride,
padding=padding,
dilation=dilation,
groups=groups,
bias=bias,
padding_mode=padding_mode,
)
self.height_halo_size = (self.dilation[-2] * (self.kernel_size[-2] - 1)) // 2
if height_padding is None:
height_padding = (self.padding[-2], self.padding[-2])
self.height_pad_top, self.height_pad_bottom = height_padding
self.padding: tuple[int, int]
if self.height_halo_size > 0:
self._padding = (0, 0, 0, 0)
else:
self._padding = (0, 0, self.padding[0], self.padding[0])
_set_conv_padding(self, (0, self.padding[1]))
self._halo_recv_top_buf: torch.Tensor | None = None
self._halo_recv_bottom_buf: torch.Tensor | None = None
self.rank = get_decode_parallel_rank()
self.world_size = get_decode_parallel_world_size()
def forward(self, x):
if spatial_parallel_decode_disabled():
return self._direct_forward(x)
if any(self._padding):
x = _pad_with_mode(x, self._padding, self.padding_mode)
return _spatial_parallel_conv_forward(
self,
x,
super().forward,
height_pad_mode=self.padding_mode,
)
def _direct_forward(self, x):
width_pad = self.padding[-1]
padding = (
width_pad,
width_pad,
self.height_pad_top,
self.height_pad_bottom,
)
if any(padding):
x = _pad_with_mode(x, padding, self.padding_mode)
return F.conv2d(
x,
self.weight,
self.bias,
self.stride,
(0, 0),
self.dilation,
self.groups,
)
class SpatialParallelCausalConv3d(nn.Conv3d):
def __init__(
self,
in_channels: int,
out_channels: int,
kernel_size: int | tuple[int, int, int],
stride: int | tuple[int, int, int] = 1,
padding: int | tuple[int, int, int] = 0,
):
super().__init__(
in_channels=in_channels,
out_channels=out_channels,
kernel_size=kernel_size,
stride=stride,
padding=padding,
)
self.height_pad_top = self.padding[1]
self.height_pad_bottom = self.padding[1]
self.height_halo_size = (self.kernel_size[-2] - 1) // 2
self.padding: tuple[int, int, int]
if self.height_halo_size > 0:
self._padding = (
self.padding[2],
self.padding[2],
0,
0,
2 * self.padding[0],
0,
)
else:
self._padding = (
self.padding[2],
self.padding[2],
self.padding[1],
self.padding[1],
2 * self.padding[0],
0,
)
self.padding = (0, 0, 0)
self._halo_recv_top_buf: torch.Tensor | None = None
self._halo_recv_bottom_buf: torch.Tensor | None = None
self.rank = get_decode_parallel_rank()
self.world_size = get_decode_parallel_world_size()
def forward(self, x, cache_x=None):
padding = list(self._padding)
if spatial_parallel_decode_disabled():
padding[2] = self.height_pad_top
padding[3] = self.height_pad_bottom
x = causal_conv3d_cat_pad(x, cache_x, padding)
x = x if current_platform.is_amp_supported() else x.to(self.weight.dtype)
if spatial_parallel_decode_disabled():
x = _match_conv3d_input_format(x, self.weight)
return F.conv3d(
x,
self.weight,
self.bias,
self.stride,
(0, 0, 0),
self.dilation,
self.groups,
)
return _spatial_parallel_conv_forward(
self,
x,
super().forward,
height_pad_mode="zeros",
match_conv3d_format=True,
)
class SpatialParallelConv3d(nn.Conv3d):
def __init__(
self,
in_channels: int,
out_channels: int,
kernel_size: int | tuple[int, int, int],
stride: int | tuple[int, int, int] = 1,
padding: int | tuple[int, int, int] = 0,
dilation: int | tuple[int, int, int] = 1,
groups: int = 1,
bias: bool = True,
padding_mode: str = "zeros",
height_padding: tuple[int, int] | None = None,
):
super().__init__(
in_channels=in_channels,
out_channels=out_channels,
kernel_size=kernel_size,
stride=stride,
padding=padding,
dilation=dilation,
groups=groups,
bias=bias,
padding_mode=padding_mode,
)
self.height_halo_size = (self.dilation[-2] * (self.kernel_size[-2] - 1)) // 2
if height_padding is None:
height_padding = (self.padding[-2], self.padding[-2])
self.height_pad_top, self.height_pad_bottom = height_padding
self.padding: tuple[int, int, int]
if self.height_halo_size > 0:
self._padding = (0, 0, 0, 0, 0, 0)
else:
self._padding = (
0,
0,
self.padding[1],
self.padding[1],
0,
0,
)
_set_conv_padding(self, (self.padding[0], 0, self.padding[2]))
self._halo_recv_top_buf: torch.Tensor | None = None
self._halo_recv_bottom_buf: torch.Tensor | None = None
self.rank = get_decode_parallel_rank()
self.world_size = get_decode_parallel_world_size()
def forward(self, x):
if spatial_parallel_decode_disabled():
return self._direct_forward(x)
if any(self._padding):
x = _pad_with_mode(x, self._padding, self.padding_mode)
return _spatial_parallel_conv_forward(
self,
x,
super().forward,
height_pad_mode=self.padding_mode,
match_conv3d_format=True,
)
def _direct_forward(self, x):
time_pad = self.padding[0]
width_pad = self.padding[-1]
padding = (
width_pad,
width_pad,
self.height_pad_top,
self.height_pad_bottom,
time_pad,
time_pad,
)
if any(padding):
x = _pad_with_mode(x, padding, self.padding_mode)
x = _match_conv3d_input_format(x, self.weight)
return F.conv3d(
x,
self.weight,
self.bias,
self.stride,
(0, 0, 0),
self.dilation,
self.groups,
)
class SpatialParallelZeroPad2d(nn.Module):
def __init__(self, padding: tuple[int, int, int, int]) -> None:
super().__init__()
self.padding = padding
self.rank = get_decode_parallel_rank()
self.world_size = get_decode_parallel_world_size()
def forward(self, x: torch.Tensor) -> torch.Tensor:
if spatial_parallel_decode_disabled():
return F.pad(x, self.padding)
left, right, top, bottom = self.padding
top = top if self.rank == 0 else 0
bottom = bottom if self.rank == self.world_size - 1 else 0
return F.pad(x, (left, right, top, bottom))
def _trim_conv_output_height(
out: torch.Tensor,
*,
local_height: int,
global_height: int,
global_start: int,
height_halo_size: int,
height_pad_top: int,
height_pad_bottom: int,
kernel_height: int,
dilation_height: int,
stride_height: int,
) -> torch.Tensor:
kernel_span = dilation_height * (kernel_height - 1)
min_i = math.ceil(
((-height_pad_top) - (global_start - height_halo_size)) / stride_height
)
max_i = math.floor(
(
(global_height - 1 + height_pad_bottom)
- kernel_span
- (global_start - height_halo_size)
)
/ stride_height
)
start = max(min_i, 0)
end = min(max_i + 1, out.shape[-2])
if start != 0 or end != out.shape[-2]:
out = out[..., start:end, :]
return out