# triton cross scan, 2x speed than pytorch implementation ========================= import torch import triton import triton.language as tl @triton.jit def triton_cross_scan( x, # (B, C, H, W) y, # (B, 4, C, H, W) BC: tl.constexpr, BH: tl.constexpr, BW: tl.constexpr, DC: tl.constexpr, DH: tl.constexpr, DW: tl.constexpr, NH: tl.constexpr, NW: tl.constexpr, ): i_hw, i_c, i_b = tl.program_id(0), tl.program_id(1), tl.program_id(2) i_h, i_w = (i_hw // NW), (i_hw % NW) _mask_h = (i_h * BH + tl.arange(0, BH)) < DH _mask_w = (i_w * BW + tl.arange(0, BW)) < DW _mask_hw = _mask_h[:, None] & _mask_w[None, :] _for_C = min(DC - i_c * BC, BC) _tmp0 = i_c * BC * DH * DW _tmp1 = DC * DH * DW _tmp2 = _tmp0 + i_h * BH * DW + tl.arange(0, BH)[:, None] * DW + i_w * BW + tl.arange(0, BW)[None, :] p_x = x + i_b * _tmp1 + _tmp2 p_y1 = y + i_b * 4 * _tmp1 + _tmp2 # same p_y2 = y + i_b * 4 * _tmp1 + _tmp1 + _tmp0 + i_w * BW * DH + tl.arange(0, BW)[None, :] * DH + i_h * BH + tl.arange(0, BH)[:, None] # trans p_y3 = y + i_b * 4 * _tmp1 + 2 * _tmp1 + _tmp0 + (NH - i_h - 1) * BH * DW + (BH - 1 - tl.arange(0, BH)[:, None]) * DW + (NW - i_w - 1) * BW + (BW - 1 - tl.arange(0, BW)[None, :]) + (DH - NH * BH) * DW + (DW - NW * BW) # flip p_y4 = y + i_b * 4 * _tmp1 + 3 * _tmp1 + _tmp0 + (NW - i_w - 1) * BW * DH + (BW - 1 - tl.arange(0, BW)[None, :]) * DH + (NH - i_h - 1) * BH + (BH - 1 - tl.arange(0, BH)[:, None]) + (DH - NH * BH) + (DW - NW * BW) * DH # trans + flip for idxc in range(_for_C): _idx = idxc * DH * DW _x = tl.load(p_x + _idx, mask=_mask_hw) tl.store(p_y1 + _idx, _x, mask=_mask_hw) tl.store(p_y2 + _idx, _x, mask=_mask_hw) tl.store(p_y3 + _idx, _x, mask=_mask_hw) tl.store(p_y4 + _idx, _x, mask=_mask_hw) @triton.jit def triton_cross_merge( x, # (B, C, H, W) y, # (B, 4, C, H, W) BC: tl.constexpr, BH: tl.constexpr, BW: tl.constexpr, DC: tl.constexpr, DH: tl.constexpr, DW: tl.constexpr, NH: tl.constexpr, NW: tl.constexpr, ): i_hw, i_c, i_b = tl.program_id(0), tl.program_id(1), tl.program_id(2) i_h, i_w = (i_hw // NW), (i_hw % NW) _mask_h = (i_h * BH + tl.arange(0, BH)) < DH _mask_w = (i_w * BW + tl.arange(0, BW)) < DW _mask_hw = _mask_h[:, None] & _mask_w[None, :] _for_C = min(DC - i_c * BC, BC) _tmp0 = i_c * BC * DH * DW _tmp1 = DC * DH * DW _tmp2 = _tmp0 + i_h * BH * DW + tl.arange(0, BH)[:, None] * DW + i_w * BW + tl.arange(0, BW)[None, :] p_x = x + i_b * _tmp1 + _tmp2 p_y1 = y + i_b * 4 * _tmp1 + _tmp2 # same p_y2 = y + i_b * 4 * _tmp1 + _tmp1 + _tmp0 + i_w * BW * DH + tl.arange(0, BW)[None, :] * DH + i_h * BH + tl.arange(0, BH)[:, None] # trans p_y3 = y + i_b * 4 * _tmp1 + 2 * _tmp1 + _tmp0 + (NH - i_h - 1) * BH * DW + (BH - 1 - tl.arange(0, BH)[:, None]) * DW + (NW - i_w - 1) * BW + (BW - 1 - tl.arange(0, BW)[None, :]) + (DH - NH * BH) * DW + (DW - NW * BW) # flip p_y4 = y + i_b * 4 * _tmp1 + 3 * _tmp1 + _tmp0 + (NW - i_w - 1) * BW * DH + (BW - 1 - tl.arange(0, BW)[None, :]) * DH + (NH - i_h - 1) * BH + (BH - 1 - tl.arange(0, BH)[:, None]) + (DH - NH * BH) + (DW - NW * BW) * DH # trans + flip for idxc in range(_for_C): _idx = idxc * DH * DW _y1 = tl.load(p_y1 + _idx, mask=_mask_hw) _y2 = tl.load(p_y2 + _idx, mask=_mask_hw) _y3 = tl.load(p_y3 + _idx, mask=_mask_hw) _y4 = tl.load(p_y4 + _idx, mask=_mask_hw) tl.store(p_x + _idx, _y1 + _y2 + _y3 + _y4, mask=_mask_hw) @triton.jit def triton_cross_scan_1b1( x, # (B, C, H, W) y, # (B, 4, C, H, W) BC: tl.constexpr, BH: tl.constexpr, BW: tl.constexpr, DC: tl.constexpr, DH: tl.constexpr, DW: tl.constexpr, NH: tl.constexpr, NW: tl.constexpr, ): i_hw, i_c, i_b = tl.program_id(0), tl.program_id(1), tl.program_id(2) i_h, i_w = (i_hw // NW), (i_hw % NW) _mask_h = (i_h * BH + tl.arange(0, BH)) < DH _mask_w = (i_w * BW + tl.arange(0, BW)) < DW _mask_hw = _mask_h[:, None] & _mask_w[None, :] _for_C = min(DC - i_c * BC, BC) _tmp0 = i_c * BC * DH * DW _tmp1 = DC * DH * DW _tmp2 = _tmp0 + i_h * BH * DW + tl.arange(0, BH)[:, None] * DW + i_w * BW + tl.arange(0, BW)[None, :] p_y1 = y + i_b * 4 * _tmp1 + _tmp2 # same p_y2 = y + i_b * 4 * _tmp1 + _tmp1 + _tmp0 + i_w * BW * DH + tl.arange(0, BW)[None, :] * DH + i_h * BH + tl.arange(0, BH)[:, None] # trans p_y3 = y + i_b * 4 * _tmp1 + 2 * _tmp1 + _tmp0 + (NH - i_h - 1) * BH * DW + (BH - 1 - tl.arange(0, BH)[:, None]) * DW + (NW - i_w - 1) * BW + (BW - 1 - tl.arange(0, BW)[None, :]) + (DH - NH * BH) * DW + (DW - NW * BW) # flip p_y4 = y + i_b * 4 * _tmp1 + 3 * _tmp1 + _tmp0 + (NW - i_w - 1) * BW * DH + (BW - 1 - tl.arange(0, BW)[None, :]) * DH + (NH - i_h - 1) * BH + (BH - 1 - tl.arange(0, BH)[:, None]) + (DH - NH * BH) + (DW - NW * BW) * DH # trans + flip p_x1 = x + i_b * 4 * _tmp1 + _tmp2 p_x2 = p_x1 + _tmp1 p_x3 = p_x2 + _tmp1 p_x4 = p_x3 + _tmp1 for idxc in range(_for_C): _idx = idxc * DH * DW tl.store(p_y1 + _idx, tl.load(p_x1 + _idx, mask=_mask_hw), mask=_mask_hw) tl.store(p_y2 + _idx, tl.load(p_x2 + _idx, mask=_mask_hw), mask=_mask_hw) tl.store(p_y3 + _idx, tl.load(p_x3 + _idx, mask=_mask_hw), mask=_mask_hw) tl.store(p_y4 + _idx, tl.load(p_x4 + _idx, mask=_mask_hw), mask=_mask_hw) @triton.jit def triton_cross_merge_1b1( x, # (B, C, H, W) y, # (B, 4, C, H, W) BC: tl.constexpr, BH: tl.constexpr, BW: tl.constexpr, DC: tl.constexpr, DH: tl.constexpr, DW: tl.constexpr, NH: tl.constexpr, NW: tl.constexpr, ): i_hw, i_c, i_b = tl.program_id(0), tl.program_id(1), tl.program_id(2) i_h, i_w = (i_hw // NW), (i_hw % NW) _mask_h = (i_h * BH + tl.arange(0, BH)) < DH _mask_w = (i_w * BW + tl.arange(0, BW)) < DW _mask_hw = _mask_h[:, None] & _mask_w[None, :] _for_C = min(DC - i_c * BC, BC) _tmp0 = i_c * BC * DH * DW _tmp1 = DC * DH * DW _tmp2 = _tmp0 + i_h * BH * DW + tl.arange(0, BH)[:, None] * DW + i_w * BW + tl.arange(0, BW)[None, :] p_y1 = y + i_b * 4 * _tmp1 + _tmp2 # same p_y2 = y + i_b * 4 * _tmp1 + _tmp1 + _tmp0 + i_w * BW * DH + tl.arange(0, BW)[None, :] * DH + i_h * BH + tl.arange(0, BH)[:, None] # trans p_y3 = y + i_b * 4 * _tmp1 + 2 * _tmp1 + _tmp0 + (NH - i_h - 1) * BH * DW + (BH - 1 - tl.arange(0, BH)[:, None]) * DW + (NW - i_w - 1) * BW + (BW - 1 - tl.arange(0, BW)[None, :]) + (DH - NH * BH) * DW + (DW - NW * BW) # flip p_y4 = y + i_b * 4 * _tmp1 + 3 * _tmp1 + _tmp0 + (NW - i_w - 1) * BW * DH + (BW - 1 - tl.arange(0, BW)[None, :]) * DH + (NH - i_h - 1) * BH + (BH - 1 - tl.arange(0, BH)[:, None]) + (DH - NH * BH) + (DW - NW * BW) * DH # trans + flip p_x1 = x + i_b * 4 * _tmp1 + _tmp2 p_x2 = p_x1 + _tmp1 p_x3 = p_x2 + _tmp1 p_x4 = p_x3 + _tmp1 for idxc in range(_for_C): _idx = idxc * DH * DW tl.store(p_x1 + _idx, tl.load(p_y1 + _idx), mask=_mask_hw) tl.store(p_x2 + _idx, tl.load(p_y2 + _idx), mask=_mask_hw) tl.store(p_x3 + _idx, tl.load(p_y3 + _idx), mask=_mask_hw) tl.store(p_x4 + _idx, tl.load(p_y4 + _idx), mask=_mask_hw) class CrossScanTriton(torch.autograd.Function): @staticmethod def forward(ctx, x: torch.Tensor): B, C, H, W = x.shape B, C, H, W = int(B), int(C), int(H), int(W) BC, BH, BW = min(triton.next_power_of_2(C), 1), min(triton.next_power_of_2(H), 64), min(triton.next_power_of_2(W), 64) NH, NW, NC = triton.cdiv(H, BH), triton.cdiv(W, BW), triton.cdiv(C, BC) ctx.shape = (B, C, H, W) ctx.triton_shape = (BC, BH, BW, NC, NH, NW) x = x.contiguous() y = x.new_empty((B, 4, C, H, W)) triton_cross_scan[(NH * NW, NC, B)](x, y, BC, BH, BW, C, H, W, NH, NW) return y.view(B, 4, C, -1) @staticmethod def backward(ctx, y: torch.Tensor): # out: (b, k, d, l) B, C, H, W = ctx.shape BC, BH, BW, NC, NH, NW = ctx.triton_shape y = y.contiguous().view(B, 4, C, H, W) x = y.new_empty((B, C, H, W)) triton_cross_merge[(NH * NW, NC, B)](x, y, BC, BH, BW, C, H, W, NH, NW) return x class CrossMergeTriton(torch.autograd.Function): @staticmethod def forward(ctx, y: torch.Tensor): B, K, C, H, W = y.shape B, C, H, W = int(B), int(C), int(H), int(W) BC, BH, BW = min(triton.next_power_of_2(C), 1), min(triton.next_power_of_2(H), 64), min(triton.next_power_of_2(W), 64) NH, NW, NC = triton.cdiv(H, BH), triton.cdiv(W, BW), triton.cdiv(C, BC) ctx.shape = (B, C, H, W) ctx.triton_shape = (BC, BH, BW, NC, NH, NW) y = y.contiguous().view(B, 4, C, H, W) x = y.new_empty((B, C, H, W)) triton_cross_merge[(NH * NW, NC, B)](x, y, BC, BH, BW, C, H, W, NH, NW) return x.view(B, C, -1) @staticmethod def backward(ctx, x: torch.Tensor): # out: (b, d, l) B, C, H, W = ctx.shape BC, BH, BW, NC, NH, NW = ctx.triton_shape x = x.contiguous() y = x.new_empty((B, 4, C, H, W)) triton_cross_scan[(NH * NW, NC, B)](x, y, BC, BH, BW, C, H, W, NH, NW) return y class CrossScanTriton1b1(torch.autograd.Function): @staticmethod def forward(ctx, x: torch.Tensor): B, K, C, H, W = x.shape B, C, H, W = int(B), int(C), int(H), int(W) BC, BH, BW = min(triton.next_power_of_2(C), 1), min(triton.next_power_of_2(H), 64), min(triton.next_power_of_2(W), 64) NH, NW, NC = triton.cdiv(H, BH), triton.cdiv(W, BW), triton.cdiv(C, BC) ctx.shape = (B, C, H, W) ctx.triton_shape = (BC, BH, BW, NC, NH, NW) x = x.contiguous() y = x.new_empty((B, 4, C, H, W)) triton_cross_scan_1b1[(NH * NW, NC, B)](x, y, BC, BH, BW, C, H, W, NH, NW) return y.view(B, 4, C, -1) @staticmethod def backward(ctx, y: torch.Tensor): # out: (b, k, d, l) B, C, H, W = ctx.shape BC, BH, BW, NC, NH, NW = ctx.triton_shape y = y.contiguous().view(B, 4, C, H, W) x = y.new_empty((B, 4, C, H, W)) triton_cross_merge_1b1[(NH * NW, NC, B)](x, y, BC, BH, BW, C, H, W, NH, NW) return x