File size: 2,880 Bytes
7755bd1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# Copied and adapted from LightX2V's Wan Triton scale-shift kernel.

import torch
import triton  # type: ignore
import triton.language as tl  # type: ignore


@triton.autotune(
    configs=[
        triton.Config({"BLOCK_N": 64}, num_warps=2),
        triton.Config({"BLOCK_N": 128}, num_warps=4),
        triton.Config({"BLOCK_N": 256}, num_warps=4),
        triton.Config({"BLOCK_N": 512}, num_warps=4),
        triton.Config({"BLOCK_N": 1024}, num_warps=8),
    ],
    key=["inner_dim"],
)
@triton.jit
def _fused_scale_shift_4d_kernel(
    output_ptr,
    normalized_ptr,
    scale_ptr,
    shift_ptr,
    rows,
    inner_dim,
    seq_len,
    num_frames,
    frame_seqlen,
    BLOCK_N: tl.constexpr,
):
    pid_row = tl.program_id(0)
    pid_col = tl.program_id(1)

    col_offsets = pid_col * BLOCK_N + tl.arange(0, BLOCK_N)
    mask = col_offsets < inner_dim

    row_base = pid_row * inner_dim
    norm_ptrs = normalized_ptr + row_base + col_offsets
    out_ptrs = output_ptr + row_base + col_offsets

    b_idx = pid_row // seq_len
    t_idx = pid_row % seq_len
    frame_idx_in_batch = t_idx // frame_seqlen

    scale_row_idx = b_idx * num_frames + frame_idx_in_batch
    scale_ptrs = scale_ptr + scale_row_idx * inner_dim + col_offsets
    shift_ptrs = shift_ptr + scale_row_idx * inner_dim + col_offsets

    normalized = tl.load(norm_ptrs, mask=mask, other=0.0)
    scale = tl.load(scale_ptrs, mask=mask, other=0.0)
    shift = tl.load(shift_ptrs, mask=mask, other=0.0)

    one = tl.full([BLOCK_N], 1.0, dtype=scale.dtype)
    output = normalized * (one + scale) + shift

    tl.store(out_ptrs, output, mask=mask)


def fuse_scale_shift_kernel(
    x: torch.Tensor,
    scale: torch.Tensor,
    shift: torch.Tensor,
):
    if not x.is_contiguous():
        x = x.contiguous()

    assert x.dim() == 3, "x must be [B, L, C]"
    assert scale.dim() == 4 and shift.dim() == 4, "scale/shift must be [B, F, 1, C]"
    assert scale.shape == shift.shape, "scale and shift must have the same shape"
    assert scale.shape[0] == x.shape[0] and scale.shape[2] == 1 and scale.shape[3] == x.shape[2], \
        "scale/shift must match x as [B, F, 1, C]"

    B, L, C = x.shape
    output = torch.empty_like(x)

    rows = B * L
    x_2d = x.view(rows, C)
    output_2d = output.view(rows, C)
    grid = lambda META: (rows, triton.cdiv(C, META["BLOCK_N"]))  # noqa
    num_frames = scale.shape[1]
    assert L % num_frames == 0, "seq_len must be divisible by num_frames for scale/shift"
    frame_seqlen = L // num_frames

    scale_reshaped = scale.squeeze(2).reshape(-1, C).contiguous()
    shift_reshaped = shift.squeeze(2).reshape(-1, C).contiguous()

    _fused_scale_shift_4d_kernel[grid](
        output_2d,
        x_2d,
        scale_reshaped,
        shift_reshaped,
        rows,
        C,
        L,
        num_frames,
        frame_seqlen,
    )
    return output