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
802 lines
25 KiB
Python
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
|