| 1 | # SPDX-License-Identifier: Apache-2.0 |
| 2 | # SPDX-FileCopyrightText: Copyright contributors to the vLLM project |
| 3 | # SPDX-FileCopyrightText: Songlin Yang, Yu Zhang |
| 4 | # |
| 5 | # This file contains code copied from the flash-linear-attention project. |
| 6 | # The original source code was licensed under the MIT license and included |
| 7 | # the following copyright notice: |
| 8 | # Copyright (c) 2023-2025, Songlin Yang, Yu Zhang |
| 9 | |
| 10 | # ruff: noqa: E501 |
| 11 | |
| 12 | |
| 13 | import torch |
| 14 | |
| 15 | from vllm.triton_utils import tl, triton |
| 16 | |
| 17 | from .index import prepare_chunk_indices |
| 18 | from .op import exp |
| 19 | from .utils import FLA_CHUNK_SIZE, check_shared_mem, is_nvidia_hopper |
| 20 | |
| 21 | BKV_LIST = [64, 128] if check_shared_mem() else [32, 64] |
| 22 | NUM_WARPS = [2, 4] if is_nvidia_hopper else [2, 4, 8] |
| 23 | |
| 24 | |
| 25 | @triton.heuristics( |
| 26 | { |
| 27 | "USE_G": lambda args: args["g"] is not None, |
| 28 | "IS_VARLEN": lambda args: args["cu_seqlens"] is not None, |
| 29 | } |
| 30 | ) |
| 31 | @triton.autotune( |
| 32 | configs=[ |
| 33 | triton.Config({"BK": BK, "BV": BV}, num_warps=num_warps, num_stages=num_stages) |
| 34 | for BK in BKV_LIST |
| 35 | for BV in BKV_LIST |
| 36 | for num_warps in NUM_WARPS |
| 37 | for num_stages in [2, 3, 4] |
| 38 | ], |
| 39 | key=["H", "K", "V", "BT"], |
| 40 | ) |
| 41 | @triton.jit(do_not_specialize=["T"]) |
| 42 | def chunk_fwd_kernel_o( |
| 43 | q, |
| 44 | k, |
| 45 | v, |
| 46 | h, |
| 47 | g, |
| 48 | o, |
| 49 | cu_seqlens, |
| 50 | chunk_indices, |
| 51 | scale, |
| 52 | T, |
| 53 | H: tl.constexpr, |
| 54 | Hg: tl.constexpr, |
| 55 | K: tl.constexpr, |
| 56 | V: tl.constexpr, |
| 57 | BT: tl.constexpr, |
| 58 | BK: tl.constexpr, |
| 59 | BV: tl.constexpr, |
| 60 | USE_G: tl.constexpr, |
| 61 | IS_VARLEN: tl.constexpr, |
| 62 | ): |
| 63 | i_v, i_t, i_bh = tl.program_id(0), tl.program_id(1), tl.program_id(2) |
| 64 | i_b, i_h = i_bh // H, i_bh % H |
| 65 | |
| 66 | if IS_VARLEN: |
| 67 | i_tg = i_t |
| 68 | i_n, i_t = ( |
| 69 | tl.load(chunk_indices + i_t * 2).to(tl.int32), |
| 70 | tl.load(chunk_indices + i_t * 2 + 1).to(tl.int32), |
| 71 | ) |
| 72 | bos, eos = ( |
| 73 | tl.load(cu_seqlens + i_n).to(tl.int32), |
| 74 | tl.load(cu_seqlens + i_n + 1).to(tl.int32), |
| 75 | ) |
| 76 | T = eos - bos |
| 77 | NT = tl.cdiv(T, BT) |
| 78 | else: |
| 79 | NT = tl.cdiv(T, BT) |
| 80 | i_tg = i_b * NT + i_t |
| 81 | bos, eos = i_b * T, i_b * T + T |
| 82 | |
| 83 | # offset calculation |
| 84 | q += (bos * Hg + i_h // (H // Hg)) * K |
| 85 | k += (bos * Hg + i_h // (H // Hg)) * K |
| 86 | v += (bos * H + i_h) * V |
| 87 | o += (bos * H + i_h) * V |
| 88 | h += (i_tg * H + i_h).to(tl.int64) * V * K |
| 89 | |
| 90 | b_o = tl.zeros([BT, BV], dtype=tl.float32) |
| 91 | b_A = tl.zeros([BT, BT], dtype=tl.float32) |
| 92 | |
| 93 | for i_k in range(tl.cdiv(K, BK)): |
| 94 | p_q = tl.make_block_ptr( |
| 95 | q, (T, K), (Hg * K, 1), (i_t * BT, i_k * BK), (BT, BK), (1, 0) |
| 96 | ) |
| 97 | p_k = tl.make_block_ptr( |
| 98 | k, (K, T), (1, Hg * K), (i_k * BK, i_t * BT), (BK, BT), (0, 1) |
| 99 | ) |
| 100 | p_h = tl.make_block_ptr( |
| 101 | h, (V, K), (K, 1), (i_v * BV, i_k * BK), (BV, BK), (1, 0) |
| 102 | ) |
| 103 | # [BT, BK] |
| 104 | b_q = tl.load(p_q, boundary_check=(0, 1)) |
| 105 | # [BK, BT] |
| 106 | b_k = tl.load(p_k, boundary_check=(0, 1)) |
| 107 | # [BV, BK] |
| 108 | b_h = tl.load(p_h, boundary_check=(0, 1)) |
| 109 | |
| 110 | # [BT, BK] @ [BK, BV] -> [BT, BV] |
| 111 | b_o += tl.dot(b_q, tl.trans(b_h)) |
| 112 | # [BT, BK] @ [BK, BT] -> [BT, BT] |
| 113 | b_A += tl.dot(b_q, b_k) |
| 114 | |
| 115 | if USE_G: |
| 116 | g += bos * H + i_h |
| 117 | p_g = tl.make_block_ptr(g, (T,), (H,), (i_t * BT,), (BT,), (0,)) |
| 118 | b_g = tl.load(p_g, boundary_check=(0,)) |
| 119 | b_o = b_o * exp(b_g)[:, None] |
| 120 | b_A = b_A * exp(b_g[:, None] - b_g[None, :]) |
| 121 | |
| 122 | o_t = i_t * BT + tl.arange(0, BT) |
| 123 | m_t = o_t < T |
| 124 | m_A = (o_t[:, None] >= o_t[None, :]) & (m_t[:, None] & m_t) |
| 125 | b_A = tl.where(m_A, b_A, 0) |
| 126 | |
| 127 | p_v = tl.make_block_ptr( |
| 128 | v, (T, V), (H * V, 1), (i_t * BT, i_v * BV), (BT, BV), (1, 0) |
| 129 | ) |
| 130 | p_o = tl.make_block_ptr( |
| 131 | o, (T, V), (H * V, 1), (i_t * BT, i_v * BV), (BT, BV), (1, 0) |
| 132 | ) |
| 133 | b_v = tl.load(p_v, boundary_check=(0, 1)) |
| 134 | |
| 135 | # to fix mma -> mma layout conversion |
| 136 | # already solved by triton v3.2 or higher |
| 137 | b_o = b_o * scale + tl.dot(b_A.to(b_v.dtype), b_v) * scale |
| 138 | tl.store(p_o, b_o.to(p_o.dtype.element_ty), boundary_check=(0, 1)) |
| 139 | |
| 140 | |
| 141 | def chunk_fwd_o( |
| 142 | q: torch.Tensor, |
| 143 | k: torch.Tensor, |
| 144 | v: torch.Tensor, |
| 145 | h: torch.Tensor, |
| 146 | g: torch.Tensor | None = None, # cumsum of log decay |
| 147 | scale: float | None = None, |
| 148 | cu_seqlens: torch.Tensor | None = None, |
| 149 | chunk_indices: torch.Tensor | None = None, |
| 150 | chunk_size: int = FLA_CHUNK_SIZE, |
| 151 | core_attn_out: torch.Tensor | None = None, |
| 152 | ) -> torch.Tensor: |
| 153 | B, T, Hg, K, V = *q.shape, v.shape[-1] |
| 154 | H = v.shape[-2] |
| 155 | BT = chunk_size |
| 156 | if chunk_indices is None and cu_seqlens is not None: |
| 157 | chunk_indices = prepare_chunk_indices(cu_seqlens, BT) |
| 158 | NT = triton.cdiv(T, BT) if cu_seqlens is None else len(chunk_indices) |
| 159 | if scale is None: |
| 160 | scale = k.shape[-1] ** -0.5 |
| 161 | |
| 162 | if core_attn_out is not None: |
| 163 | assert core_attn_out.numel() >= v.numel(), ( |
| 164 | f"core_attn_out too small: {core_attn_out.numel()} < {v.numel()}" |
| 165 | ) |
| 166 | o = core_attn_out[: v.numel()].view(*v.shape) |
| 167 | else: |
| 168 | o = torch.empty_like(v) |
| 169 | |
| 170 | def grid(meta): |
| 171 | return (triton.cdiv(V, meta["BV"]), NT, B * H) |
| 172 | |
| 173 | chunk_fwd_kernel_o[grid]( |
| 174 | q, |
| 175 | k, |
| 176 | v, |
| 177 | h, |
| 178 | g, |
| 179 | o, |
| 180 | cu_seqlens, |
| 181 | chunk_indices, |
| 182 | scale, |
| 183 | T=T, |
| 184 | H=H, |
| 185 | Hg=Hg, |
| 186 | K=K, |
| 187 | V=V, |
| 188 | BT=BT, |
| 189 | ) |
| 190 | return o |