Uploaded using `kernel-builder`.
Browse files- build/torch-cuda/__init__.py +29 -0
- build/torch-cuda/_ops.py +38 -0
- build/torch-cuda/activations.py +296 -0
- build/torch-cuda/assert_inputs.py +327 -0
- build/torch-cuda/autotuner.py +360 -0
- build/torch-cuda/block_info.py +277 -0
- build/torch-cuda/cache_utils.py +180 -0
- build/torch-cuda/flash_bwd_postprocess.py +215 -0
- build/torch-cuda/flash_bwd_preprocess.py +266 -0
- build/torch-cuda/flash_dec_combine.py +222 -0
- build/torch-cuda/flash_dense_bwd.py +1019 -0
- build/torch-cuda/flash_dense_dec.py +857 -0
- build/torch-cuda/flash_dense_fwd.py +957 -0
- build/torch-cuda/flash_fwd_combine.py +239 -0
- build/torch-cuda/flash_gated_bwd.py +1317 -0
- build/torch-cuda/flash_gated_dec.py +1218 -0
- build/torch-cuda/flash_gated_fwd.py +1337 -0
- build/torch-cuda/flash_proj_bwd.py +21 -0
- build/torch-cuda/flash_proj_fwd.py +45 -0
- build/torch-cuda/flash_sparse_attention/__init__.py +26 -0
- build/torch-cuda/flash_sparse_bwd.py +1041 -0
- build/torch-cuda/flash_sparse_dec.py +871 -0
- build/torch-cuda/flash_sparse_fwd.py +974 -0
- build/torch-cuda/interface.py +1912 -0
- build/torch-cuda/kernel_repr.py +115 -0
- build/torch-cuda/launch_grid.py +202 -0
- build/torch-cuda/launch_template.py +457 -0
- build/torch-cuda/mask.py +106 -0
- build/torch-cuda/metadata.json +47 -0
- build/torch-cuda/quant.py +66 -0
- build/torch-cuda/scheduler.py +2609 -0
- build/torch-cuda/seqlen_info.py +273 -0
- build/torch-cuda/utils.py +167 -0
build/torch-cuda/__init__.py
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from .interface import (
|
| 2 |
+
flash_dense_attn_func,
|
| 3 |
+
flash_dense_attn_with_kvcache_func,
|
| 4 |
+
flash_dense_attn_varlen_func,
|
| 5 |
+
flash_dense_attn_varlen_with_kvcache_func,
|
| 6 |
+
flash_sparse_attn_func,
|
| 7 |
+
flash_sparse_attn_with_kvcache_func,
|
| 8 |
+
flash_sparse_attn_varlen_func,
|
| 9 |
+
flash_sparse_attn_varlen_with_kvcache_func,
|
| 10 |
+
flash_gated_attn_func,
|
| 11 |
+
flash_gated_attn_with_kvcache_func,
|
| 12 |
+
flash_gated_attn_varlen_func,
|
| 13 |
+
flash_gated_attn_varlen_with_kvcache_func,
|
| 14 |
+
)
|
| 15 |
+
|
| 16 |
+
__all__ = [
|
| 17 |
+
"flash_dense_attn_func",
|
| 18 |
+
"flash_dense_attn_with_kvcache_func",
|
| 19 |
+
"flash_dense_attn_varlen_func",
|
| 20 |
+
"flash_dense_attn_varlen_with_kvcache_func",
|
| 21 |
+
"flash_sparse_attn_func",
|
| 22 |
+
"flash_sparse_attn_with_kvcache_func",
|
| 23 |
+
"flash_sparse_attn_varlen_func",
|
| 24 |
+
"flash_sparse_attn_varlen_with_kvcache_func",
|
| 25 |
+
"flash_gated_attn_func",
|
| 26 |
+
"flash_gated_attn_with_kvcache_func",
|
| 27 |
+
"flash_gated_attn_varlen_func",
|
| 28 |
+
"flash_gated_attn_varlen_with_kvcache_func",
|
| 29 |
+
]
|
build/torch-cuda/_ops.py
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
|
| 3 |
+
def get_backend() -> str:
|
| 4 |
+
"""Detect the backend by inspecting torch."""
|
| 5 |
+
import torch
|
| 6 |
+
|
| 7 |
+
if hasattr(torch, "neuron"):
|
| 8 |
+
# Needs to be sorted before specific Torch builds, since Neuron
|
| 9 |
+
# extension can be loaded into e.g. CUDA Torch builds.
|
| 10 |
+
return "neuron"
|
| 11 |
+
elif torch.version.cuda is not None:
|
| 12 |
+
return "cuda"
|
| 13 |
+
elif torch.version.hip is not None:
|
| 14 |
+
return "rocm"
|
| 15 |
+
elif torch.backends.mps.is_available():
|
| 16 |
+
return "metal"
|
| 17 |
+
elif hasattr(torch.version, "xpu") and torch.version.xpu is not None:
|
| 18 |
+
return "xpu"
|
| 19 |
+
else:
|
| 20 |
+
return "cpu"
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
def _find_ops_name() -> str:
|
| 24 |
+
kernel_name = "flash_sparse_attention"
|
| 25 |
+
unique_id = "7db628a_dirty"
|
| 26 |
+
backend = get_backend()
|
| 27 |
+
return f"_{kernel_name}_{backend}_{unique_id}"
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
_OPS_NAME = _find_ops_name()
|
| 31 |
+
|
| 32 |
+
ops = getattr(torch.ops, _OPS_NAME)
|
| 33 |
+
|
| 34 |
+
def add_op_namespace_prefix(op_name: str) -> str:
|
| 35 |
+
"""
|
| 36 |
+
Prefix op by namespace.
|
| 37 |
+
"""
|
| 38 |
+
return f"{_OPS_NAME}::{op_name}"
|
build/torch-cuda/activations.py
ADDED
|
@@ -0,0 +1,296 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import triton
|
| 2 |
+
import triton.language as tl
|
| 3 |
+
|
| 4 |
+
|
| 5 |
+
@triton.jit
|
| 6 |
+
def check_inf(x):
|
| 7 |
+
return tl.maximum(x, -1e6)
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
@triton.jit
|
| 11 |
+
def exp2(x):
|
| 12 |
+
"""
|
| 13 |
+
Compute 2^x, using fast hardware approximation on CUDA and default implementation on other backends.
|
| 14 |
+
|
| 15 |
+
:param x: Input tensor of shape [BLOCK_M, BLOCK_N].
|
| 16 |
+
|
| 17 |
+
:return: Tensor of shape [BLOCK_M, BLOCK_N] containing 2^x values.
|
| 18 |
+
"""
|
| 19 |
+
if tl.target_info.is_cuda():
|
| 20 |
+
return tl.inline_asm_elementwise(
|
| 21 |
+
"ex2.approx.ftz.f32 $0, $1;",
|
| 22 |
+
"=r, r",
|
| 23 |
+
[x],
|
| 24 |
+
dtype=tl.float32,
|
| 25 |
+
is_pure=True,
|
| 26 |
+
pack=1,
|
| 27 |
+
)
|
| 28 |
+
else:
|
| 29 |
+
return tl.exp2(x)
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
@triton.jit
|
| 33 |
+
def exp(x):
|
| 34 |
+
"""
|
| 35 |
+
Compute e^x, using fast hardware approximation on CUDA and default implementation on other backends.
|
| 36 |
+
|
| 37 |
+
:param x: Input tensor of shape [BLOCK_M, BLOCK_N].
|
| 38 |
+
|
| 39 |
+
:return: Tensor of shape [BLOCK_M, BLOCK_N] containing e^x values.
|
| 40 |
+
"""
|
| 41 |
+
log2_e: tl.constexpr = 1.4426950408889634
|
| 42 |
+
x *= log2_e
|
| 43 |
+
return exp2(x)
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
@triton.jit
|
| 47 |
+
def online_softmax(
|
| 48 |
+
acc_s,
|
| 49 |
+
row_max,
|
| 50 |
+
row_sum,
|
| 51 |
+
scale_log2,
|
| 52 |
+
CHECK_INF: tl.constexpr,
|
| 53 |
+
RESCALE_THRESHOLD: tl.constexpr,
|
| 54 |
+
):
|
| 55 |
+
"""
|
| 56 |
+
Apply online softmax to acc_s, and update row_max and row_sum.
|
| 57 |
+
|
| 58 |
+
:param acc_s: Attention scores tensor of shape [BLOCK_M, BLOCK_N].
|
| 59 |
+
:param row_max: Current maximum values per row of shape [BLOCK_M], init to -inf.
|
| 60 |
+
:param row_sum: Current sum values per row of shape [BLOCK_M], init to 0.
|
| 61 |
+
:param scale_log2: Log2 of the scaling factor to be applied to acc_s.
|
| 62 |
+
:param CHECK_INF: Boolean flag indicating if -inf row_max should be clamped to 0.
|
| 63 |
+
:param RESCALE_THRESHOLD: Threshold for rescaling to avoid underflow. If <= 0, rescaling is disabled.
|
| 64 |
+
|
| 65 |
+
:return p: Softmax probabilities tensor of shape [BLOCK_M, BLOCK_N].
|
| 66 |
+
:return row_max_new: Updated maximum values per row of shape [BLOCK_M].
|
| 67 |
+
:return row_sum_new: Updated sum values per row of shape [BLOCK_M].
|
| 68 |
+
:return row_scale: Scaling factors per row of shape [BLOCK_M].
|
| 69 |
+
"""
|
| 70 |
+
# Compute current row max
|
| 71 |
+
row_max_curr = tl.max(acc_s, axis=1)
|
| 72 |
+
|
| 73 |
+
# Update row max
|
| 74 |
+
row_max_new = tl.maximum(row_max_curr, row_max)
|
| 75 |
+
|
| 76 |
+
# Avoid exp(-inf - (-inf)) = nan by clamping -inf to 0
|
| 77 |
+
if CHECK_INF:
|
| 78 |
+
row_max_new = check_inf(row_max_new)
|
| 79 |
+
|
| 80 |
+
# Compute scaled differences to new row max
|
| 81 |
+
acc_scale_log2 = (row_max - row_max_new) * scale_log2
|
| 82 |
+
|
| 83 |
+
# Compute row scale
|
| 84 |
+
if RESCALE_THRESHOLD > 0.0:
|
| 85 |
+
# Triton can only skip computation at block granularity
|
| 86 |
+
if tl.min(acc_scale_log2) < -RESCALE_THRESHOLD:
|
| 87 |
+
row_scale = exp2(acc_scale_log2)
|
| 88 |
+
else:
|
| 89 |
+
row_max_new = row_max
|
| 90 |
+
row_scale = acc_scale_log2 * 0.0 + 1.0
|
| 91 |
+
else:
|
| 92 |
+
row_scale = exp2(acc_scale_log2)
|
| 93 |
+
|
| 94 |
+
# Compute attention weights
|
| 95 |
+
p = exp2(acc_s * scale_log2 - row_max_new[:, None] * scale_log2)
|
| 96 |
+
|
| 97 |
+
# Update row sum
|
| 98 |
+
row_sum_cur = tl.sum(p, axis=1)
|
| 99 |
+
row_sum_new = row_sum * row_scale + row_sum_cur
|
| 100 |
+
|
| 101 |
+
return p, row_max_new, row_sum_new, row_scale
|
| 102 |
+
|
| 103 |
+
|
| 104 |
+
@triton.jit
|
| 105 |
+
def online_sparse_softmax(
|
| 106 |
+
acc_s,
|
| 107 |
+
row_max,
|
| 108 |
+
row_sum,
|
| 109 |
+
scale_log2,
|
| 110 |
+
softmax_threshold_log2,
|
| 111 |
+
CHECK_INF: tl.constexpr,
|
| 112 |
+
):
|
| 113 |
+
"""
|
| 114 |
+
Apply online sparse softmax to acc_s, and update row_max and row_sum.
|
| 115 |
+
|
| 116 |
+
:param acc_s: Attention scores tensor of shape [BLOCK_M, BLOCK_N].
|
| 117 |
+
:param row_max: Current maximum values per row of shape [BLOCK_M], init to -inf.
|
| 118 |
+
:param row_sum: Current sum values per row of shape [BLOCK_M], init to 0.
|
| 119 |
+
:param scale_log2: Log2 of the scaling factor to be applied to acc_s.
|
| 120 |
+
:param softmax_threshold_log2: Threshold in log2-domain for block-level skip.
|
| 121 |
+
:param CHECK_INF: Boolean flag indicating if -inf row_max should be clamped to 0.
|
| 122 |
+
|
| 123 |
+
:return p: Softmax probabilities tensor of shape [BLOCK_M, BLOCK_N].
|
| 124 |
+
:return row_max_new: Updated maximum values per row of shape [BLOCK_M].
|
| 125 |
+
:return row_sum_new: Updated sum values per row of shape [BLOCK_M].
|
| 126 |
+
:return row_scale: Scaling factors per row of shape [BLOCK_M].
|
| 127 |
+
:return skip_softmax: Boolean indicating whether this block was skipped.
|
| 128 |
+
"""
|
| 129 |
+
# Compute current row max
|
| 130 |
+
row_max_curr = tl.max(acc_s, axis=1)
|
| 131 |
+
|
| 132 |
+
# Update skip condition based on threshold
|
| 133 |
+
row_max_diff_log2 = (row_max_curr - row_max) * scale_log2
|
| 134 |
+
skip_softmax = tl.max(row_max_diff_log2 - softmax_threshold_log2) < 0.0
|
| 135 |
+
|
| 136 |
+
# Return zero attention weights
|
| 137 |
+
if skip_softmax:
|
| 138 |
+
p = acc_s * 0.0
|
| 139 |
+
row_max_new = row_max
|
| 140 |
+
row_sum_new = row_sum
|
| 141 |
+
row_scale = row_max * 0.0 + 1.0
|
| 142 |
+
else:
|
| 143 |
+
# Update row max
|
| 144 |
+
row_max_new = tl.maximum(row_max_curr, row_max)
|
| 145 |
+
|
| 146 |
+
# Avoid exp(-inf - (-inf)) = nan by clamping -inf to 0
|
| 147 |
+
if CHECK_INF:
|
| 148 |
+
row_max_new = check_inf(row_max_new)
|
| 149 |
+
|
| 150 |
+
# Compute scaled differences to new row max
|
| 151 |
+
acc_scale_log2 = (row_max - row_max_new) * scale_log2
|
| 152 |
+
|
| 153 |
+
# Compute row scale
|
| 154 |
+
row_scale = exp2(acc_scale_log2)
|
| 155 |
+
|
| 156 |
+
# Compute attention weights
|
| 157 |
+
p = exp2(acc_s * scale_log2 - row_max_new[:, None] * scale_log2)
|
| 158 |
+
|
| 159 |
+
# Update row sum
|
| 160 |
+
row_sum_cur = tl.sum(p, axis=1)
|
| 161 |
+
row_sum_new = row_sum * row_scale + row_sum_cur
|
| 162 |
+
|
| 163 |
+
return p, row_max_new, row_sum_new, row_scale, skip_softmax
|
| 164 |
+
|
| 165 |
+
|
| 166 |
+
@triton.jit
|
| 167 |
+
def finalize(
|
| 168 |
+
row_max,
|
| 169 |
+
row_sum,
|
| 170 |
+
scale_log2,
|
| 171 |
+
final_scale,
|
| 172 |
+
IS_LOG2: tl.constexpr,
|
| 173 |
+
CHECK_NAN: tl.constexpr,
|
| 174 |
+
):
|
| 175 |
+
"""
|
| 176 |
+
Finalize online softmax by computing output scale and logsumexp.
|
| 177 |
+
|
| 178 |
+
:param row_max: Final maximum values per row of shape [BLOCK_M].
|
| 179 |
+
:param row_sum: Final sum values per row of shape [BLOCK_M].
|
| 180 |
+
:param final_scale: Scaling factor to be applied to the output.
|
| 181 |
+
:param IS_LOG2: Boolean flag indicating if the returned logsumexp should be in log2-space.
|
| 182 |
+
:param CHECK_NAN: Boolean flag indicating if nan values in row_sum should be checked and set to 1 to avoid returning nan.
|
| 183 |
+
|
| 184 |
+
:return row_scale: Final scaling factors per row of shape [BLOCK_M].
|
| 185 |
+
:return lse: Logsumexp values per row of shape [BLOCK_M].
|
| 186 |
+
"""
|
| 187 |
+
# if row_sum is zero or nan, set it to 1 to avoid division by zero
|
| 188 |
+
if CHECK_NAN:
|
| 189 |
+
acc_o_is_zero_or_nan = (row_sum == 0.0) | (row_sum != row_sum)
|
| 190 |
+
row_scale = tl.where(acc_o_is_zero_or_nan, 1.0, final_scale / row_sum)
|
| 191 |
+
lse = tl.where(
|
| 192 |
+
acc_o_is_zero_or_nan,
|
| 193 |
+
float("-inf"),
|
| 194 |
+
row_max * scale_log2 + tl.log2(row_sum),
|
| 195 |
+
)
|
| 196 |
+
else:
|
| 197 |
+
row_scale = final_scale / row_sum
|
| 198 |
+
lse = row_max * scale_log2 + tl.log2(row_sum)
|
| 199 |
+
if not IS_LOG2:
|
| 200 |
+
# ln2 = math.log(2.0)
|
| 201 |
+
ln2: tl.constexpr = 0.6931471805599453
|
| 202 |
+
lse *= ln2
|
| 203 |
+
return row_scale, lse
|
| 204 |
+
|
| 205 |
+
|
| 206 |
+
@triton.jit
|
| 207 |
+
def rescale_o(
|
| 208 |
+
acc_o,
|
| 209 |
+
row_scale,
|
| 210 |
+
LAZY_RESCALE: tl.constexpr,
|
| 211 |
+
):
|
| 212 |
+
"""
|
| 213 |
+
Rescale output accumulator by row_scale.
|
| 214 |
+
|
| 215 |
+
:param acc_o: Output accumulator tensor of shape [BLOCK_M, BLOCK_N].
|
| 216 |
+
:param row_scale: Scaling factors per row of shape [BLOCK_M].
|
| 217 |
+
:param LAZY_RESCALE: Boolean flag indicating if rescaling should be applied lazily.
|
| 218 |
+
|
| 219 |
+
:return: Rescaled output accumulator tensor of shape [BLOCK_M, BLOCK_N].
|
| 220 |
+
"""
|
| 221 |
+
if LAZY_RESCALE:
|
| 222 |
+
# On some hardware, the check can cause severe performance regression
|
| 223 |
+
if tl.min(row_scale) != 1.0:
|
| 224 |
+
acc_o = acc_o * row_scale[:, None]
|
| 225 |
+
else:
|
| 226 |
+
acc_o = acc_o * row_scale[:, None]
|
| 227 |
+
return acc_o
|
| 228 |
+
|
| 229 |
+
|
| 230 |
+
@triton.jit
|
| 231 |
+
def sigmoid(x):
|
| 232 |
+
"""
|
| 233 |
+
Compute sigmoid of x.
|
| 234 |
+
|
| 235 |
+
:param x: Input tensor of shape [BLOCK_M, BLOCK_N].
|
| 236 |
+
|
| 237 |
+
:return: Tensor of shape [BLOCK_M, BLOCK_N] containing sigmoid values.
|
| 238 |
+
"""
|
| 239 |
+
return 1.0 / (1.0 + exp(-x))
|
| 240 |
+
|
| 241 |
+
|
| 242 |
+
@triton.jit
|
| 243 |
+
def log_sigmoid(x, FASTMATH: tl.constexpr):
|
| 244 |
+
"""
|
| 245 |
+
Compute log-sigmoid of x.
|
| 246 |
+
|
| 247 |
+
:param x: Input tensor of shape [BLOCK_M, BLOCK_N].
|
| 248 |
+
:param FASTMATH: Boolean flag indicating if the fast approximation should be used.
|
| 249 |
+
|
| 250 |
+
:return: Tensor of shape [BLOCK_M, BLOCK_N] containing log-sigmoid values.
|
| 251 |
+
"""
|
| 252 |
+
if FASTMATH:
|
| 253 |
+
xc = tl.minimum(tl.abs(x), 4.0)
|
| 254 |
+
xc2 = xc * xc
|
| 255 |
+
out = tl.minimum(x, 0.0) - 0.05674870 * xc2 + 0.37664706 * xc - 0.65169323
|
| 256 |
+
return out
|
| 257 |
+
else:
|
| 258 |
+
out = tl.minimum(x, 0.0) - tl.log(1.0 + exp(-tl.abs(x)))
|
| 259 |
+
return out
|
| 260 |
+
|
| 261 |
+
|
| 262 |
+
@triton.jit
|
| 263 |
+
def online_gate(
|
| 264 |
+
a_max,
|
| 265 |
+
a_min,
|
| 266 |
+
d_max,
|
| 267 |
+
d_min,
|
| 268 |
+
gate_max,
|
| 269 |
+
scale_log2,
|
| 270 |
+
gate_threshold_log2,
|
| 271 |
+
):
|
| 272 |
+
"""
|
| 273 |
+
Determine whether to skip gate computation for the current tile based on the maximum possible gate value.
|
| 274 |
+
|
| 275 |
+
:param a_max: Maximum value of the Alpha tile.
|
| 276 |
+
:param a_min: Minimum value of the Alpha tile.
|
| 277 |
+
:param d_max: Maximum value of the Delta tile.
|
| 278 |
+
:param d_min: Minimum value of the Delta tile.
|
| 279 |
+
:param gate_max: Maximum value of the gate.
|
| 280 |
+
:param scale_log2: Log2 of the scaling factor applied to the gate max.
|
| 281 |
+
:param gate_threshold_log2: Threshold in log2-domain for gate-level skip.
|
| 282 |
+
|
| 283 |
+
:return gate_max_new: Updated gate max value after considering current tile.
|
| 284 |
+
:return skip_gate: Boolean indicating whether to skip the gate computation for this tile.
|
| 285 |
+
"""
|
| 286 |
+
gate_max_curr = tl.maximum(
|
| 287 |
+
tl.maximum(a_max * d_max, a_max * d_min),
|
| 288 |
+
tl.maximum(a_min * d_max, a_min * d_min),
|
| 289 |
+
)
|
| 290 |
+
gate_max_diff_log2 = (gate_max_curr - gate_max) * scale_log2
|
| 291 |
+
skip_gate = gate_max_diff_log2 < gate_threshold_log2
|
| 292 |
+
if skip_gate:
|
| 293 |
+
gate_max_new = gate_max
|
| 294 |
+
else:
|
| 295 |
+
gate_max_new = tl.maximum(gate_max_curr, gate_max)
|
| 296 |
+
return gate_max_new, skip_gate
|
build/torch-cuda/assert_inputs.py
ADDED
|
@@ -0,0 +1,327 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from typing import Optional
|
| 2 |
+
|
| 3 |
+
import torch
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
def assert_fwd_inputs(
|
| 7 |
+
query: torch.Tensor,
|
| 8 |
+
key: torch.Tensor,
|
| 9 |
+
value: torch.Tensor,
|
| 10 |
+
query_scale: Optional[torch.Tensor] = None,
|
| 11 |
+
key_scale: Optional[torch.Tensor] = None,
|
| 12 |
+
value_scale: Optional[torch.Tensor] = None,
|
| 13 |
+
window_sizes: Optional[torch.Tensor] = None,
|
| 14 |
+
alpha: Optional[torch.Tensor] = None,
|
| 15 |
+
delta: Optional[torch.Tensor] = None,
|
| 16 |
+
cu_seqlens_q: Optional[torch.Tensor] = None,
|
| 17 |
+
cu_seqlens_k: Optional[torch.Tensor] = None,
|
| 18 |
+
seqused_q: Optional[torch.Tensor] = None,
|
| 19 |
+
seqused_k: Optional[torch.Tensor] = None,
|
| 20 |
+
num_heads_q: int = None,
|
| 21 |
+
num_heads_kv: int = None,
|
| 22 |
+
head_dim: int = None,
|
| 23 |
+
is_quant: bool = False,
|
| 24 |
+
device: torch.device = None,
|
| 25 |
+
):
|
| 26 |
+
"""
|
| 27 |
+
Assert the validity of inputs for the forward kernel.
|
| 28 |
+
|
| 29 |
+
:param query: Query tensor
|
| 30 |
+
:param key: Key tensor
|
| 31 |
+
:param value: Value tensor
|
| 32 |
+
:param query_scale: Optional query scale tensor for quantized inputs
|
| 33 |
+
:param key_scale: Optional key scale tensor for quantized inputs
|
| 34 |
+
:param value_scale: Optional value scale tensor for quantized inputs
|
| 35 |
+
:param window_sizes: Optional window sizes tensor for local attention
|
| 36 |
+
:param alpha: Alpha tensor for gated attention
|
| 37 |
+
:param delta: Delta tensor for gated attention
|
| 38 |
+
:param cu_seqlens_q: Cumulative sequence lengths for queries
|
| 39 |
+
:param cu_seqlens_k: Cumulative sequence lengths for keys
|
| 40 |
+
:param seqused_q: Sequence used for queries
|
| 41 |
+
:param seqused_k: Sequence used for keys
|
| 42 |
+
:param num_heads_q: Number of query heads
|
| 43 |
+
:param num_heads_kv: Number of key/value heads
|
| 44 |
+
:param head_dim: Head dimension
|
| 45 |
+
:param is_quant: Whether the inputs are quantized
|
| 46 |
+
:param device: Device of the tensors
|
| 47 |
+
|
| 48 |
+
:raises AssertionError: If any of the assertions fail
|
| 49 |
+
"""
|
| 50 |
+
assert device == query.device == key.device == value.device, (
|
| 51 |
+
"All inputs must be on the same device"
|
| 52 |
+
)
|
| 53 |
+
if is_quant:
|
| 54 |
+
assert query.dtype in [torch.float8_e5m2, torch.float8_e4m3fn], (
|
| 55 |
+
"Input dtype must be float8_e5m2 or float8_e4m3fn"
|
| 56 |
+
)
|
| 57 |
+
assert (
|
| 58 |
+
query_scale is not None
|
| 59 |
+
and key_scale is not None
|
| 60 |
+
and value_scale is not None
|
| 61 |
+
), "Scale tensors must be provided for quantized inputs"
|
| 62 |
+
assert query_scale.dtype in [torch.float16, torch.bfloat16, torch.float32], (
|
| 63 |
+
"Scale tensors must be float16, bfloat16, or float32"
|
| 64 |
+
)
|
| 65 |
+
assert query_scale.dtype == key_scale.dtype == value_scale.dtype, (
|
| 66 |
+
"All scale tensors must have the same dtype"
|
| 67 |
+
)
|
| 68 |
+
assert query_scale.device == key_scale.device == value_scale.device == device, (
|
| 69 |
+
"All scale tensors must be on the same device as query/key/value"
|
| 70 |
+
)
|
| 71 |
+
else:
|
| 72 |
+
assert query.dtype in [torch.float16, torch.bfloat16], (
|
| 73 |
+
"Input dtype must be float16 or bfloat16"
|
| 74 |
+
)
|
| 75 |
+
assert query.dtype == key.dtype == value.dtype, (
|
| 76 |
+
"All inputs must have the same dtype"
|
| 77 |
+
)
|
| 78 |
+
assert num_heads_q % num_heads_kv == 0, (
|
| 79 |
+
"num_heads_q must be divisible by num_heads_kv"
|
| 80 |
+
)
|
| 81 |
+
assert head_dim % 16 == 0, (
|
| 82 |
+
"head_dim must be a multiple of 16 for efficient memory access"
|
| 83 |
+
)
|
| 84 |
+
assert head_dim <= 512, (
|
| 85 |
+
"head_dim must be less than or equal to 512 for efficient memory access"
|
| 86 |
+
)
|
| 87 |
+
if alpha is not None and delta is not None:
|
| 88 |
+
assert device == alpha.device == delta.device, (
|
| 89 |
+
"All inputs must be on the same device"
|
| 90 |
+
)
|
| 91 |
+
if cu_seqlens_q is not None and cu_seqlens_k is not None:
|
| 92 |
+
assert device == cu_seqlens_q.device == cu_seqlens_k.device, (
|
| 93 |
+
"All inputs must be on the same device"
|
| 94 |
+
)
|
| 95 |
+
assert cu_seqlens_q.dtype == cu_seqlens_k.dtype == torch.int32, (
|
| 96 |
+
"cu_seqlens_q and cu_seqlens_k must be int32"
|
| 97 |
+
)
|
| 98 |
+
if seqused_q is not None and seqused_k is not None:
|
| 99 |
+
assert device == seqused_q.device == seqused_k.device, (
|
| 100 |
+
"All inputs must be on the same device"
|
| 101 |
+
)
|
| 102 |
+
assert seqused_q.dtype == seqused_k.dtype == torch.int32, (
|
| 103 |
+
"seqused_q and seqused_k must be int32"
|
| 104 |
+
)
|
| 105 |
+
if window_sizes is not None:
|
| 106 |
+
assert window_sizes.dtype == torch.int32, "window_sizes must be int32"
|
| 107 |
+
assert window_sizes.ndim == 2 and window_sizes.shape[1] == 4, (
|
| 108 |
+
"window_sizes must have shape [num_kv_heads, 4] with columns [window_sink, window_left, window_right, window_dist]"
|
| 109 |
+
)
|
| 110 |
+
|
| 111 |
+
|
| 112 |
+
def assert_bwd_inputs(
|
| 113 |
+
query: torch.Tensor,
|
| 114 |
+
key: torch.Tensor,
|
| 115 |
+
value: torch.Tensor,
|
| 116 |
+
out: torch.Tensor,
|
| 117 |
+
dout: torch.Tensor,
|
| 118 |
+
lse: torch.Tensor,
|
| 119 |
+
query_scale: Optional[torch.Tensor] = None,
|
| 120 |
+
key_scale: Optional[torch.Tensor] = None,
|
| 121 |
+
value_scale: Optional[torch.Tensor] = None,
|
| 122 |
+
window_sizes: Optional[torch.Tensor] = None,
|
| 123 |
+
alpha: Optional[torch.Tensor] = None,
|
| 124 |
+
delta: Optional[torch.Tensor] = None,
|
| 125 |
+
cu_seqlens_q: Optional[torch.Tensor] = None,
|
| 126 |
+
cu_seqlens_k: Optional[torch.Tensor] = None,
|
| 127 |
+
seqused_q: Optional[torch.Tensor] = None,
|
| 128 |
+
seqused_k: Optional[torch.Tensor] = None,
|
| 129 |
+
num_heads_q: int = None,
|
| 130 |
+
num_heads_kv: int = None,
|
| 131 |
+
head_dim: int = None,
|
| 132 |
+
is_quant: bool = False,
|
| 133 |
+
device: torch.device = None,
|
| 134 |
+
):
|
| 135 |
+
"""
|
| 136 |
+
Assert the validity of inputs for the backward base kernel.
|
| 137 |
+
|
| 138 |
+
:param query: Query tensor
|
| 139 |
+
:param key: Key tensor
|
| 140 |
+
:param value: Value tensor
|
| 141 |
+
:param out: Output tensor
|
| 142 |
+
:param dout: Gradient of the output tensor
|
| 143 |
+
:param lse: Log-sum-exp tensor
|
| 144 |
+
:param query_scale: Optional query scale tensor for quantized inputs
|
| 145 |
+
:param key_scale: Optional key scale tensor for quantized inputs
|
| 146 |
+
:param value_scale: Optional value scale tensor for quantized inputs
|
| 147 |
+
:param window_sizes: Optional window sizes tensor for local attention
|
| 148 |
+
:param alpha: Alpha tensor for gated attention
|
| 149 |
+
:param delta: Delta tensor for gated attention
|
| 150 |
+
:param cu_seqlens_q: Cumulative sequence lengths for queries
|
| 151 |
+
:param cu_seqlens_k: Cumulative sequence lengths for keys
|
| 152 |
+
:param seqused_q: Sequence used for queries
|
| 153 |
+
:param seqused_k: Sequence used for keys
|
| 154 |
+
:param num_heads_q: Number of query heads
|
| 155 |
+
:param num_heads_kv: Number of key/value heads
|
| 156 |
+
:param head_dim: Head dimension
|
| 157 |
+
:param is_quant: Whether the inputs are quantized
|
| 158 |
+
:param device: Device of the tensors
|
| 159 |
+
|
| 160 |
+
:raises AssertionError: If any of the assertions fail
|
| 161 |
+
"""
|
| 162 |
+
assert (
|
| 163 |
+
device
|
| 164 |
+
== query.device
|
| 165 |
+
== key.device
|
| 166 |
+
== value.device
|
| 167 |
+
== out.device
|
| 168 |
+
== dout.device
|
| 169 |
+
== lse.device
|
| 170 |
+
), "All inputs must be on the same device"
|
| 171 |
+
if is_quant:
|
| 172 |
+
assert query.dtype in [torch.float8_e5m2, torch.float8_e4m3fn], (
|
| 173 |
+
"Input dtype must be float8_e5m2 or float8_e4m3fn"
|
| 174 |
+
)
|
| 175 |
+
assert (
|
| 176 |
+
query_scale is not None
|
| 177 |
+
and key_scale is not None
|
| 178 |
+
and value_scale is not None
|
| 179 |
+
), "Scale tensors must be provided for quantized inputs"
|
| 180 |
+
assert query_scale.dtype in [torch.float16, torch.bfloat16, torch.float32], (
|
| 181 |
+
"Scale tensors must be float16, bfloat16, or float32"
|
| 182 |
+
)
|
| 183 |
+
assert query_scale.dtype == key_scale.dtype == value_scale.dtype, (
|
| 184 |
+
"All scale tensors must have the same dtype"
|
| 185 |
+
)
|
| 186 |
+
assert query_scale.device == key_scale.device == value_scale.device == device, (
|
| 187 |
+
"All scale tensors must be on the same device as query/key/value"
|
| 188 |
+
)
|
| 189 |
+
assert query.dtype == key.dtype == value.dtype, (
|
| 190 |
+
"All inputs must have the same dtype"
|
| 191 |
+
)
|
| 192 |
+
else:
|
| 193 |
+
assert query.dtype in [torch.float16, torch.bfloat16], (
|
| 194 |
+
"Input dtype must be float16 or bfloat16"
|
| 195 |
+
)
|
| 196 |
+
assert query.dtype == key.dtype == value.dtype == out.dtype == dout.dtype, (
|
| 197 |
+
"All inputs must have the same dtype"
|
| 198 |
+
)
|
| 199 |
+
assert lse.dtype == torch.float32, (
|
| 200 |
+
"lse must be float32 for numerical stability in backward pass"
|
| 201 |
+
)
|
| 202 |
+
assert num_heads_q % num_heads_kv == 0, (
|
| 203 |
+
"num_heads_q must be divisible by num_heads_kv"
|
| 204 |
+
)
|
| 205 |
+
assert head_dim % 16 == 0, (
|
| 206 |
+
"head_dim must be a multiple of 16 for efficient memory access"
|
| 207 |
+
)
|
| 208 |
+
assert head_dim <= 512, (
|
| 209 |
+
"head_dim must be less than or equal to 512 for efficient memory access"
|
| 210 |
+
)
|
| 211 |
+
if alpha is not None and delta is not None:
|
| 212 |
+
assert device == alpha.device == delta.device, (
|
| 213 |
+
"All inputs must be on the same device"
|
| 214 |
+
)
|
| 215 |
+
if cu_seqlens_q is not None and cu_seqlens_k is not None:
|
| 216 |
+
assert device == cu_seqlens_q.device == cu_seqlens_k.device, (
|
| 217 |
+
"All inputs must be on the same device"
|
| 218 |
+
)
|
| 219 |
+
assert cu_seqlens_q.dtype == cu_seqlens_k.dtype == torch.int32, (
|
| 220 |
+
"cu_seqlens_q and cu_seqlens_k must be int32"
|
| 221 |
+
)
|
| 222 |
+
if seqused_q is not None and seqused_k is not None:
|
| 223 |
+
assert device == seqused_q.device == seqused_k.device, (
|
| 224 |
+
"All inputs must be on the same device"
|
| 225 |
+
)
|
| 226 |
+
assert seqused_q.dtype == seqused_k.dtype == torch.int32, (
|
| 227 |
+
"seqused_q and seqused_k must be int32"
|
| 228 |
+
)
|
| 229 |
+
if window_sizes is not None:
|
| 230 |
+
assert window_sizes.dtype == torch.int32, "window_sizes must be int32"
|
| 231 |
+
assert window_sizes.ndim == 2 and window_sizes.shape[1] == 4, (
|
| 232 |
+
"window_sizes must have shape [num_kv_heads, 4] with columns [window_sink, window_left, window_right, window_dist]"
|
| 233 |
+
)
|
| 234 |
+
|
| 235 |
+
|
| 236 |
+
def assert_dec_inputs(
|
| 237 |
+
query: torch.Tensor,
|
| 238 |
+
key: torch.Tensor,
|
| 239 |
+
value: torch.Tensor,
|
| 240 |
+
query_scale: Optional[torch.Tensor] = None,
|
| 241 |
+
key_scale: Optional[torch.Tensor] = None,
|
| 242 |
+
value_scale: Optional[torch.Tensor] = None,
|
| 243 |
+
window_sizes: Optional[torch.Tensor] = None,
|
| 244 |
+
alpha: Optional[torch.Tensor] = None,
|
| 245 |
+
delta: Optional[torch.Tensor] = None,
|
| 246 |
+
cu_seqlens_k: Optional[torch.Tensor] = None,
|
| 247 |
+
seqused_k: Optional[torch.Tensor] = None,
|
| 248 |
+
num_heads_q: int = None,
|
| 249 |
+
num_heads_kv: int = None,
|
| 250 |
+
head_dim: int = None,
|
| 251 |
+
is_quant: bool = False,
|
| 252 |
+
device: torch.device = None,
|
| 253 |
+
):
|
| 254 |
+
"""
|
| 255 |
+
Assert the validity of inputs for the decode kernel.
|
| 256 |
+
|
| 257 |
+
:param query: Query tensor
|
| 258 |
+
:param key: Key tensor
|
| 259 |
+
:param value: Value tensor
|
| 260 |
+
:param query_scale: Optional query scale tensor for quantized inputs
|
| 261 |
+
:param key_scale: Optional key scale tensor for quantized inputs
|
| 262 |
+
:param value_scale: Optional value scale tensor for quantized inputs
|
| 263 |
+
:param window_sizes: Optional window sizes tensor for local attention
|
| 264 |
+
:param alpha: Alpha tensor for gated attention
|
| 265 |
+
:param delta: Delta tensor for gated attention
|
| 266 |
+
:param cu_seqlens_k: Cumulative sequence lengths for keys
|
| 267 |
+
:param seqused_k: Sequence used for keys
|
| 268 |
+
:param num_heads_q: Number of query heads
|
| 269 |
+
:param num_heads_kv: Number of key/value heads
|
| 270 |
+
:param head_dim: Head dimension
|
| 271 |
+
:param is_quant: Whether the inputs are quantized
|
| 272 |
+
:param device: Device of the tensors
|
| 273 |
+
|
| 274 |
+
:raises AssertionError: If any of the assertions fail
|
| 275 |
+
"""
|
| 276 |
+
assert device == query.device == key.device == value.device, (
|
| 277 |
+
"All inputs must be on the same device"
|
| 278 |
+
)
|
| 279 |
+
if is_quant:
|
| 280 |
+
assert query.dtype in [torch.float8_e5m2, torch.float8_e4m3fn], (
|
| 281 |
+
"Input dtype must be float8_e5m2 or float8_e4m3fn"
|
| 282 |
+
)
|
| 283 |
+
assert (
|
| 284 |
+
query_scale is not None
|
| 285 |
+
and key_scale is not None
|
| 286 |
+
and value_scale is not None
|
| 287 |
+
), "Scale tensors must be provided for quantized inputs"
|
| 288 |
+
assert query_scale.dtype in [torch.float16, torch.bfloat16, torch.float32], (
|
| 289 |
+
"Scale tensors must be float16, bfloat16, or float32"
|
| 290 |
+
)
|
| 291 |
+
assert query_scale.dtype == key_scale.dtype == value_scale.dtype, (
|
| 292 |
+
"All scale tensors must have the same dtype"
|
| 293 |
+
)
|
| 294 |
+
assert query_scale.device == key_scale.device == value_scale.device == device, (
|
| 295 |
+
"All scale tensors must be on the same device as query/key/value"
|
| 296 |
+
)
|
| 297 |
+
else:
|
| 298 |
+
assert query.dtype in [torch.float16, torch.bfloat16], (
|
| 299 |
+
"Input dtype must be float16 or bfloat16"
|
| 300 |
+
)
|
| 301 |
+
assert query.dtype == key.dtype == value.dtype, (
|
| 302 |
+
"All inputs must have the same dtype"
|
| 303 |
+
)
|
| 304 |
+
assert num_heads_q % num_heads_kv == 0, (
|
| 305 |
+
"num_heads_q must be divisible by num_heads_kv"
|
| 306 |
+
)
|
| 307 |
+
assert head_dim % 16 == 0, (
|
| 308 |
+
"head_dim must be a multiple of 16 for efficient memory access"
|
| 309 |
+
)
|
| 310 |
+
assert head_dim <= 512, (
|
| 311 |
+
"head_dim must be less than or equal to 512 for efficient memory access"
|
| 312 |
+
)
|
| 313 |
+
if alpha is not None and delta is not None:
|
| 314 |
+
assert device == alpha.device == delta.device, (
|
| 315 |
+
"All inputs must be on the same device"
|
| 316 |
+
)
|
| 317 |
+
if cu_seqlens_k is not None:
|
| 318 |
+
assert device == cu_seqlens_k.device, "All inputs must be on the same device"
|
| 319 |
+
assert cu_seqlens_k.dtype == torch.int32, "cu_seqlens_k must be int32"
|
| 320 |
+
if seqused_k is not None:
|
| 321 |
+
assert device == seqused_k.device, "All inputs must be on the same device"
|
| 322 |
+
assert seqused_k.dtype == torch.int32, "seqused_k must be int32"
|
| 323 |
+
if window_sizes is not None:
|
| 324 |
+
assert window_sizes.dtype == torch.int32, "window_sizes must be int32"
|
| 325 |
+
assert window_sizes.ndim == 2 and window_sizes.shape[1] == 4, (
|
| 326 |
+
"window_sizes must have shape [num_kv_heads, 4] with columns [window_sink, window_left, window_right, window_dist]"
|
| 327 |
+
)
|
build/torch-cuda/autotuner.py
ADDED
|
@@ -0,0 +1,360 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
import triton
|
| 3 |
+
|
| 4 |
+
|
| 5 |
+
def _get_max_shared_mem():
|
| 6 |
+
props = torch.cuda.get_device_properties(torch.cuda.current_device())
|
| 7 |
+
return getattr(
|
| 8 |
+
props, "shared_memory_per_block_optin", props.shared_memory_per_block
|
| 9 |
+
)
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
def _smem_bytes_fwd(tile_m, tile_n, tile_k, num_stages, dtype_bytes):
|
| 13 |
+
resident = tile_m * tile_k * dtype_bytes
|
| 14 |
+
pipelined = 2 * tile_n * tile_k * dtype_bytes * num_stages
|
| 15 |
+
return resident + pipelined
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
def _smem_bytes_bwd(tile_m, tile_n, tile_k, num_stages, dtype_bytes):
|
| 19 |
+
resident = 2 * tile_n * tile_k * dtype_bytes
|
| 20 |
+
pipelined = 2 * tile_m * tile_k * dtype_bytes * num_stages
|
| 21 |
+
return resident + pipelined
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
def _smem_bytes_dec(tile_m, tile_n, tile_k, num_stages, dtype_bytes):
|
| 25 |
+
resident = tile_m * tile_k * dtype_bytes
|
| 26 |
+
pipelined = 2 * tile_n * tile_k * dtype_bytes * num_stages
|
| 27 |
+
return resident + pipelined
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
def _prune_fwd_configs(configs, named_args, **kwargs):
|
| 31 |
+
tile_k = kwargs.get("TILE_K", named_args.get("TILE_K", 128))
|
| 32 |
+
dtype_bytes = named_args["Q"].element_size()
|
| 33 |
+
max_smem = _get_max_shared_mem() - 4 * 1024
|
| 34 |
+
pruned = []
|
| 35 |
+
for cfg in configs:
|
| 36 |
+
tm, tn, ns = cfg.kwargs["TILE_M"], cfg.kwargs["TILE_N"], cfg.num_stages
|
| 37 |
+
if _smem_bytes_fwd(tm, tn, tile_k, ns, dtype_bytes) <= max_smem:
|
| 38 |
+
pruned.append(cfg)
|
| 39 |
+
if not pruned:
|
| 40 |
+
pruned = [
|
| 41 |
+
min(
|
| 42 |
+
configs,
|
| 43 |
+
key=lambda c: _smem_bytes_fwd(
|
| 44 |
+
c.kwargs["TILE_M"],
|
| 45 |
+
c.kwargs["TILE_N"],
|
| 46 |
+
tile_k,
|
| 47 |
+
c.num_stages,
|
| 48 |
+
dtype_bytes,
|
| 49 |
+
),
|
| 50 |
+
)
|
| 51 |
+
]
|
| 52 |
+
return pruned
|
| 53 |
+
|
| 54 |
+
|
| 55 |
+
def _prune_bwd_configs(configs, named_args, **kwargs):
|
| 56 |
+
tile_k = kwargs.get("TILE_K", named_args.get("TILE_K", 128))
|
| 57 |
+
dtype_bytes = named_args["Q"].element_size()
|
| 58 |
+
max_smem = _get_max_shared_mem() - 4 * 1024
|
| 59 |
+
pruned = []
|
| 60 |
+
for cfg in configs:
|
| 61 |
+
tm, tn, ns = cfg.kwargs["TILE_M"], cfg.kwargs["TILE_N"], cfg.num_stages
|
| 62 |
+
if _smem_bytes_bwd(tm, tn, tile_k, ns, dtype_bytes) <= max_smem:
|
| 63 |
+
pruned.append(cfg)
|
| 64 |
+
if not pruned:
|
| 65 |
+
pruned = [
|
| 66 |
+
min(
|
| 67 |
+
configs,
|
| 68 |
+
key=lambda c: _smem_bytes_bwd(
|
| 69 |
+
c.kwargs["TILE_M"],
|
| 70 |
+
c.kwargs["TILE_N"],
|
| 71 |
+
tile_k,
|
| 72 |
+
c.num_stages,
|
| 73 |
+
dtype_bytes,
|
| 74 |
+
),
|
| 75 |
+
)
|
| 76 |
+
]
|
| 77 |
+
return pruned
|
| 78 |
+
|
| 79 |
+
|
| 80 |
+
def _prune_dec_configs(configs, named_args, **kwargs):
|
| 81 |
+
tile_k = kwargs.get("TILE_K", named_args.get("TILE_K", 128))
|
| 82 |
+
seqlen_q = kwargs.get("seqlen_q", named_args.get("seqlen_q", None))
|
| 83 |
+
dtype_bytes = named_args["Q"].element_size()
|
| 84 |
+
max_smem = _get_max_shared_mem() - 4 * 1024
|
| 85 |
+
pruned = []
|
| 86 |
+
for cfg in configs:
|
| 87 |
+
tm, tn, ns = cfg.kwargs["TILE_M"], cfg.kwargs["TILE_N"], cfg.num_stages
|
| 88 |
+
if seqlen_q is not None and tm > seqlen_q:
|
| 89 |
+
continue
|
| 90 |
+
if _smem_bytes_dec(tm, tn, tile_k, ns, dtype_bytes) <= max_smem:
|
| 91 |
+
pruned.append(cfg)
|
| 92 |
+
if not pruned:
|
| 93 |
+
pruned = [
|
| 94 |
+
min(
|
| 95 |
+
configs,
|
| 96 |
+
key=lambda c: _smem_bytes_dec(
|
| 97 |
+
c.kwargs["TILE_M"],
|
| 98 |
+
c.kwargs["TILE_N"],
|
| 99 |
+
tile_k,
|
| 100 |
+
c.num_stages,
|
| 101 |
+
dtype_bytes,
|
| 102 |
+
),
|
| 103 |
+
)
|
| 104 |
+
]
|
| 105 |
+
return pruned
|
| 106 |
+
|
| 107 |
+
|
| 108 |
+
def get_fwd_dense_autotune_configs():
|
| 109 |
+
configs = []
|
| 110 |
+
for tile_m in [64, 128, 256]:
|
| 111 |
+
for tile_n in [32, 64, 128]:
|
| 112 |
+
for num_warps in [4, 8]:
|
| 113 |
+
for num_stages in [1, 2, 3]:
|
| 114 |
+
configs.append(
|
| 115 |
+
triton.Config(
|
| 116 |
+
{"TILE_M": tile_m, "TILE_N": tile_n},
|
| 117 |
+
num_warps=num_warps,
|
| 118 |
+
num_stages=num_stages,
|
| 119 |
+
num_ctas=1,
|
| 120 |
+
)
|
| 121 |
+
)
|
| 122 |
+
return configs
|
| 123 |
+
|
| 124 |
+
|
| 125 |
+
def get_fwd_sparse_autotune_configs():
|
| 126 |
+
configs = []
|
| 127 |
+
for tile_m in [64, 128, 256]:
|
| 128 |
+
for tile_n in [32, 64, 128]:
|
| 129 |
+
for num_warps in [4, 8]:
|
| 130 |
+
for num_stages in [1, 2, 3]:
|
| 131 |
+
configs.append(
|
| 132 |
+
triton.Config(
|
| 133 |
+
{"TILE_M": tile_m, "TILE_N": tile_n},
|
| 134 |
+
num_warps=num_warps,
|
| 135 |
+
num_stages=num_stages,
|
| 136 |
+
num_ctas=1,
|
| 137 |
+
)
|
| 138 |
+
)
|
| 139 |
+
return configs
|
| 140 |
+
|
| 141 |
+
|
| 142 |
+
def get_fwd_gated_autotune_configs():
|
| 143 |
+
configs = []
|
| 144 |
+
for tile_m in [64, 128, 256]:
|
| 145 |
+
for tile_n in [32, 64, 128]:
|
| 146 |
+
for num_warps in [4, 8]:
|
| 147 |
+
for num_stages in [1, 2, 3]:
|
| 148 |
+
configs.append(
|
| 149 |
+
triton.Config(
|
| 150 |
+
{"TILE_M": tile_m, "TILE_N": tile_n},
|
| 151 |
+
num_warps=num_warps,
|
| 152 |
+
num_stages=num_stages,
|
| 153 |
+
num_ctas=1,
|
| 154 |
+
)
|
| 155 |
+
)
|
| 156 |
+
return configs
|
| 157 |
+
|
| 158 |
+
|
| 159 |
+
def get_bwd_dense_autotune_configs():
|
| 160 |
+
configs = []
|
| 161 |
+
for tile_m in [32, 64, 128]:
|
| 162 |
+
for tile_n in [64, 128, 256]:
|
| 163 |
+
for num_warps in [4, 8]:
|
| 164 |
+
for num_stages in [1, 2, 3]:
|
| 165 |
+
configs.append(
|
| 166 |
+
triton.Config(
|
| 167 |
+
{"TILE_M": tile_m, "TILE_N": tile_n},
|
| 168 |
+
num_warps=num_warps,
|
| 169 |
+
num_stages=num_stages,
|
| 170 |
+
num_ctas=1,
|
| 171 |
+
)
|
| 172 |
+
)
|
| 173 |
+
return configs
|
| 174 |
+
|
| 175 |
+
|
| 176 |
+
def get_bwd_sparse_autotune_configs():
|
| 177 |
+
configs = []
|
| 178 |
+
for tile_m in [32, 64, 128]:
|
| 179 |
+
for tile_n in [64, 128, 256]:
|
| 180 |
+
for num_warps in [4, 8]:
|
| 181 |
+
for num_stages in [1, 2, 3]:
|
| 182 |
+
configs.append(
|
| 183 |
+
triton.Config(
|
| 184 |
+
{"TILE_M": tile_m, "TILE_N": tile_n},
|
| 185 |
+
num_warps=num_warps,
|
| 186 |
+
num_stages=num_stages,
|
| 187 |
+
num_ctas=1,
|
| 188 |
+
)
|
| 189 |
+
)
|
| 190 |
+
return configs
|
| 191 |
+
|
| 192 |
+
|
| 193 |
+
def get_bwd_gated_autotune_configs():
|
| 194 |
+
configs = []
|
| 195 |
+
for tile_m in [32, 64, 128]:
|
| 196 |
+
for tile_n in [64, 128, 256]:
|
| 197 |
+
for num_warps in [4, 8]:
|
| 198 |
+
for num_stages in [1, 2, 3]:
|
| 199 |
+
configs.append(
|
| 200 |
+
triton.Config(
|
| 201 |
+
{"TILE_M": tile_m, "TILE_N": tile_n},
|
| 202 |
+
num_warps=num_warps,
|
| 203 |
+
num_stages=num_stages,
|
| 204 |
+
num_ctas=1,
|
| 205 |
+
)
|
| 206 |
+
)
|
| 207 |
+
return configs
|
| 208 |
+
|
| 209 |
+
|
| 210 |
+
def get_dec_dense_autotune_configs():
|
| 211 |
+
configs = []
|
| 212 |
+
for tile_m in [16, 32, 64]:
|
| 213 |
+
for tile_n in [64, 128, 256]:
|
| 214 |
+
for num_warps in [4, 8]:
|
| 215 |
+
for num_stages in [1, 2, 3]:
|
| 216 |
+
configs.append(
|
| 217 |
+
triton.Config(
|
| 218 |
+
{"TILE_M": tile_m, "TILE_N": tile_n},
|
| 219 |
+
num_warps=num_warps,
|
| 220 |
+
num_stages=num_stages,
|
| 221 |
+
num_ctas=1,
|
| 222 |
+
)
|
| 223 |
+
)
|
| 224 |
+
return configs
|
| 225 |
+
|
| 226 |
+
|
| 227 |
+
def get_dec_sparse_autotune_configs():
|
| 228 |
+
configs = []
|
| 229 |
+
for tile_m in [16, 32, 64]:
|
| 230 |
+
for tile_n in [64, 128, 256]:
|
| 231 |
+
for num_warps in [4, 8]:
|
| 232 |
+
for num_stages in [1, 2, 3]:
|
| 233 |
+
configs.append(
|
| 234 |
+
triton.Config(
|
| 235 |
+
{"TILE_M": tile_m, "TILE_N": tile_n},
|
| 236 |
+
num_warps=num_warps,
|
| 237 |
+
num_stages=num_stages,
|
| 238 |
+
num_ctas=1,
|
| 239 |
+
)
|
| 240 |
+
)
|
| 241 |
+
return configs
|
| 242 |
+
|
| 243 |
+
|
| 244 |
+
def get_dec_gated_autotune_configs():
|
| 245 |
+
configs = []
|
| 246 |
+
for tile_m in [16, 32, 64]:
|
| 247 |
+
for tile_n in [64, 128, 256]:
|
| 248 |
+
for num_warps in [4, 8]:
|
| 249 |
+
for num_stages in [1, 2, 3]:
|
| 250 |
+
configs.append(
|
| 251 |
+
triton.Config(
|
| 252 |
+
{"TILE_M": tile_m, "TILE_N": tile_n},
|
| 253 |
+
num_warps=num_warps,
|
| 254 |
+
num_stages=num_stages,
|
| 255 |
+
num_ctas=1,
|
| 256 |
+
)
|
| 257 |
+
)
|
| 258 |
+
return configs
|
| 259 |
+
|
| 260 |
+
|
| 261 |
+
def make_fwd_dense_autotuned_kernel(jit_kernel):
|
| 262 |
+
configs = get_fwd_dense_autotune_configs()
|
| 263 |
+
return triton.autotune(
|
| 264 |
+
configs=configs,
|
| 265 |
+
key=["SEQLEN_Q_CACHE", "SEQLEN_K_CACHE", "TILE_K", "IS_CAUSAL", "IS_LOCAL"],
|
| 266 |
+
prune_configs_by={"early_config_prune": _prune_fwd_configs},
|
| 267 |
+
)(jit_kernel)
|
| 268 |
+
|
| 269 |
+
|
| 270 |
+
def make_fwd_sparse_autotuned_kernel(jit_kernel):
|
| 271 |
+
configs = get_fwd_sparse_autotune_configs()
|
| 272 |
+
return triton.autotune(
|
| 273 |
+
configs=configs,
|
| 274 |
+
key=["SEQLEN_Q_CACHE", "SEQLEN_K_CACHE", "TILE_K", "IS_CAUSAL", "IS_LOCAL"],
|
| 275 |
+
prune_configs_by={"early_config_prune": _prune_fwd_configs},
|
| 276 |
+
)(jit_kernel)
|
| 277 |
+
|
| 278 |
+
|
| 279 |
+
def make_fwd_gated_autotuned_kernel(jit_kernel):
|
| 280 |
+
configs = get_fwd_gated_autotune_configs()
|
| 281 |
+
return triton.autotune(
|
| 282 |
+
configs=configs,
|
| 283 |
+
key=["SEQLEN_Q_CACHE", "SEQLEN_K_CACHE", "TILE_K", "IS_CAUSAL", "IS_LOCAL"],
|
| 284 |
+
prune_configs_by={"early_config_prune": _prune_fwd_configs},
|
| 285 |
+
)(jit_kernel)
|
| 286 |
+
|
| 287 |
+
|
| 288 |
+
def make_bwd_dense_autotuned_kernel(jit_kernel):
|
| 289 |
+
configs = get_bwd_dense_autotune_configs()
|
| 290 |
+
return triton.autotune(
|
| 291 |
+
configs=configs,
|
| 292 |
+
key=["SEQLEN_Q_CACHE", "SEQLEN_K_CACHE", "TILE_K", "IS_CAUSAL", "IS_LOCAL"],
|
| 293 |
+
prune_configs_by={"early_config_prune": _prune_bwd_configs},
|
| 294 |
+
)(jit_kernel)
|
| 295 |
+
|
| 296 |
+
|
| 297 |
+
def make_bwd_sparse_autotuned_kernel(jit_kernel):
|
| 298 |
+
configs = get_bwd_sparse_autotune_configs()
|
| 299 |
+
return triton.autotune(
|
| 300 |
+
configs=configs,
|
| 301 |
+
key=["SEQLEN_Q_CACHE", "SEQLEN_K_CACHE", "TILE_K", "IS_CAUSAL", "IS_LOCAL"],
|
| 302 |
+
prune_configs_by={"early_config_prune": _prune_bwd_configs},
|
| 303 |
+
)(jit_kernel)
|
| 304 |
+
|
| 305 |
+
|
| 306 |
+
def make_bwd_gated_autotuned_kernel(jit_kernel):
|
| 307 |
+
configs = get_bwd_gated_autotune_configs()
|
| 308 |
+
return triton.autotune(
|
| 309 |
+
configs=configs,
|
| 310 |
+
key=["SEQLEN_Q_CACHE", "SEQLEN_K_CACHE", "TILE_K", "IS_CAUSAL", "IS_LOCAL"],
|
| 311 |
+
prune_configs_by={"early_config_prune": _prune_bwd_configs},
|
| 312 |
+
)(jit_kernel)
|
| 313 |
+
|
| 314 |
+
|
| 315 |
+
def make_dec_dense_autotuned_kernel(jit_kernel):
|
| 316 |
+
configs = get_dec_dense_autotune_configs()
|
| 317 |
+
return triton.autotune(
|
| 318 |
+
configs=configs,
|
| 319 |
+
key=["SEQLEN_Q_CACHE", "SEQLEN_K_CACHE", "TILE_K", "IS_LOCAL"],
|
| 320 |
+
prune_configs_by={"early_config_prune": _prune_dec_configs},
|
| 321 |
+
)(jit_kernel)
|
| 322 |
+
|
| 323 |
+
|
| 324 |
+
def make_dec_sparse_autotuned_kernel(jit_kernel):
|
| 325 |
+
configs = get_dec_sparse_autotune_configs()
|
| 326 |
+
return triton.autotune(
|
| 327 |
+
configs=configs,
|
| 328 |
+
key=["SEQLEN_Q_CACHE", "SEQLEN_K_CACHE", "TILE_K", "IS_LOCAL"],
|
| 329 |
+
prune_configs_by={"early_config_prune": _prune_dec_configs},
|
| 330 |
+
)(jit_kernel)
|
| 331 |
+
|
| 332 |
+
|
| 333 |
+
def make_dec_gated_autotuned_kernel(jit_kernel):
|
| 334 |
+
configs = get_dec_gated_autotune_configs()
|
| 335 |
+
return triton.autotune(
|
| 336 |
+
configs=configs,
|
| 337 |
+
key=["SEQLEN_Q_CACHE", "SEQLEN_K_CACHE", "TILE_K", "IS_LOCAL"],
|
| 338 |
+
prune_configs_by={"early_config_prune": _prune_dec_configs},
|
| 339 |
+
)(jit_kernel)
|
| 340 |
+
|
| 341 |
+
|
| 342 |
+
class AutotunedKernel:
|
| 343 |
+
STRIP_KWARGS = {"TILE_M", "TILE_N", "num_warps", "num_stages", "num_ctas"}
|
| 344 |
+
|
| 345 |
+
def __init__(self, autotuned_kernel):
|
| 346 |
+
self._autotuned = autotuned_kernel
|
| 347 |
+
|
| 348 |
+
def __getitem__(self, grid):
|
| 349 |
+
autotuned = self._autotuned
|
| 350 |
+
|
| 351 |
+
class _Launcher:
|
| 352 |
+
def __call__(_, *args, **kwargs):
|
| 353 |
+
for key in AutotunedKernel.STRIP_KWARGS:
|
| 354 |
+
kwargs.pop(key, None)
|
| 355 |
+
return autotuned[grid](*args, **kwargs)
|
| 356 |
+
|
| 357 |
+
return _Launcher()
|
| 358 |
+
|
| 359 |
+
def __getattr__(self, name):
|
| 360 |
+
return getattr(self._autotuned, name)
|
build/torch-cuda/block_info.py
ADDED
|
@@ -0,0 +1,277 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import triton
|
| 2 |
+
import triton.language as tl
|
| 3 |
+
|
| 4 |
+
|
| 5 |
+
@triton.jit
|
| 6 |
+
def get_n_block_min_max(
|
| 7 |
+
seqlen_q,
|
| 8 |
+
seqlen_k,
|
| 9 |
+
m_block,
|
| 10 |
+
split_idx,
|
| 11 |
+
num_splits,
|
| 12 |
+
window_size_sink,
|
| 13 |
+
window_size_left,
|
| 14 |
+
window_size_right,
|
| 15 |
+
window_size_dist,
|
| 16 |
+
TILE_N: tl.constexpr,
|
| 17 |
+
TILE_M: tl.constexpr,
|
| 18 |
+
IS_CAUSAL: tl.constexpr,
|
| 19 |
+
IS_LOCAL: tl.constexpr,
|
| 20 |
+
IS_SPLIT_KV: tl.constexpr,
|
| 21 |
+
QHEAD_PER_KVHEAD_PACKGQA: tl.constexpr,
|
| 22 |
+
):
|
| 23 |
+
n_block_max = tl.cdiv(seqlen_k, TILE_N)
|
| 24 |
+
n_block_min = 0
|
| 25 |
+
n_block_window_max = n_block_max
|
| 26 |
+
n_block_window_min = n_block_min
|
| 27 |
+
n_block_sink_min = 0
|
| 28 |
+
n_block_sink_max = 0
|
| 29 |
+
if IS_CAUSAL or IS_LOCAL:
|
| 30 |
+
m_idx_max = (m_block + 1) * TILE_M
|
| 31 |
+
if QHEAD_PER_KVHEAD_PACKGQA > 1:
|
| 32 |
+
m_idx_max = tl.cdiv(m_idx_max, QHEAD_PER_KVHEAD_PACKGQA)
|
| 33 |
+
m_idx_max = tl.minimum(m_idx_max, seqlen_q)
|
| 34 |
+
n_idx = m_idx_max + seqlen_k - seqlen_q
|
| 35 |
+
n_block_max = tl.minimum(n_block_max, tl.cdiv(n_idx, TILE_N))
|
| 36 |
+
if IS_LOCAL:
|
| 37 |
+
n_idx_right = n_idx - window_size_dist - window_size_right
|
| 38 |
+
n_block_window_max = tl.minimum(
|
| 39 |
+
n_block_window_max, tl.maximum(tl.cdiv(n_idx_right, TILE_N), 0)
|
| 40 |
+
)
|
| 41 |
+
if IS_LOCAL:
|
| 42 |
+
n_block_sink_max = tl.minimum(
|
| 43 |
+
tl.cdiv(window_size_sink, TILE_N),
|
| 44 |
+
tl.maximum(tl.cdiv(n_idx, TILE_N), 0),
|
| 45 |
+
)
|
| 46 |
+
n_block_sink_exclude_max = tl.cdiv(window_size_sink, TILE_N)
|
| 47 |
+
m_idx_min = m_block * TILE_M
|
| 48 |
+
if QHEAD_PER_KVHEAD_PACKGQA > 1:
|
| 49 |
+
m_idx_min = m_idx_min // QHEAD_PER_KVHEAD_PACKGQA
|
| 50 |
+
n_idx = m_idx_min + seqlen_k - seqlen_q
|
| 51 |
+
n_idx_dist = n_idx - window_size_dist
|
| 52 |
+
n_block_min = tl.maximum(n_idx_dist // TILE_N, 0)
|
| 53 |
+
n_block_min = tl.maximum(n_block_min, n_block_sink_exclude_max)
|
| 54 |
+
n_idx_left = n_idx - window_size_dist - window_size_right - window_size_left
|
| 55 |
+
n_block_window_min = tl.maximum(n_idx_left // TILE_N, 0)
|
| 56 |
+
n_block_window_min = tl.maximum(n_block_window_min, n_block_sink_exclude_max)
|
| 57 |
+
if IS_SPLIT_KV:
|
| 58 |
+
if IS_LOCAL:
|
| 59 |
+
n_block_min = n_block_window_min
|
| 60 |
+
n_block_max_with_diag = n_block_max
|
| 61 |
+
n_block_max = tl.maximum(n_block_window_max, n_block_min)
|
| 62 |
+
total_n_blocks = tl.maximum(n_block_max - n_block_min, 0)
|
| 63 |
+
base = total_n_blocks // num_splits
|
| 64 |
+
extra = total_n_blocks % num_splits
|
| 65 |
+
n_block_min_new = n_block_min + tl.where(
|
| 66 |
+
split_idx < extra,
|
| 67 |
+
split_idx * (base + 1),
|
| 68 |
+
extra * (base + 1) + (split_idx - extra) * base,
|
| 69 |
+
)
|
| 70 |
+
n_block_count = tl.where(split_idx < extra, base + 1, base)
|
| 71 |
+
n_block_max = tl.minimum(n_block_min_new + n_block_count, n_block_max)
|
| 72 |
+
n_block_min = n_block_min_new
|
| 73 |
+
if IS_LOCAL:
|
| 74 |
+
n_block_max = tl.where(
|
| 75 |
+
split_idx >= num_splits - 1,
|
| 76 |
+
n_block_max_with_diag,
|
| 77 |
+
n_block_max,
|
| 78 |
+
)
|
| 79 |
+
if IS_SPLIT_KV:
|
| 80 |
+
n_block_sink_max = tl.where(split_idx == 0, n_block_sink_max, 0)
|
| 81 |
+
return (
|
| 82 |
+
n_block_min,
|
| 83 |
+
n_block_max,
|
| 84 |
+
n_block_window_min,
|
| 85 |
+
n_block_window_max,
|
| 86 |
+
n_block_sink_min,
|
| 87 |
+
n_block_sink_max,
|
| 88 |
+
)
|
| 89 |
+
|
| 90 |
+
|
| 91 |
+
@triton.jit
|
| 92 |
+
def get_m_block_min_max(
|
| 93 |
+
seqlen_q,
|
| 94 |
+
seqlen_k,
|
| 95 |
+
n_block,
|
| 96 |
+
split_idx,
|
| 97 |
+
num_splits,
|
| 98 |
+
window_size_sink,
|
| 99 |
+
window_size_left,
|
| 100 |
+
window_size_right,
|
| 101 |
+
window_size_dist,
|
| 102 |
+
TILE_N: tl.constexpr,
|
| 103 |
+
TILE_M: tl.constexpr,
|
| 104 |
+
IS_CAUSAL: tl.constexpr,
|
| 105 |
+
IS_LOCAL: tl.constexpr,
|
| 106 |
+
IS_SPLIT_QO: tl.constexpr,
|
| 107 |
+
):
|
| 108 |
+
m_block_max = tl.cdiv(seqlen_q, TILE_M)
|
| 109 |
+
m_block_min = 0
|
| 110 |
+
m_block_window_max = m_block_max
|
| 111 |
+
m_block_window_min = m_block_min
|
| 112 |
+
m_block_sink_min = 0
|
| 113 |
+
m_block_sink_max = 0
|
| 114 |
+
if IS_CAUSAL or IS_LOCAL:
|
| 115 |
+
n_idx_min = n_block * TILE_N
|
| 116 |
+
m_idx = n_idx_min + seqlen_q - seqlen_k
|
| 117 |
+
m_block_min = tl.maximum(m_block_min, m_idx // TILE_M)
|
| 118 |
+
if IS_LOCAL:
|
| 119 |
+
m_idx_right = m_idx + window_size_dist + window_size_right
|
| 120 |
+
m_block_window_min = tl.maximum(m_block_window_min, m_idx_right // TILE_M)
|
| 121 |
+
if IS_LOCAL:
|
| 122 |
+
n_block_sink_exclude_max = tl.cdiv(window_size_sink, TILE_N)
|
| 123 |
+
is_sink_block = n_block < n_block_sink_exclude_max
|
| 124 |
+
n_idx_min = n_block * TILE_N
|
| 125 |
+
m_idx_sink = n_idx_min + seqlen_q - seqlen_k
|
| 126 |
+
m_block_sink_min = tl.maximum(m_idx_sink // TILE_M, 0)
|
| 127 |
+
m_block_sink_max = tl.where(is_sink_block, tl.cdiv(seqlen_q, TILE_M), 0)
|
| 128 |
+
m_block_sink_min = tl.where(is_sink_block, m_block_sink_min, 0)
|
| 129 |
+
n_idx_max = (n_block + 1) * TILE_N
|
| 130 |
+
m_idx = n_idx_max + seqlen_q - seqlen_k
|
| 131 |
+
m_idx_dist = m_idx + window_size_dist
|
| 132 |
+
m_block_max = tl.minimum(m_block_max, tl.cdiv(m_idx_dist, TILE_M))
|
| 133 |
+
m_idx_left = m_idx + window_size_dist + window_size_right + window_size_left
|
| 134 |
+
m_block_window_max = tl.minimum(m_block_window_max, tl.cdiv(m_idx_left, TILE_M))
|
| 135 |
+
m_block_min = tl.where(is_sink_block, 0, m_block_min)
|
| 136 |
+
m_block_max = tl.where(is_sink_block, 0, m_block_max)
|
| 137 |
+
m_block_window_min = tl.where(is_sink_block, 0, m_block_window_min)
|
| 138 |
+
m_block_window_max = tl.where(is_sink_block, 0, m_block_window_max)
|
| 139 |
+
if IS_SPLIT_QO:
|
| 140 |
+
if IS_LOCAL:
|
| 141 |
+
n_idx_max = (n_block + 1) * TILE_N
|
| 142 |
+
m_idx = n_idx_max + seqlen_q - seqlen_k
|
| 143 |
+
m_block_min_no_mask = tl.maximum(m_block_min, tl.cdiv(m_idx, TILE_M))
|
| 144 |
+
m_block_window_min = tl.maximum(m_block_window_min, m_block_min_no_mask)
|
| 145 |
+
win_blocks = tl.maximum(m_block_window_max - m_block_window_min, 0)
|
| 146 |
+
base = win_blocks // num_splits
|
| 147 |
+
extra = win_blocks % num_splits
|
| 148 |
+
win_off = tl.where(
|
| 149 |
+
split_idx < extra,
|
| 150 |
+
split_idx * (base + 1),
|
| 151 |
+
extra * (base + 1) + (split_idx - extra) * base,
|
| 152 |
+
)
|
| 153 |
+
win_cnt = tl.where(split_idx < extra, base + 1, base)
|
| 154 |
+
m_block_window_min = m_block_window_min + win_off
|
| 155 |
+
m_block_window_max = tl.minimum(
|
| 156 |
+
m_block_window_min + win_cnt, m_block_window_max
|
| 157 |
+
)
|
| 158 |
+
# First split keeps diagonal, others skip it
|
| 159 |
+
m_block_min = tl.where(split_idx == 0, m_block_min, m_block_max)
|
| 160 |
+
m_block_sink_max = tl.where(split_idx == 0, m_block_sink_max, 0)
|
| 161 |
+
m_block_sink_min = tl.where(split_idx == 0, m_block_sink_min, 0)
|
| 162 |
+
else:
|
| 163 |
+
total_m_blocks = tl.maximum(m_block_max - m_block_min, 0)
|
| 164 |
+
base = total_m_blocks // num_splits
|
| 165 |
+
extra = total_m_blocks % num_splits
|
| 166 |
+
m_block_min_new = m_block_min + tl.where(
|
| 167 |
+
split_idx < extra,
|
| 168 |
+
split_idx * (base + 1),
|
| 169 |
+
extra * (base + 1) + (split_idx - extra) * base,
|
| 170 |
+
)
|
| 171 |
+
m_block_count = tl.where(split_idx < extra, base + 1, base)
|
| 172 |
+
m_block_max = tl.minimum(m_block_min_new + m_block_count, m_block_max)
|
| 173 |
+
m_block_min = m_block_min_new
|
| 174 |
+
return (
|
| 175 |
+
m_block_min,
|
| 176 |
+
m_block_max,
|
| 177 |
+
m_block_window_min,
|
| 178 |
+
m_block_window_max,
|
| 179 |
+
m_block_sink_min,
|
| 180 |
+
m_block_sink_max,
|
| 181 |
+
)
|
| 182 |
+
|
| 183 |
+
|
| 184 |
+
@triton.jit
|
| 185 |
+
def get_n_block_min_causal_local_mask(
|
| 186 |
+
seqlen_q,
|
| 187 |
+
seqlen_k,
|
| 188 |
+
m_block,
|
| 189 |
+
n_block_min,
|
| 190 |
+
window_size_right,
|
| 191 |
+
window_size_dist,
|
| 192 |
+
TILE_N: tl.constexpr,
|
| 193 |
+
TILE_M: tl.constexpr,
|
| 194 |
+
IS_LOCAL: tl.constexpr,
|
| 195 |
+
QHEAD_PER_KVHEAD_PACKGQA: tl.constexpr,
|
| 196 |
+
):
|
| 197 |
+
m_idx_min = m_block * TILE_M
|
| 198 |
+
if QHEAD_PER_KVHEAD_PACKGQA > 1:
|
| 199 |
+
m_idx_min = m_idx_min // QHEAD_PER_KVHEAD_PACKGQA
|
| 200 |
+
n_idx = m_idx_min + seqlen_k - seqlen_q
|
| 201 |
+
n_idx_right = (
|
| 202 |
+
n_idx if not IS_LOCAL else n_idx - window_size_dist - window_size_right
|
| 203 |
+
)
|
| 204 |
+
return tl.maximum(n_block_min, n_idx_right // TILE_N)
|
| 205 |
+
|
| 206 |
+
|
| 207 |
+
@triton.jit
|
| 208 |
+
def get_n_block_min_before_local_mask(
|
| 209 |
+
seqlen_q,
|
| 210 |
+
seqlen_k,
|
| 211 |
+
m_block,
|
| 212 |
+
n_block_min,
|
| 213 |
+
window_size_left,
|
| 214 |
+
window_size_right,
|
| 215 |
+
window_size_dist,
|
| 216 |
+
TILE_N: tl.constexpr,
|
| 217 |
+
TILE_M: tl.constexpr,
|
| 218 |
+
IS_LOCAL: tl.constexpr,
|
| 219 |
+
QHEAD_PER_KVHEAD_PACKGQA: tl.constexpr,
|
| 220 |
+
):
|
| 221 |
+
if not IS_LOCAL:
|
| 222 |
+
return n_block_min
|
| 223 |
+
else:
|
| 224 |
+
m_idx_max = (m_block + 1) * TILE_M
|
| 225 |
+
if QHEAD_PER_KVHEAD_PACKGQA > 1:
|
| 226 |
+
m_idx_max = tl.cdiv(m_idx_max, QHEAD_PER_KVHEAD_PACKGQA)
|
| 227 |
+
m_idx_max = tl.minimum(m_idx_max, seqlen_q)
|
| 228 |
+
n_idx = m_idx_max + seqlen_k - seqlen_q
|
| 229 |
+
n_idx_left = n_idx - window_size_dist - window_size_right - window_size_left
|
| 230 |
+
return tl.maximum(n_block_min, tl.cdiv(n_idx_left, TILE_N))
|
| 231 |
+
|
| 232 |
+
|
| 233 |
+
@triton.jit
|
| 234 |
+
def get_m_block_min_causal_local_mask(
|
| 235 |
+
seqlen_q,
|
| 236 |
+
seqlen_k,
|
| 237 |
+
n_block,
|
| 238 |
+
m_block_min,
|
| 239 |
+
window_size_right,
|
| 240 |
+
window_size_dist,
|
| 241 |
+
TILE_N: tl.constexpr,
|
| 242 |
+
TILE_M: tl.constexpr,
|
| 243 |
+
IS_CAUSAL: tl.constexpr,
|
| 244 |
+
IS_LOCAL: tl.constexpr,
|
| 245 |
+
):
|
| 246 |
+
if not IS_CAUSAL and not IS_LOCAL:
|
| 247 |
+
return m_block_min
|
| 248 |
+
else:
|
| 249 |
+
n_idx_max = (n_block + 1) * TILE_N
|
| 250 |
+
m_idx = n_idx_max + seqlen_q - seqlen_k
|
| 251 |
+
if IS_LOCAL:
|
| 252 |
+
m_idx_right = m_idx + window_size_dist + window_size_right
|
| 253 |
+
else:
|
| 254 |
+
m_idx_right = m_idx
|
| 255 |
+
return tl.maximum(m_block_min, tl.cdiv(m_idx_right, TILE_M))
|
| 256 |
+
|
| 257 |
+
|
| 258 |
+
@triton.jit
|
| 259 |
+
def get_m_block_max_before_local_mask(
|
| 260 |
+
seqlen_q,
|
| 261 |
+
seqlen_k,
|
| 262 |
+
n_block,
|
| 263 |
+
m_block_max,
|
| 264 |
+
window_size_left,
|
| 265 |
+
window_size_right,
|
| 266 |
+
window_size_dist,
|
| 267 |
+
TILE_N: tl.constexpr,
|
| 268 |
+
TILE_M: tl.constexpr,
|
| 269 |
+
IS_LOCAL: tl.constexpr,
|
| 270 |
+
):
|
| 271 |
+
if not IS_LOCAL:
|
| 272 |
+
return m_block_max
|
| 273 |
+
else:
|
| 274 |
+
n_idx_min = n_block * TILE_N
|
| 275 |
+
m_idx = n_idx_min + seqlen_q - seqlen_k
|
| 276 |
+
m_idx_left = m_idx + window_size_dist + window_size_right + window_size_left
|
| 277 |
+
return tl.minimum(m_block_max, m_idx_left // TILE_M)
|
build/torch-cuda/cache_utils.py
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import functools
|
| 2 |
+
import inspect
|
| 3 |
+
from collections import OrderedDict
|
| 4 |
+
|
| 5 |
+
import torch
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
@functools.lru_cache(maxsize=8)
|
| 9 |
+
def get_device_num_sms(device: torch.device) -> int:
|
| 10 |
+
"""
|
| 11 |
+
Get the SM count for a given device.
|
| 12 |
+
|
| 13 |
+
:param device: Torch device.
|
| 14 |
+
|
| 15 |
+
:return num_sms: Number of streaming multiprocessors.
|
| 16 |
+
"""
|
| 17 |
+
return torch.cuda.get_device_properties(device).multi_processor_count
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
@functools.lru_cache(maxsize=8)
|
| 21 |
+
def get_device_arch(device: torch.device) -> int:
|
| 22 |
+
"""
|
| 23 |
+
Get the architecture for a given device.
|
| 24 |
+
|
| 25 |
+
:param device: Torch device.
|
| 26 |
+
|
| 27 |
+
:return arch: Architecture model as a number.
|
| 28 |
+
"""
|
| 29 |
+
if device.type == "cuda":
|
| 30 |
+
major, minor = torch.cuda.get_device_capability(device)
|
| 31 |
+
sm = major * 10 + minor
|
| 32 |
+
return sm if sm >= 80 else -1
|
| 33 |
+
if device.type in {"xpu", "mps", "cpu"}:
|
| 34 |
+
return -1
|
| 35 |
+
raise ValueError(f"Unsupported device: {device}")
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
def cache_launch_config(fn):
|
| 39 |
+
"""
|
| 40 |
+
Cache launch-config lookups by (arch, *args, **kwargs).
|
| 41 |
+
|
| 42 |
+
:param fn: Launch-config function to wrap.
|
| 43 |
+
|
| 44 |
+
:return wrapper: Wrapped function with cache.
|
| 45 |
+
"""
|
| 46 |
+
params = list(inspect.signature(fn).parameters.keys())
|
| 47 |
+
device_idx = params.index("device") if "device" in params else None
|
| 48 |
+
arch_idx = params.index("arch") if "arch" in params else None
|
| 49 |
+
cached = functools.lru_cache(maxsize=None)(
|
| 50 |
+
lambda _arch, *args, **kwargs: fn(*args, **kwargs)
|
| 51 |
+
)
|
| 52 |
+
|
| 53 |
+
@functools.wraps(fn)
|
| 54 |
+
def wrapper(*args, **kwargs):
|
| 55 |
+
device = (
|
| 56 |
+
args[device_idx]
|
| 57 |
+
if (device_idx is not None and device_idx < len(args))
|
| 58 |
+
else kwargs.get("device")
|
| 59 |
+
)
|
| 60 |
+
arch = (
|
| 61 |
+
args[arch_idx]
|
| 62 |
+
if (arch_idx is not None and arch_idx < len(args))
|
| 63 |
+
else kwargs.get("arch")
|
| 64 |
+
)
|
| 65 |
+
return cached((device.type, arch), *args, **kwargs)
|
| 66 |
+
|
| 67 |
+
return wrapper
|
| 68 |
+
|
| 69 |
+
|
| 70 |
+
def cache_launch_grid(fn, maxsize: int = 512):
|
| 71 |
+
"""
|
| 72 |
+
Bounded cache for Triton grid-factory closures.
|
| 73 |
+
|
| 74 |
+
:param fn: Grid-factory function to wrap.
|
| 75 |
+
:param maxsize: Maximum number of cached entries.
|
| 76 |
+
|
| 77 |
+
:return wrapper: Wrapped function.
|
| 78 |
+
"""
|
| 79 |
+
return functools.lru_cache(maxsize=maxsize)(fn)
|
| 80 |
+
|
| 81 |
+
|
| 82 |
+
class CachedKernel:
|
| 83 |
+
"""
|
| 84 |
+
Wrap a `triton.jit`-decorated function so that each launch reuses a
|
| 85 |
+
previously-compiled `CompiledKernel` when the specialization matches.
|
| 86 |
+
"""
|
| 87 |
+
|
| 88 |
+
__slots__ = ("_kernel", "_cache")
|
| 89 |
+
|
| 90 |
+
def __init__(self, kernel):
|
| 91 |
+
self._kernel = kernel
|
| 92 |
+
self._cache = OrderedDict()
|
| 93 |
+
|
| 94 |
+
def __getitem__(self, grid):
|
| 95 |
+
kernel = self._kernel
|
| 96 |
+
kernel_warmup = kernel.warmup
|
| 97 |
+
kernel_arg_names = kernel.arg_names
|
| 98 |
+
cache = self._cache
|
| 99 |
+
maxsize = 1024
|
| 100 |
+
tensor_indices = None
|
| 101 |
+
static_grid = None
|
| 102 |
+
|
| 103 |
+
if not callable(grid):
|
| 104 |
+
static_grid = tuple(grid)
|
| 105 |
+
if len(static_grid) < 3:
|
| 106 |
+
static_grid = static_grid + (1,) * (3 - len(static_grid))
|
| 107 |
+
|
| 108 |
+
def launcher(
|
| 109 |
+
*args,
|
| 110 |
+
num_warps: int = 4,
|
| 111 |
+
num_stages: int = 1,
|
| 112 |
+
num_ctas: int = 1,
|
| 113 |
+
**constexprs,
|
| 114 |
+
):
|
| 115 |
+
nonlocal tensor_indices
|
| 116 |
+
|
| 117 |
+
if static_grid is not None:
|
| 118 |
+
launch_grid = static_grid
|
| 119 |
+
else:
|
| 120 |
+
launch_grid = tuple(grid(constexprs))
|
| 121 |
+
if len(launch_grid) < 3:
|
| 122 |
+
launch_grid = launch_grid + (1,) * (3 - len(launch_grid))
|
| 123 |
+
|
| 124 |
+
if tensor_indices is None:
|
| 125 |
+
tensor_indices = tuple(
|
| 126 |
+
i for i, a in enumerate(args) if torch.is_tensor(a)
|
| 127 |
+
)
|
| 128 |
+
|
| 129 |
+
key = (
|
| 130 |
+
tuple((args[i].dtype, args[i].device.type) for i in tensor_indices),
|
| 131 |
+
tuple(constexprs.values()) if constexprs else (),
|
| 132 |
+
num_warps,
|
| 133 |
+
num_stages,
|
| 134 |
+
num_ctas,
|
| 135 |
+
)
|
| 136 |
+
|
| 137 |
+
compiled = cache.get(key)
|
| 138 |
+
if compiled is None:
|
| 139 |
+
compiled = kernel_warmup(
|
| 140 |
+
*args,
|
| 141 |
+
grid=launch_grid,
|
| 142 |
+
num_warps=num_warps,
|
| 143 |
+
num_stages=num_stages,
|
| 144 |
+
num_ctas=num_ctas,
|
| 145 |
+
**constexprs,
|
| 146 |
+
)
|
| 147 |
+
cache[key] = compiled
|
| 148 |
+
if len(cache) > maxsize:
|
| 149 |
+
cache.popitem(last=False)
|
| 150 |
+
else:
|
| 151 |
+
cache.move_to_end(key)
|
| 152 |
+
|
| 153 |
+
runner = compiled[launch_grid]
|
| 154 |
+
|
| 155 |
+
if not constexprs:
|
| 156 |
+
runner(*args)
|
| 157 |
+
return
|
| 158 |
+
|
| 159 |
+
args_iter = iter(args)
|
| 160 |
+
full_args = [
|
| 161 |
+
constexprs[name] if name in constexprs else next(args_iter)
|
| 162 |
+
for name in kernel_arg_names
|
| 163 |
+
]
|
| 164 |
+
runner(*full_args)
|
| 165 |
+
|
| 166 |
+
return launcher
|
| 167 |
+
|
| 168 |
+
def __getattr__(self, name):
|
| 169 |
+
return getattr(self._kernel, name)
|
| 170 |
+
|
| 171 |
+
|
| 172 |
+
def wrap_kernel(kernel):
|
| 173 |
+
"""
|
| 174 |
+
Wrap a `triton.jit` kernel for compiled-kernel caching.
|
| 175 |
+
|
| 176 |
+
:param kernel: Triton JIT kernel.
|
| 177 |
+
|
| 178 |
+
:return wrapped: Wrapped kernel preserving the `kernel[grid](...)` syntax.
|
| 179 |
+
"""
|
| 180 |
+
return CachedKernel(kernel)
|
build/torch-cuda/flash_bwd_postprocess.py
ADDED
|
@@ -0,0 +1,215 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from typing import Optional
|
| 2 |
+
|
| 3 |
+
import torch
|
| 4 |
+
import triton
|
| 5 |
+
import triton.language as tl
|
| 6 |
+
|
| 7 |
+
from . import (
|
| 8 |
+
utils,
|
| 9 |
+
cache_utils,
|
| 10 |
+
launch_grid,
|
| 11 |
+
seqlen_info,
|
| 12 |
+
kernel_repr,
|
| 13 |
+
)
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
@triton.jit(repr=kernel_repr.bwd_postprocess_repr)
|
| 17 |
+
def _bwd_postprocess_kernel(
|
| 18 |
+
dQaccum,
|
| 19 |
+
dQ,
|
| 20 |
+
dAaccum,
|
| 21 |
+
dA,
|
| 22 |
+
stride_dqab,
|
| 23 |
+
stride_dqah,
|
| 24 |
+
stride_dqam,
|
| 25 |
+
stride_dqb,
|
| 26 |
+
stride_dqh,
|
| 27 |
+
stride_dqm,
|
| 28 |
+
stride_daab,
|
| 29 |
+
stride_daah,
|
| 30 |
+
stride_daam,
|
| 31 |
+
stride_dab,
|
| 32 |
+
stride_dah,
|
| 33 |
+
stride_dam,
|
| 34 |
+
cu_seqlens_q,
|
| 35 |
+
seqused_q,
|
| 36 |
+
seqlen_q,
|
| 37 |
+
head_dim,
|
| 38 |
+
head_dim_rounded,
|
| 39 |
+
scale,
|
| 40 |
+
HAS_CU_SEQLENS_Q: tl.constexpr,
|
| 41 |
+
HAS_SEQUSED_Q: tl.constexpr,
|
| 42 |
+
HAS_DA: tl.constexpr,
|
| 43 |
+
TILE_M: tl.constexpr,
|
| 44 |
+
TILE_K: tl.constexpr,
|
| 45 |
+
):
|
| 46 |
+
m_block = tl.program_id(0)
|
| 47 |
+
head_idx = tl.program_id(1)
|
| 48 |
+
batch_idx = tl.program_id(2)
|
| 49 |
+
|
| 50 |
+
# Get seqlen info for this batch
|
| 51 |
+
offset_q, actual_seqlen_q = seqlen_info.get_seqlen_info(
|
| 52 |
+
batch_idx=batch_idx,
|
| 53 |
+
seqlen_static=seqlen_q,
|
| 54 |
+
cu_seqlens=cu_seqlens_q,
|
| 55 |
+
seqused=seqused_q,
|
| 56 |
+
HAS_CU_SEQLENS=HAS_CU_SEQLENS_Q,
|
| 57 |
+
HAS_SEQUSED=HAS_SEQUSED_Q,
|
| 58 |
+
)
|
| 59 |
+
padded_offset_q = (offset_q + batch_idx * TILE_M) // TILE_M * TILE_M
|
| 60 |
+
|
| 61 |
+
# Initialize base pointers
|
| 62 |
+
dq_accum_base = seqlen_info.offset_batch_Q(
|
| 63 |
+
dQaccum + head_idx * stride_dqah,
|
| 64 |
+
batch_idx,
|
| 65 |
+
offset_q,
|
| 66 |
+
padded_offset_q,
|
| 67 |
+
stride_dqab,
|
| 68 |
+
stride_dqam,
|
| 69 |
+
HAS_CU_SEQLENS_Q,
|
| 70 |
+
USE_PADDED=True,
|
| 71 |
+
)
|
| 72 |
+
dq_base = seqlen_info.offset_batch_Q(
|
| 73 |
+
dQ + head_idx * stride_dqh,
|
| 74 |
+
batch_idx,
|
| 75 |
+
offset_q,
|
| 76 |
+
padded_offset_q,
|
| 77 |
+
stride_dqb,
|
| 78 |
+
stride_dqm,
|
| 79 |
+
HAS_CU_SEQLENS_Q,
|
| 80 |
+
USE_PADDED=False,
|
| 81 |
+
)
|
| 82 |
+
|
| 83 |
+
# Create descriptors
|
| 84 |
+
dq_accum_desc = tl.make_tensor_descriptor(
|
| 85 |
+
base=dq_accum_base,
|
| 86 |
+
shape=[actual_seqlen_q, head_dim_rounded],
|
| 87 |
+
strides=[stride_dqam, 1],
|
| 88 |
+
block_shape=[TILE_M, TILE_K],
|
| 89 |
+
)
|
| 90 |
+
dq_desc = tl.make_tensor_descriptor(
|
| 91 |
+
base=dq_base,
|
| 92 |
+
shape=[actual_seqlen_q, head_dim],
|
| 93 |
+
strides=[stride_dqm, 1],
|
| 94 |
+
block_shape=[TILE_M, TILE_K],
|
| 95 |
+
)
|
| 96 |
+
|
| 97 |
+
# Load accumulators
|
| 98 |
+
acc_dq = dq_accum_desc.load([m_block * TILE_M, 0])
|
| 99 |
+
|
| 100 |
+
# Scale dq
|
| 101 |
+
dq = (acc_dq * scale).to(dQ.dtype.element_ty)
|
| 102 |
+
|
| 103 |
+
# Store dq
|
| 104 |
+
dq_desc.store([m_block * TILE_M, 0], dq)
|
| 105 |
+
|
| 106 |
+
if HAS_DA:
|
| 107 |
+
da_accum_base = seqlen_info.offset_batch_Q(
|
| 108 |
+
dAaccum + head_idx * stride_daah,
|
| 109 |
+
batch_idx,
|
| 110 |
+
offset_q,
|
| 111 |
+
padded_offset_q,
|
| 112 |
+
stride_daab,
|
| 113 |
+
stride_daam,
|
| 114 |
+
HAS_CU_SEQLENS_Q,
|
| 115 |
+
USE_PADDED=True,
|
| 116 |
+
)
|
| 117 |
+
da_base = seqlen_info.offset_batch_Q(
|
| 118 |
+
dA + head_idx * stride_dah,
|
| 119 |
+
batch_idx,
|
| 120 |
+
offset_q,
|
| 121 |
+
padded_offset_q,
|
| 122 |
+
stride_dab,
|
| 123 |
+
stride_dam,
|
| 124 |
+
HAS_CU_SEQLENS_Q,
|
| 125 |
+
USE_PADDED=False,
|
| 126 |
+
)
|
| 127 |
+
|
| 128 |
+
da_accum_desc = tl.make_tensor_descriptor(
|
| 129 |
+
base=da_accum_base,
|
| 130 |
+
shape=[actual_seqlen_q],
|
| 131 |
+
strides=[stride_daam],
|
| 132 |
+
block_shape=[TILE_M],
|
| 133 |
+
)
|
| 134 |
+
da_desc = tl.make_tensor_descriptor(
|
| 135 |
+
base=da_base,
|
| 136 |
+
shape=[actual_seqlen_q],
|
| 137 |
+
strides=[stride_dam],
|
| 138 |
+
block_shape=[TILE_M],
|
| 139 |
+
)
|
| 140 |
+
|
| 141 |
+
# Load da accumulators
|
| 142 |
+
acc_da = da_accum_desc.load([m_block * TILE_M])
|
| 143 |
+
|
| 144 |
+
# Scale da
|
| 145 |
+
da = (acc_da * scale).to(dA.dtype.element_ty)
|
| 146 |
+
|
| 147 |
+
# Store da
|
| 148 |
+
da_desc.store([m_block * TILE_M], da)
|
| 149 |
+
|
| 150 |
+
|
| 151 |
+
_bwd_postprocess_kernel = cache_utils.wrap_kernel(_bwd_postprocess_kernel)
|
| 152 |
+
|
| 153 |
+
|
| 154 |
+
def _flash_attn_bwd_postprocess(
|
| 155 |
+
dq_accum: torch.Tensor,
|
| 156 |
+
dq: torch.Tensor,
|
| 157 |
+
scale: float,
|
| 158 |
+
head_dim_rounded: int,
|
| 159 |
+
da_accum: Optional[torch.Tensor] = None,
|
| 160 |
+
da: Optional[torch.Tensor] = None,
|
| 161 |
+
cu_seqlens_q: Optional[torch.Tensor] = None,
|
| 162 |
+
seqused_q: Optional[torch.Tensor] = None,
|
| 163 |
+
max_seqlen_q: Optional[int] = None,
|
| 164 |
+
tile_m: int = 128,
|
| 165 |
+
tile_k: int = 128,
|
| 166 |
+
) -> torch.Tensor:
|
| 167 |
+
is_varlen = cu_seqlens_q is not None
|
| 168 |
+
if not is_varlen:
|
| 169 |
+
batch_size, seqlen_q, num_heads_q, head_dim = dq.shape
|
| 170 |
+
else:
|
| 171 |
+
_, num_heads_q, head_dim = dq.shape
|
| 172 |
+
batch_size = cu_seqlens_q.shape[0] - 1
|
| 173 |
+
seqlen_q = max_seqlen_q
|
| 174 |
+
has_da = da_accum is not None and da is not None
|
| 175 |
+
|
| 176 |
+
grid = launch_grid.get_bwd_postprocess_grid(batch_size, seqlen_q, num_heads_q)
|
| 177 |
+
|
| 178 |
+
triton.set_allocator(utils.alloc_fn)
|
| 179 |
+
|
| 180 |
+
_bwd_postprocess_kernel[grid](
|
| 181 |
+
dq_accum,
|
| 182 |
+
dq,
|
| 183 |
+
da_accum if has_da else dq_accum,
|
| 184 |
+
da if has_da else dq,
|
| 185 |
+
dq_accum.stride(0) if not is_varlen else 0,
|
| 186 |
+
dq_accum.stride(1) if not is_varlen else dq_accum.stride(0),
|
| 187 |
+
head_dim_rounded,
|
| 188 |
+
dq.stride(0) if not is_varlen else 0,
|
| 189 |
+
dq.stride(-2),
|
| 190 |
+
dq.stride(-3) if not is_varlen else dq.stride(0),
|
| 191 |
+
da_accum.stride(0) if (has_da and not is_varlen) else 0,
|
| 192 |
+
da_accum.stride(1)
|
| 193 |
+
if (has_da and not is_varlen)
|
| 194 |
+
else (da_accum.stride(0) if has_da else 0),
|
| 195 |
+
da_accum.stride(-1) if has_da else 0,
|
| 196 |
+
da.stride(0) if (has_da and not is_varlen) else 0,
|
| 197 |
+
da.stride(-2) if has_da else 0,
|
| 198 |
+
da.stride(-1) if has_da else 0,
|
| 199 |
+
cu_seqlens_q,
|
| 200 |
+
seqused_q,
|
| 201 |
+
seqlen_q,
|
| 202 |
+
head_dim,
|
| 203 |
+
head_dim_rounded,
|
| 204 |
+
scale,
|
| 205 |
+
HAS_CU_SEQLENS_Q=is_varlen,
|
| 206 |
+
HAS_SEQUSED_Q=seqused_q is not None,
|
| 207 |
+
HAS_DA=has_da,
|
| 208 |
+
TILE_M=tile_m,
|
| 209 |
+
TILE_K=tile_k,
|
| 210 |
+
num_warps=4,
|
| 211 |
+
num_stages=1,
|
| 212 |
+
num_ctas=1,
|
| 213 |
+
)
|
| 214 |
+
|
| 215 |
+
return dq
|
build/torch-cuda/flash_bwd_preprocess.py
ADDED
|
@@ -0,0 +1,266 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from typing import Optional
|
| 2 |
+
|
| 3 |
+
import math
|
| 4 |
+
import torch
|
| 5 |
+
import triton
|
| 6 |
+
import triton.language as tl
|
| 7 |
+
|
| 8 |
+
from . import (
|
| 9 |
+
utils,
|
| 10 |
+
cache_utils,
|
| 11 |
+
launch_grid,
|
| 12 |
+
seqlen_info,
|
| 13 |
+
activations,
|
| 14 |
+
kernel_repr,
|
| 15 |
+
)
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
@triton.jit(repr=kernel_repr.bwd_preprocess_repr)
|
| 19 |
+
def _bwd_preprocess_kernel(
|
| 20 |
+
Out,
|
| 21 |
+
dO,
|
| 22 |
+
dPsum,
|
| 23 |
+
LSE,
|
| 24 |
+
LSELog2,
|
| 25 |
+
dQaccum,
|
| 26 |
+
stride_ob,
|
| 27 |
+
stride_oh,
|
| 28 |
+
stride_om,
|
| 29 |
+
stride_dob,
|
| 30 |
+
stride_doh,
|
| 31 |
+
stride_dom,
|
| 32 |
+
stride_pb,
|
| 33 |
+
stride_ph,
|
| 34 |
+
stride_pm,
|
| 35 |
+
stride_lb,
|
| 36 |
+
stride_lh,
|
| 37 |
+
stride_lm,
|
| 38 |
+
stride_l2b,
|
| 39 |
+
stride_l2h,
|
| 40 |
+
stride_l2m,
|
| 41 |
+
stride_dqab,
|
| 42 |
+
stride_dqah,
|
| 43 |
+
stride_dqam,
|
| 44 |
+
cu_seqlens_q,
|
| 45 |
+
seqused_q,
|
| 46 |
+
seqlen_q,
|
| 47 |
+
head_dim,
|
| 48 |
+
head_dim_rounded,
|
| 49 |
+
HAS_CU_SEQLENS_Q: tl.constexpr,
|
| 50 |
+
HAS_SEQUSED_Q: tl.constexpr,
|
| 51 |
+
TILE_M: tl.constexpr,
|
| 52 |
+
TILE_K: tl.constexpr,
|
| 53 |
+
):
|
| 54 |
+
m_block = tl.program_id(0)
|
| 55 |
+
head_idx = tl.program_id(1)
|
| 56 |
+
batch_idx = tl.program_id(2)
|
| 57 |
+
|
| 58 |
+
# Get seqlen info for this batch
|
| 59 |
+
offset_q, actual_seqlen_q = seqlen_info.get_seqlen_info(
|
| 60 |
+
batch_idx=batch_idx,
|
| 61 |
+
seqlen_static=seqlen_q,
|
| 62 |
+
cu_seqlens=cu_seqlens_q,
|
| 63 |
+
seqused=seqused_q,
|
| 64 |
+
HAS_CU_SEQLENS=HAS_CU_SEQLENS_Q,
|
| 65 |
+
HAS_SEQUSED=HAS_SEQUSED_Q,
|
| 66 |
+
)
|
| 67 |
+
padded_offset_q = (offset_q + batch_idx * TILE_M) // TILE_M * TILE_M
|
| 68 |
+
|
| 69 |
+
# Initialize base pointers
|
| 70 |
+
o_base = seqlen_info.offset_batch_Q(
|
| 71 |
+
Out + head_idx * stride_oh,
|
| 72 |
+
batch_idx,
|
| 73 |
+
offset_q,
|
| 74 |
+
padded_offset_q,
|
| 75 |
+
stride_ob,
|
| 76 |
+
stride_om,
|
| 77 |
+
HAS_CU_SEQLENS_Q,
|
| 78 |
+
USE_PADDED=False,
|
| 79 |
+
)
|
| 80 |
+
do_base = seqlen_info.offset_batch_Q(
|
| 81 |
+
dO + head_idx * stride_doh,
|
| 82 |
+
batch_idx,
|
| 83 |
+
offset_q,
|
| 84 |
+
padded_offset_q,
|
| 85 |
+
stride_dob,
|
| 86 |
+
stride_dom,
|
| 87 |
+
HAS_CU_SEQLENS_Q,
|
| 88 |
+
USE_PADDED=False,
|
| 89 |
+
)
|
| 90 |
+
dpsum_base = seqlen_info.offset_batch_Q(
|
| 91 |
+
dPsum + head_idx * stride_ph,
|
| 92 |
+
batch_idx,
|
| 93 |
+
offset_q,
|
| 94 |
+
padded_offset_q,
|
| 95 |
+
stride_pb,
|
| 96 |
+
stride_pm,
|
| 97 |
+
HAS_CU_SEQLENS_Q,
|
| 98 |
+
USE_PADDED=True,
|
| 99 |
+
)
|
| 100 |
+
lse_base = seqlen_info.offset_batch_Q(
|
| 101 |
+
LSE + head_idx * stride_lh,
|
| 102 |
+
batch_idx,
|
| 103 |
+
offset_q,
|
| 104 |
+
padded_offset_q,
|
| 105 |
+
stride_lb,
|
| 106 |
+
stride_lm,
|
| 107 |
+
HAS_CU_SEQLENS_Q,
|
| 108 |
+
USE_PADDED=False,
|
| 109 |
+
)
|
| 110 |
+
lse_log2_base = seqlen_info.offset_batch_Q(
|
| 111 |
+
LSELog2 + head_idx * stride_l2h,
|
| 112 |
+
batch_idx,
|
| 113 |
+
offset_q,
|
| 114 |
+
padded_offset_q,
|
| 115 |
+
stride_l2b,
|
| 116 |
+
stride_l2m,
|
| 117 |
+
HAS_CU_SEQLENS_Q,
|
| 118 |
+
USE_PADDED=True,
|
| 119 |
+
)
|
| 120 |
+
dq_accum_base = seqlen_info.offset_batch_Q(
|
| 121 |
+
dQaccum + head_idx * stride_dqah,
|
| 122 |
+
batch_idx,
|
| 123 |
+
offset_q,
|
| 124 |
+
padded_offset_q,
|
| 125 |
+
stride_dqab,
|
| 126 |
+
stride_dqam,
|
| 127 |
+
HAS_CU_SEQLENS_Q,
|
| 128 |
+
USE_PADDED=True,
|
| 129 |
+
)
|
| 130 |
+
|
| 131 |
+
# Create descriptors
|
| 132 |
+
o_desc = tl.make_tensor_descriptor(
|
| 133 |
+
base=o_base,
|
| 134 |
+
shape=[actual_seqlen_q, head_dim],
|
| 135 |
+
strides=[stride_om, 1],
|
| 136 |
+
block_shape=[TILE_M, TILE_K],
|
| 137 |
+
)
|
| 138 |
+
do_desc = tl.make_tensor_descriptor(
|
| 139 |
+
base=do_base,
|
| 140 |
+
shape=[actual_seqlen_q, head_dim],
|
| 141 |
+
strides=[stride_dom, 1],
|
| 142 |
+
block_shape=[TILE_M, TILE_K],
|
| 143 |
+
)
|
| 144 |
+
dpsum_desc = tl.make_tensor_descriptor(
|
| 145 |
+
base=dpsum_base,
|
| 146 |
+
shape=[actual_seqlen_q],
|
| 147 |
+
strides=[stride_pm],
|
| 148 |
+
block_shape=[TILE_M],
|
| 149 |
+
)
|
| 150 |
+
lse_desc = tl.make_tensor_descriptor(
|
| 151 |
+
base=lse_base,
|
| 152 |
+
shape=[actual_seqlen_q],
|
| 153 |
+
strides=[stride_lm],
|
| 154 |
+
block_shape=[TILE_M],
|
| 155 |
+
)
|
| 156 |
+
lse_log2_desc = tl.make_tensor_descriptor(
|
| 157 |
+
base=lse_log2_base,
|
| 158 |
+
shape=[actual_seqlen_q],
|
| 159 |
+
strides=[stride_l2m],
|
| 160 |
+
block_shape=[TILE_M],
|
| 161 |
+
)
|
| 162 |
+
dq_accum_desc = tl.make_tensor_descriptor(
|
| 163 |
+
base=dq_accum_base,
|
| 164 |
+
shape=[actual_seqlen_q, head_dim_rounded],
|
| 165 |
+
strides=[stride_dqam, 1],
|
| 166 |
+
block_shape=[TILE_M, TILE_K],
|
| 167 |
+
)
|
| 168 |
+
|
| 169 |
+
# Initialize accumulators
|
| 170 |
+
acc_dq = tl.zeros((TILE_M, TILE_K), dtype=tl.float32)
|
| 171 |
+
|
| 172 |
+
# Load o tile
|
| 173 |
+
o_tile = o_desc.load([m_block * TILE_M, 0]).to(tl.float32)
|
| 174 |
+
|
| 175 |
+
# Load do tile
|
| 176 |
+
do_tile = do_desc.load([m_block * TILE_M, 0]).to(tl.float32)
|
| 177 |
+
|
| 178 |
+
# Compute dpsum
|
| 179 |
+
dpsum = tl.sum(o_tile * do_tile, axis=1)
|
| 180 |
+
|
| 181 |
+
# Store dpsum
|
| 182 |
+
dpsum_desc.store([m_block * TILE_M], dpsum)
|
| 183 |
+
|
| 184 |
+
# Load lse tile
|
| 185 |
+
lse_tile = lse_desc.load([m_block * TILE_M])
|
| 186 |
+
|
| 187 |
+
# Compute lse_log2
|
| 188 |
+
lse_log2 = tl.where(lse_tile == lse_tile, lse_tile * math.log2(math.e), 1e6)
|
| 189 |
+
lse_log2 = activations.check_inf(lse_log2)
|
| 190 |
+
|
| 191 |
+
# Store dq_accum
|
| 192 |
+
dq_accum_desc.store([m_block * TILE_M, 0], acc_dq)
|
| 193 |
+
|
| 194 |
+
# Store lse_log2
|
| 195 |
+
lse_log2_desc.store([m_block * TILE_M], lse_log2)
|
| 196 |
+
|
| 197 |
+
|
| 198 |
+
_bwd_preprocess_kernel = cache_utils.wrap_kernel(_bwd_preprocess_kernel)
|
| 199 |
+
|
| 200 |
+
|
| 201 |
+
def _flash_attn_bwd_preprocess(
|
| 202 |
+
out: torch.Tensor,
|
| 203 |
+
dout: torch.Tensor,
|
| 204 |
+
dpsum: torch.Tensor,
|
| 205 |
+
lse: torch.Tensor,
|
| 206 |
+
lse_log2: torch.Tensor,
|
| 207 |
+
dq_accum: torch.Tensor,
|
| 208 |
+
head_dim_rounded: int,
|
| 209 |
+
cu_seqlens_q: Optional[torch.Tensor] = None,
|
| 210 |
+
seqused_q: Optional[torch.Tensor] = None,
|
| 211 |
+
max_seqlen_q: Optional[int] = None,
|
| 212 |
+
tile_m: int = 128,
|
| 213 |
+
tile_k: int = 128,
|
| 214 |
+
) -> torch.Tensor:
|
| 215 |
+
is_varlen = cu_seqlens_q is not None
|
| 216 |
+
if not is_varlen:
|
| 217 |
+
batch_size, seqlen_q, num_heads_q, head_dim = out.shape
|
| 218 |
+
else:
|
| 219 |
+
_, num_heads_q, head_dim = out.shape
|
| 220 |
+
batch_size = cu_seqlens_q.shape[0] - 1
|
| 221 |
+
seqlen_q = max_seqlen_q
|
| 222 |
+
|
| 223 |
+
grid = launch_grid.get_bwd_preprocess_grid(batch_size, seqlen_q, num_heads_q)
|
| 224 |
+
|
| 225 |
+
triton.set_allocator(utils.alloc_fn)
|
| 226 |
+
|
| 227 |
+
_bwd_preprocess_kernel[grid](
|
| 228 |
+
out,
|
| 229 |
+
dout,
|
| 230 |
+
dpsum,
|
| 231 |
+
lse,
|
| 232 |
+
lse_log2,
|
| 233 |
+
dq_accum,
|
| 234 |
+
out.stride(0) if not is_varlen else 0,
|
| 235 |
+
out.stride(-2),
|
| 236 |
+
out.stride(-3) if not is_varlen else out.stride(0),
|
| 237 |
+
dout.stride(0) if not is_varlen else 0,
|
| 238 |
+
dout.stride(-2),
|
| 239 |
+
dout.stride(-3) if not is_varlen else dout.stride(0),
|
| 240 |
+
dpsum.stride(0) if not is_varlen else 0,
|
| 241 |
+
dpsum.stride(1) if not is_varlen else dpsum.stride(0),
|
| 242 |
+
dpsum.stride(-1),
|
| 243 |
+
lse.stride(0) if not is_varlen else 0,
|
| 244 |
+
lse.stride(1) if not is_varlen else lse.stride(0),
|
| 245 |
+
lse.stride(-1),
|
| 246 |
+
lse_log2.stride(0) if not is_varlen else 0,
|
| 247 |
+
lse_log2.stride(1) if not is_varlen else lse_log2.stride(0),
|
| 248 |
+
lse_log2.stride(-1),
|
| 249 |
+
dq_accum.stride(0) if not is_varlen else 0,
|
| 250 |
+
dq_accum.stride(1) if not is_varlen else dq_accum.stride(0),
|
| 251 |
+
head_dim_rounded,
|
| 252 |
+
cu_seqlens_q,
|
| 253 |
+
seqused_q,
|
| 254 |
+
seqlen_q,
|
| 255 |
+
head_dim,
|
| 256 |
+
head_dim_rounded,
|
| 257 |
+
HAS_CU_SEQLENS_Q=is_varlen,
|
| 258 |
+
HAS_SEQUSED_Q=seqused_q is not None,
|
| 259 |
+
TILE_M=tile_m,
|
| 260 |
+
TILE_K=tile_k,
|
| 261 |
+
num_warps=4,
|
| 262 |
+
num_stages=1,
|
| 263 |
+
num_ctas=1,
|
| 264 |
+
)
|
| 265 |
+
|
| 266 |
+
return dpsum
|
build/torch-cuda/flash_dec_combine.py
ADDED
|
@@ -0,0 +1,222 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
import triton
|
| 3 |
+
import triton.language as tl
|
| 4 |
+
|
| 5 |
+
from . import (
|
| 6 |
+
utils,
|
| 7 |
+
cache_utils,
|
| 8 |
+
launch_template,
|
| 9 |
+
launch_grid,
|
| 10 |
+
seqlen_info,
|
| 11 |
+
kernel_repr,
|
| 12 |
+
)
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
@triton.jit(repr=kernel_repr.dec_combine_repr)
|
| 16 |
+
def _dec_combine_kernel(
|
| 17 |
+
Out_partial,
|
| 18 |
+
Lse_partial,
|
| 19 |
+
Out,
|
| 20 |
+
Lse,
|
| 21 |
+
stride_ops,
|
| 22 |
+
stride_opb,
|
| 23 |
+
stride_oph,
|
| 24 |
+
stride_opm,
|
| 25 |
+
stride_lps,
|
| 26 |
+
stride_lpb,
|
| 27 |
+
stride_lph,
|
| 28 |
+
stride_lpm,
|
| 29 |
+
stride_ob,
|
| 30 |
+
stride_oh,
|
| 31 |
+
stride_om,
|
| 32 |
+
stride_lb,
|
| 33 |
+
stride_lh,
|
| 34 |
+
stride_lm,
|
| 35 |
+
cu_seqlens_q,
|
| 36 |
+
seqused_q,
|
| 37 |
+
num_splits,
|
| 38 |
+
num_heads_q,
|
| 39 |
+
head_dim,
|
| 40 |
+
TILE_K: tl.constexpr,
|
| 41 |
+
HAS_CU_SEQLENS_Q: tl.constexpr,
|
| 42 |
+
HAS_SEQUSED_Q: tl.constexpr,
|
| 43 |
+
):
|
| 44 |
+
bh_idx = tl.program_id(0)
|
| 45 |
+
batch_idx = bh_idx // num_heads_q
|
| 46 |
+
head_idx = bh_idx - batch_idx * num_heads_q
|
| 47 |
+
|
| 48 |
+
# Get seqlen info for this batch
|
| 49 |
+
offset_q, _ = seqlen_info.get_seqlen_info(
|
| 50 |
+
batch_idx=batch_idx,
|
| 51 |
+
seqlen_static=1,
|
| 52 |
+
cu_seqlens=cu_seqlens_q,
|
| 53 |
+
seqused=seqused_q,
|
| 54 |
+
HAS_CU_SEQLENS=HAS_CU_SEQLENS_Q,
|
| 55 |
+
HAS_SEQUSED=HAS_SEQUSED_Q,
|
| 56 |
+
)
|
| 57 |
+
|
| 58 |
+
# Initialize base pointers
|
| 59 |
+
out_part_base = seqlen_info.offset_batch_Q(
|
| 60 |
+
Out_partial + head_idx * stride_oph,
|
| 61 |
+
batch_idx,
|
| 62 |
+
offset_q,
|
| 63 |
+
0,
|
| 64 |
+
stride_opb,
|
| 65 |
+
stride_opm,
|
| 66 |
+
HAS_CU_SEQLENS_Q,
|
| 67 |
+
USE_PADDED=False,
|
| 68 |
+
)
|
| 69 |
+
lse_part_base = seqlen_info.offset_batch_Q(
|
| 70 |
+
Lse_partial + head_idx * stride_lph,
|
| 71 |
+
batch_idx,
|
| 72 |
+
offset_q,
|
| 73 |
+
0,
|
| 74 |
+
stride_lpb,
|
| 75 |
+
stride_lpm,
|
| 76 |
+
HAS_CU_SEQLENS_Q,
|
| 77 |
+
USE_PADDED=False,
|
| 78 |
+
)
|
| 79 |
+
out_base = seqlen_info.offset_batch_Q(
|
| 80 |
+
Out + head_idx * stride_oh,
|
| 81 |
+
batch_idx,
|
| 82 |
+
offset_q,
|
| 83 |
+
0,
|
| 84 |
+
stride_ob,
|
| 85 |
+
stride_om,
|
| 86 |
+
HAS_CU_SEQLENS_Q,
|
| 87 |
+
USE_PADDED=False,
|
| 88 |
+
)
|
| 89 |
+
lse_base = seqlen_info.offset_batch_Q(
|
| 90 |
+
Lse + head_idx * stride_lh,
|
| 91 |
+
batch_idx,
|
| 92 |
+
offset_q,
|
| 93 |
+
0,
|
| 94 |
+
stride_lb,
|
| 95 |
+
stride_lm,
|
| 96 |
+
HAS_CU_SEQLENS_Q,
|
| 97 |
+
USE_PADDED=False,
|
| 98 |
+
)
|
| 99 |
+
|
| 100 |
+
# Create descriptors
|
| 101 |
+
out_part_desc = tl.make_tensor_descriptor(
|
| 102 |
+
base=out_part_base,
|
| 103 |
+
shape=[num_splits, head_dim],
|
| 104 |
+
strides=[stride_ops, 1],
|
| 105 |
+
block_shape=[1, TILE_K],
|
| 106 |
+
)
|
| 107 |
+
out_desc = tl.make_tensor_descriptor(
|
| 108 |
+
base=out_base,
|
| 109 |
+
shape=[head_dim],
|
| 110 |
+
strides=[1],
|
| 111 |
+
block_shape=[TILE_K],
|
| 112 |
+
)
|
| 113 |
+
|
| 114 |
+
# Initialize accumulators
|
| 115 |
+
e_sum = tl.full((), 0.0, dtype=tl.float32)
|
| 116 |
+
e_max = tl.full((), float("-inf"), dtype=tl.float32)
|
| 117 |
+
acc_o = tl.zeros((TILE_K,), dtype=tl.float32)
|
| 118 |
+
|
| 119 |
+
# Combine split outputs
|
| 120 |
+
for s in tl.range(0, num_splits):
|
| 121 |
+
# Load partial LSE
|
| 122 |
+
lse_s = tl.load(lse_part_base + s * stride_lps).to(tl.float32)
|
| 123 |
+
|
| 124 |
+
# Compute normalized exponentials
|
| 125 |
+
new_e_max = tl.maximum(lse_s, e_max)
|
| 126 |
+
old_scale = tl.where(e_max == float("-inf"), 0.0, tl.exp2(e_max - new_e_max))
|
| 127 |
+
exp_logic = tl.where(lse_s == float("-inf"), 0.0, tl.exp2(lse_s - new_e_max))
|
| 128 |
+
|
| 129 |
+
# Load partial outputs
|
| 130 |
+
o_s = tl.sum(out_part_desc.load([s, 0]), axis=0)
|
| 131 |
+
|
| 132 |
+
# Compute scaled outputs
|
| 133 |
+
acc_o *= old_scale
|
| 134 |
+
acc_o += exp_logic * o_s
|
| 135 |
+
|
| 136 |
+
# Update e_sum and e_max
|
| 137 |
+
e_sum = e_sum * old_scale + exp_logic
|
| 138 |
+
e_max = new_e_max
|
| 139 |
+
|
| 140 |
+
# Normalize output
|
| 141 |
+
inv_sum = tl.where((e_sum == 0.0) | (e_sum != e_sum), 0.0, 1.0 / e_sum)
|
| 142 |
+
acc_o *= inv_sum
|
| 143 |
+
|
| 144 |
+
# Store output
|
| 145 |
+
out_desc.store([0], acc_o.to(Out.dtype.element_ty))
|
| 146 |
+
|
| 147 |
+
# Compute LSE
|
| 148 |
+
# ln2 = math.log(2.0)
|
| 149 |
+
ln2 = 0.6931471805599453
|
| 150 |
+
lse = tl.where(e_sum > 0.0, (e_max + tl.log2(e_sum)) * ln2, float("-inf"))
|
| 151 |
+
|
| 152 |
+
# Store LSE
|
| 153 |
+
tl.store(lse_base, lse)
|
| 154 |
+
|
| 155 |
+
|
| 156 |
+
_dec_combine_kernel = cache_utils.wrap_kernel(_dec_combine_kernel)
|
| 157 |
+
|
| 158 |
+
|
| 159 |
+
def _flash_attn_dec_combine(
|
| 160 |
+
out_partial: torch.Tensor,
|
| 161 |
+
lse_partial: torch.Tensor,
|
| 162 |
+
out: torch.Tensor,
|
| 163 |
+
lse: torch.Tensor,
|
| 164 |
+
cu_seqlens_q: torch.Tensor = None,
|
| 165 |
+
seqused_q: torch.Tensor = None,
|
| 166 |
+
):
|
| 167 |
+
device = out.device
|
| 168 |
+
arch = cache_utils.get_device_arch(device)
|
| 169 |
+
is_varlen = cu_seqlens_q is not None
|
| 170 |
+
num_splits = out_partial.shape[0]
|
| 171 |
+
if not is_varlen:
|
| 172 |
+
batch_size, num_heads_q, head_dim = out_partial.shape[1:]
|
| 173 |
+
else:
|
| 174 |
+
_, num_heads_q, head_dim = out_partial.shape[1:]
|
| 175 |
+
batch_size = cu_seqlens_q.shape[0] - 1
|
| 176 |
+
|
| 177 |
+
TILE_K = max(triton.next_power_of_2(head_dim), 16)
|
| 178 |
+
|
| 179 |
+
num_warps, num_stages, num_ctas = launch_template.get_dec_combine_launch_config(
|
| 180 |
+
tile_k=TILE_K,
|
| 181 |
+
device=device,
|
| 182 |
+
arch=arch,
|
| 183 |
+
)
|
| 184 |
+
|
| 185 |
+
grid = launch_grid.get_dec_combine_grid(
|
| 186 |
+
batch_size=batch_size,
|
| 187 |
+
num_heads_q=num_heads_q,
|
| 188 |
+
)
|
| 189 |
+
|
| 190 |
+
triton.set_allocator(utils.alloc_fn)
|
| 191 |
+
|
| 192 |
+
_dec_combine_kernel[grid](
|
| 193 |
+
out_partial,
|
| 194 |
+
lse_partial,
|
| 195 |
+
out,
|
| 196 |
+
lse,
|
| 197 |
+
out_partial.stride(0),
|
| 198 |
+
out_partial.stride(1) if not is_varlen else 0,
|
| 199 |
+
out_partial.stride(-2),
|
| 200 |
+
1 if not is_varlen else out_partial.stride(1),
|
| 201 |
+
lse_partial.stride(0),
|
| 202 |
+
lse_partial.stride(1) if not is_varlen else 0,
|
| 203 |
+
lse_partial.stride(-1),
|
| 204 |
+
1 if not is_varlen else lse_partial.stride(1),
|
| 205 |
+
out.stride(0) if not is_varlen else 0,
|
| 206 |
+
out.stride(-2),
|
| 207 |
+
1 if not is_varlen else out.stride(0),
|
| 208 |
+
lse.stride(0) if not is_varlen else 0,
|
| 209 |
+
lse.stride(-1),
|
| 210 |
+
1 if not is_varlen else lse.stride(0),
|
| 211 |
+
cu_seqlens_q,
|
| 212 |
+
seqused_q,
|
| 213 |
+
num_splits,
|
| 214 |
+
num_heads_q,
|
| 215 |
+
head_dim,
|
| 216 |
+
TILE_K=TILE_K,
|
| 217 |
+
HAS_CU_SEQLENS_Q=is_varlen,
|
| 218 |
+
HAS_SEQUSED_Q=seqused_q is not None,
|
| 219 |
+
num_warps=num_warps,
|
| 220 |
+
num_stages=num_stages,
|
| 221 |
+
num_ctas=num_ctas,
|
| 222 |
+
)
|
build/torch-cuda/flash_dense_bwd.py
ADDED
|
@@ -0,0 +1,1019 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from typing import Optional, Tuple
|
| 2 |
+
|
| 3 |
+
import math
|
| 4 |
+
import torch
|
| 5 |
+
import triton
|
| 6 |
+
import triton.language as tl
|
| 7 |
+
|
| 8 |
+
from . import (
|
| 9 |
+
assert_inputs,
|
| 10 |
+
utils,
|
| 11 |
+
cache_utils,
|
| 12 |
+
launch_template,
|
| 13 |
+
launch_grid,
|
| 14 |
+
activations,
|
| 15 |
+
flash_bwd_preprocess,
|
| 16 |
+
flash_bwd_postprocess,
|
| 17 |
+
kernel_repr,
|
| 18 |
+
autotuner,
|
| 19 |
+
)
|
| 20 |
+
from .scheduler import (
|
| 21 |
+
AttnBwdGridIndex,
|
| 22 |
+
AttnBwdConfig,
|
| 23 |
+
AttnBwdBlockScheduler,
|
| 24 |
+
AttnBwdPointerScheduler,
|
| 25 |
+
AttnMaskScheduler,
|
| 26 |
+
)
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
@triton.jit
|
| 30 |
+
def _bwd_inner_dense_kernel(
|
| 31 |
+
config,
|
| 32 |
+
ptrs_sched,
|
| 33 |
+
mask_sched,
|
| 34 |
+
acc_dk,
|
| 35 |
+
acc_dv,
|
| 36 |
+
k_tile,
|
| 37 |
+
v_tile,
|
| 38 |
+
q_ptrs,
|
| 39 |
+
do_ptrs,
|
| 40 |
+
dq_accum_ptrs,
|
| 41 |
+
lse_ptrs,
|
| 42 |
+
dpsum_ptrs,
|
| 43 |
+
m_block,
|
| 44 |
+
IS_MASK: tl.constexpr,
|
| 45 |
+
MASK_CAUSAL: tl.constexpr,
|
| 46 |
+
MASK_LOCAL: tl.constexpr,
|
| 47 |
+
MASK_SINK: tl.constexpr,
|
| 48 |
+
):
|
| 49 |
+
# Load query tile
|
| 50 |
+
q_tile = ptrs_sched.load_q(config, q_ptrs, m_block)
|
| 51 |
+
|
| 52 |
+
# Compute attention scores
|
| 53 |
+
acc_s = tl.dot(k_tile, q_tile.T)
|
| 54 |
+
|
| 55 |
+
# Load LSE
|
| 56 |
+
lse_log2 = ptrs_sched.load_lse(config, lse_ptrs, m_block)
|
| 57 |
+
|
| 58 |
+
if IS_MASK:
|
| 59 |
+
# Apply mask to attention scores
|
| 60 |
+
acc_s = mask_sched.apply_mask(
|
| 61 |
+
acc_s=acc_s,
|
| 62 |
+
iter_block=m_block,
|
| 63 |
+
MASK_CAUSAL=MASK_CAUSAL,
|
| 64 |
+
MASK_LOCAL=MASK_LOCAL,
|
| 65 |
+
MASK_SINK=MASK_SINK,
|
| 66 |
+
)
|
| 67 |
+
|
| 68 |
+
# Compute attention weights
|
| 69 |
+
p = activations.exp2(acc_s * config.softmax_scale_log2 - lse_log2[None, :]).to(
|
| 70 |
+
q_tile.dtype
|
| 71 |
+
)
|
| 72 |
+
|
| 73 |
+
# Load output gradients tile
|
| 74 |
+
do_tile = ptrs_sched.load_do(config, do_ptrs, m_block)
|
| 75 |
+
|
| 76 |
+
# Compute value gradients
|
| 77 |
+
acc_dv += tl.dot(p, do_tile)
|
| 78 |
+
|
| 79 |
+
# Compute attention weight gradients
|
| 80 |
+
acc_dp = tl.dot(v_tile, tl.trans(do_tile))
|
| 81 |
+
|
| 82 |
+
# Load dpsum
|
| 83 |
+
dpsum = ptrs_sched.load_dpsum(config, dpsum_ptrs, m_block)
|
| 84 |
+
|
| 85 |
+
# Compute attention score gradients
|
| 86 |
+
ds = p * (acc_dp - dpsum[None, :]).to(q_tile.dtype)
|
| 87 |
+
|
| 88 |
+
# Compute query gradients
|
| 89 |
+
dq = tl.dot(tl.trans(ds), k_tile)
|
| 90 |
+
|
| 91 |
+
# Store query gradients
|
| 92 |
+
ptrs_sched.store_dq(config, dq_accum_ptrs, m_block, dq)
|
| 93 |
+
|
| 94 |
+
# Compute key gradients
|
| 95 |
+
acc_dk += tl.dot(ds, q_tile)
|
| 96 |
+
|
| 97 |
+
return acc_dk, acc_dv
|
| 98 |
+
|
| 99 |
+
|
| 100 |
+
@triton.jit(repr=kernel_repr.bwd_dense_repr)
|
| 101 |
+
def _bwd_dense_kernel(
|
| 102 |
+
Q,
|
| 103 |
+
K,
|
| 104 |
+
V,
|
| 105 |
+
dO,
|
| 106 |
+
LSELog2,
|
| 107 |
+
dPsum,
|
| 108 |
+
dQaccum,
|
| 109 |
+
dK,
|
| 110 |
+
dV,
|
| 111 |
+
softmax_scale,
|
| 112 |
+
query_scale,
|
| 113 |
+
key_scale,
|
| 114 |
+
value_scale,
|
| 115 |
+
window_sizes,
|
| 116 |
+
stride_qb,
|
| 117 |
+
stride_qh,
|
| 118 |
+
stride_qm,
|
| 119 |
+
stride_kb,
|
| 120 |
+
stride_kh,
|
| 121 |
+
stride_kn,
|
| 122 |
+
stride_vb,
|
| 123 |
+
stride_vh,
|
| 124 |
+
stride_vn,
|
| 125 |
+
stride_dob,
|
| 126 |
+
stride_doh,
|
| 127 |
+
stride_dom,
|
| 128 |
+
stride_lb,
|
| 129 |
+
stride_lh,
|
| 130 |
+
stride_pb,
|
| 131 |
+
stride_ph,
|
| 132 |
+
stride_dqab,
|
| 133 |
+
stride_dqah,
|
| 134 |
+
stride_dqam,
|
| 135 |
+
stride_dkb,
|
| 136 |
+
stride_dkh,
|
| 137 |
+
stride_dkn,
|
| 138 |
+
stride_dks,
|
| 139 |
+
stride_dvb,
|
| 140 |
+
stride_dvh,
|
| 141 |
+
stride_dvn,
|
| 142 |
+
stride_dvs,
|
| 143 |
+
stride_wh,
|
| 144 |
+
cu_seqlens_q,
|
| 145 |
+
cu_seqlens_k,
|
| 146 |
+
seqused_q,
|
| 147 |
+
seqused_k,
|
| 148 |
+
num_splits,
|
| 149 |
+
seqlen_q,
|
| 150 |
+
seqlen_k,
|
| 151 |
+
head_dim,
|
| 152 |
+
SEQLEN_Q_CACHE: tl.constexpr,
|
| 153 |
+
SEQLEN_K_CACHE: tl.constexpr,
|
| 154 |
+
QHEAD_PER_KVHEAD: tl.constexpr,
|
| 155 |
+
TILE_M: tl.constexpr,
|
| 156 |
+
TILE_N: tl.constexpr,
|
| 157 |
+
TILE_K: tl.constexpr,
|
| 158 |
+
IS_CAUSAL: tl.constexpr,
|
| 159 |
+
IS_LOCAL: tl.constexpr,
|
| 160 |
+
IS_SPLIT_QO: tl.constexpr,
|
| 161 |
+
HAS_CU_SEQLENS_Q: tl.constexpr,
|
| 162 |
+
HAS_CU_SEQLENS_K: tl.constexpr,
|
| 163 |
+
HAS_SEQUSED_Q: tl.constexpr,
|
| 164 |
+
HAS_SEQUSED_K: tl.constexpr,
|
| 165 |
+
):
|
| 166 |
+
# Create grid index
|
| 167 |
+
grid_idx = AttnBwdGridIndex.create(
|
| 168 |
+
num_splits=num_splits,
|
| 169 |
+
QHEAD_PER_KVHEAD=QHEAD_PER_KVHEAD,
|
| 170 |
+
IS_SPLIT_QO=IS_SPLIT_QO,
|
| 171 |
+
)
|
| 172 |
+
|
| 173 |
+
# Load window sizes
|
| 174 |
+
(
|
| 175 |
+
window_size_sink,
|
| 176 |
+
window_size_left,
|
| 177 |
+
window_size_right,
|
| 178 |
+
window_size_dist,
|
| 179 |
+
) = grid_idx.load_window_sizes(
|
| 180 |
+
window_sizes=window_sizes,
|
| 181 |
+
stride_wh=stride_wh,
|
| 182 |
+
IS_LOCAL=IS_LOCAL,
|
| 183 |
+
)
|
| 184 |
+
|
| 185 |
+
# Create config
|
| 186 |
+
config = AttnBwdConfig.create(
|
| 187 |
+
softmax_scale=softmax_scale,
|
| 188 |
+
query_scale=query_scale,
|
| 189 |
+
key_scale=key_scale,
|
| 190 |
+
value_scale=value_scale,
|
| 191 |
+
n_block=grid_idx.n_block,
|
| 192 |
+
batch_idx=grid_idx.batch_idx,
|
| 193 |
+
window_size_sink=window_size_sink,
|
| 194 |
+
window_size_left=window_size_left,
|
| 195 |
+
window_size_right=window_size_right,
|
| 196 |
+
window_size_dist=window_size_dist,
|
| 197 |
+
head_dim=head_dim,
|
| 198 |
+
cu_seqlens_q=cu_seqlens_q,
|
| 199 |
+
cu_seqlens_k=cu_seqlens_k,
|
| 200 |
+
seqused_q=seqused_q,
|
| 201 |
+
seqused_k=seqused_k,
|
| 202 |
+
seqlen_q=seqlen_q,
|
| 203 |
+
seqlen_k=seqlen_k,
|
| 204 |
+
QHEAD_PER_KVHEAD=QHEAD_PER_KVHEAD,
|
| 205 |
+
TILE_M=TILE_M,
|
| 206 |
+
TILE_N=TILE_N,
|
| 207 |
+
TILE_K=TILE_K,
|
| 208 |
+
IS_CAUSAL=IS_CAUSAL,
|
| 209 |
+
HAS_CU_SEQLENS_Q=HAS_CU_SEQLENS_Q,
|
| 210 |
+
HAS_CU_SEQLENS_K=HAS_CU_SEQLENS_K,
|
| 211 |
+
HAS_SEQUSED_Q=HAS_SEQUSED_Q,
|
| 212 |
+
HAS_SEQUSED_K=HAS_SEQUSED_K,
|
| 213 |
+
)
|
| 214 |
+
|
| 215 |
+
# Create pointer scheduler
|
| 216 |
+
ptrs_sched = AttnBwdPointerScheduler.create(
|
| 217 |
+
config=config,
|
| 218 |
+
Q=Q,
|
| 219 |
+
K=K,
|
| 220 |
+
V=V,
|
| 221 |
+
dO=dO,
|
| 222 |
+
LSELog2=LSELog2,
|
| 223 |
+
dPsum=dPsum,
|
| 224 |
+
dQaccum=dQaccum,
|
| 225 |
+
dK=dK,
|
| 226 |
+
dV=dV,
|
| 227 |
+
batch_idx=grid_idx.batch_idx,
|
| 228 |
+
head_idx=grid_idx.head_idx,
|
| 229 |
+
head_kv_idx=grid_idx.head_kv_idx,
|
| 230 |
+
split_idx=grid_idx.split_idx,
|
| 231 |
+
stride_qb=stride_qb,
|
| 232 |
+
stride_qh=stride_qh,
|
| 233 |
+
stride_qm=stride_qm,
|
| 234 |
+
stride_kb=stride_kb,
|
| 235 |
+
stride_kh=stride_kh,
|
| 236 |
+
stride_kn=stride_kn,
|
| 237 |
+
stride_vb=stride_vb,
|
| 238 |
+
stride_vh=stride_vh,
|
| 239 |
+
stride_vn=stride_vn,
|
| 240 |
+
stride_dob=stride_dob,
|
| 241 |
+
stride_doh=stride_doh,
|
| 242 |
+
stride_dom=stride_dom,
|
| 243 |
+
stride_lb=stride_lb,
|
| 244 |
+
stride_lh=stride_lh,
|
| 245 |
+
stride_pb=stride_pb,
|
| 246 |
+
stride_ph=stride_ph,
|
| 247 |
+
stride_dqab=stride_dqab,
|
| 248 |
+
stride_dqah=stride_dqah,
|
| 249 |
+
stride_dqam=stride_dqam,
|
| 250 |
+
stride_dkb=stride_dkb,
|
| 251 |
+
stride_dkh=stride_dkh,
|
| 252 |
+
stride_dkn=stride_dkn,
|
| 253 |
+
stride_dks=stride_dks,
|
| 254 |
+
stride_dvb=stride_dvb,
|
| 255 |
+
stride_dvh=stride_dvh,
|
| 256 |
+
stride_dvn=stride_dvn,
|
| 257 |
+
stride_dvs=stride_dvs,
|
| 258 |
+
IS_SPLIT_QO=IS_SPLIT_QO,
|
| 259 |
+
HAS_CU_SEQLENS_Q=HAS_CU_SEQLENS_Q,
|
| 260 |
+
HAS_CU_SEQLENS_K=HAS_CU_SEQLENS_K,
|
| 261 |
+
)
|
| 262 |
+
|
| 263 |
+
# Create block scheduler
|
| 264 |
+
block_sched = AttnBwdBlockScheduler.create(
|
| 265 |
+
config=config,
|
| 266 |
+
split_idx=grid_idx.split_idx,
|
| 267 |
+
num_splits=num_splits,
|
| 268 |
+
IS_CAUSAL=IS_CAUSAL,
|
| 269 |
+
IS_LOCAL=IS_LOCAL,
|
| 270 |
+
IS_SPLIT_QO=IS_SPLIT_QO,
|
| 271 |
+
)
|
| 272 |
+
|
| 273 |
+
# Create mask scheduler
|
| 274 |
+
mask_sched = AttnMaskScheduler.create(config, SWAP_AB=True)
|
| 275 |
+
|
| 276 |
+
# Early exit if no n_blocks to process
|
| 277 |
+
if grid_idx.n_block * TILE_N >= config.actual_seqlen_k:
|
| 278 |
+
return
|
| 279 |
+
|
| 280 |
+
# Create pointers
|
| 281 |
+
q_ptrs = ptrs_sched.make_q_ptrs(config)
|
| 282 |
+
k_ptrs = ptrs_sched.make_k_ptrs(config)
|
| 283 |
+
v_ptrs = ptrs_sched.make_v_ptrs(config)
|
| 284 |
+
do_ptrs = ptrs_sched.make_do_ptrs(config)
|
| 285 |
+
lse_ptrs = ptrs_sched.make_lse_ptrs(config)
|
| 286 |
+
dpsum_ptrs = ptrs_sched.make_dpsum_ptrs(config)
|
| 287 |
+
dq_accum_ptrs = ptrs_sched.make_dq_accum_ptrs(config)
|
| 288 |
+
dk_ptrs = ptrs_sched.make_dk_ptrs(config)
|
| 289 |
+
dv_ptrs = ptrs_sched.make_dv_ptrs(config)
|
| 290 |
+
|
| 291 |
+
# Initialize accumulators
|
| 292 |
+
acc_dk = tl.zeros((TILE_N, TILE_K), dtype=tl.float32)
|
| 293 |
+
acc_dv = tl.zeros((TILE_N, TILE_K), dtype=tl.float32)
|
| 294 |
+
|
| 295 |
+
# Load key tile
|
| 296 |
+
k_tile = ptrs_sched.load_k(config, k_ptrs)
|
| 297 |
+
|
| 298 |
+
# Load value tile
|
| 299 |
+
v_tile = ptrs_sched.load_v(config, v_ptrs)
|
| 300 |
+
|
| 301 |
+
# Process m_blocks with causal masking
|
| 302 |
+
if IS_CAUSAL or IS_LOCAL:
|
| 303 |
+
for m_block in tl.range(
|
| 304 |
+
block_sched.m_block_min, block_sched.m_block_min_no_mask
|
| 305 |
+
):
|
| 306 |
+
acc_dk, acc_dv = _bwd_inner_dense_kernel(
|
| 307 |
+
config=config,
|
| 308 |
+
ptrs_sched=ptrs_sched,
|
| 309 |
+
mask_sched=mask_sched,
|
| 310 |
+
acc_dk=acc_dk,
|
| 311 |
+
acc_dv=acc_dv,
|
| 312 |
+
k_tile=k_tile,
|
| 313 |
+
v_tile=v_tile,
|
| 314 |
+
q_ptrs=q_ptrs,
|
| 315 |
+
do_ptrs=do_ptrs,
|
| 316 |
+
dq_accum_ptrs=dq_accum_ptrs,
|
| 317 |
+
lse_ptrs=lse_ptrs,
|
| 318 |
+
dpsum_ptrs=dpsum_ptrs,
|
| 319 |
+
m_block=m_block,
|
| 320 |
+
IS_MASK=True,
|
| 321 |
+
MASK_CAUSAL=IS_CAUSAL,
|
| 322 |
+
MASK_LOCAL=True if IS_LOCAL else False,
|
| 323 |
+
MASK_SINK=False,
|
| 324 |
+
)
|
| 325 |
+
|
| 326 |
+
# Process m_blocks without masking
|
| 327 |
+
if not IS_LOCAL and block_sched.m_block_min_no_mask < block_sched.m_block_max:
|
| 328 |
+
for m_block in tl.range(
|
| 329 |
+
block_sched.m_block_min_no_mask, block_sched.m_block_max
|
| 330 |
+
):
|
| 331 |
+
acc_dk, acc_dv = _bwd_inner_dense_kernel(
|
| 332 |
+
config=config,
|
| 333 |
+
ptrs_sched=ptrs_sched,
|
| 334 |
+
mask_sched=mask_sched,
|
| 335 |
+
acc_dk=acc_dk,
|
| 336 |
+
acc_dv=acc_dv,
|
| 337 |
+
k_tile=k_tile,
|
| 338 |
+
v_tile=v_tile,
|
| 339 |
+
q_ptrs=q_ptrs,
|
| 340 |
+
do_ptrs=do_ptrs,
|
| 341 |
+
dq_accum_ptrs=dq_accum_ptrs,
|
| 342 |
+
lse_ptrs=lse_ptrs,
|
| 343 |
+
dpsum_ptrs=dpsum_ptrs,
|
| 344 |
+
m_block=m_block,
|
| 345 |
+
IS_MASK=False,
|
| 346 |
+
MASK_CAUSAL=False,
|
| 347 |
+
MASK_LOCAL=False,
|
| 348 |
+
MASK_SINK=False,
|
| 349 |
+
)
|
| 350 |
+
|
| 351 |
+
if IS_LOCAL:
|
| 352 |
+
# Process m_blocks with local right masking
|
| 353 |
+
if block_sched.m_block_window_min < block_sched.m_block_window_min_no_mask:
|
| 354 |
+
for m_block in tl.range(
|
| 355 |
+
block_sched.m_block_window_min,
|
| 356 |
+
block_sched.m_block_window_min_no_mask,
|
| 357 |
+
):
|
| 358 |
+
acc_dk, acc_dv = _bwd_inner_dense_kernel(
|
| 359 |
+
config=config,
|
| 360 |
+
ptrs_sched=ptrs_sched,
|
| 361 |
+
mask_sched=mask_sched,
|
| 362 |
+
acc_dk=acc_dk,
|
| 363 |
+
acc_dv=acc_dv,
|
| 364 |
+
k_tile=k_tile,
|
| 365 |
+
v_tile=v_tile,
|
| 366 |
+
q_ptrs=q_ptrs,
|
| 367 |
+
do_ptrs=do_ptrs,
|
| 368 |
+
dq_accum_ptrs=dq_accum_ptrs,
|
| 369 |
+
lse_ptrs=lse_ptrs,
|
| 370 |
+
dpsum_ptrs=dpsum_ptrs,
|
| 371 |
+
m_block=m_block,
|
| 372 |
+
IS_MASK=True,
|
| 373 |
+
MASK_CAUSAL=False,
|
| 374 |
+
MASK_LOCAL=True,
|
| 375 |
+
MASK_SINK=False,
|
| 376 |
+
)
|
| 377 |
+
|
| 378 |
+
# Process m_blocks without masking
|
| 379 |
+
if (
|
| 380 |
+
block_sched.m_block_window_min_no_mask
|
| 381 |
+
< block_sched.m_block_window_max_no_mask
|
| 382 |
+
):
|
| 383 |
+
for m_block in tl.range(
|
| 384 |
+
block_sched.m_block_window_min_no_mask,
|
| 385 |
+
block_sched.m_block_window_max_no_mask,
|
| 386 |
+
):
|
| 387 |
+
acc_dk, acc_dv = _bwd_inner_dense_kernel(
|
| 388 |
+
config=config,
|
| 389 |
+
ptrs_sched=ptrs_sched,
|
| 390 |
+
mask_sched=mask_sched,
|
| 391 |
+
acc_dk=acc_dk,
|
| 392 |
+
acc_dv=acc_dv,
|
| 393 |
+
k_tile=k_tile,
|
| 394 |
+
v_tile=v_tile,
|
| 395 |
+
q_ptrs=q_ptrs,
|
| 396 |
+
do_ptrs=do_ptrs,
|
| 397 |
+
dq_accum_ptrs=dq_accum_ptrs,
|
| 398 |
+
lse_ptrs=lse_ptrs,
|
| 399 |
+
dpsum_ptrs=dpsum_ptrs,
|
| 400 |
+
m_block=m_block,
|
| 401 |
+
IS_MASK=False,
|
| 402 |
+
MASK_CAUSAL=False,
|
| 403 |
+
MASK_LOCAL=False,
|
| 404 |
+
MASK_SINK=False,
|
| 405 |
+
)
|
| 406 |
+
|
| 407 |
+
# Process m_blocks with local left masking
|
| 408 |
+
if block_sched.m_block_window_max_no_mask < block_sched.m_block_window_max:
|
| 409 |
+
for m_block in tl.range(
|
| 410 |
+
block_sched.m_block_window_max_no_mask,
|
| 411 |
+
block_sched.m_block_window_max,
|
| 412 |
+
):
|
| 413 |
+
acc_dk, acc_dv = _bwd_inner_dense_kernel(
|
| 414 |
+
config=config,
|
| 415 |
+
ptrs_sched=ptrs_sched,
|
| 416 |
+
mask_sched=mask_sched,
|
| 417 |
+
acc_dk=acc_dk,
|
| 418 |
+
acc_dv=acc_dv,
|
| 419 |
+
k_tile=k_tile,
|
| 420 |
+
v_tile=v_tile,
|
| 421 |
+
q_ptrs=q_ptrs,
|
| 422 |
+
do_ptrs=do_ptrs,
|
| 423 |
+
dq_accum_ptrs=dq_accum_ptrs,
|
| 424 |
+
lse_ptrs=lse_ptrs,
|
| 425 |
+
dpsum_ptrs=dpsum_ptrs,
|
| 426 |
+
m_block=m_block,
|
| 427 |
+
IS_MASK=True,
|
| 428 |
+
MASK_CAUSAL=False,
|
| 429 |
+
MASK_LOCAL=True,
|
| 430 |
+
MASK_SINK=False,
|
| 431 |
+
)
|
| 432 |
+
|
| 433 |
+
# Process m_blocks with local sink masking
|
| 434 |
+
if block_sched.m_block_sink_max > block_sched.m_block_sink_min:
|
| 435 |
+
for m_block in tl.range(
|
| 436 |
+
block_sched.m_block_sink_min,
|
| 437 |
+
block_sched.m_block_sink_max,
|
| 438 |
+
):
|
| 439 |
+
acc_dk, acc_dv = _bwd_inner_dense_kernel(
|
| 440 |
+
config=config,
|
| 441 |
+
ptrs_sched=ptrs_sched,
|
| 442 |
+
mask_sched=mask_sched,
|
| 443 |
+
acc_dk=acc_dk,
|
| 444 |
+
acc_dv=acc_dv,
|
| 445 |
+
k_tile=k_tile,
|
| 446 |
+
v_tile=v_tile,
|
| 447 |
+
q_ptrs=q_ptrs,
|
| 448 |
+
do_ptrs=do_ptrs,
|
| 449 |
+
dq_accum_ptrs=dq_accum_ptrs,
|
| 450 |
+
lse_ptrs=lse_ptrs,
|
| 451 |
+
dpsum_ptrs=dpsum_ptrs,
|
| 452 |
+
m_block=m_block,
|
| 453 |
+
IS_MASK=True,
|
| 454 |
+
MASK_CAUSAL=False,
|
| 455 |
+
MASK_LOCAL=True,
|
| 456 |
+
MASK_SINK=True,
|
| 457 |
+
)
|
| 458 |
+
|
| 459 |
+
# Store value gradients
|
| 460 |
+
ptrs_sched.store_dv(config, dv_ptrs, acc_dv)
|
| 461 |
+
|
| 462 |
+
# Scale key gradients
|
| 463 |
+
acc_dk = acc_dk * softmax_scale
|
| 464 |
+
|
| 465 |
+
# Store key gradients
|
| 466 |
+
ptrs_sched.store_dk(config, dk_ptrs, acc_dk)
|
| 467 |
+
|
| 468 |
+
|
| 469 |
+
_bwd_dense_kernel = cache_utils.wrap_kernel(_bwd_dense_kernel)
|
| 470 |
+
|
| 471 |
+
|
| 472 |
+
_bwd_dense_kernel_autotuned = None
|
| 473 |
+
|
| 474 |
+
|
| 475 |
+
def _get_autotuned_kernel():
|
| 476 |
+
global _bwd_dense_kernel_autotuned
|
| 477 |
+
if _bwd_dense_kernel_autotuned is None:
|
| 478 |
+
jit_kernel = _bwd_dense_kernel._kernel
|
| 479 |
+
autotuned = autotuner.make_bwd_dense_autotuned_kernel(jit_kernel)
|
| 480 |
+
_bwd_dense_kernel_autotuned = autotuner.AutotunedKernel(autotuned)
|
| 481 |
+
return _bwd_dense_kernel_autotuned
|
| 482 |
+
|
| 483 |
+
|
| 484 |
+
def _flash_dense_attn_backward(
|
| 485 |
+
query: torch.Tensor,
|
| 486 |
+
key: torch.Tensor,
|
| 487 |
+
value: torch.Tensor,
|
| 488 |
+
out: torch.Tensor,
|
| 489 |
+
dout: torch.Tensor,
|
| 490 |
+
lse: torch.Tensor,
|
| 491 |
+
is_causal: bool = False,
|
| 492 |
+
softmax_scale: float = None,
|
| 493 |
+
query_scale: Optional[torch.Tensor] = None,
|
| 494 |
+
key_scale: Optional[torch.Tensor] = None,
|
| 495 |
+
value_scale: Optional[torch.Tensor] = None,
|
| 496 |
+
window_sizes: Optional[torch.Tensor] = None,
|
| 497 |
+
is_local: bool = False,
|
| 498 |
+
is_quant: bool = False,
|
| 499 |
+
is_split_qo: bool = False,
|
| 500 |
+
is_autotune: bool = False,
|
| 501 |
+
skip_checks: bool = False,
|
| 502 |
+
) -> Tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
|
| 503 |
+
device = query.device
|
| 504 |
+
num_SMs = cache_utils.get_device_num_sms(device)
|
| 505 |
+
batch_size, seqlen_q, num_heads_q, head_dim = query.shape
|
| 506 |
+
_, seqlen_k, num_heads_kv, _ = key.shape
|
| 507 |
+
softmax_scale = (
|
| 508 |
+
softmax_scale if softmax_scale is not None else 1.0 / (head_dim**0.5)
|
| 509 |
+
)
|
| 510 |
+
qhead_per_kvhead = num_heads_q // num_heads_kv
|
| 511 |
+
if is_local and window_sizes is None:
|
| 512 |
+
window_sizes = utils.window_sizes_heuristic(seqlen_k, num_heads_kv, device)
|
| 513 |
+
elif not is_local:
|
| 514 |
+
window_sizes = torch.zeros((num_heads_kv, 4), dtype=torch.int32, device=device)
|
| 515 |
+
|
| 516 |
+
if not skip_checks:
|
| 517 |
+
assert_inputs.assert_bwd_inputs(
|
| 518 |
+
query,
|
| 519 |
+
key,
|
| 520 |
+
value,
|
| 521 |
+
out,
|
| 522 |
+
dout,
|
| 523 |
+
lse,
|
| 524 |
+
query_scale=query_scale,
|
| 525 |
+
key_scale=key_scale,
|
| 526 |
+
value_scale=value_scale,
|
| 527 |
+
window_sizes=window_sizes,
|
| 528 |
+
cu_seqlens_q=None,
|
| 529 |
+
cu_seqlens_k=None,
|
| 530 |
+
seqused_q=None,
|
| 531 |
+
seqused_k=None,
|
| 532 |
+
num_heads_q=num_heads_q,
|
| 533 |
+
num_heads_kv=num_heads_kv,
|
| 534 |
+
head_dim=head_dim,
|
| 535 |
+
is_quant=is_quant,
|
| 536 |
+
device=device,
|
| 537 |
+
)
|
| 538 |
+
|
| 539 |
+
TILE_K = max(triton.next_power_of_2(head_dim), 16)
|
| 540 |
+
|
| 541 |
+
launch_config = launch_template.load_launch_config(
|
| 542 |
+
device=device,
|
| 543 |
+
kernel_name="bwd_dense",
|
| 544 |
+
seqlen_q=seqlen_q,
|
| 545 |
+
seqlen_k=seqlen_k,
|
| 546 |
+
tile_k=TILE_K,
|
| 547 |
+
is_local=is_local,
|
| 548 |
+
qhead_per_kvhead=qhead_per_kvhead,
|
| 549 |
+
is_causal=is_causal,
|
| 550 |
+
is_quant=is_quant,
|
| 551 |
+
)
|
| 552 |
+
if launch_config is not None and not is_autotune:
|
| 553 |
+
kernel = _bwd_dense_kernel
|
| 554 |
+
TILE_M, TILE_N, num_warps, num_stages, num_ctas = launch_config
|
| 555 |
+
else:
|
| 556 |
+
kernel = _get_autotuned_kernel()
|
| 557 |
+
# Placeholder for pre-launch computations
|
| 558 |
+
TILE_M = TILE_N = 64
|
| 559 |
+
num_warps = num_stages = num_ctas = None
|
| 560 |
+
|
| 561 |
+
num_splits = (
|
| 562 |
+
utils.num_splits_heuristic(
|
| 563 |
+
seqlen_q=seqlen_k,
|
| 564 |
+
seqlen_k=seqlen_q,
|
| 565 |
+
num_SMs=num_SMs,
|
| 566 |
+
TILE_M=TILE_N,
|
| 567 |
+
TILE_N=TILE_M,
|
| 568 |
+
max_split_blocks=utils.max_split_blocks_from_window_sizes(
|
| 569 |
+
window_sizes, TILE_M
|
| 570 |
+
)
|
| 571 |
+
if is_local
|
| 572 |
+
else None,
|
| 573 |
+
)
|
| 574 |
+
if is_split_qo
|
| 575 |
+
else 1
|
| 576 |
+
)
|
| 577 |
+
|
| 578 |
+
seqlen_q_rounded = int(math.ceil(seqlen_q / 128) * 128)
|
| 579 |
+
head_dim_rounded = int(math.ceil(head_dim / 32) * 32)
|
| 580 |
+
|
| 581 |
+
if not is_quant:
|
| 582 |
+
query_scale = torch.ones(1, device=device, dtype=query.dtype)
|
| 583 |
+
key_scale = torch.ones(1, device=device, dtype=query.dtype)
|
| 584 |
+
value_scale = torch.ones(1, device=device, dtype=query.dtype)
|
| 585 |
+
|
| 586 |
+
dq = torch.empty_like(query, dtype=query_scale.dtype)
|
| 587 |
+
dk = torch.empty_like(key, dtype=key_scale.dtype)
|
| 588 |
+
dv = torch.empty_like(value, dtype=value_scale.dtype)
|
| 589 |
+
lse_log2 = torch.empty(
|
| 590 |
+
(batch_size, num_heads_q, seqlen_q_rounded),
|
| 591 |
+
dtype=torch.float32,
|
| 592 |
+
device=query.device,
|
| 593 |
+
)
|
| 594 |
+
dpsum = torch.empty(
|
| 595 |
+
(batch_size, num_heads_q, seqlen_q_rounded),
|
| 596 |
+
dtype=torch.float32,
|
| 597 |
+
device=query.device,
|
| 598 |
+
)
|
| 599 |
+
dq_accum = torch.empty(
|
| 600 |
+
(batch_size, num_heads_q, seqlen_q_rounded * head_dim_rounded),
|
| 601 |
+
dtype=torch.float32,
|
| 602 |
+
device=query.device,
|
| 603 |
+
)
|
| 604 |
+
dk_accum = torch.zeros(
|
| 605 |
+
(num_splits, batch_size, seqlen_k, num_heads_kv, head_dim)
|
| 606 |
+
if is_split_qo and num_splits > 1
|
| 607 |
+
else (batch_size, seqlen_k, num_heads_kv, head_dim),
|
| 608 |
+
dtype=torch.float32,
|
| 609 |
+
device=query.device,
|
| 610 |
+
)
|
| 611 |
+
dv_accum = torch.zeros(
|
| 612 |
+
(num_splits, batch_size, seqlen_k, num_heads_kv, head_dim)
|
| 613 |
+
if is_split_qo and num_splits > 1
|
| 614 |
+
else (batch_size, seqlen_k, num_heads_kv, head_dim),
|
| 615 |
+
dtype=torch.float32,
|
| 616 |
+
device=query.device,
|
| 617 |
+
)
|
| 618 |
+
|
| 619 |
+
flash_bwd_preprocess._flash_attn_bwd_preprocess(
|
| 620 |
+
out=out,
|
| 621 |
+
dout=dout,
|
| 622 |
+
dpsum=dpsum,
|
| 623 |
+
lse=lse,
|
| 624 |
+
lse_log2=lse_log2,
|
| 625 |
+
dq_accum=dq_accum,
|
| 626 |
+
head_dim_rounded=head_dim_rounded,
|
| 627 |
+
tile_m=TILE_M,
|
| 628 |
+
tile_k=TILE_K,
|
| 629 |
+
)
|
| 630 |
+
|
| 631 |
+
grid = launch_grid.get_bwd_grid(
|
| 632 |
+
seqlen_k=seqlen_k,
|
| 633 |
+
num_heads_q=num_heads_q,
|
| 634 |
+
batch_size=batch_size,
|
| 635 |
+
num_splits=num_splits,
|
| 636 |
+
)
|
| 637 |
+
|
| 638 |
+
triton.set_allocator(utils.alloc_fn)
|
| 639 |
+
|
| 640 |
+
kernel[grid](
|
| 641 |
+
query,
|
| 642 |
+
key,
|
| 643 |
+
value,
|
| 644 |
+
dout,
|
| 645 |
+
lse_log2,
|
| 646 |
+
dpsum,
|
| 647 |
+
dq_accum,
|
| 648 |
+
dk_accum,
|
| 649 |
+
dv_accum,
|
| 650 |
+
softmax_scale,
|
| 651 |
+
query_scale,
|
| 652 |
+
key_scale,
|
| 653 |
+
value_scale,
|
| 654 |
+
window_sizes,
|
| 655 |
+
query.stride(0),
|
| 656 |
+
query.stride(-2),
|
| 657 |
+
query.stride(-3),
|
| 658 |
+
key.stride(0),
|
| 659 |
+
key.stride(-2),
|
| 660 |
+
key.stride(-3),
|
| 661 |
+
value.stride(0),
|
| 662 |
+
value.stride(-2),
|
| 663 |
+
value.stride(-3),
|
| 664 |
+
dout.stride(0),
|
| 665 |
+
dout.stride(-2),
|
| 666 |
+
dout.stride(-3),
|
| 667 |
+
lse_log2.stride(0),
|
| 668 |
+
lse_log2.stride(-2),
|
| 669 |
+
dpsum.stride(0),
|
| 670 |
+
dpsum.stride(-2),
|
| 671 |
+
dq_accum.stride(0),
|
| 672 |
+
dq_accum.stride(1),
|
| 673 |
+
head_dim_rounded,
|
| 674 |
+
dk_accum.stride(-4) if is_split_qo and num_splits > 1 else dk_accum.stride(0),
|
| 675 |
+
dk_accum.stride(-2),
|
| 676 |
+
dk_accum.stride(-3),
|
| 677 |
+
dk_accum.stride(0) if is_split_qo and num_splits > 1 else 0,
|
| 678 |
+
dv_accum.stride(-4) if is_split_qo and num_splits > 1 else dv_accum.stride(0),
|
| 679 |
+
dv_accum.stride(-2),
|
| 680 |
+
dv_accum.stride(-3),
|
| 681 |
+
dv_accum.stride(0) if is_split_qo and num_splits > 1 else 0,
|
| 682 |
+
window_sizes.stride(0),
|
| 683 |
+
None,
|
| 684 |
+
None,
|
| 685 |
+
None,
|
| 686 |
+
None,
|
| 687 |
+
num_splits,
|
| 688 |
+
seqlen_q=seqlen_q,
|
| 689 |
+
seqlen_k=seqlen_k,
|
| 690 |
+
head_dim=head_dim,
|
| 691 |
+
SEQLEN_Q_CACHE=seqlen_q // 1024,
|
| 692 |
+
SEQLEN_K_CACHE=seqlen_k // 1024,
|
| 693 |
+
QHEAD_PER_KVHEAD=qhead_per_kvhead,
|
| 694 |
+
TILE_M=TILE_M,
|
| 695 |
+
TILE_N=TILE_N,
|
| 696 |
+
TILE_K=TILE_K,
|
| 697 |
+
IS_CAUSAL=is_causal,
|
| 698 |
+
IS_LOCAL=is_local,
|
| 699 |
+
IS_SPLIT_QO=is_split_qo and num_splits > 1,
|
| 700 |
+
HAS_CU_SEQLENS_Q=False,
|
| 701 |
+
HAS_CU_SEQLENS_K=False,
|
| 702 |
+
HAS_SEQUSED_Q=False,
|
| 703 |
+
HAS_SEQUSED_K=False,
|
| 704 |
+
num_warps=num_warps,
|
| 705 |
+
num_stages=num_stages,
|
| 706 |
+
num_ctas=num_ctas,
|
| 707 |
+
)
|
| 708 |
+
|
| 709 |
+
if launch_config is None or is_autotune:
|
| 710 |
+
best = launch_template.extract_best_config(_get_autotuned_kernel())
|
| 711 |
+
if best is not None:
|
| 712 |
+
launch_template.store_launch_config(
|
| 713 |
+
device=device,
|
| 714 |
+
kernel_name="bwd_dense",
|
| 715 |
+
seqlen_q=seqlen_q,
|
| 716 |
+
seqlen_k=seqlen_k,
|
| 717 |
+
tile_k=TILE_K,
|
| 718 |
+
config=best,
|
| 719 |
+
is_local=is_local,
|
| 720 |
+
qhead_per_kvhead=qhead_per_kvhead,
|
| 721 |
+
is_causal=is_causal,
|
| 722 |
+
is_quant=is_quant,
|
| 723 |
+
)
|
| 724 |
+
|
| 725 |
+
flash_bwd_postprocess._flash_attn_bwd_postprocess(
|
| 726 |
+
dq_accum=dq_accum,
|
| 727 |
+
dq=dq,
|
| 728 |
+
scale=softmax_scale,
|
| 729 |
+
head_dim_rounded=head_dim_rounded,
|
| 730 |
+
tile_m=TILE_M,
|
| 731 |
+
tile_k=TILE_K,
|
| 732 |
+
)
|
| 733 |
+
|
| 734 |
+
if is_split_qo and num_splits > 1:
|
| 735 |
+
dk_accum = dk_accum.sum(dim=0)
|
| 736 |
+
dv_accum = dv_accum.sum(dim=0)
|
| 737 |
+
|
| 738 |
+
dk.copy_(dk_accum)
|
| 739 |
+
dv.copy_(dv_accum)
|
| 740 |
+
|
| 741 |
+
return dq, dk, dv
|
| 742 |
+
|
| 743 |
+
|
| 744 |
+
def _flash_dense_attn_varlen_backward(
|
| 745 |
+
query: torch.Tensor,
|
| 746 |
+
key: torch.Tensor,
|
| 747 |
+
value: torch.Tensor,
|
| 748 |
+
out: torch.Tensor,
|
| 749 |
+
dout: torch.Tensor,
|
| 750 |
+
lse: torch.Tensor,
|
| 751 |
+
cu_seqlens_q: torch.Tensor,
|
| 752 |
+
cu_seqlens_k: torch.Tensor,
|
| 753 |
+
max_seqlen_q: Optional[int] = None,
|
| 754 |
+
max_seqlen_k: Optional[int] = None,
|
| 755 |
+
is_causal: bool = False,
|
| 756 |
+
softmax_scale: float = None,
|
| 757 |
+
query_scale: Optional[torch.Tensor] = None,
|
| 758 |
+
key_scale: Optional[torch.Tensor] = None,
|
| 759 |
+
value_scale: Optional[torch.Tensor] = None,
|
| 760 |
+
window_sizes: Optional[torch.Tensor] = None,
|
| 761 |
+
is_local: bool = False,
|
| 762 |
+
is_quant: bool = False,
|
| 763 |
+
is_split_qo: bool = False,
|
| 764 |
+
seqused_q: Optional[torch.Tensor] = None,
|
| 765 |
+
seqused_k: Optional[torch.Tensor] = None,
|
| 766 |
+
is_autotune: bool = False,
|
| 767 |
+
skip_checks: bool = False,
|
| 768 |
+
) -> Tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
|
| 769 |
+
device = query.device
|
| 770 |
+
num_SMs = cache_utils.get_device_num_sms(device)
|
| 771 |
+
total_q, num_heads_q, head_dim = query.shape
|
| 772 |
+
total_k, num_heads_kv, _ = key.shape
|
| 773 |
+
batch_size = cu_seqlens_q.shape[0] - 1
|
| 774 |
+
seqlen_q = max_seqlen_q
|
| 775 |
+
seqlen_k = max_seqlen_k
|
| 776 |
+
softmax_scale = (
|
| 777 |
+
softmax_scale if softmax_scale is not None else 1.0 / (head_dim**0.5)
|
| 778 |
+
)
|
| 779 |
+
qhead_per_kvhead = num_heads_q // num_heads_kv
|
| 780 |
+
if is_local and window_sizes is None:
|
| 781 |
+
window_sizes = utils.window_sizes_heuristic(seqlen_k, num_heads_kv, device)
|
| 782 |
+
elif not is_local:
|
| 783 |
+
window_sizes = torch.zeros((num_heads_kv, 4), dtype=torch.int32, device=device)
|
| 784 |
+
|
| 785 |
+
if not skip_checks:
|
| 786 |
+
assert_inputs.assert_bwd_inputs(
|
| 787 |
+
query,
|
| 788 |
+
key,
|
| 789 |
+
value,
|
| 790 |
+
out,
|
| 791 |
+
dout,
|
| 792 |
+
lse,
|
| 793 |
+
query_scale=query_scale,
|
| 794 |
+
key_scale=key_scale,
|
| 795 |
+
value_scale=value_scale,
|
| 796 |
+
window_sizes=window_sizes,
|
| 797 |
+
cu_seqlens_q=cu_seqlens_q,
|
| 798 |
+
cu_seqlens_k=cu_seqlens_k,
|
| 799 |
+
seqused_q=seqused_q,
|
| 800 |
+
seqused_k=seqused_k,
|
| 801 |
+
num_heads_q=num_heads_q,
|
| 802 |
+
num_heads_kv=num_heads_kv,
|
| 803 |
+
head_dim=head_dim,
|
| 804 |
+
is_quant=is_quant,
|
| 805 |
+
device=device,
|
| 806 |
+
)
|
| 807 |
+
|
| 808 |
+
TILE_K = max(triton.next_power_of_2(head_dim), 16)
|
| 809 |
+
|
| 810 |
+
launch_config = launch_template.load_launch_config(
|
| 811 |
+
device=device,
|
| 812 |
+
kernel_name="bwd_dense",
|
| 813 |
+
seqlen_q=seqlen_q,
|
| 814 |
+
seqlen_k=seqlen_k,
|
| 815 |
+
tile_k=TILE_K,
|
| 816 |
+
is_local=is_local,
|
| 817 |
+
qhead_per_kvhead=qhead_per_kvhead,
|
| 818 |
+
is_causal=is_causal,
|
| 819 |
+
is_quant=is_quant,
|
| 820 |
+
)
|
| 821 |
+
if launch_config is not None and not is_autotune:
|
| 822 |
+
kernel = _bwd_dense_kernel
|
| 823 |
+
TILE_M, TILE_N, num_warps, num_stages, num_ctas = launch_config
|
| 824 |
+
else:
|
| 825 |
+
kernel = _get_autotuned_kernel()
|
| 826 |
+
# Placeholder for pre-launch computations
|
| 827 |
+
TILE_M = TILE_N = 64
|
| 828 |
+
num_warps = num_stages = num_ctas = None
|
| 829 |
+
|
| 830 |
+
num_splits = (
|
| 831 |
+
utils.num_splits_heuristic(
|
| 832 |
+
seqlen_q=seqlen_k,
|
| 833 |
+
seqlen_k=seqlen_q,
|
| 834 |
+
num_SMs=num_SMs,
|
| 835 |
+
TILE_M=TILE_N,
|
| 836 |
+
TILE_N=TILE_M,
|
| 837 |
+
max_split_blocks=utils.max_split_blocks_from_window_sizes(
|
| 838 |
+
window_sizes, TILE_M
|
| 839 |
+
)
|
| 840 |
+
if is_local
|
| 841 |
+
else None,
|
| 842 |
+
)
|
| 843 |
+
if is_split_qo
|
| 844 |
+
else 1
|
| 845 |
+
)
|
| 846 |
+
|
| 847 |
+
total_q_rounded_padded = int(math.ceil((total_q + batch_size * 128) / 128) * 128)
|
| 848 |
+
head_dim_rounded = int(math.ceil(head_dim / 32) * 32)
|
| 849 |
+
|
| 850 |
+
if not is_quant:
|
| 851 |
+
query_scale = torch.ones(1, device=device, dtype=query.dtype)
|
| 852 |
+
key_scale = torch.ones(1, device=device, dtype=query.dtype)
|
| 853 |
+
value_scale = torch.ones(1, device=device, dtype=query.dtype)
|
| 854 |
+
|
| 855 |
+
dq = torch.empty_like(query, dtype=query_scale.dtype)
|
| 856 |
+
dk = torch.empty_like(key, dtype=key_scale.dtype)
|
| 857 |
+
dv = torch.empty_like(value, dtype=value_scale.dtype)
|
| 858 |
+
lse_log2 = torch.empty(
|
| 859 |
+
num_heads_q,
|
| 860 |
+
total_q_rounded_padded,
|
| 861 |
+
dtype=torch.float32,
|
| 862 |
+
device=query.device,
|
| 863 |
+
)
|
| 864 |
+
dpsum = torch.empty(
|
| 865 |
+
num_heads_q,
|
| 866 |
+
total_q_rounded_padded,
|
| 867 |
+
dtype=torch.float32,
|
| 868 |
+
device=query.device,
|
| 869 |
+
)
|
| 870 |
+
dq_accum = torch.empty(
|
| 871 |
+
num_heads_q,
|
| 872 |
+
total_q_rounded_padded * head_dim_rounded,
|
| 873 |
+
dtype=torch.float32,
|
| 874 |
+
device=query.device,
|
| 875 |
+
)
|
| 876 |
+
dk_accum = torch.zeros(
|
| 877 |
+
(num_splits, total_k, num_heads_kv, head_dim)
|
| 878 |
+
if is_split_qo and num_splits > 1
|
| 879 |
+
else (total_k, num_heads_kv, head_dim),
|
| 880 |
+
dtype=torch.float32,
|
| 881 |
+
device=query.device,
|
| 882 |
+
)
|
| 883 |
+
dv_accum = torch.zeros(
|
| 884 |
+
(num_splits, total_k, num_heads_kv, head_dim)
|
| 885 |
+
if is_split_qo and num_splits > 1
|
| 886 |
+
else (total_k, num_heads_kv, head_dim),
|
| 887 |
+
dtype=torch.float32,
|
| 888 |
+
device=query.device,
|
| 889 |
+
)
|
| 890 |
+
|
| 891 |
+
flash_bwd_preprocess._flash_attn_bwd_preprocess(
|
| 892 |
+
out=out,
|
| 893 |
+
dout=dout,
|
| 894 |
+
dpsum=dpsum,
|
| 895 |
+
lse=lse,
|
| 896 |
+
lse_log2=lse_log2,
|
| 897 |
+
dq_accum=dq_accum,
|
| 898 |
+
head_dim_rounded=head_dim_rounded,
|
| 899 |
+
cu_seqlens_q=cu_seqlens_q,
|
| 900 |
+
seqused_q=seqused_q,
|
| 901 |
+
max_seqlen_q=max_seqlen_q,
|
| 902 |
+
tile_m=TILE_M,
|
| 903 |
+
tile_k=TILE_K,
|
| 904 |
+
)
|
| 905 |
+
|
| 906 |
+
grid = launch_grid.get_bwd_grid(
|
| 907 |
+
seqlen_k=seqlen_k,
|
| 908 |
+
num_heads_q=num_heads_q,
|
| 909 |
+
batch_size=batch_size,
|
| 910 |
+
num_splits=num_splits,
|
| 911 |
+
)
|
| 912 |
+
|
| 913 |
+
triton.set_allocator(utils.alloc_fn)
|
| 914 |
+
|
| 915 |
+
kernel[grid](
|
| 916 |
+
query,
|
| 917 |
+
key,
|
| 918 |
+
value,
|
| 919 |
+
dout,
|
| 920 |
+
lse_log2,
|
| 921 |
+
dpsum,
|
| 922 |
+
dq_accum,
|
| 923 |
+
dk_accum,
|
| 924 |
+
dv_accum,
|
| 925 |
+
softmax_scale,
|
| 926 |
+
query_scale,
|
| 927 |
+
key_scale,
|
| 928 |
+
value_scale,
|
| 929 |
+
window_sizes,
|
| 930 |
+
0,
|
| 931 |
+
query.stride(-2),
|
| 932 |
+
query.stride(0),
|
| 933 |
+
0,
|
| 934 |
+
key.stride(-2),
|
| 935 |
+
key.stride(0),
|
| 936 |
+
0,
|
| 937 |
+
value.stride(-2),
|
| 938 |
+
value.stride(0),
|
| 939 |
+
0,
|
| 940 |
+
dout.stride(-2),
|
| 941 |
+
dout.stride(0),
|
| 942 |
+
0,
|
| 943 |
+
lse_log2.stride(0),
|
| 944 |
+
0,
|
| 945 |
+
dpsum.stride(0),
|
| 946 |
+
0,
|
| 947 |
+
dq_accum.stride(0),
|
| 948 |
+
head_dim_rounded,
|
| 949 |
+
0,
|
| 950 |
+
dk_accum.stride(-2),
|
| 951 |
+
dk_accum.stride(-3),
|
| 952 |
+
dk_accum.stride(0) if is_split_qo and num_splits > 1 else 0,
|
| 953 |
+
0,
|
| 954 |
+
dv_accum.stride(-2),
|
| 955 |
+
dv_accum.stride(-3),
|
| 956 |
+
dv_accum.stride(0) if is_split_qo and num_splits > 1 else 0,
|
| 957 |
+
window_sizes.stride(0),
|
| 958 |
+
cu_seqlens_q,
|
| 959 |
+
cu_seqlens_k,
|
| 960 |
+
seqused_q,
|
| 961 |
+
seqused_k,
|
| 962 |
+
num_splits,
|
| 963 |
+
seqlen_q=seqlen_q,
|
| 964 |
+
seqlen_k=seqlen_k,
|
| 965 |
+
head_dim=head_dim,
|
| 966 |
+
SEQLEN_Q_CACHE=seqlen_q // 1024,
|
| 967 |
+
SEQLEN_K_CACHE=seqlen_k // 1024,
|
| 968 |
+
QHEAD_PER_KVHEAD=qhead_per_kvhead,
|
| 969 |
+
TILE_M=TILE_M,
|
| 970 |
+
TILE_N=TILE_N,
|
| 971 |
+
TILE_K=TILE_K,
|
| 972 |
+
IS_CAUSAL=is_causal,
|
| 973 |
+
IS_LOCAL=is_local,
|
| 974 |
+
IS_SPLIT_QO=is_split_qo and num_splits > 1,
|
| 975 |
+
HAS_CU_SEQLENS_Q=True,
|
| 976 |
+
HAS_CU_SEQLENS_K=True,
|
| 977 |
+
HAS_SEQUSED_Q=seqused_q is not None,
|
| 978 |
+
HAS_SEQUSED_K=seqused_k is not None,
|
| 979 |
+
num_warps=num_warps,
|
| 980 |
+
num_stages=num_stages,
|
| 981 |
+
num_ctas=num_ctas,
|
| 982 |
+
)
|
| 983 |
+
|
| 984 |
+
if launch_config is None or is_autotune:
|
| 985 |
+
best = launch_template.extract_best_config(_get_autotuned_kernel())
|
| 986 |
+
if best is not None:
|
| 987 |
+
launch_template.store_launch_config(
|
| 988 |
+
device=device,
|
| 989 |
+
kernel_name="bwd_dense",
|
| 990 |
+
seqlen_q=seqlen_q,
|
| 991 |
+
seqlen_k=seqlen_k,
|
| 992 |
+
tile_k=TILE_K,
|
| 993 |
+
config=best,
|
| 994 |
+
is_local=is_local,
|
| 995 |
+
qhead_per_kvhead=qhead_per_kvhead,
|
| 996 |
+
is_causal=is_causal,
|
| 997 |
+
is_quant=is_quant,
|
| 998 |
+
)
|
| 999 |
+
|
| 1000 |
+
flash_bwd_postprocess._flash_attn_bwd_postprocess(
|
| 1001 |
+
dq_accum=dq_accum,
|
| 1002 |
+
dq=dq,
|
| 1003 |
+
scale=softmax_scale,
|
| 1004 |
+
head_dim_rounded=head_dim_rounded,
|
| 1005 |
+
cu_seqlens_q=cu_seqlens_q,
|
| 1006 |
+
seqused_q=seqused_q,
|
| 1007 |
+
max_seqlen_q=max_seqlen_q,
|
| 1008 |
+
tile_m=TILE_M,
|
| 1009 |
+
tile_k=TILE_K,
|
| 1010 |
+
)
|
| 1011 |
+
|
| 1012 |
+
if is_split_qo and num_splits > 1:
|
| 1013 |
+
dk_accum = dk_accum.sum(dim=0)
|
| 1014 |
+
dv_accum = dv_accum.sum(dim=0)
|
| 1015 |
+
|
| 1016 |
+
dk.copy_(dk_accum)
|
| 1017 |
+
dv.copy_(dv_accum)
|
| 1018 |
+
|
| 1019 |
+
return dq, dk, dv
|
build/torch-cuda/flash_dense_dec.py
ADDED
|
@@ -0,0 +1,857 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from typing import Tuple, Optional
|
| 2 |
+
|
| 3 |
+
import torch
|
| 4 |
+
import triton
|
| 5 |
+
import triton.language as tl
|
| 6 |
+
|
| 7 |
+
from . import (
|
| 8 |
+
assert_inputs,
|
| 9 |
+
utils,
|
| 10 |
+
cache_utils,
|
| 11 |
+
launch_template,
|
| 12 |
+
launch_grid,
|
| 13 |
+
flash_dec_combine,
|
| 14 |
+
kernel_repr,
|
| 15 |
+
autotuner,
|
| 16 |
+
)
|
| 17 |
+
from .scheduler import (
|
| 18 |
+
AttnDecGridIndex,
|
| 19 |
+
AttnDecConfig,
|
| 20 |
+
AttnDecBlockScheduler,
|
| 21 |
+
AttnDecPointerScheduler,
|
| 22 |
+
AttnMaskScheduler,
|
| 23 |
+
SoftmaxScheduler,
|
| 24 |
+
)
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
@triton.jit
|
| 28 |
+
def _dec_inner_dense_kernel(
|
| 29 |
+
config: AttnDecConfig,
|
| 30 |
+
ptrs_sched: AttnDecPointerScheduler,
|
| 31 |
+
mask_sched: AttnMaskScheduler,
|
| 32 |
+
softmax_sched: SoftmaxScheduler,
|
| 33 |
+
q_tile,
|
| 34 |
+
k_tile,
|
| 35 |
+
k_ptrs,
|
| 36 |
+
v_ptrs,
|
| 37 |
+
acc_o,
|
| 38 |
+
row_max,
|
| 39 |
+
row_sum,
|
| 40 |
+
n_block,
|
| 41 |
+
n_block_min,
|
| 42 |
+
IS_MASK: tl.constexpr,
|
| 43 |
+
MASK_LOCAL: tl.constexpr,
|
| 44 |
+
MASK_SINK: tl.constexpr,
|
| 45 |
+
CHECK_INF: tl.constexpr,
|
| 46 |
+
):
|
| 47 |
+
# Compute attention scores
|
| 48 |
+
acc_s = tl.dot(q_tile, k_tile.T)
|
| 49 |
+
|
| 50 |
+
# Prefetch next key tile
|
| 51 |
+
if n_block > n_block_min:
|
| 52 |
+
# Load next key tile
|
| 53 |
+
k_tile = ptrs_sched.load_k(config, k_ptrs, n_block - 1)
|
| 54 |
+
|
| 55 |
+
if IS_MASK:
|
| 56 |
+
# Apply mask to attention scores
|
| 57 |
+
acc_s = mask_sched.apply_mask(
|
| 58 |
+
acc_s=acc_s,
|
| 59 |
+
iter_block=n_block,
|
| 60 |
+
MASK_LOCAL=MASK_LOCAL,
|
| 61 |
+
MASK_SINK=MASK_SINK,
|
| 62 |
+
)
|
| 63 |
+
|
| 64 |
+
# Apply online softmax
|
| 65 |
+
p, row_max, row_sum, row_scale = softmax_sched.online_softmax(
|
| 66 |
+
acc_s=acc_s,
|
| 67 |
+
row_max=row_max,
|
| 68 |
+
row_sum=row_sum,
|
| 69 |
+
CHECK_INF=CHECK_INF,
|
| 70 |
+
)
|
| 71 |
+
|
| 72 |
+
# Load value tile
|
| 73 |
+
v_tile = ptrs_sched.load_v(config, v_ptrs, n_block)
|
| 74 |
+
|
| 75 |
+
# Rescale output accumulator
|
| 76 |
+
acc_o = softmax_sched.rescale_o(
|
| 77 |
+
acc_o=acc_o,
|
| 78 |
+
row_scale=row_scale,
|
| 79 |
+
)
|
| 80 |
+
|
| 81 |
+
# Update output accumulator
|
| 82 |
+
acc_o += tl.dot(p.to(v_tile.dtype), v_tile)
|
| 83 |
+
|
| 84 |
+
return k_tile, acc_o, row_max, row_sum
|
| 85 |
+
|
| 86 |
+
|
| 87 |
+
@triton.jit(repr=kernel_repr.dec_dense_repr)
|
| 88 |
+
def _dec_dense_kernel(
|
| 89 |
+
Q,
|
| 90 |
+
K,
|
| 91 |
+
V,
|
| 92 |
+
Out,
|
| 93 |
+
Lse,
|
| 94 |
+
softmax_scale,
|
| 95 |
+
query_scale,
|
| 96 |
+
key_scale,
|
| 97 |
+
value_scale,
|
| 98 |
+
window_sizes,
|
| 99 |
+
stride_qb,
|
| 100 |
+
stride_qh,
|
| 101 |
+
stride_qm,
|
| 102 |
+
stride_kb,
|
| 103 |
+
stride_kh,
|
| 104 |
+
stride_kn,
|
| 105 |
+
stride_vb,
|
| 106 |
+
stride_vh,
|
| 107 |
+
stride_vn,
|
| 108 |
+
stride_ob,
|
| 109 |
+
stride_oh,
|
| 110 |
+
stride_om,
|
| 111 |
+
stride_os,
|
| 112 |
+
stride_lb,
|
| 113 |
+
stride_lh,
|
| 114 |
+
stride_lm,
|
| 115 |
+
stride_ls,
|
| 116 |
+
stride_wh,
|
| 117 |
+
cu_seqlens_q,
|
| 118 |
+
cu_seqlens_k,
|
| 119 |
+
seqused_q,
|
| 120 |
+
seqused_k,
|
| 121 |
+
num_splits,
|
| 122 |
+
seqlen_q,
|
| 123 |
+
seqlen_k,
|
| 124 |
+
head_dim,
|
| 125 |
+
SEQLEN_Q_CACHE: tl.constexpr,
|
| 126 |
+
SEQLEN_K_CACHE: tl.constexpr,
|
| 127 |
+
QHEAD_PER_KVHEAD_PACKGQA: tl.constexpr,
|
| 128 |
+
TILE_M: tl.constexpr,
|
| 129 |
+
TILE_N: tl.constexpr,
|
| 130 |
+
TILE_K: tl.constexpr,
|
| 131 |
+
IS_LOCAL: tl.constexpr,
|
| 132 |
+
HAS_CU_SEQLENS_Q: tl.constexpr,
|
| 133 |
+
HAS_CU_SEQLENS_K: tl.constexpr,
|
| 134 |
+
HAS_SEQUSED_Q: tl.constexpr,
|
| 135 |
+
HAS_SEQUSED_K: tl.constexpr,
|
| 136 |
+
):
|
| 137 |
+
# Create grid index
|
| 138 |
+
grid_idx = AttnDecGridIndex.create(
|
| 139 |
+
num_splits=num_splits,
|
| 140 |
+
)
|
| 141 |
+
|
| 142 |
+
# Load window sizes
|
| 143 |
+
(
|
| 144 |
+
window_size_sink,
|
| 145 |
+
window_size_left,
|
| 146 |
+
window_size_right,
|
| 147 |
+
window_size_dist,
|
| 148 |
+
) = grid_idx.load_window_sizes(
|
| 149 |
+
window_sizes=window_sizes,
|
| 150 |
+
stride_wh=stride_wh,
|
| 151 |
+
IS_LOCAL=IS_LOCAL,
|
| 152 |
+
)
|
| 153 |
+
|
| 154 |
+
# Create config
|
| 155 |
+
config = AttnDecConfig.create(
|
| 156 |
+
softmax_scale=softmax_scale,
|
| 157 |
+
query_scale=query_scale,
|
| 158 |
+
key_scale=key_scale,
|
| 159 |
+
value_scale=value_scale,
|
| 160 |
+
batch_idx=grid_idx.batch_idx,
|
| 161 |
+
window_size_sink=window_size_sink,
|
| 162 |
+
window_size_left=window_size_left,
|
| 163 |
+
window_size_right=window_size_right,
|
| 164 |
+
window_size_dist=window_size_dist,
|
| 165 |
+
head_dim=head_dim,
|
| 166 |
+
cu_seqlens_q=cu_seqlens_q,
|
| 167 |
+
cu_seqlens_k=cu_seqlens_k,
|
| 168 |
+
seqused_q=seqused_q,
|
| 169 |
+
seqused_k=seqused_k,
|
| 170 |
+
seqlen_q=seqlen_q,
|
| 171 |
+
seqlen_k=seqlen_k,
|
| 172 |
+
QHEAD_PER_KVHEAD_PACKGQA=QHEAD_PER_KVHEAD_PACKGQA,
|
| 173 |
+
TILE_M=TILE_M,
|
| 174 |
+
TILE_N=TILE_N,
|
| 175 |
+
TILE_K=TILE_K,
|
| 176 |
+
HAS_CU_SEQLENS_Q=HAS_CU_SEQLENS_Q,
|
| 177 |
+
HAS_CU_SEQLENS_K=HAS_CU_SEQLENS_K,
|
| 178 |
+
HAS_SEQUSED_Q=HAS_SEQUSED_Q,
|
| 179 |
+
HAS_SEQUSED_K=HAS_SEQUSED_K,
|
| 180 |
+
)
|
| 181 |
+
|
| 182 |
+
# Create pointer scheduler
|
| 183 |
+
ptrs_sched = AttnDecPointerScheduler.create(
|
| 184 |
+
config=config,
|
| 185 |
+
Q=Q,
|
| 186 |
+
K=K,
|
| 187 |
+
V=V,
|
| 188 |
+
Out=Out,
|
| 189 |
+
Lse=Lse,
|
| 190 |
+
batch_idx=grid_idx.batch_idx,
|
| 191 |
+
head_idx=grid_idx.head_idx,
|
| 192 |
+
head_kv_idx=grid_idx.head_kv_idx,
|
| 193 |
+
split_idx=grid_idx.split_idx,
|
| 194 |
+
stride_qb=stride_qb,
|
| 195 |
+
stride_qh=stride_qh,
|
| 196 |
+
stride_qm=stride_qm,
|
| 197 |
+
stride_kb=stride_kb,
|
| 198 |
+
stride_kh=stride_kh,
|
| 199 |
+
stride_kn=stride_kn,
|
| 200 |
+
stride_vb=stride_vb,
|
| 201 |
+
stride_vh=stride_vh,
|
| 202 |
+
stride_vn=stride_vn,
|
| 203 |
+
stride_ob=stride_ob,
|
| 204 |
+
stride_oh=stride_oh,
|
| 205 |
+
stride_om=stride_om,
|
| 206 |
+
stride_os=stride_os,
|
| 207 |
+
stride_lb=stride_lb,
|
| 208 |
+
stride_lh=stride_lh,
|
| 209 |
+
stride_lm=stride_lm,
|
| 210 |
+
stride_ls=stride_ls,
|
| 211 |
+
HAS_CU_SEQLENS_Q=HAS_CU_SEQLENS_Q,
|
| 212 |
+
HAS_CU_SEQLENS_K=HAS_CU_SEQLENS_K,
|
| 213 |
+
)
|
| 214 |
+
|
| 215 |
+
# Create block scheduler
|
| 216 |
+
block_sched = AttnDecBlockScheduler.create(
|
| 217 |
+
config=config,
|
| 218 |
+
split_idx=grid_idx.split_idx,
|
| 219 |
+
num_splits=num_splits,
|
| 220 |
+
IS_LOCAL=IS_LOCAL,
|
| 221 |
+
)
|
| 222 |
+
|
| 223 |
+
# Create mask scheduler
|
| 224 |
+
mask_sched = AttnMaskScheduler.create(config)
|
| 225 |
+
|
| 226 |
+
# Create softmax scheduler
|
| 227 |
+
softmax_sched = SoftmaxScheduler.create(config)
|
| 228 |
+
|
| 229 |
+
# Create pointers
|
| 230 |
+
out_ptrs = ptrs_sched.make_out_ptrs(config)
|
| 231 |
+
lse_ptrs = ptrs_sched.make_lse_ptrs(config)
|
| 232 |
+
|
| 233 |
+
# Early exit if no n_blocks to process
|
| 234 |
+
if block_sched.is_empty():
|
| 235 |
+
ptrs_sched.store_empty(config, out_ptrs, lse_ptrs, Out)
|
| 236 |
+
return
|
| 237 |
+
|
| 238 |
+
q_ptrs = ptrs_sched.make_q_ptrs(config)
|
| 239 |
+
k_ptrs = ptrs_sched.make_k_ptrs(config)
|
| 240 |
+
v_ptrs = ptrs_sched.make_v_ptrs(config)
|
| 241 |
+
|
| 242 |
+
# Initialize accumulators
|
| 243 |
+
row_max = tl.full((TILE_M,), float("-inf"), dtype=tl.float32)
|
| 244 |
+
row_sum = tl.zeros((TILE_M,), dtype=tl.float32)
|
| 245 |
+
acc_o = tl.zeros((TILE_M, TILE_K), dtype=tl.float32)
|
| 246 |
+
|
| 247 |
+
# Load query tile
|
| 248 |
+
q_tile = ptrs_sched.load_q(config, q_ptrs)
|
| 249 |
+
|
| 250 |
+
# Load key tile
|
| 251 |
+
k_tile = ptrs_sched.load_k(config, k_ptrs, block_sched.n_block_max - 1)
|
| 252 |
+
|
| 253 |
+
# Process n_blocks with seqlen masking
|
| 254 |
+
for n_block in tl.range(
|
| 255 |
+
block_sched.n_block_max - 1, block_sched.n_block_max_no_mask - 1, -1
|
| 256 |
+
):
|
| 257 |
+
k_tile, acc_o, row_max, row_sum = _dec_inner_dense_kernel(
|
| 258 |
+
config=config,
|
| 259 |
+
ptrs_sched=ptrs_sched,
|
| 260 |
+
mask_sched=mask_sched,
|
| 261 |
+
softmax_sched=softmax_sched,
|
| 262 |
+
q_tile=q_tile,
|
| 263 |
+
k_tile=k_tile,
|
| 264 |
+
k_ptrs=k_ptrs,
|
| 265 |
+
v_ptrs=v_ptrs,
|
| 266 |
+
acc_o=acc_o,
|
| 267 |
+
row_max=row_max,
|
| 268 |
+
row_sum=row_sum,
|
| 269 |
+
n_block=n_block,
|
| 270 |
+
n_block_min=block_sched.n_block_max_no_mask,
|
| 271 |
+
IS_MASK=True,
|
| 272 |
+
MASK_LOCAL=True if IS_LOCAL else False,
|
| 273 |
+
MASK_SINK=False,
|
| 274 |
+
CHECK_INF=True,
|
| 275 |
+
)
|
| 276 |
+
|
| 277 |
+
# Process n_blocks without masking
|
| 278 |
+
if not IS_LOCAL and block_sched.n_block_max_no_mask > block_sched.n_block_min:
|
| 279 |
+
# Load key tile
|
| 280 |
+
k_tile = ptrs_sched.load_k(config, k_ptrs, block_sched.n_block_max_no_mask - 1)
|
| 281 |
+
|
| 282 |
+
for n_block in tl.range(
|
| 283 |
+
block_sched.n_block_max_no_mask - 1, block_sched.n_block_min - 1, -1
|
| 284 |
+
):
|
| 285 |
+
k_tile, acc_o, row_max, row_sum = _dec_inner_dense_kernel(
|
| 286 |
+
config=config,
|
| 287 |
+
ptrs_sched=ptrs_sched,
|
| 288 |
+
mask_sched=mask_sched,
|
| 289 |
+
softmax_sched=softmax_sched,
|
| 290 |
+
q_tile=q_tile,
|
| 291 |
+
k_tile=k_tile,
|
| 292 |
+
k_ptrs=k_ptrs,
|
| 293 |
+
v_ptrs=v_ptrs,
|
| 294 |
+
acc_o=acc_o,
|
| 295 |
+
row_max=row_max,
|
| 296 |
+
row_sum=row_sum,
|
| 297 |
+
n_block=n_block,
|
| 298 |
+
n_block_min=block_sched.n_block_min,
|
| 299 |
+
IS_MASK=False,
|
| 300 |
+
MASK_LOCAL=False,
|
| 301 |
+
MASK_SINK=False,
|
| 302 |
+
CHECK_INF=False,
|
| 303 |
+
)
|
| 304 |
+
|
| 305 |
+
if IS_LOCAL:
|
| 306 |
+
# Process n_blocks with local right masking
|
| 307 |
+
if block_sched.n_block_window_max > block_sched.n_block_window_max_no_mask:
|
| 308 |
+
# Load key tile
|
| 309 |
+
k_tile = ptrs_sched.load_k(
|
| 310 |
+
config, k_ptrs, block_sched.n_block_window_max - 1
|
| 311 |
+
)
|
| 312 |
+
|
| 313 |
+
for n_block in tl.range(
|
| 314 |
+
block_sched.n_block_window_max - 1,
|
| 315 |
+
block_sched.n_block_window_max_no_mask - 1,
|
| 316 |
+
-1,
|
| 317 |
+
):
|
| 318 |
+
k_tile, acc_o, row_max, row_sum = _dec_inner_dense_kernel(
|
| 319 |
+
config=config,
|
| 320 |
+
ptrs_sched=ptrs_sched,
|
| 321 |
+
mask_sched=mask_sched,
|
| 322 |
+
softmax_sched=softmax_sched,
|
| 323 |
+
q_tile=q_tile,
|
| 324 |
+
k_tile=k_tile,
|
| 325 |
+
k_ptrs=k_ptrs,
|
| 326 |
+
v_ptrs=v_ptrs,
|
| 327 |
+
acc_o=acc_o,
|
| 328 |
+
row_max=row_max,
|
| 329 |
+
row_sum=row_sum,
|
| 330 |
+
n_block=n_block,
|
| 331 |
+
n_block_min=block_sched.n_block_window_max_no_mask,
|
| 332 |
+
IS_MASK=True,
|
| 333 |
+
MASK_LOCAL=True,
|
| 334 |
+
MASK_SINK=False,
|
| 335 |
+
CHECK_INF=True,
|
| 336 |
+
)
|
| 337 |
+
|
| 338 |
+
# Process n_blocks without masking
|
| 339 |
+
if (
|
| 340 |
+
block_sched.n_block_window_max_no_mask
|
| 341 |
+
> block_sched.n_block_window_min_no_mask
|
| 342 |
+
):
|
| 343 |
+
# Load key tile
|
| 344 |
+
k_tile = ptrs_sched.load_k(
|
| 345 |
+
config, k_ptrs, block_sched.n_block_window_max_no_mask - 1
|
| 346 |
+
)
|
| 347 |
+
|
| 348 |
+
for n_block in tl.range(
|
| 349 |
+
block_sched.n_block_window_max_no_mask - 1,
|
| 350 |
+
block_sched.n_block_window_min_no_mask - 1,
|
| 351 |
+
-1,
|
| 352 |
+
):
|
| 353 |
+
k_tile, acc_o, row_max, row_sum = _dec_inner_dense_kernel(
|
| 354 |
+
config=config,
|
| 355 |
+
ptrs_sched=ptrs_sched,
|
| 356 |
+
mask_sched=mask_sched,
|
| 357 |
+
softmax_sched=softmax_sched,
|
| 358 |
+
q_tile=q_tile,
|
| 359 |
+
k_tile=k_tile,
|
| 360 |
+
k_ptrs=k_ptrs,
|
| 361 |
+
v_ptrs=v_ptrs,
|
| 362 |
+
acc_o=acc_o,
|
| 363 |
+
row_max=row_max,
|
| 364 |
+
row_sum=row_sum,
|
| 365 |
+
n_block=n_block,
|
| 366 |
+
n_block_min=block_sched.n_block_window_min_no_mask,
|
| 367 |
+
IS_MASK=False,
|
| 368 |
+
MASK_LOCAL=False,
|
| 369 |
+
MASK_SINK=False,
|
| 370 |
+
CHECK_INF=False,
|
| 371 |
+
)
|
| 372 |
+
|
| 373 |
+
# Process n_blocks with local left masking
|
| 374 |
+
if block_sched.n_block_window_min_no_mask > block_sched.n_block_window_min:
|
| 375 |
+
# Load key tile
|
| 376 |
+
k_tile = ptrs_sched.load_k(
|
| 377 |
+
config, k_ptrs, block_sched.n_block_window_min_no_mask - 1
|
| 378 |
+
)
|
| 379 |
+
|
| 380 |
+
for n_block in tl.range(
|
| 381 |
+
block_sched.n_block_window_min_no_mask - 1,
|
| 382 |
+
block_sched.n_block_window_min - 1,
|
| 383 |
+
-1,
|
| 384 |
+
):
|
| 385 |
+
k_tile, acc_o, row_max, row_sum = _dec_inner_dense_kernel(
|
| 386 |
+
config=config,
|
| 387 |
+
ptrs_sched=ptrs_sched,
|
| 388 |
+
mask_sched=mask_sched,
|
| 389 |
+
softmax_sched=softmax_sched,
|
| 390 |
+
q_tile=q_tile,
|
| 391 |
+
k_tile=k_tile,
|
| 392 |
+
k_ptrs=k_ptrs,
|
| 393 |
+
v_ptrs=v_ptrs,
|
| 394 |
+
acc_o=acc_o,
|
| 395 |
+
row_max=row_max,
|
| 396 |
+
row_sum=row_sum,
|
| 397 |
+
n_block=n_block,
|
| 398 |
+
n_block_min=block_sched.n_block_window_min,
|
| 399 |
+
IS_MASK=True,
|
| 400 |
+
MASK_LOCAL=True,
|
| 401 |
+
MASK_SINK=False,
|
| 402 |
+
CHECK_INF=True,
|
| 403 |
+
)
|
| 404 |
+
|
| 405 |
+
# Process n_blocks with local sink masking
|
| 406 |
+
if block_sched.n_block_sink_max > block_sched.n_block_sink_min:
|
| 407 |
+
# Load key tile
|
| 408 |
+
k_tile = ptrs_sched.load_k(config, k_ptrs, block_sched.n_block_sink_max - 1)
|
| 409 |
+
|
| 410 |
+
for n_block in tl.range(
|
| 411 |
+
block_sched.n_block_sink_max - 1,
|
| 412 |
+
block_sched.n_block_sink_min - 1,
|
| 413 |
+
-1,
|
| 414 |
+
):
|
| 415 |
+
k_tile, acc_o, row_max, row_sum = _dec_inner_dense_kernel(
|
| 416 |
+
config=config,
|
| 417 |
+
ptrs_sched=ptrs_sched,
|
| 418 |
+
mask_sched=mask_sched,
|
| 419 |
+
softmax_sched=softmax_sched,
|
| 420 |
+
q_tile=q_tile,
|
| 421 |
+
k_tile=k_tile,
|
| 422 |
+
k_ptrs=k_ptrs,
|
| 423 |
+
v_ptrs=v_ptrs,
|
| 424 |
+
acc_o=acc_o,
|
| 425 |
+
row_max=row_max,
|
| 426 |
+
row_sum=row_sum,
|
| 427 |
+
n_block=n_block,
|
| 428 |
+
n_block_min=block_sched.n_block_sink_min,
|
| 429 |
+
IS_MASK=True,
|
| 430 |
+
MASK_LOCAL=True,
|
| 431 |
+
MASK_SINK=True,
|
| 432 |
+
CHECK_INF=True,
|
| 433 |
+
)
|
| 434 |
+
|
| 435 |
+
# Finalize softmax
|
| 436 |
+
row_scale, lse_tile = softmax_sched.finalize(
|
| 437 |
+
row_max=row_max,
|
| 438 |
+
row_sum=row_sum,
|
| 439 |
+
IS_LOG2=True,
|
| 440 |
+
)
|
| 441 |
+
|
| 442 |
+
# Store LSE
|
| 443 |
+
ptrs_sched.store_lse(config, lse_ptrs, lse_tile)
|
| 444 |
+
|
| 445 |
+
# Finalize rescale
|
| 446 |
+
acc_o = softmax_sched.rescale_o(
|
| 447 |
+
acc_o=acc_o,
|
| 448 |
+
row_scale=row_scale,
|
| 449 |
+
)
|
| 450 |
+
|
| 451 |
+
# Store output
|
| 452 |
+
ptrs_sched.store_out(config, out_ptrs, acc_o)
|
| 453 |
+
|
| 454 |
+
|
| 455 |
+
_dec_dense_kernel = cache_utils.wrap_kernel(_dec_dense_kernel)
|
| 456 |
+
|
| 457 |
+
|
| 458 |
+
_dec_dense_autotuned_kernel = None
|
| 459 |
+
|
| 460 |
+
|
| 461 |
+
def _get_autotuned_kernel():
|
| 462 |
+
global _dec_dense_autotuned_kernel
|
| 463 |
+
if _dec_dense_autotuned_kernel is None:
|
| 464 |
+
jit_kernel = _dec_dense_kernel._kernel
|
| 465 |
+
autotuned = autotuner.make_dec_dense_autotuned_kernel(jit_kernel)
|
| 466 |
+
_dec_dense_autotuned_kernel = autotuner.AutotunedKernel(autotuned)
|
| 467 |
+
return _dec_dense_autotuned_kernel
|
| 468 |
+
|
| 469 |
+
|
| 470 |
+
def _flash_dense_attn_decode(
|
| 471 |
+
query: torch.Tensor,
|
| 472 |
+
key: torch.Tensor,
|
| 473 |
+
value: torch.Tensor,
|
| 474 |
+
softmax_scale: float = None,
|
| 475 |
+
query_scale: Optional[torch.Tensor] = None,
|
| 476 |
+
key_scale: Optional[torch.Tensor] = None,
|
| 477 |
+
value_scale: Optional[torch.Tensor] = None,
|
| 478 |
+
window_sizes: Optional[torch.Tensor] = None,
|
| 479 |
+
is_local: bool = False,
|
| 480 |
+
is_quant: bool = False,
|
| 481 |
+
out: Optional[torch.Tensor] = None,
|
| 482 |
+
lse: Optional[torch.Tensor] = None,
|
| 483 |
+
is_autotune: bool = False,
|
| 484 |
+
skip_checks: bool = False,
|
| 485 |
+
) -> Tuple[torch.Tensor, torch.Tensor]:
|
| 486 |
+
device = query.device
|
| 487 |
+
num_SMs = cache_utils.get_device_num_sms(device)
|
| 488 |
+
batch_size, num_heads_q, head_dim = query.shape
|
| 489 |
+
_, seqlen_k, num_heads_kv, _ = key.shape
|
| 490 |
+
softmax_scale = (
|
| 491 |
+
softmax_scale if softmax_scale is not None else 1.0 / (head_dim**0.5)
|
| 492 |
+
)
|
| 493 |
+
qhead_per_kvhead = num_heads_q // num_heads_kv
|
| 494 |
+
if is_local and window_sizes is None:
|
| 495 |
+
window_sizes = utils.window_sizes_heuristic(seqlen_k, num_heads_kv, device)
|
| 496 |
+
elif not is_local:
|
| 497 |
+
window_sizes = torch.zeros((num_heads_kv, 4), dtype=torch.int32, device=device)
|
| 498 |
+
|
| 499 |
+
if not skip_checks:
|
| 500 |
+
assert_inputs.assert_dec_inputs(
|
| 501 |
+
query,
|
| 502 |
+
key,
|
| 503 |
+
value,
|
| 504 |
+
query_scale=query_scale,
|
| 505 |
+
key_scale=key_scale,
|
| 506 |
+
value_scale=value_scale,
|
| 507 |
+
window_sizes=window_sizes,
|
| 508 |
+
cu_seqlens_k=None,
|
| 509 |
+
seqused_k=None,
|
| 510 |
+
num_heads_q=num_heads_q,
|
| 511 |
+
num_heads_kv=num_heads_kv,
|
| 512 |
+
head_dim=head_dim,
|
| 513 |
+
is_quant=is_quant,
|
| 514 |
+
device=device,
|
| 515 |
+
)
|
| 516 |
+
|
| 517 |
+
TILE_K = max(triton.next_power_of_2(head_dim), 16)
|
| 518 |
+
|
| 519 |
+
launch_config = launch_template.load_launch_config(
|
| 520 |
+
device=device,
|
| 521 |
+
kernel_name="dec_dense",
|
| 522 |
+
seqlen_q=1,
|
| 523 |
+
seqlen_k=seqlen_k,
|
| 524 |
+
tile_k=TILE_K,
|
| 525 |
+
is_local=is_local,
|
| 526 |
+
qhead_per_kvhead=qhead_per_kvhead,
|
| 527 |
+
is_quant=is_quant,
|
| 528 |
+
)
|
| 529 |
+
if launch_config is not None and not is_autotune:
|
| 530 |
+
kernel = _dec_dense_kernel
|
| 531 |
+
TILE_M, TILE_N, num_warps, num_stages, num_ctas = launch_config
|
| 532 |
+
else:
|
| 533 |
+
kernel = _get_autotuned_kernel()
|
| 534 |
+
TILE_M = max(triton.next_power_of_2(qhead_per_kvhead), 16)
|
| 535 |
+
TILE_N = 128
|
| 536 |
+
num_warps = num_stages = num_ctas = None
|
| 537 |
+
|
| 538 |
+
num_splits = utils.num_splits_heuristic(
|
| 539 |
+
seqlen_q=qhead_per_kvhead,
|
| 540 |
+
seqlen_k=seqlen_k,
|
| 541 |
+
num_SMs=num_SMs,
|
| 542 |
+
TILE_M=TILE_M,
|
| 543 |
+
TILE_N=TILE_N,
|
| 544 |
+
max_split_blocks=utils.max_split_blocks_from_window_sizes(window_sizes, TILE_N)
|
| 545 |
+
if is_local
|
| 546 |
+
else None,
|
| 547 |
+
)
|
| 548 |
+
|
| 549 |
+
out_dtype = torch.bfloat16 if is_quant else query.dtype
|
| 550 |
+
out = (
|
| 551 |
+
out
|
| 552 |
+
if out is not None
|
| 553 |
+
else torch.empty(query.shape, dtype=out_dtype, device=device)
|
| 554 |
+
)
|
| 555 |
+
lse = (
|
| 556 |
+
lse
|
| 557 |
+
if lse is not None
|
| 558 |
+
else torch.empty((batch_size, num_heads_q), dtype=torch.float32, device=device)
|
| 559 |
+
)
|
| 560 |
+
|
| 561 |
+
out_partial = torch.empty(
|
| 562 |
+
(num_splits, batch_size, num_heads_q, head_dim),
|
| 563 |
+
dtype=torch.float32,
|
| 564 |
+
device=device,
|
| 565 |
+
)
|
| 566 |
+
lse_partial = torch.empty(
|
| 567 |
+
(num_splits, batch_size, num_heads_q),
|
| 568 |
+
dtype=torch.float32,
|
| 569 |
+
device=device,
|
| 570 |
+
)
|
| 571 |
+
|
| 572 |
+
if not is_quant:
|
| 573 |
+
query_scale = torch.ones(1, device=device, dtype=query.dtype)
|
| 574 |
+
key_scale = torch.ones(1, device=device, dtype=query.dtype)
|
| 575 |
+
value_scale = torch.ones(1, device=device, dtype=query.dtype)
|
| 576 |
+
|
| 577 |
+
grid = launch_grid.get_dec_grid(
|
| 578 |
+
batch_size=batch_size,
|
| 579 |
+
num_heads_kv=num_heads_kv,
|
| 580 |
+
num_splits=num_splits,
|
| 581 |
+
)
|
| 582 |
+
|
| 583 |
+
triton.set_allocator(utils.alloc_fn)
|
| 584 |
+
|
| 585 |
+
kernel[grid](
|
| 586 |
+
query,
|
| 587 |
+
key,
|
| 588 |
+
value,
|
| 589 |
+
out_partial,
|
| 590 |
+
lse_partial,
|
| 591 |
+
softmax_scale,
|
| 592 |
+
query_scale,
|
| 593 |
+
key_scale,
|
| 594 |
+
value_scale,
|
| 595 |
+
window_sizes,
|
| 596 |
+
query.stride(0),
|
| 597 |
+
query.stride(-2),
|
| 598 |
+
1,
|
| 599 |
+
key.stride(0),
|
| 600 |
+
key.stride(-2),
|
| 601 |
+
key.stride(-3),
|
| 602 |
+
value.stride(0),
|
| 603 |
+
value.stride(-2),
|
| 604 |
+
value.stride(-3),
|
| 605 |
+
out_partial.stride(1),
|
| 606 |
+
out_partial.stride(-2),
|
| 607 |
+
1,
|
| 608 |
+
out_partial.stride(0),
|
| 609 |
+
lse_partial.stride(1),
|
| 610 |
+
lse_partial.stride(-1),
|
| 611 |
+
1,
|
| 612 |
+
lse_partial.stride(0),
|
| 613 |
+
window_sizes.stride(0),
|
| 614 |
+
None,
|
| 615 |
+
None,
|
| 616 |
+
None,
|
| 617 |
+
None,
|
| 618 |
+
num_splits,
|
| 619 |
+
seqlen_q=1,
|
| 620 |
+
seqlen_k=seqlen_k,
|
| 621 |
+
head_dim=head_dim,
|
| 622 |
+
SEQLEN_Q_CACHE=0,
|
| 623 |
+
SEQLEN_K_CACHE=seqlen_k // 1024,
|
| 624 |
+
QHEAD_PER_KVHEAD_PACKGQA=qhead_per_kvhead,
|
| 625 |
+
TILE_M=TILE_M,
|
| 626 |
+
TILE_N=TILE_N,
|
| 627 |
+
TILE_K=TILE_K,
|
| 628 |
+
IS_LOCAL=is_local,
|
| 629 |
+
HAS_CU_SEQLENS_Q=False,
|
| 630 |
+
HAS_CU_SEQLENS_K=False,
|
| 631 |
+
HAS_SEQUSED_Q=False,
|
| 632 |
+
HAS_SEQUSED_K=False,
|
| 633 |
+
num_warps=num_warps,
|
| 634 |
+
num_stages=num_stages,
|
| 635 |
+
num_ctas=num_ctas,
|
| 636 |
+
)
|
| 637 |
+
|
| 638 |
+
if launch_config is None or is_autotune:
|
| 639 |
+
best = launch_template.extract_best_config(_get_autotuned_kernel())
|
| 640 |
+
if best is not None:
|
| 641 |
+
launch_template.store_launch_config(
|
| 642 |
+
device=device,
|
| 643 |
+
kernel_name="dec_dense",
|
| 644 |
+
seqlen_q=1,
|
| 645 |
+
seqlen_k=seqlen_k,
|
| 646 |
+
tile_k=TILE_K,
|
| 647 |
+
config=best,
|
| 648 |
+
is_local=is_local,
|
| 649 |
+
qhead_per_kvhead=qhead_per_kvhead,
|
| 650 |
+
is_quant=is_quant,
|
| 651 |
+
)
|
| 652 |
+
|
| 653 |
+
flash_dec_combine._flash_attn_dec_combine(
|
| 654 |
+
out_partial,
|
| 655 |
+
lse_partial,
|
| 656 |
+
out,
|
| 657 |
+
lse,
|
| 658 |
+
)
|
| 659 |
+
|
| 660 |
+
return out, lse
|
| 661 |
+
|
| 662 |
+
|
| 663 |
+
def _flash_dense_attn_varlen_decode(
|
| 664 |
+
query: torch.Tensor,
|
| 665 |
+
key: torch.Tensor,
|
| 666 |
+
value: torch.Tensor,
|
| 667 |
+
cu_seqlens_k: torch.Tensor,
|
| 668 |
+
max_seqlen_k: int,
|
| 669 |
+
softmax_scale: float = None,
|
| 670 |
+
query_scale: Optional[torch.Tensor] = None,
|
| 671 |
+
key_scale: Optional[torch.Tensor] = None,
|
| 672 |
+
value_scale: Optional[torch.Tensor] = None,
|
| 673 |
+
window_sizes: Optional[torch.Tensor] = None,
|
| 674 |
+
is_local: bool = False,
|
| 675 |
+
is_quant: bool = False,
|
| 676 |
+
seqused_k: Optional[torch.Tensor] = None,
|
| 677 |
+
out: Optional[torch.Tensor] = None,
|
| 678 |
+
lse: Optional[torch.Tensor] = None,
|
| 679 |
+
is_autotune: bool = False,
|
| 680 |
+
skip_checks: bool = False,
|
| 681 |
+
) -> Tuple[torch.Tensor, torch.Tensor]:
|
| 682 |
+
device = query.device
|
| 683 |
+
num_SMs = cache_utils.get_device_num_sms(device)
|
| 684 |
+
batch_size, num_heads_q, head_dim = query.shape
|
| 685 |
+
_, num_heads_kv, _ = key.shape
|
| 686 |
+
seqlen_k = max_seqlen_k
|
| 687 |
+
softmax_scale = (
|
| 688 |
+
softmax_scale if softmax_scale is not None else 1.0 / (head_dim**0.5)
|
| 689 |
+
)
|
| 690 |
+
qhead_per_kvhead = num_heads_q // num_heads_kv
|
| 691 |
+
if is_local and window_sizes is None:
|
| 692 |
+
window_sizes = utils.window_sizes_heuristic(seqlen_k, num_heads_kv, device)
|
| 693 |
+
elif not is_local:
|
| 694 |
+
window_sizes = torch.zeros((num_heads_kv, 4), dtype=torch.int32, device=device)
|
| 695 |
+
|
| 696 |
+
if not skip_checks:
|
| 697 |
+
assert_inputs.assert_dec_inputs(
|
| 698 |
+
query,
|
| 699 |
+
key,
|
| 700 |
+
value,
|
| 701 |
+
query_scale=query_scale,
|
| 702 |
+
key_scale=key_scale,
|
| 703 |
+
value_scale=value_scale,
|
| 704 |
+
window_sizes=window_sizes,
|
| 705 |
+
cu_seqlens_k=cu_seqlens_k,
|
| 706 |
+
seqused_k=seqused_k,
|
| 707 |
+
num_heads_q=num_heads_q,
|
| 708 |
+
num_heads_kv=num_heads_kv,
|
| 709 |
+
head_dim=head_dim,
|
| 710 |
+
is_quant=is_quant,
|
| 711 |
+
device=device,
|
| 712 |
+
)
|
| 713 |
+
|
| 714 |
+
TILE_K = max(triton.next_power_of_2(head_dim), 16)
|
| 715 |
+
|
| 716 |
+
launch_config = launch_template.load_launch_config(
|
| 717 |
+
device=device,
|
| 718 |
+
kernel_name="dec_dense",
|
| 719 |
+
seqlen_q=1,
|
| 720 |
+
seqlen_k=seqlen_k,
|
| 721 |
+
tile_k=TILE_K,
|
| 722 |
+
is_local=is_local,
|
| 723 |
+
qhead_per_kvhead=qhead_per_kvhead,
|
| 724 |
+
is_quant=is_quant,
|
| 725 |
+
)
|
| 726 |
+
if launch_config is not None and not is_autotune:
|
| 727 |
+
kernel = _dec_dense_kernel
|
| 728 |
+
TILE_M, TILE_N, num_warps, num_stages, num_ctas = launch_config
|
| 729 |
+
else:
|
| 730 |
+
kernel = _get_autotuned_kernel()
|
| 731 |
+
TILE_M = max(triton.next_power_of_2(qhead_per_kvhead), 16)
|
| 732 |
+
TILE_N = 128
|
| 733 |
+
num_warps = num_stages = num_ctas = None
|
| 734 |
+
|
| 735 |
+
num_splits = utils.num_splits_heuristic(
|
| 736 |
+
seqlen_q=qhead_per_kvhead,
|
| 737 |
+
seqlen_k=seqlen_k,
|
| 738 |
+
num_SMs=num_SMs,
|
| 739 |
+
TILE_M=TILE_M,
|
| 740 |
+
TILE_N=TILE_N,
|
| 741 |
+
max_split_blocks=utils.max_split_blocks_from_window_sizes(window_sizes, TILE_N)
|
| 742 |
+
if is_local
|
| 743 |
+
else None,
|
| 744 |
+
)
|
| 745 |
+
|
| 746 |
+
out_dtype = torch.bfloat16 if is_quant else query.dtype
|
| 747 |
+
out = (
|
| 748 |
+
out
|
| 749 |
+
if out is not None
|
| 750 |
+
else torch.empty(query.shape, dtype=out_dtype, device=device)
|
| 751 |
+
)
|
| 752 |
+
lse = (
|
| 753 |
+
lse
|
| 754 |
+
if lse is not None
|
| 755 |
+
else torch.empty((batch_size, num_heads_q), dtype=torch.float32, device=device)
|
| 756 |
+
)
|
| 757 |
+
|
| 758 |
+
out_partial = torch.empty(
|
| 759 |
+
(num_splits, batch_size, num_heads_q, head_dim),
|
| 760 |
+
dtype=torch.float32,
|
| 761 |
+
device=device,
|
| 762 |
+
)
|
| 763 |
+
lse_partial = torch.empty(
|
| 764 |
+
(num_splits, batch_size, num_heads_q),
|
| 765 |
+
dtype=torch.float32,
|
| 766 |
+
device=device,
|
| 767 |
+
)
|
| 768 |
+
|
| 769 |
+
if not is_quant:
|
| 770 |
+
query_scale = torch.ones(1, device=device, dtype=query.dtype)
|
| 771 |
+
key_scale = torch.ones(1, device=device, dtype=query.dtype)
|
| 772 |
+
value_scale = torch.ones(1, device=device, dtype=query.dtype)
|
| 773 |
+
|
| 774 |
+
grid = launch_grid.get_dec_grid(
|
| 775 |
+
batch_size=batch_size,
|
| 776 |
+
num_heads_kv=num_heads_kv,
|
| 777 |
+
num_splits=num_splits,
|
| 778 |
+
)
|
| 779 |
+
|
| 780 |
+
triton.set_allocator(utils.alloc_fn)
|
| 781 |
+
|
| 782 |
+
kernel[grid](
|
| 783 |
+
query,
|
| 784 |
+
key,
|
| 785 |
+
value,
|
| 786 |
+
out_partial,
|
| 787 |
+
lse_partial,
|
| 788 |
+
softmax_scale,
|
| 789 |
+
query_scale,
|
| 790 |
+
key_scale,
|
| 791 |
+
value_scale,
|
| 792 |
+
window_sizes,
|
| 793 |
+
query.stride(0),
|
| 794 |
+
query.stride(-2),
|
| 795 |
+
1,
|
| 796 |
+
0,
|
| 797 |
+
key.stride(-2),
|
| 798 |
+
key.stride(0),
|
| 799 |
+
0,
|
| 800 |
+
value.stride(-2),
|
| 801 |
+
value.stride(0),
|
| 802 |
+
out_partial.stride(1),
|
| 803 |
+
out_partial.stride(-2),
|
| 804 |
+
1,
|
| 805 |
+
out_partial.stride(0),
|
| 806 |
+
lse_partial.stride(1),
|
| 807 |
+
lse_partial.stride(-1),
|
| 808 |
+
1,
|
| 809 |
+
lse_partial.stride(0),
|
| 810 |
+
window_sizes.stride(0),
|
| 811 |
+
None,
|
| 812 |
+
cu_seqlens_k,
|
| 813 |
+
None,
|
| 814 |
+
seqused_k,
|
| 815 |
+
num_splits,
|
| 816 |
+
seqlen_q=1,
|
| 817 |
+
seqlen_k=seqlen_k,
|
| 818 |
+
head_dim=head_dim,
|
| 819 |
+
SEQLEN_Q_CACHE=0,
|
| 820 |
+
SEQLEN_K_CACHE=seqlen_k // 1024,
|
| 821 |
+
QHEAD_PER_KVHEAD_PACKGQA=qhead_per_kvhead,
|
| 822 |
+
TILE_M=TILE_M,
|
| 823 |
+
TILE_N=TILE_N,
|
| 824 |
+
TILE_K=TILE_K,
|
| 825 |
+
IS_LOCAL=is_local,
|
| 826 |
+
HAS_CU_SEQLENS_Q=False,
|
| 827 |
+
HAS_CU_SEQLENS_K=True,
|
| 828 |
+
HAS_SEQUSED_Q=False,
|
| 829 |
+
HAS_SEQUSED_K=seqused_k is not None,
|
| 830 |
+
num_warps=num_warps,
|
| 831 |
+
num_stages=num_stages,
|
| 832 |
+
num_ctas=num_ctas,
|
| 833 |
+
)
|
| 834 |
+
|
| 835 |
+
if launch_config is None or is_autotune:
|
| 836 |
+
best = launch_template.extract_best_config(_get_autotuned_kernel())
|
| 837 |
+
if best is not None:
|
| 838 |
+
launch_template.store_launch_config(
|
| 839 |
+
device=device,
|
| 840 |
+
kernel_name="dec_dense",
|
| 841 |
+
seqlen_q=1,
|
| 842 |
+
seqlen_k=seqlen_k,
|
| 843 |
+
tile_k=TILE_K,
|
| 844 |
+
config=best,
|
| 845 |
+
is_local=is_local,
|
| 846 |
+
qhead_per_kvhead=qhead_per_kvhead,
|
| 847 |
+
is_quant=is_quant,
|
| 848 |
+
)
|
| 849 |
+
|
| 850 |
+
flash_dec_combine._flash_attn_dec_combine(
|
| 851 |
+
out_partial,
|
| 852 |
+
lse_partial,
|
| 853 |
+
out,
|
| 854 |
+
lse,
|
| 855 |
+
)
|
| 856 |
+
|
| 857 |
+
return out, lse
|
build/torch-cuda/flash_dense_fwd.py
ADDED
|
@@ -0,0 +1,957 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from typing import Tuple, Optional
|
| 2 |
+
|
| 3 |
+
import torch
|
| 4 |
+
import triton
|
| 5 |
+
import triton.language as tl
|
| 6 |
+
|
| 7 |
+
from . import (
|
| 8 |
+
assert_inputs,
|
| 9 |
+
utils,
|
| 10 |
+
cache_utils,
|
| 11 |
+
launch_template,
|
| 12 |
+
launch_grid,
|
| 13 |
+
flash_fwd_combine,
|
| 14 |
+
kernel_repr,
|
| 15 |
+
autotuner,
|
| 16 |
+
)
|
| 17 |
+
from .scheduler import (
|
| 18 |
+
AttnFwdGridIndex,
|
| 19 |
+
AttnFwdConfig,
|
| 20 |
+
AttnFwdBlockScheduler,
|
| 21 |
+
AttnFwdPointerScheduler,
|
| 22 |
+
AttnMaskScheduler,
|
| 23 |
+
SoftmaxScheduler,
|
| 24 |
+
)
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
@triton.jit
|
| 28 |
+
def _fwd_inner_dense_kernel(
|
| 29 |
+
config: AttnFwdConfig,
|
| 30 |
+
ptrs_sched: AttnFwdPointerScheduler,
|
| 31 |
+
mask_sched: AttnMaskScheduler,
|
| 32 |
+
softmax_sched: SoftmaxScheduler,
|
| 33 |
+
q_tile,
|
| 34 |
+
k_tile,
|
| 35 |
+
k_ptrs,
|
| 36 |
+
v_ptrs,
|
| 37 |
+
acc_o,
|
| 38 |
+
row_max,
|
| 39 |
+
row_sum,
|
| 40 |
+
n_block,
|
| 41 |
+
n_block_min,
|
| 42 |
+
IS_MASK: tl.constexpr,
|
| 43 |
+
MASK_CAUSAL: tl.constexpr,
|
| 44 |
+
MASK_LOCAL: tl.constexpr,
|
| 45 |
+
MASK_SINK: tl.constexpr,
|
| 46 |
+
CHECK_INF: tl.constexpr,
|
| 47 |
+
):
|
| 48 |
+
# Compute attention scores
|
| 49 |
+
acc_s = tl.dot(q_tile, k_tile.T)
|
| 50 |
+
|
| 51 |
+
# Prefetch next key tile
|
| 52 |
+
if n_block > n_block_min:
|
| 53 |
+
# Load next key tile
|
| 54 |
+
k_tile = ptrs_sched.load_k(config, k_ptrs, n_block - 1)
|
| 55 |
+
|
| 56 |
+
if IS_MASK:
|
| 57 |
+
# Apply mask to attention scores
|
| 58 |
+
acc_s = mask_sched.apply_mask(
|
| 59 |
+
acc_s=acc_s,
|
| 60 |
+
iter_block=n_block,
|
| 61 |
+
MASK_CAUSAL=MASK_CAUSAL,
|
| 62 |
+
MASK_LOCAL=MASK_LOCAL,
|
| 63 |
+
MASK_SINK=MASK_SINK,
|
| 64 |
+
)
|
| 65 |
+
|
| 66 |
+
# Apply online softmax
|
| 67 |
+
p, row_max, row_sum, row_scale = softmax_sched.online_softmax(
|
| 68 |
+
acc_s=acc_s,
|
| 69 |
+
row_max=row_max,
|
| 70 |
+
row_sum=row_sum,
|
| 71 |
+
CHECK_INF=CHECK_INF,
|
| 72 |
+
)
|
| 73 |
+
|
| 74 |
+
# Load value tile
|
| 75 |
+
v_tile = ptrs_sched.load_v(config, v_ptrs, n_block)
|
| 76 |
+
|
| 77 |
+
# Rescale output accumulator
|
| 78 |
+
acc_o = softmax_sched.rescale_o(
|
| 79 |
+
acc_o=acc_o,
|
| 80 |
+
row_scale=row_scale,
|
| 81 |
+
)
|
| 82 |
+
|
| 83 |
+
# Update output accumulator
|
| 84 |
+
acc_o += tl.dot(p.to(v_tile.dtype), v_tile)
|
| 85 |
+
|
| 86 |
+
return k_tile, acc_o, row_max, row_sum
|
| 87 |
+
|
| 88 |
+
|
| 89 |
+
@triton.jit(repr=kernel_repr.fwd_dense_repr)
|
| 90 |
+
def _fwd_dense_kernel(
|
| 91 |
+
Q,
|
| 92 |
+
K,
|
| 93 |
+
V,
|
| 94 |
+
Out,
|
| 95 |
+
Lse,
|
| 96 |
+
softmax_scale,
|
| 97 |
+
query_scale,
|
| 98 |
+
key_scale,
|
| 99 |
+
value_scale,
|
| 100 |
+
window_sizes,
|
| 101 |
+
stride_qb,
|
| 102 |
+
stride_qh,
|
| 103 |
+
stride_qm,
|
| 104 |
+
stride_kb,
|
| 105 |
+
stride_kh,
|
| 106 |
+
stride_kn,
|
| 107 |
+
stride_vb,
|
| 108 |
+
stride_vh,
|
| 109 |
+
stride_vn,
|
| 110 |
+
stride_ob,
|
| 111 |
+
stride_oh,
|
| 112 |
+
stride_om,
|
| 113 |
+
stride_os,
|
| 114 |
+
stride_lb,
|
| 115 |
+
stride_lh,
|
| 116 |
+
stride_ls,
|
| 117 |
+
stride_wh,
|
| 118 |
+
cu_seqlens_q,
|
| 119 |
+
cu_seqlens_k,
|
| 120 |
+
seqused_q,
|
| 121 |
+
seqused_k,
|
| 122 |
+
num_splits,
|
| 123 |
+
seqlen_q,
|
| 124 |
+
seqlen_k,
|
| 125 |
+
head_dim,
|
| 126 |
+
SEQLEN_Q_CACHE: tl.constexpr,
|
| 127 |
+
SEQLEN_K_CACHE: tl.constexpr,
|
| 128 |
+
QHEAD_PER_KVHEAD: tl.constexpr,
|
| 129 |
+
PACK_GQA: tl.constexpr,
|
| 130 |
+
QHEAD_PER_KVHEAD_PACKGQA: tl.constexpr,
|
| 131 |
+
TILE_M: tl.constexpr,
|
| 132 |
+
TILE_N: tl.constexpr,
|
| 133 |
+
TILE_K: tl.constexpr,
|
| 134 |
+
IS_CAUSAL: tl.constexpr,
|
| 135 |
+
IS_LOCAL: tl.constexpr,
|
| 136 |
+
IS_SPLIT_KV: tl.constexpr,
|
| 137 |
+
HAS_CU_SEQLENS_Q: tl.constexpr,
|
| 138 |
+
HAS_CU_SEQLENS_K: tl.constexpr,
|
| 139 |
+
HAS_SEQUSED_Q: tl.constexpr,
|
| 140 |
+
HAS_SEQUSED_K: tl.constexpr,
|
| 141 |
+
):
|
| 142 |
+
# Create grid index
|
| 143 |
+
grid_idx = AttnFwdGridIndex.create(
|
| 144 |
+
num_splits=num_splits,
|
| 145 |
+
QHEAD_PER_KVHEAD=QHEAD_PER_KVHEAD,
|
| 146 |
+
IS_SPLIT_KV=IS_SPLIT_KV,
|
| 147 |
+
PACK_GQA=PACK_GQA,
|
| 148 |
+
)
|
| 149 |
+
|
| 150 |
+
# Load window sizes
|
| 151 |
+
(
|
| 152 |
+
window_size_sink,
|
| 153 |
+
window_size_left,
|
| 154 |
+
window_size_right,
|
| 155 |
+
window_size_dist,
|
| 156 |
+
) = grid_idx.load_window_sizes(
|
| 157 |
+
window_sizes=window_sizes,
|
| 158 |
+
stride_wh=stride_wh,
|
| 159 |
+
IS_LOCAL=IS_LOCAL,
|
| 160 |
+
)
|
| 161 |
+
|
| 162 |
+
# Create config
|
| 163 |
+
config = AttnFwdConfig.create(
|
| 164 |
+
softmax_scale=softmax_scale,
|
| 165 |
+
query_scale=query_scale,
|
| 166 |
+
key_scale=key_scale,
|
| 167 |
+
value_scale=value_scale,
|
| 168 |
+
m_block=grid_idx.m_block,
|
| 169 |
+
batch_idx=grid_idx.batch_idx,
|
| 170 |
+
window_size_sink=window_size_sink,
|
| 171 |
+
window_size_left=window_size_left,
|
| 172 |
+
window_size_right=window_size_right,
|
| 173 |
+
window_size_dist=window_size_dist,
|
| 174 |
+
head_dim=head_dim,
|
| 175 |
+
cu_seqlens_q=cu_seqlens_q,
|
| 176 |
+
cu_seqlens_k=cu_seqlens_k,
|
| 177 |
+
seqused_q=seqused_q,
|
| 178 |
+
seqused_k=seqused_k,
|
| 179 |
+
seqlen_q=seqlen_q,
|
| 180 |
+
seqlen_k=seqlen_k,
|
| 181 |
+
PACK_GQA=PACK_GQA,
|
| 182 |
+
QHEAD_PER_KVHEAD_PACKGQA=QHEAD_PER_KVHEAD_PACKGQA,
|
| 183 |
+
TILE_M=TILE_M,
|
| 184 |
+
TILE_N=TILE_N,
|
| 185 |
+
TILE_K=TILE_K,
|
| 186 |
+
HAS_CU_SEQLENS_Q=HAS_CU_SEQLENS_Q,
|
| 187 |
+
HAS_CU_SEQLENS_K=HAS_CU_SEQLENS_K,
|
| 188 |
+
HAS_SEQUSED_Q=HAS_SEQUSED_Q,
|
| 189 |
+
HAS_SEQUSED_K=HAS_SEQUSED_K,
|
| 190 |
+
)
|
| 191 |
+
|
| 192 |
+
# Create pointer scheduler
|
| 193 |
+
ptrs_sched = AttnFwdPointerScheduler.create(
|
| 194 |
+
config=config,
|
| 195 |
+
Q=Q,
|
| 196 |
+
K=K,
|
| 197 |
+
V=V,
|
| 198 |
+
Out=Out,
|
| 199 |
+
Lse=Lse,
|
| 200 |
+
batch_idx=grid_idx.batch_idx,
|
| 201 |
+
head_idx=grid_idx.head_idx,
|
| 202 |
+
head_kv_idx=grid_idx.head_kv_idx,
|
| 203 |
+
split_idx=grid_idx.split_idx,
|
| 204 |
+
stride_qb=stride_qb,
|
| 205 |
+
stride_qh=stride_qh,
|
| 206 |
+
stride_qm=stride_qm,
|
| 207 |
+
stride_kb=stride_kb,
|
| 208 |
+
stride_kh=stride_kh,
|
| 209 |
+
stride_kn=stride_kn,
|
| 210 |
+
stride_vb=stride_vb,
|
| 211 |
+
stride_vh=stride_vh,
|
| 212 |
+
stride_vn=stride_vn,
|
| 213 |
+
stride_ob=stride_ob,
|
| 214 |
+
stride_oh=stride_oh,
|
| 215 |
+
stride_om=stride_om,
|
| 216 |
+
stride_os=stride_os,
|
| 217 |
+
stride_lb=stride_lb,
|
| 218 |
+
stride_lh=stride_lh,
|
| 219 |
+
stride_ls=stride_ls,
|
| 220 |
+
IS_SPLIT_KV=IS_SPLIT_KV,
|
| 221 |
+
HAS_CU_SEQLENS_Q=HAS_CU_SEQLENS_Q,
|
| 222 |
+
HAS_CU_SEQLENS_K=HAS_CU_SEQLENS_K,
|
| 223 |
+
)
|
| 224 |
+
|
| 225 |
+
# Create block scheduler
|
| 226 |
+
block_sched = AttnFwdBlockScheduler.create(
|
| 227 |
+
config=config,
|
| 228 |
+
split_idx=grid_idx.split_idx,
|
| 229 |
+
num_splits=num_splits,
|
| 230 |
+
IS_CAUSAL=IS_CAUSAL,
|
| 231 |
+
IS_LOCAL=IS_LOCAL,
|
| 232 |
+
IS_SPLIT_KV=IS_SPLIT_KV,
|
| 233 |
+
)
|
| 234 |
+
|
| 235 |
+
# Create mask scheduler
|
| 236 |
+
mask_sched = AttnMaskScheduler.create(config)
|
| 237 |
+
|
| 238 |
+
# Create softmax scheduler
|
| 239 |
+
softmax_sched = SoftmaxScheduler.create(config)
|
| 240 |
+
|
| 241 |
+
# Create pointers
|
| 242 |
+
out_ptrs = ptrs_sched.make_out_ptrs(config)
|
| 243 |
+
lse_ptrs = ptrs_sched.make_lse_ptrs(config)
|
| 244 |
+
|
| 245 |
+
# Early exit if no n_blocks to process
|
| 246 |
+
if block_sched.is_empty():
|
| 247 |
+
ptrs_sched.store_empty(config, out_ptrs, lse_ptrs, IS_SPLIT_KV=IS_SPLIT_KV)
|
| 248 |
+
return
|
| 249 |
+
|
| 250 |
+
q_ptrs = ptrs_sched.make_q_ptrs(config)
|
| 251 |
+
k_ptrs = ptrs_sched.make_k_ptrs(config)
|
| 252 |
+
v_ptrs = ptrs_sched.make_v_ptrs(config)
|
| 253 |
+
|
| 254 |
+
# Initialize accumulators
|
| 255 |
+
row_max = tl.full((TILE_M,), float("-inf"), dtype=tl.float32)
|
| 256 |
+
row_sum = tl.zeros((TILE_M,), dtype=tl.float32)
|
| 257 |
+
acc_o = tl.zeros((TILE_M, TILE_K), dtype=tl.float32)
|
| 258 |
+
|
| 259 |
+
# Load query tile
|
| 260 |
+
q_tile = ptrs_sched.load_q(config, q_ptrs)
|
| 261 |
+
|
| 262 |
+
# Load key tile
|
| 263 |
+
k_tile = ptrs_sched.load_k(config, k_ptrs, block_sched.n_block_max - 1)
|
| 264 |
+
|
| 265 |
+
# Process n_blocks with causal masking
|
| 266 |
+
if IS_CAUSAL or IS_LOCAL:
|
| 267 |
+
n_block_max_no_mask = block_sched.n_block_max_no_mask
|
| 268 |
+
for n_block in tl.range(
|
| 269 |
+
block_sched.n_block_max - 1, block_sched.n_block_max_no_mask - 1, -1
|
| 270 |
+
):
|
| 271 |
+
k_tile, acc_o, row_max, row_sum = _fwd_inner_dense_kernel(
|
| 272 |
+
config=config,
|
| 273 |
+
ptrs_sched=ptrs_sched,
|
| 274 |
+
mask_sched=mask_sched,
|
| 275 |
+
softmax_sched=softmax_sched,
|
| 276 |
+
q_tile=q_tile,
|
| 277 |
+
k_tile=k_tile,
|
| 278 |
+
k_ptrs=k_ptrs,
|
| 279 |
+
v_ptrs=v_ptrs,
|
| 280 |
+
acc_o=acc_o,
|
| 281 |
+
row_max=row_max,
|
| 282 |
+
row_sum=row_sum,
|
| 283 |
+
n_block=n_block,
|
| 284 |
+
n_block_min=block_sched.n_block_max_no_mask,
|
| 285 |
+
IS_MASK=True,
|
| 286 |
+
MASK_CAUSAL=IS_CAUSAL,
|
| 287 |
+
MASK_LOCAL=True if IS_LOCAL else False,
|
| 288 |
+
MASK_SINK=False,
|
| 289 |
+
CHECK_INF=True,
|
| 290 |
+
)
|
| 291 |
+
else:
|
| 292 |
+
# First iteration with seqlen masking
|
| 293 |
+
n_block = block_sched.n_block_max - 1
|
| 294 |
+
# Triton aggregate does not support attribute reassignment yet
|
| 295 |
+
# block_sched.n_block_max_no_mask = n_block
|
| 296 |
+
n_block_max_no_mask = n_block
|
| 297 |
+
|
| 298 |
+
k_tile, acc_o, row_max, row_sum = _fwd_inner_dense_kernel(
|
| 299 |
+
config=config,
|
| 300 |
+
ptrs_sched=ptrs_sched,
|
| 301 |
+
mask_sched=mask_sched,
|
| 302 |
+
softmax_sched=softmax_sched,
|
| 303 |
+
q_tile=q_tile,
|
| 304 |
+
k_tile=k_tile,
|
| 305 |
+
k_ptrs=k_ptrs,
|
| 306 |
+
v_ptrs=v_ptrs,
|
| 307 |
+
acc_o=acc_o,
|
| 308 |
+
row_max=row_max,
|
| 309 |
+
row_sum=row_sum,
|
| 310 |
+
n_block=n_block,
|
| 311 |
+
n_block_min=n_block,
|
| 312 |
+
IS_MASK=True,
|
| 313 |
+
MASK_CAUSAL=False,
|
| 314 |
+
MASK_LOCAL=False,
|
| 315 |
+
MASK_SINK=False,
|
| 316 |
+
CHECK_INF=True,
|
| 317 |
+
)
|
| 318 |
+
|
| 319 |
+
# Process n_blocks without masking
|
| 320 |
+
if not IS_LOCAL and n_block_max_no_mask > block_sched.n_block_min:
|
| 321 |
+
# Load key tile
|
| 322 |
+
k_tile = ptrs_sched.load_k(config, k_ptrs, n_block_max_no_mask - 1)
|
| 323 |
+
|
| 324 |
+
for n_block in tl.range(
|
| 325 |
+
n_block_max_no_mask - 1, block_sched.n_block_min - 1, -1
|
| 326 |
+
):
|
| 327 |
+
k_tile, acc_o, row_max, row_sum = _fwd_inner_dense_kernel(
|
| 328 |
+
config=config,
|
| 329 |
+
ptrs_sched=ptrs_sched,
|
| 330 |
+
mask_sched=mask_sched,
|
| 331 |
+
softmax_sched=softmax_sched,
|
| 332 |
+
q_tile=q_tile,
|
| 333 |
+
k_tile=k_tile,
|
| 334 |
+
k_ptrs=k_ptrs,
|
| 335 |
+
v_ptrs=v_ptrs,
|
| 336 |
+
acc_o=acc_o,
|
| 337 |
+
row_max=row_max,
|
| 338 |
+
row_sum=row_sum,
|
| 339 |
+
n_block=n_block,
|
| 340 |
+
n_block_min=block_sched.n_block_min,
|
| 341 |
+
IS_MASK=False,
|
| 342 |
+
MASK_CAUSAL=False,
|
| 343 |
+
MASK_LOCAL=False,
|
| 344 |
+
MASK_SINK=False,
|
| 345 |
+
CHECK_INF=False,
|
| 346 |
+
)
|
| 347 |
+
|
| 348 |
+
if IS_LOCAL:
|
| 349 |
+
# Process n_blocks with local right masking
|
| 350 |
+
if block_sched.n_block_window_max > block_sched.n_block_window_max_no_mask:
|
| 351 |
+
# Load key tile
|
| 352 |
+
k_tile = ptrs_sched.load_k(
|
| 353 |
+
config, k_ptrs, block_sched.n_block_window_max - 1
|
| 354 |
+
)
|
| 355 |
+
|
| 356 |
+
for n_block in tl.range(
|
| 357 |
+
block_sched.n_block_window_max - 1,
|
| 358 |
+
block_sched.n_block_window_max_no_mask - 1,
|
| 359 |
+
-1,
|
| 360 |
+
):
|
| 361 |
+
k_tile, acc_o, row_max, row_sum = _fwd_inner_dense_kernel(
|
| 362 |
+
config=config,
|
| 363 |
+
ptrs_sched=ptrs_sched,
|
| 364 |
+
mask_sched=mask_sched,
|
| 365 |
+
softmax_sched=softmax_sched,
|
| 366 |
+
q_tile=q_tile,
|
| 367 |
+
k_tile=k_tile,
|
| 368 |
+
k_ptrs=k_ptrs,
|
| 369 |
+
v_ptrs=v_ptrs,
|
| 370 |
+
acc_o=acc_o,
|
| 371 |
+
row_max=row_max,
|
| 372 |
+
row_sum=row_sum,
|
| 373 |
+
n_block=n_block,
|
| 374 |
+
n_block_min=block_sched.n_block_window_max_no_mask,
|
| 375 |
+
IS_MASK=True,
|
| 376 |
+
MASK_CAUSAL=False,
|
| 377 |
+
MASK_LOCAL=True,
|
| 378 |
+
MASK_SINK=False,
|
| 379 |
+
CHECK_INF=True,
|
| 380 |
+
)
|
| 381 |
+
|
| 382 |
+
# Process n_blocks without masking
|
| 383 |
+
if (
|
| 384 |
+
block_sched.n_block_window_max_no_mask
|
| 385 |
+
> block_sched.n_block_window_min_no_mask
|
| 386 |
+
):
|
| 387 |
+
# Load key tile
|
| 388 |
+
k_tile = ptrs_sched.load_k(
|
| 389 |
+
config, k_ptrs, block_sched.n_block_window_max_no_mask - 1
|
| 390 |
+
)
|
| 391 |
+
|
| 392 |
+
for n_block in tl.range(
|
| 393 |
+
block_sched.n_block_window_max_no_mask - 1,
|
| 394 |
+
block_sched.n_block_window_min_no_mask - 1,
|
| 395 |
+
-1,
|
| 396 |
+
):
|
| 397 |
+
k_tile, acc_o, row_max, row_sum = _fwd_inner_dense_kernel(
|
| 398 |
+
config=config,
|
| 399 |
+
ptrs_sched=ptrs_sched,
|
| 400 |
+
mask_sched=mask_sched,
|
| 401 |
+
softmax_sched=softmax_sched,
|
| 402 |
+
q_tile=q_tile,
|
| 403 |
+
k_tile=k_tile,
|
| 404 |
+
k_ptrs=k_ptrs,
|
| 405 |
+
v_ptrs=v_ptrs,
|
| 406 |
+
acc_o=acc_o,
|
| 407 |
+
row_max=row_max,
|
| 408 |
+
row_sum=row_sum,
|
| 409 |
+
n_block=n_block,
|
| 410 |
+
n_block_min=block_sched.n_block_window_min_no_mask,
|
| 411 |
+
IS_MASK=False,
|
| 412 |
+
MASK_CAUSAL=False,
|
| 413 |
+
MASK_LOCAL=False,
|
| 414 |
+
MASK_SINK=False,
|
| 415 |
+
CHECK_INF=False,
|
| 416 |
+
)
|
| 417 |
+
|
| 418 |
+
# Process n_blocks with local left masking
|
| 419 |
+
if block_sched.n_block_window_min_no_mask > block_sched.n_block_window_min:
|
| 420 |
+
# Load key tile
|
| 421 |
+
k_tile = ptrs_sched.load_k(
|
| 422 |
+
config, k_ptrs, block_sched.n_block_window_min_no_mask - 1
|
| 423 |
+
)
|
| 424 |
+
|
| 425 |
+
for n_block in tl.range(
|
| 426 |
+
block_sched.n_block_window_min_no_mask - 1,
|
| 427 |
+
block_sched.n_block_window_min - 1,
|
| 428 |
+
-1,
|
| 429 |
+
):
|
| 430 |
+
k_tile, acc_o, row_max, row_sum = _fwd_inner_dense_kernel(
|
| 431 |
+
config=config,
|
| 432 |
+
ptrs_sched=ptrs_sched,
|
| 433 |
+
mask_sched=mask_sched,
|
| 434 |
+
softmax_sched=softmax_sched,
|
| 435 |
+
q_tile=q_tile,
|
| 436 |
+
k_tile=k_tile,
|
| 437 |
+
k_ptrs=k_ptrs,
|
| 438 |
+
v_ptrs=v_ptrs,
|
| 439 |
+
acc_o=acc_o,
|
| 440 |
+
row_max=row_max,
|
| 441 |
+
row_sum=row_sum,
|
| 442 |
+
n_block=n_block,
|
| 443 |
+
n_block_min=block_sched.n_block_window_min,
|
| 444 |
+
IS_MASK=True,
|
| 445 |
+
MASK_CAUSAL=False,
|
| 446 |
+
MASK_LOCAL=True,
|
| 447 |
+
MASK_SINK=False,
|
| 448 |
+
CHECK_INF=True,
|
| 449 |
+
)
|
| 450 |
+
|
| 451 |
+
# Process n_blocks with local sink masking
|
| 452 |
+
if block_sched.n_block_sink_max > block_sched.n_block_sink_min:
|
| 453 |
+
# Load key tile
|
| 454 |
+
k_tile = ptrs_sched.load_k(config, k_ptrs, block_sched.n_block_sink_max - 1)
|
| 455 |
+
|
| 456 |
+
for n_block in tl.range(
|
| 457 |
+
block_sched.n_block_sink_max - 1,
|
| 458 |
+
block_sched.n_block_sink_min - 1,
|
| 459 |
+
-1,
|
| 460 |
+
):
|
| 461 |
+
k_tile, acc_o, row_max, row_sum = _fwd_inner_dense_kernel(
|
| 462 |
+
config=config,
|
| 463 |
+
ptrs_sched=ptrs_sched,
|
| 464 |
+
mask_sched=mask_sched,
|
| 465 |
+
softmax_sched=softmax_sched,
|
| 466 |
+
q_tile=q_tile,
|
| 467 |
+
k_tile=k_tile,
|
| 468 |
+
k_ptrs=k_ptrs,
|
| 469 |
+
v_ptrs=v_ptrs,
|
| 470 |
+
acc_o=acc_o,
|
| 471 |
+
row_max=row_max,
|
| 472 |
+
row_sum=row_sum,
|
| 473 |
+
n_block=n_block,
|
| 474 |
+
n_block_min=block_sched.n_block_sink_min,
|
| 475 |
+
IS_MASK=True,
|
| 476 |
+
MASK_CAUSAL=False,
|
| 477 |
+
MASK_LOCAL=True,
|
| 478 |
+
MASK_SINK=True,
|
| 479 |
+
CHECK_INF=True,
|
| 480 |
+
)
|
| 481 |
+
|
| 482 |
+
# Finalize softmax
|
| 483 |
+
row_scale, lse_tile = softmax_sched.finalize(
|
| 484 |
+
row_max=row_max,
|
| 485 |
+
row_sum=row_sum,
|
| 486 |
+
IS_LOG2=IS_SPLIT_KV,
|
| 487 |
+
)
|
| 488 |
+
|
| 489 |
+
# Store LSE
|
| 490 |
+
ptrs_sched.store_lse(config, lse_ptrs, lse_tile)
|
| 491 |
+
|
| 492 |
+
# Finalize rescale
|
| 493 |
+
acc_o = softmax_sched.rescale_o(
|
| 494 |
+
acc_o=acc_o,
|
| 495 |
+
row_scale=row_scale,
|
| 496 |
+
)
|
| 497 |
+
|
| 498 |
+
# Store output
|
| 499 |
+
ptrs_sched.store_out(config, out_ptrs, acc_o, IS_SPLIT_KV=IS_SPLIT_KV)
|
| 500 |
+
|
| 501 |
+
|
| 502 |
+
_fwd_dense_kernel = cache_utils.wrap_kernel(_fwd_dense_kernel)
|
| 503 |
+
|
| 504 |
+
|
| 505 |
+
_fwd_dense_kernel_autotuned = None
|
| 506 |
+
|
| 507 |
+
|
| 508 |
+
def _get_autotuned_kernel():
|
| 509 |
+
global _fwd_dense_kernel_autotuned
|
| 510 |
+
if _fwd_dense_kernel_autotuned is None:
|
| 511 |
+
jit_kernel = _fwd_dense_kernel._kernel
|
| 512 |
+
autotuned = autotuner.make_fwd_dense_autotuned_kernel(jit_kernel)
|
| 513 |
+
_fwd_dense_kernel_autotuned = autotuner.AutotunedKernel(autotuned)
|
| 514 |
+
return _fwd_dense_kernel_autotuned
|
| 515 |
+
|
| 516 |
+
|
| 517 |
+
def _flash_dense_attn_forward(
|
| 518 |
+
query: torch.Tensor,
|
| 519 |
+
key: torch.Tensor,
|
| 520 |
+
value: torch.Tensor,
|
| 521 |
+
is_causal: bool = False,
|
| 522 |
+
softmax_scale: float = None,
|
| 523 |
+
query_scale: Optional[torch.Tensor] = None,
|
| 524 |
+
key_scale: Optional[torch.Tensor] = None,
|
| 525 |
+
value_scale: Optional[torch.Tensor] = None,
|
| 526 |
+
window_sizes: Optional[torch.Tensor] = None,
|
| 527 |
+
is_local: bool = False,
|
| 528 |
+
is_quant: bool = False,
|
| 529 |
+
is_split_kv: bool = False,
|
| 530 |
+
pack_gqa: bool = False,
|
| 531 |
+
out: Optional[torch.Tensor] = None,
|
| 532 |
+
lse: Optional[torch.Tensor] = None,
|
| 533 |
+
is_autotune: bool = False,
|
| 534 |
+
skip_checks: bool = False,
|
| 535 |
+
) -> Tuple[torch.Tensor, torch.Tensor, float]:
|
| 536 |
+
device = query.device
|
| 537 |
+
num_SMs = cache_utils.get_device_num_sms(device)
|
| 538 |
+
batch_size, seqlen_q, num_heads_q, head_dim = query.shape
|
| 539 |
+
_, seqlen_k, num_heads_kv, _ = key.shape
|
| 540 |
+
softmax_scale = (
|
| 541 |
+
softmax_scale if softmax_scale is not None else 1.0 / (head_dim**0.5)
|
| 542 |
+
)
|
| 543 |
+
qhead_per_kvhead = num_heads_q // num_heads_kv
|
| 544 |
+
qhead_per_kvhead_packgqa = num_heads_q // num_heads_kv if pack_gqa else 1
|
| 545 |
+
if is_local and window_sizes is None:
|
| 546 |
+
window_sizes = utils.window_sizes_heuristic(seqlen_k, num_heads_kv, device)
|
| 547 |
+
elif not is_local:
|
| 548 |
+
window_sizes = torch.zeros((num_heads_kv, 4), dtype=torch.int32, device=device)
|
| 549 |
+
|
| 550 |
+
if not skip_checks:
|
| 551 |
+
assert_inputs.assert_fwd_inputs(
|
| 552 |
+
query,
|
| 553 |
+
key,
|
| 554 |
+
value,
|
| 555 |
+
query_scale=query_scale,
|
| 556 |
+
key_scale=key_scale,
|
| 557 |
+
value_scale=value_scale,
|
| 558 |
+
window_sizes=window_sizes,
|
| 559 |
+
cu_seqlens_q=None,
|
| 560 |
+
cu_seqlens_k=None,
|
| 561 |
+
seqused_q=None,
|
| 562 |
+
seqused_k=None,
|
| 563 |
+
num_heads_q=num_heads_q,
|
| 564 |
+
num_heads_kv=num_heads_kv,
|
| 565 |
+
head_dim=head_dim,
|
| 566 |
+
is_quant=is_quant,
|
| 567 |
+
device=device,
|
| 568 |
+
)
|
| 569 |
+
|
| 570 |
+
TILE_K = max(triton.next_power_of_2(head_dim), 16)
|
| 571 |
+
|
| 572 |
+
_kernel_name = f"fwd_dense{'_split' if is_split_kv else ''}"
|
| 573 |
+
launch_config = launch_template.load_launch_config(
|
| 574 |
+
device=device,
|
| 575 |
+
kernel_name=_kernel_name,
|
| 576 |
+
seqlen_q=seqlen_q,
|
| 577 |
+
seqlen_k=seqlen_k,
|
| 578 |
+
tile_k=TILE_K,
|
| 579 |
+
is_local=is_local,
|
| 580 |
+
qhead_per_kvhead=qhead_per_kvhead,
|
| 581 |
+
is_causal=is_causal,
|
| 582 |
+
pack_gqa=pack_gqa,
|
| 583 |
+
is_quant=is_quant,
|
| 584 |
+
)
|
| 585 |
+
if launch_config is not None and not is_autotune:
|
| 586 |
+
kernel = _fwd_dense_kernel
|
| 587 |
+
TILE_M, TILE_N, num_warps, num_stages, num_ctas = launch_config
|
| 588 |
+
else:
|
| 589 |
+
kernel = _get_autotuned_kernel()
|
| 590 |
+
# Placeholder for pre-launch computations
|
| 591 |
+
TILE_M = TILE_N = 64
|
| 592 |
+
num_warps = num_stages = num_ctas = None
|
| 593 |
+
|
| 594 |
+
num_splits = (
|
| 595 |
+
utils.num_splits_heuristic(
|
| 596 |
+
seqlen_q=seqlen_q * qhead_per_kvhead_packgqa,
|
| 597 |
+
seqlen_k=seqlen_k,
|
| 598 |
+
num_SMs=num_SMs,
|
| 599 |
+
TILE_M=TILE_M,
|
| 600 |
+
TILE_N=TILE_N,
|
| 601 |
+
max_split_blocks=utils.max_split_blocks_from_window_sizes(
|
| 602 |
+
window_sizes, TILE_N
|
| 603 |
+
)
|
| 604 |
+
if is_local
|
| 605 |
+
else None,
|
| 606 |
+
)
|
| 607 |
+
if is_split_kv
|
| 608 |
+
else 1
|
| 609 |
+
)
|
| 610 |
+
|
| 611 |
+
out_dtype = torch.bfloat16 if is_quant else query.dtype
|
| 612 |
+
out = out if out is not None else torch.empty_like(query, dtype=out_dtype)
|
| 613 |
+
lse = (
|
| 614 |
+
lse
|
| 615 |
+
if lse is not None
|
| 616 |
+
else torch.empty(
|
| 617 |
+
(batch_size, num_heads_q, seqlen_q),
|
| 618 |
+
dtype=torch.float32,
|
| 619 |
+
device=query.device,
|
| 620 |
+
)
|
| 621 |
+
)
|
| 622 |
+
|
| 623 |
+
if is_split_kv:
|
| 624 |
+
out_partial = torch.empty(
|
| 625 |
+
(num_splits, batch_size, seqlen_q, num_heads_q, head_dim),
|
| 626 |
+
dtype=torch.float32,
|
| 627 |
+
device=query.device,
|
| 628 |
+
)
|
| 629 |
+
lse_partial = torch.empty(
|
| 630 |
+
(num_splits, batch_size, num_heads_q, seqlen_q),
|
| 631 |
+
dtype=torch.float32,
|
| 632 |
+
device=query.device,
|
| 633 |
+
)
|
| 634 |
+
|
| 635 |
+
if not is_quant:
|
| 636 |
+
query_scale = torch.ones(1, device=device, dtype=query.dtype)
|
| 637 |
+
key_scale = torch.ones(1, device=device, dtype=query.dtype)
|
| 638 |
+
value_scale = torch.ones(1, device=device, dtype=query.dtype)
|
| 639 |
+
|
| 640 |
+
grid = launch_grid.get_fwd_grid(
|
| 641 |
+
batch_size=batch_size,
|
| 642 |
+
seqlen_q=seqlen_q,
|
| 643 |
+
num_heads_q=num_heads_q,
|
| 644 |
+
num_heads_kv=num_heads_kv,
|
| 645 |
+
pack_gqa=pack_gqa,
|
| 646 |
+
num_splits=num_splits,
|
| 647 |
+
)
|
| 648 |
+
|
| 649 |
+
triton.set_allocator(utils.alloc_fn)
|
| 650 |
+
|
| 651 |
+
kernel[grid](
|
| 652 |
+
query,
|
| 653 |
+
key,
|
| 654 |
+
value,
|
| 655 |
+
out if not is_split_kv else out_partial,
|
| 656 |
+
lse if not is_split_kv else lse_partial,
|
| 657 |
+
softmax_scale,
|
| 658 |
+
query_scale,
|
| 659 |
+
key_scale,
|
| 660 |
+
value_scale,
|
| 661 |
+
window_sizes,
|
| 662 |
+
query.stride(0),
|
| 663 |
+
query.stride(-2),
|
| 664 |
+
query.stride(-3),
|
| 665 |
+
key.stride(0),
|
| 666 |
+
key.stride(-2),
|
| 667 |
+
key.stride(-3),
|
| 668 |
+
value.stride(0),
|
| 669 |
+
value.stride(-2),
|
| 670 |
+
value.stride(-3),
|
| 671 |
+
out.stride(0) if not is_split_kv else out_partial.stride(1),
|
| 672 |
+
out.stride(-2) if not is_split_kv else out_partial.stride(-2),
|
| 673 |
+
out.stride(-3) if not is_split_kv else out_partial.stride(-3),
|
| 674 |
+
0 if not is_split_kv else out_partial.stride(0),
|
| 675 |
+
lse.stride(0) if not is_split_kv else lse_partial.stride(1),
|
| 676 |
+
lse.stride(-2) if not is_split_kv else lse_partial.stride(-2),
|
| 677 |
+
0 if not is_split_kv else lse_partial.stride(0),
|
| 678 |
+
window_sizes.stride(0),
|
| 679 |
+
None,
|
| 680 |
+
None,
|
| 681 |
+
None,
|
| 682 |
+
None,
|
| 683 |
+
num_splits,
|
| 684 |
+
seqlen_q=seqlen_q,
|
| 685 |
+
seqlen_k=seqlen_k,
|
| 686 |
+
head_dim=head_dim,
|
| 687 |
+
SEQLEN_Q_CACHE=seqlen_q // 1024,
|
| 688 |
+
SEQLEN_K_CACHE=seqlen_k // 1024,
|
| 689 |
+
QHEAD_PER_KVHEAD=qhead_per_kvhead,
|
| 690 |
+
PACK_GQA=pack_gqa,
|
| 691 |
+
QHEAD_PER_KVHEAD_PACKGQA=qhead_per_kvhead_packgqa,
|
| 692 |
+
TILE_M=TILE_M,
|
| 693 |
+
TILE_N=TILE_N,
|
| 694 |
+
TILE_K=TILE_K,
|
| 695 |
+
IS_CAUSAL=is_causal,
|
| 696 |
+
IS_LOCAL=is_local,
|
| 697 |
+
IS_SPLIT_KV=is_split_kv,
|
| 698 |
+
HAS_CU_SEQLENS_Q=False,
|
| 699 |
+
HAS_CU_SEQLENS_K=False,
|
| 700 |
+
HAS_SEQUSED_Q=False,
|
| 701 |
+
HAS_SEQUSED_K=False,
|
| 702 |
+
num_warps=num_warps,
|
| 703 |
+
num_stages=num_stages,
|
| 704 |
+
num_ctas=num_ctas,
|
| 705 |
+
)
|
| 706 |
+
|
| 707 |
+
if launch_config is None or is_autotune:
|
| 708 |
+
best = launch_template.extract_best_config(_get_autotuned_kernel())
|
| 709 |
+
if best is not None:
|
| 710 |
+
launch_template.store_launch_config(
|
| 711 |
+
device=device,
|
| 712 |
+
kernel_name=_kernel_name,
|
| 713 |
+
seqlen_q=seqlen_q,
|
| 714 |
+
seqlen_k=seqlen_k,
|
| 715 |
+
tile_k=TILE_K,
|
| 716 |
+
config=best,
|
| 717 |
+
is_local=is_local,
|
| 718 |
+
qhead_per_kvhead=qhead_per_kvhead,
|
| 719 |
+
is_causal=is_causal,
|
| 720 |
+
pack_gqa=pack_gqa,
|
| 721 |
+
is_quant=is_quant,
|
| 722 |
+
)
|
| 723 |
+
|
| 724 |
+
if is_split_kv:
|
| 725 |
+
flash_fwd_combine._flash_attn_fwd_combine(
|
| 726 |
+
out_partial,
|
| 727 |
+
lse_partial,
|
| 728 |
+
out,
|
| 729 |
+
lse,
|
| 730 |
+
)
|
| 731 |
+
|
| 732 |
+
return out, lse, softmax_scale
|
| 733 |
+
|
| 734 |
+
|
| 735 |
+
def _flash_dense_attn_varlen_forward(
|
| 736 |
+
query: torch.Tensor,
|
| 737 |
+
key: torch.Tensor,
|
| 738 |
+
value: torch.Tensor,
|
| 739 |
+
cu_seqlens_q: torch.Tensor,
|
| 740 |
+
cu_seqlens_k: torch.Tensor,
|
| 741 |
+
max_seqlen_q: int,
|
| 742 |
+
max_seqlen_k: int,
|
| 743 |
+
is_causal: bool = False,
|
| 744 |
+
softmax_scale: float = None,
|
| 745 |
+
query_scale: Optional[torch.Tensor] = None,
|
| 746 |
+
key_scale: Optional[torch.Tensor] = None,
|
| 747 |
+
value_scale: Optional[torch.Tensor] = None,
|
| 748 |
+
window_sizes: Optional[torch.Tensor] = None,
|
| 749 |
+
is_local: bool = False,
|
| 750 |
+
is_quant: bool = False,
|
| 751 |
+
is_split_kv: bool = False,
|
| 752 |
+
pack_gqa: bool = False,
|
| 753 |
+
seqused_q: Optional[torch.Tensor] = None,
|
| 754 |
+
seqused_k: Optional[torch.Tensor] = None,
|
| 755 |
+
out: Optional[torch.Tensor] = None,
|
| 756 |
+
lse: Optional[torch.Tensor] = None,
|
| 757 |
+
is_autotune: bool = False,
|
| 758 |
+
skip_checks: bool = False,
|
| 759 |
+
) -> Tuple[torch.Tensor, torch.Tensor, float]:
|
| 760 |
+
device = query.device
|
| 761 |
+
num_SMs = cache_utils.get_device_num_sms(device)
|
| 762 |
+
total_seqlen_q, num_heads_q, head_dim = query.shape
|
| 763 |
+
_, num_heads_kv, _ = key.shape
|
| 764 |
+
batch_size = cu_seqlens_q.shape[0] - 1
|
| 765 |
+
seqlen_q = max_seqlen_q
|
| 766 |
+
seqlen_k = max_seqlen_k
|
| 767 |
+
softmax_scale = (
|
| 768 |
+
softmax_scale if softmax_scale is not None else 1.0 / (head_dim**0.5)
|
| 769 |
+
)
|
| 770 |
+
qhead_per_kvhead = num_heads_q // num_heads_kv
|
| 771 |
+
qhead_per_kvhead_packgqa = num_heads_q // num_heads_kv if pack_gqa else 1
|
| 772 |
+
if is_local and window_sizes is None:
|
| 773 |
+
window_sizes = utils.window_sizes_heuristic(seqlen_k, num_heads_kv, device)
|
| 774 |
+
elif not is_local:
|
| 775 |
+
window_sizes = torch.zeros((num_heads_kv, 4), dtype=torch.int32, device=device)
|
| 776 |
+
|
| 777 |
+
if not skip_checks:
|
| 778 |
+
assert_inputs.assert_fwd_inputs(
|
| 779 |
+
query,
|
| 780 |
+
key,
|
| 781 |
+
value,
|
| 782 |
+
query_scale=query_scale,
|
| 783 |
+
key_scale=key_scale,
|
| 784 |
+
value_scale=value_scale,
|
| 785 |
+
window_sizes=window_sizes,
|
| 786 |
+
cu_seqlens_q=cu_seqlens_q,
|
| 787 |
+
cu_seqlens_k=cu_seqlens_k,
|
| 788 |
+
seqused_q=seqused_q,
|
| 789 |
+
seqused_k=seqused_k,
|
| 790 |
+
num_heads_q=num_heads_q,
|
| 791 |
+
num_heads_kv=num_heads_kv,
|
| 792 |
+
head_dim=head_dim,
|
| 793 |
+
is_quant=is_quant,
|
| 794 |
+
device=device,
|
| 795 |
+
)
|
| 796 |
+
|
| 797 |
+
TILE_K = max(triton.next_power_of_2(head_dim), 16)
|
| 798 |
+
|
| 799 |
+
_kernel_name = f"fwd_dense{'_split' if is_split_kv else ''}"
|
| 800 |
+
launch_config = launch_template.load_launch_config(
|
| 801 |
+
device=device,
|
| 802 |
+
kernel_name=_kernel_name,
|
| 803 |
+
seqlen_q=seqlen_q,
|
| 804 |
+
seqlen_k=seqlen_k,
|
| 805 |
+
tile_k=TILE_K,
|
| 806 |
+
is_local=is_local,
|
| 807 |
+
qhead_per_kvhead=qhead_per_kvhead,
|
| 808 |
+
is_causal=is_causal,
|
| 809 |
+
pack_gqa=pack_gqa,
|
| 810 |
+
is_quant=is_quant,
|
| 811 |
+
)
|
| 812 |
+
if launch_config is not None and not is_autotune:
|
| 813 |
+
kernel = _fwd_dense_kernel
|
| 814 |
+
TILE_M, TILE_N, num_warps, num_stages, num_ctas = launch_config
|
| 815 |
+
else:
|
| 816 |
+
kernel = _get_autotuned_kernel()
|
| 817 |
+
# Placeholder for pre-launch computations
|
| 818 |
+
TILE_M = TILE_N = 64
|
| 819 |
+
num_warps = num_stages = num_ctas = None
|
| 820 |
+
|
| 821 |
+
num_splits = (
|
| 822 |
+
utils.num_splits_heuristic(
|
| 823 |
+
seqlen_q=seqlen_q * qhead_per_kvhead_packgqa,
|
| 824 |
+
seqlen_k=seqlen_k,
|
| 825 |
+
num_SMs=num_SMs,
|
| 826 |
+
TILE_M=TILE_M,
|
| 827 |
+
TILE_N=TILE_N,
|
| 828 |
+
max_split_blocks=utils.max_split_blocks_from_window_sizes(
|
| 829 |
+
window_sizes, TILE_N
|
| 830 |
+
)
|
| 831 |
+
if is_local
|
| 832 |
+
else None,
|
| 833 |
+
)
|
| 834 |
+
if is_split_kv
|
| 835 |
+
else 1
|
| 836 |
+
)
|
| 837 |
+
|
| 838 |
+
out_dtype = torch.bfloat16 if is_quant else query.dtype
|
| 839 |
+
out = torch.empty_like(query, dtype=out_dtype)
|
| 840 |
+
lse = torch.empty(
|
| 841 |
+
(num_heads_q, total_seqlen_q),
|
| 842 |
+
dtype=torch.float32,
|
| 843 |
+
device=query.device,
|
| 844 |
+
)
|
| 845 |
+
|
| 846 |
+
if is_split_kv:
|
| 847 |
+
out_partial = torch.empty(
|
| 848 |
+
(num_splits, total_seqlen_q, num_heads_q, head_dim),
|
| 849 |
+
dtype=torch.float32,
|
| 850 |
+
device=query.device,
|
| 851 |
+
)
|
| 852 |
+
lse_partial = torch.empty(
|
| 853 |
+
(num_splits, num_heads_q, total_seqlen_q),
|
| 854 |
+
dtype=torch.float32,
|
| 855 |
+
device=query.device,
|
| 856 |
+
)
|
| 857 |
+
|
| 858 |
+
if not is_quant:
|
| 859 |
+
query_scale = torch.ones(1, device=device, dtype=query.dtype)
|
| 860 |
+
key_scale = torch.ones(1, device=device, dtype=query.dtype)
|
| 861 |
+
value_scale = torch.ones(1, device=device, dtype=query.dtype)
|
| 862 |
+
|
| 863 |
+
grid = launch_grid.get_fwd_grid(
|
| 864 |
+
batch_size=batch_size,
|
| 865 |
+
seqlen_q=seqlen_q,
|
| 866 |
+
num_heads_q=num_heads_q,
|
| 867 |
+
num_heads_kv=num_heads_kv,
|
| 868 |
+
pack_gqa=pack_gqa,
|
| 869 |
+
num_splits=num_splits,
|
| 870 |
+
)
|
| 871 |
+
|
| 872 |
+
triton.set_allocator(utils.alloc_fn)
|
| 873 |
+
|
| 874 |
+
kernel[grid](
|
| 875 |
+
query,
|
| 876 |
+
key,
|
| 877 |
+
value,
|
| 878 |
+
out if not is_split_kv else out_partial,
|
| 879 |
+
lse if not is_split_kv else lse_partial,
|
| 880 |
+
softmax_scale,
|
| 881 |
+
query_scale,
|
| 882 |
+
key_scale,
|
| 883 |
+
value_scale,
|
| 884 |
+
window_sizes,
|
| 885 |
+
0,
|
| 886 |
+
query.stride(-2),
|
| 887 |
+
query.stride(0),
|
| 888 |
+
0,
|
| 889 |
+
key.stride(-2),
|
| 890 |
+
key.stride(0),
|
| 891 |
+
0,
|
| 892 |
+
value.stride(-2),
|
| 893 |
+
value.stride(0),
|
| 894 |
+
0,
|
| 895 |
+
out.stride(-2) if not is_split_kv else out_partial.stride(-2),
|
| 896 |
+
out.stride(0) if not is_split_kv else out_partial.stride(-3),
|
| 897 |
+
0 if not is_split_kv else out_partial.stride(0),
|
| 898 |
+
0,
|
| 899 |
+
lse.stride(-2) if not is_split_kv else lse_partial.stride(-2),
|
| 900 |
+
0 if not is_split_kv else lse_partial.stride(0),
|
| 901 |
+
window_sizes.stride(0),
|
| 902 |
+
cu_seqlens_q,
|
| 903 |
+
cu_seqlens_k,
|
| 904 |
+
seqused_q,
|
| 905 |
+
seqused_k,
|
| 906 |
+
num_splits,
|
| 907 |
+
seqlen_q=seqlen_q,
|
| 908 |
+
seqlen_k=seqlen_k,
|
| 909 |
+
head_dim=head_dim,
|
| 910 |
+
SEQLEN_Q_CACHE=seqlen_q // 1024,
|
| 911 |
+
SEQLEN_K_CACHE=seqlen_k // 1024,
|
| 912 |
+
QHEAD_PER_KVHEAD=qhead_per_kvhead,
|
| 913 |
+
PACK_GQA=pack_gqa,
|
| 914 |
+
QHEAD_PER_KVHEAD_PACKGQA=qhead_per_kvhead_packgqa,
|
| 915 |
+
TILE_M=TILE_M,
|
| 916 |
+
TILE_N=TILE_N,
|
| 917 |
+
TILE_K=TILE_K,
|
| 918 |
+
IS_CAUSAL=is_causal,
|
| 919 |
+
IS_LOCAL=is_local,
|
| 920 |
+
IS_SPLIT_KV=is_split_kv,
|
| 921 |
+
HAS_CU_SEQLENS_Q=True,
|
| 922 |
+
HAS_CU_SEQLENS_K=True,
|
| 923 |
+
HAS_SEQUSED_Q=seqused_q is not None,
|
| 924 |
+
HAS_SEQUSED_K=seqused_k is not None,
|
| 925 |
+
num_warps=num_warps,
|
| 926 |
+
num_stages=num_stages,
|
| 927 |
+
num_ctas=num_ctas,
|
| 928 |
+
)
|
| 929 |
+
|
| 930 |
+
if launch_config is None or is_autotune:
|
| 931 |
+
best = launch_template.extract_best_config(_get_autotuned_kernel())
|
| 932 |
+
if best is not None:
|
| 933 |
+
launch_template.store_launch_config(
|
| 934 |
+
device=device,
|
| 935 |
+
kernel_name=_kernel_name,
|
| 936 |
+
seqlen_q=seqlen_q,
|
| 937 |
+
seqlen_k=seqlen_k,
|
| 938 |
+
tile_k=TILE_K,
|
| 939 |
+
config=best,
|
| 940 |
+
is_local=is_local,
|
| 941 |
+
qhead_per_kvhead=qhead_per_kvhead,
|
| 942 |
+
is_causal=is_causal,
|
| 943 |
+
pack_gqa=pack_gqa,
|
| 944 |
+
is_quant=is_quant,
|
| 945 |
+
)
|
| 946 |
+
|
| 947 |
+
if is_split_kv:
|
| 948 |
+
flash_fwd_combine._flash_attn_fwd_combine(
|
| 949 |
+
out_partial,
|
| 950 |
+
lse_partial,
|
| 951 |
+
out,
|
| 952 |
+
lse,
|
| 953 |
+
cu_seqlens_q=cu_seqlens_q,
|
| 954 |
+
seqused_q=seqused_q,
|
| 955 |
+
)
|
| 956 |
+
|
| 957 |
+
return out, lse, softmax_scale
|
build/torch-cuda/flash_fwd_combine.py
ADDED
|
@@ -0,0 +1,239 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
import triton
|
| 3 |
+
import triton.language as tl
|
| 4 |
+
|
| 5 |
+
from . import (
|
| 6 |
+
utils,
|
| 7 |
+
cache_utils,
|
| 8 |
+
launch_template,
|
| 9 |
+
launch_grid,
|
| 10 |
+
seqlen_info,
|
| 11 |
+
kernel_repr,
|
| 12 |
+
)
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
@triton.jit(repr=kernel_repr.fwd_combine_repr)
|
| 16 |
+
def _fwd_combine_kernel(
|
| 17 |
+
Out_partial,
|
| 18 |
+
Lse_partial,
|
| 19 |
+
Out,
|
| 20 |
+
Lse,
|
| 21 |
+
stride_ops,
|
| 22 |
+
stride_opb,
|
| 23 |
+
stride_oph,
|
| 24 |
+
stride_opm,
|
| 25 |
+
stride_lps,
|
| 26 |
+
stride_lpb,
|
| 27 |
+
stride_lph,
|
| 28 |
+
stride_ob,
|
| 29 |
+
stride_oh,
|
| 30 |
+
stride_om,
|
| 31 |
+
stride_lb,
|
| 32 |
+
stride_lh,
|
| 33 |
+
cu_seqlens_q,
|
| 34 |
+
seqused_q,
|
| 35 |
+
num_splits,
|
| 36 |
+
seqlen_q,
|
| 37 |
+
num_heads_q,
|
| 38 |
+
head_dim,
|
| 39 |
+
TILE_M: tl.constexpr,
|
| 40 |
+
TILE_K: tl.constexpr,
|
| 41 |
+
HAS_CU_SEQLENS_Q: tl.constexpr,
|
| 42 |
+
HAS_SEQUSED_Q: tl.constexpr,
|
| 43 |
+
):
|
| 44 |
+
m_block = tl.program_id(0)
|
| 45 |
+
bh_idx = tl.program_id(1)
|
| 46 |
+
batch_idx = bh_idx // num_heads_q
|
| 47 |
+
head_idx = bh_idx - batch_idx * num_heads_q
|
| 48 |
+
|
| 49 |
+
# Get seqlen info for this batch
|
| 50 |
+
offset_q, actual_seqlen_q = seqlen_info.get_seqlen_info(
|
| 51 |
+
batch_idx=batch_idx,
|
| 52 |
+
seqlen_static=seqlen_q,
|
| 53 |
+
cu_seqlens=cu_seqlens_q,
|
| 54 |
+
seqused=seqused_q,
|
| 55 |
+
HAS_CU_SEQLENS=HAS_CU_SEQLENS_Q,
|
| 56 |
+
HAS_SEQUSED=HAS_SEQUSED_Q,
|
| 57 |
+
)
|
| 58 |
+
|
| 59 |
+
# Initialize base pointers
|
| 60 |
+
out_part_base = seqlen_info.offset_batch_Q(
|
| 61 |
+
Out_partial + head_idx * stride_oph,
|
| 62 |
+
batch_idx,
|
| 63 |
+
offset_q,
|
| 64 |
+
0,
|
| 65 |
+
stride_opb,
|
| 66 |
+
stride_opm,
|
| 67 |
+
HAS_CU_SEQLENS_Q,
|
| 68 |
+
USE_PADDED=False,
|
| 69 |
+
)
|
| 70 |
+
lse_part_base = seqlen_info.offset_batch_Q(
|
| 71 |
+
Lse_partial + head_idx * stride_lph,
|
| 72 |
+
batch_idx,
|
| 73 |
+
offset_q,
|
| 74 |
+
0,
|
| 75 |
+
stride_lpb,
|
| 76 |
+
1,
|
| 77 |
+
HAS_CU_SEQLENS_Q,
|
| 78 |
+
USE_PADDED=False,
|
| 79 |
+
)
|
| 80 |
+
out_base = seqlen_info.offset_batch_Q(
|
| 81 |
+
Out + head_idx * stride_oh,
|
| 82 |
+
batch_idx,
|
| 83 |
+
offset_q,
|
| 84 |
+
0,
|
| 85 |
+
stride_ob,
|
| 86 |
+
stride_om,
|
| 87 |
+
HAS_CU_SEQLENS_Q,
|
| 88 |
+
USE_PADDED=False,
|
| 89 |
+
)
|
| 90 |
+
lse_base = seqlen_info.offset_batch_Q(
|
| 91 |
+
Lse + head_idx * stride_lh,
|
| 92 |
+
batch_idx,
|
| 93 |
+
offset_q,
|
| 94 |
+
0,
|
| 95 |
+
stride_lb,
|
| 96 |
+
1,
|
| 97 |
+
HAS_CU_SEQLENS_Q,
|
| 98 |
+
USE_PADDED=False,
|
| 99 |
+
)
|
| 100 |
+
|
| 101 |
+
# Create descriptors
|
| 102 |
+
out_part_desc = tl.make_tensor_descriptor(
|
| 103 |
+
base=out_part_base,
|
| 104 |
+
shape=[num_splits, actual_seqlen_q, head_dim],
|
| 105 |
+
strides=[stride_ops, stride_opm, 1],
|
| 106 |
+
block_shape=[1, TILE_M, TILE_K],
|
| 107 |
+
)
|
| 108 |
+
lse_part_desc = tl.make_tensor_descriptor(
|
| 109 |
+
base=lse_part_base,
|
| 110 |
+
shape=[num_splits, actual_seqlen_q],
|
| 111 |
+
strides=[stride_lps, 1],
|
| 112 |
+
block_shape=[1, TILE_M],
|
| 113 |
+
)
|
| 114 |
+
out_desc = tl.make_tensor_descriptor(
|
| 115 |
+
base=out_base,
|
| 116 |
+
shape=[actual_seqlen_q, head_dim],
|
| 117 |
+
strides=[stride_om, 1],
|
| 118 |
+
block_shape=[TILE_M, TILE_K],
|
| 119 |
+
)
|
| 120 |
+
lse_desc = tl.make_tensor_descriptor(
|
| 121 |
+
base=lse_base,
|
| 122 |
+
shape=[actual_seqlen_q],
|
| 123 |
+
strides=[1],
|
| 124 |
+
block_shape=[TILE_M],
|
| 125 |
+
)
|
| 126 |
+
|
| 127 |
+
# Initialize accumulators
|
| 128 |
+
e_sum = tl.zeros((TILE_M,), dtype=tl.float32)
|
| 129 |
+
e_max = tl.full((TILE_M,), float("-inf"), dtype=tl.float32)
|
| 130 |
+
acc_o = tl.zeros((TILE_M, TILE_K), dtype=tl.float32)
|
| 131 |
+
|
| 132 |
+
# Combine split outputs
|
| 133 |
+
for s in tl.range(0, num_splits):
|
| 134 |
+
# Load partial LSE
|
| 135 |
+
lse_s = tl.sum(lse_part_desc.load([s, m_block * TILE_M]), axis=0)
|
| 136 |
+
|
| 137 |
+
# Compute normalized exponentials
|
| 138 |
+
new_e_max = tl.maximum(lse_s, e_max)
|
| 139 |
+
old_scale = tl.where(e_max == float("-inf"), 0.0, tl.exp2(e_max - new_e_max))
|
| 140 |
+
exp_logic = tl.where(lse_s == float("-inf"), 0.0, tl.exp2(lse_s - new_e_max))
|
| 141 |
+
|
| 142 |
+
# Load partial outputs
|
| 143 |
+
o_s = tl.sum(out_part_desc.load([s, m_block * TILE_M, 0]), axis=0)
|
| 144 |
+
|
| 145 |
+
# Compute scaled outputs
|
| 146 |
+
acc_o *= old_scale[:, None]
|
| 147 |
+
acc_o += exp_logic[:, None] * o_s
|
| 148 |
+
|
| 149 |
+
# Update e_sum and e_max
|
| 150 |
+
e_sum = e_sum * old_scale + exp_logic
|
| 151 |
+
e_max = new_e_max
|
| 152 |
+
|
| 153 |
+
# Normalize output
|
| 154 |
+
inv_sum = tl.where((e_sum == 0.0) | (e_sum != e_sum), 0.0, 1.0 / e_sum)
|
| 155 |
+
acc_o *= inv_sum[:, None]
|
| 156 |
+
|
| 157 |
+
# Store output
|
| 158 |
+
out_desc.store([m_block * TILE_M, 0], acc_o.to(Out.dtype.element_ty))
|
| 159 |
+
|
| 160 |
+
# Compute LSE
|
| 161 |
+
# ln2 = math.log(2.0)
|
| 162 |
+
ln2 = 0.6931471805599453
|
| 163 |
+
lse = tl.where(e_sum > 0.0, (e_max + tl.log2(e_sum)) * ln2, float("-inf"))
|
| 164 |
+
|
| 165 |
+
# Store LSE
|
| 166 |
+
lse_desc.store([m_block * TILE_M], lse)
|
| 167 |
+
|
| 168 |
+
|
| 169 |
+
_fwd_combine_kernel = cache_utils.wrap_kernel(_fwd_combine_kernel)
|
| 170 |
+
|
| 171 |
+
|
| 172 |
+
def _flash_attn_fwd_combine(
|
| 173 |
+
out_partial: torch.Tensor,
|
| 174 |
+
lse_partial: torch.Tensor,
|
| 175 |
+
out: torch.Tensor,
|
| 176 |
+
lse: torch.Tensor,
|
| 177 |
+
cu_seqlens_q: torch.Tensor = None,
|
| 178 |
+
seqused_q: torch.Tensor = None,
|
| 179 |
+
):
|
| 180 |
+
device = out.device
|
| 181 |
+
arch = cache_utils.get_device_arch(device)
|
| 182 |
+
is_varlen = cu_seqlens_q is not None
|
| 183 |
+
num_splits = out_partial.shape[0]
|
| 184 |
+
if not is_varlen:
|
| 185 |
+
batch_size, seqlen_q, num_heads_q, head_dim = out_partial.shape[1:]
|
| 186 |
+
else:
|
| 187 |
+
total_q, num_heads_q, head_dim = out_partial.shape[1:]
|
| 188 |
+
batch_size = cu_seqlens_q.shape[0] - 1
|
| 189 |
+
seqlen_q = total_q
|
| 190 |
+
|
| 191 |
+
TILE_K = max(triton.next_power_of_2(head_dim), 16)
|
| 192 |
+
|
| 193 |
+
TILE_M, num_warps, num_stages, num_ctas = (
|
| 194 |
+
launch_template.get_fwd_combine_launch_config(
|
| 195 |
+
tile_k=TILE_K,
|
| 196 |
+
device=device,
|
| 197 |
+
arch=arch,
|
| 198 |
+
)
|
| 199 |
+
)
|
| 200 |
+
|
| 201 |
+
grid = launch_grid.get_fwd_combine_grid(
|
| 202 |
+
batch_size=batch_size,
|
| 203 |
+
seqlen_q=seqlen_q,
|
| 204 |
+
num_heads_q=num_heads_q,
|
| 205 |
+
)
|
| 206 |
+
|
| 207 |
+
triton.set_allocator(utils.alloc_fn)
|
| 208 |
+
|
| 209 |
+
_fwd_combine_kernel[grid](
|
| 210 |
+
out_partial,
|
| 211 |
+
lse_partial,
|
| 212 |
+
out,
|
| 213 |
+
lse,
|
| 214 |
+
out_partial.stride(0),
|
| 215 |
+
out_partial.stride(1) if not is_varlen else 0,
|
| 216 |
+
out_partial.stride(-2),
|
| 217 |
+
out_partial.stride(-3),
|
| 218 |
+
lse_partial.stride(0),
|
| 219 |
+
lse_partial.stride(1) if not is_varlen else 0,
|
| 220 |
+
lse_partial.stride(-2),
|
| 221 |
+
out.stride(0) if not is_varlen else 0,
|
| 222 |
+
out.stride(-2),
|
| 223 |
+
out.stride(-3) if not is_varlen else out.stride(0),
|
| 224 |
+
lse.stride(0) if not is_varlen else 0,
|
| 225 |
+
lse.stride(-2) if not is_varlen else lse.stride(0),
|
| 226 |
+
cu_seqlens_q,
|
| 227 |
+
seqused_q,
|
| 228 |
+
num_splits,
|
| 229 |
+
seqlen_q,
|
| 230 |
+
num_heads_q,
|
| 231 |
+
head_dim,
|
| 232 |
+
TILE_M=TILE_M,
|
| 233 |
+
TILE_K=TILE_K,
|
| 234 |
+
HAS_CU_SEQLENS_Q=is_varlen,
|
| 235 |
+
HAS_SEQUSED_Q=seqused_q is not None,
|
| 236 |
+
num_warps=num_warps,
|
| 237 |
+
num_stages=num_stages,
|
| 238 |
+
num_ctas=num_ctas,
|
| 239 |
+
)
|
build/torch-cuda/flash_gated_bwd.py
ADDED
|
@@ -0,0 +1,1317 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from typing import Optional, Tuple
|
| 2 |
+
|
| 3 |
+
import math
|
| 4 |
+
import torch
|
| 5 |
+
import triton
|
| 6 |
+
import triton.language as tl
|
| 7 |
+
|
| 8 |
+
from . import (
|
| 9 |
+
assert_inputs,
|
| 10 |
+
utils,
|
| 11 |
+
cache_utils,
|
| 12 |
+
launch_template,
|
| 13 |
+
launch_grid,
|
| 14 |
+
activations,
|
| 15 |
+
flash_bwd_preprocess,
|
| 16 |
+
flash_bwd_postprocess,
|
| 17 |
+
kernel_repr,
|
| 18 |
+
autotuner,
|
| 19 |
+
)
|
| 20 |
+
from .scheduler import (
|
| 21 |
+
AttnBwdGridIndex,
|
| 22 |
+
AttnBwdConfig,
|
| 23 |
+
AttnBwdBlockScheduler,
|
| 24 |
+
AttnBwdPointerScheduler,
|
| 25 |
+
AttnMaskScheduler,
|
| 26 |
+
SoftmaxScheduler,
|
| 27 |
+
)
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
@triton.jit
|
| 31 |
+
def _bwd_inner_gated_kernel(
|
| 32 |
+
config,
|
| 33 |
+
ptrs_sched,
|
| 34 |
+
mask_sched,
|
| 35 |
+
softmax_sched,
|
| 36 |
+
acc_dk,
|
| 37 |
+
acc_dv,
|
| 38 |
+
acc_dd,
|
| 39 |
+
k_tile,
|
| 40 |
+
v_tile,
|
| 41 |
+
d_tile,
|
| 42 |
+
q_ptrs,
|
| 43 |
+
a_ptrs,
|
| 44 |
+
do_ptrs,
|
| 45 |
+
dq_accum_ptrs,
|
| 46 |
+
da_ptrs,
|
| 47 |
+
lse_ptrs,
|
| 48 |
+
dpsum_ptrs,
|
| 49 |
+
d_max,
|
| 50 |
+
d_min,
|
| 51 |
+
gate_max,
|
| 52 |
+
m_block,
|
| 53 |
+
IS_MASK: tl.constexpr,
|
| 54 |
+
MASK_CAUSAL: tl.constexpr,
|
| 55 |
+
MASK_LOCAL: tl.constexpr,
|
| 56 |
+
MASK_SINK: tl.constexpr,
|
| 57 |
+
):
|
| 58 |
+
skip_gate = False
|
| 59 |
+
skip_softmax = False
|
| 60 |
+
|
| 61 |
+
# Load alpha tile
|
| 62 |
+
a_tile = ptrs_sched.load_a(config, a_ptrs, m_block)
|
| 63 |
+
a_max = tl.max(a_tile)
|
| 64 |
+
a_min = tl.min(a_tile)
|
| 65 |
+
|
| 66 |
+
# Compute gate threshold for this m_block
|
| 67 |
+
gate_threshold_log2 = config.get_gate_threshold_log2(
|
| 68 |
+
m_block=m_block,
|
| 69 |
+
)
|
| 70 |
+
|
| 71 |
+
# Check if any gates are active for current tile
|
| 72 |
+
gate_max, skip_gate = softmax_sched.online_gate(
|
| 73 |
+
a_max=a_max,
|
| 74 |
+
a_min=a_min,
|
| 75 |
+
d_max=d_max,
|
| 76 |
+
d_min=d_min,
|
| 77 |
+
gate_max=gate_max,
|
| 78 |
+
gate_threshold_log2=gate_threshold_log2,
|
| 79 |
+
)
|
| 80 |
+
|
| 81 |
+
if not skip_gate:
|
| 82 |
+
# Compute attention gates
|
| 83 |
+
acc_s = d_tile[:, None] * a_tile[None, :]
|
| 84 |
+
|
| 85 |
+
# Compute scaling factor for gated attention score gradients
|
| 86 |
+
if config.IS_LOGSIGMOID_GATE:
|
| 87 |
+
ds_scale = tl.sigmoid(-acc_s)
|
| 88 |
+
else:
|
| 89 |
+
ds_scale = 1.0
|
| 90 |
+
|
| 91 |
+
# Load query tile
|
| 92 |
+
q_tile = ptrs_sched.load_q(config, q_ptrs, m_block)
|
| 93 |
+
|
| 94 |
+
# Compute attention scores
|
| 95 |
+
if config.IS_LOGSIGMOID_GATE:
|
| 96 |
+
acc_s = softmax_sched.log_sigmoid(
|
| 97 |
+
acc_s=acc_s,
|
| 98 |
+
)
|
| 99 |
+
acc_s += tl.dot(k_tile, q_tile.T)
|
| 100 |
+
|
| 101 |
+
if IS_MASK:
|
| 102 |
+
# Apply mask to attention scores
|
| 103 |
+
acc_s = mask_sched.apply_mask(
|
| 104 |
+
acc_s=acc_s,
|
| 105 |
+
iter_block=m_block,
|
| 106 |
+
MASK_CAUSAL=MASK_CAUSAL,
|
| 107 |
+
MASK_LOCAL=MASK_LOCAL,
|
| 108 |
+
MASK_SINK=MASK_SINK,
|
| 109 |
+
)
|
| 110 |
+
|
| 111 |
+
# Load LSE
|
| 112 |
+
lse_log2 = ptrs_sched.load_lse(config, lse_ptrs, m_block)
|
| 113 |
+
|
| 114 |
+
# Compute attention weights in log2-domain
|
| 115 |
+
p_log2 = acc_s * config.softmax_scale_log2 - lse_log2[None, :]
|
| 116 |
+
|
| 117 |
+
# Compute softmax threshold for this m_block
|
| 118 |
+
softmax_threshold_log2 = config.get_softmax_threshold_log2(
|
| 119 |
+
m_block=m_block,
|
| 120 |
+
)
|
| 121 |
+
|
| 122 |
+
# Update skip condition based on threshold
|
| 123 |
+
skip_softmax = tl.max(p_log2 - softmax_threshold_log2[None, :]) < 0.0
|
| 124 |
+
|
| 125 |
+
if not skip_softmax:
|
| 126 |
+
# Compute attention weights
|
| 127 |
+
p = activations.exp2(p_log2).to(q_tile.dtype)
|
| 128 |
+
|
| 129 |
+
# Load output gradients tile
|
| 130 |
+
do_tile = ptrs_sched.load_do(config, do_ptrs, m_block)
|
| 131 |
+
|
| 132 |
+
# Compute value gradients
|
| 133 |
+
acc_dv += tl.dot(p, do_tile)
|
| 134 |
+
|
| 135 |
+
# Compute attention weight gradients
|
| 136 |
+
acc_dp = tl.dot(v_tile, tl.trans(do_tile))
|
| 137 |
+
|
| 138 |
+
# Load dpsum
|
| 139 |
+
dpsum = ptrs_sched.load_dpsum(config, dpsum_ptrs, m_block)
|
| 140 |
+
|
| 141 |
+
# Compute attention score gradients
|
| 142 |
+
ds = p * (acc_dp - dpsum[None, :]).to(q_tile.dtype)
|
| 143 |
+
|
| 144 |
+
# Compute query gradients
|
| 145 |
+
dq = tl.dot(tl.trans(ds), k_tile)
|
| 146 |
+
|
| 147 |
+
# Store query gradients
|
| 148 |
+
ptrs_sched.store_dq(config, dq_accum_ptrs, m_block, dq)
|
| 149 |
+
|
| 150 |
+
# Compute key gradients
|
| 151 |
+
acc_dk += tl.dot(ds, q_tile)
|
| 152 |
+
|
| 153 |
+
# Compute alpha gradients
|
| 154 |
+
da = tl.sum(ds * ds_scale * d_tile[:, None], axis=0)
|
| 155 |
+
|
| 156 |
+
# Store alpha gradients
|
| 157 |
+
ptrs_sched.store_da(config, da_ptrs, m_block, da)
|
| 158 |
+
|
| 159 |
+
# Compute delta gradients
|
| 160 |
+
acc_dd += tl.sum(ds * ds_scale * a_tile[None, :], axis=1)
|
| 161 |
+
|
| 162 |
+
return (
|
| 163 |
+
acc_dk,
|
| 164 |
+
acc_dv,
|
| 165 |
+
acc_dd,
|
| 166 |
+
gate_max,
|
| 167 |
+
)
|
| 168 |
+
|
| 169 |
+
|
| 170 |
+
@triton.jit(repr=kernel_repr.bwd_gated_repr)
|
| 171 |
+
def _bwd_gated_kernel(
|
| 172 |
+
Q,
|
| 173 |
+
K,
|
| 174 |
+
V,
|
| 175 |
+
A,
|
| 176 |
+
D,
|
| 177 |
+
dO,
|
| 178 |
+
LSELog2,
|
| 179 |
+
dPsum,
|
| 180 |
+
dQaccum,
|
| 181 |
+
dK,
|
| 182 |
+
dV,
|
| 183 |
+
dA,
|
| 184 |
+
dD,
|
| 185 |
+
softmax_scale,
|
| 186 |
+
query_scale,
|
| 187 |
+
key_scale,
|
| 188 |
+
value_scale,
|
| 189 |
+
window_sizes,
|
| 190 |
+
softmax_threshold,
|
| 191 |
+
gate_threshold,
|
| 192 |
+
stride_qb,
|
| 193 |
+
stride_qh,
|
| 194 |
+
stride_qm,
|
| 195 |
+
stride_kb,
|
| 196 |
+
stride_kh,
|
| 197 |
+
stride_kn,
|
| 198 |
+
stride_vb,
|
| 199 |
+
stride_vh,
|
| 200 |
+
stride_vn,
|
| 201 |
+
stride_ab,
|
| 202 |
+
stride_ah,
|
| 203 |
+
stride_db,
|
| 204 |
+
stride_dh,
|
| 205 |
+
stride_dob,
|
| 206 |
+
stride_doh,
|
| 207 |
+
stride_dom,
|
| 208 |
+
stride_lb,
|
| 209 |
+
stride_lh,
|
| 210 |
+
stride_pb,
|
| 211 |
+
stride_ph,
|
| 212 |
+
stride_dqab,
|
| 213 |
+
stride_dqah,
|
| 214 |
+
stride_dqam,
|
| 215 |
+
stride_dkb,
|
| 216 |
+
stride_dkh,
|
| 217 |
+
stride_dkn,
|
| 218 |
+
stride_dks,
|
| 219 |
+
stride_dvb,
|
| 220 |
+
stride_dvh,
|
| 221 |
+
stride_dvn,
|
| 222 |
+
stride_dvs,
|
| 223 |
+
stride_dab,
|
| 224 |
+
stride_dah,
|
| 225 |
+
stride_ddb,
|
| 226 |
+
stride_ddh,
|
| 227 |
+
stride_dds,
|
| 228 |
+
stride_wh,
|
| 229 |
+
cu_seqlens_q,
|
| 230 |
+
cu_seqlens_k,
|
| 231 |
+
seqused_q,
|
| 232 |
+
seqused_k,
|
| 233 |
+
num_splits,
|
| 234 |
+
seqlen_q,
|
| 235 |
+
seqlen_k,
|
| 236 |
+
head_dim,
|
| 237 |
+
SEQLEN_Q_CACHE: tl.constexpr,
|
| 238 |
+
SEQLEN_K_CACHE: tl.constexpr,
|
| 239 |
+
QHEAD_PER_KVHEAD: tl.constexpr,
|
| 240 |
+
TILE_M: tl.constexpr,
|
| 241 |
+
TILE_N: tl.constexpr,
|
| 242 |
+
TILE_K: tl.constexpr,
|
| 243 |
+
IS_CAUSAL: tl.constexpr,
|
| 244 |
+
IS_LOCAL: tl.constexpr,
|
| 245 |
+
IS_SPLIT_QO: tl.constexpr,
|
| 246 |
+
IS_LOGSIGMOID_GATE: tl.constexpr,
|
| 247 |
+
IS_ADAPT_GATE: tl.constexpr,
|
| 248 |
+
HAS_CU_SEQLENS_Q: tl.constexpr,
|
| 249 |
+
HAS_CU_SEQLENS_K: tl.constexpr,
|
| 250 |
+
HAS_SEQUSED_Q: tl.constexpr,
|
| 251 |
+
HAS_SEQUSED_K: tl.constexpr,
|
| 252 |
+
):
|
| 253 |
+
# Create grid index
|
| 254 |
+
grid_idx = AttnBwdGridIndex.create(
|
| 255 |
+
num_splits=num_splits,
|
| 256 |
+
QHEAD_PER_KVHEAD=QHEAD_PER_KVHEAD,
|
| 257 |
+
IS_SPLIT_QO=IS_SPLIT_QO,
|
| 258 |
+
)
|
| 259 |
+
|
| 260 |
+
# Load window sizes
|
| 261 |
+
(
|
| 262 |
+
window_size_sink,
|
| 263 |
+
window_size_left,
|
| 264 |
+
window_size_right,
|
| 265 |
+
window_size_dist,
|
| 266 |
+
) = grid_idx.load_window_sizes(
|
| 267 |
+
window_sizes=window_sizes,
|
| 268 |
+
stride_wh=stride_wh,
|
| 269 |
+
IS_LOCAL=IS_LOCAL,
|
| 270 |
+
)
|
| 271 |
+
|
| 272 |
+
# Create config
|
| 273 |
+
config = AttnBwdConfig.create(
|
| 274 |
+
softmax_scale=softmax_scale,
|
| 275 |
+
softmax_threshold=softmax_threshold,
|
| 276 |
+
gate_threshold=gate_threshold,
|
| 277 |
+
query_scale=query_scale,
|
| 278 |
+
key_scale=key_scale,
|
| 279 |
+
value_scale=value_scale,
|
| 280 |
+
n_block=grid_idx.n_block,
|
| 281 |
+
batch_idx=grid_idx.batch_idx,
|
| 282 |
+
window_size_sink=window_size_sink,
|
| 283 |
+
window_size_left=window_size_left,
|
| 284 |
+
window_size_right=window_size_right,
|
| 285 |
+
window_size_dist=window_size_dist,
|
| 286 |
+
head_dim=head_dim,
|
| 287 |
+
cu_seqlens_q=cu_seqlens_q,
|
| 288 |
+
cu_seqlens_k=cu_seqlens_k,
|
| 289 |
+
seqused_q=seqused_q,
|
| 290 |
+
seqused_k=seqused_k,
|
| 291 |
+
seqlen_q=seqlen_q,
|
| 292 |
+
seqlen_k=seqlen_k,
|
| 293 |
+
QHEAD_PER_KVHEAD=QHEAD_PER_KVHEAD,
|
| 294 |
+
TILE_M=TILE_M,
|
| 295 |
+
TILE_N=TILE_N,
|
| 296 |
+
TILE_K=TILE_K,
|
| 297 |
+
IS_CAUSAL=IS_CAUSAL,
|
| 298 |
+
IS_LOGSIGMOID_GATE=IS_LOGSIGMOID_GATE,
|
| 299 |
+
IS_ADAPT_GATE=IS_ADAPT_GATE,
|
| 300 |
+
HAS_CU_SEQLENS_Q=HAS_CU_SEQLENS_Q,
|
| 301 |
+
HAS_CU_SEQLENS_K=HAS_CU_SEQLENS_K,
|
| 302 |
+
HAS_SEQUSED_Q=HAS_SEQUSED_Q,
|
| 303 |
+
HAS_SEQUSED_K=HAS_SEQUSED_K,
|
| 304 |
+
)
|
| 305 |
+
|
| 306 |
+
# Create pointer scheduler
|
| 307 |
+
ptrs_sched = AttnBwdPointerScheduler.create(
|
| 308 |
+
config=config,
|
| 309 |
+
Q=Q,
|
| 310 |
+
K=K,
|
| 311 |
+
V=V,
|
| 312 |
+
A=A,
|
| 313 |
+
D=D,
|
| 314 |
+
dO=dO,
|
| 315 |
+
LSELog2=LSELog2,
|
| 316 |
+
dPsum=dPsum,
|
| 317 |
+
dQaccum=dQaccum,
|
| 318 |
+
dK=dK,
|
| 319 |
+
dV=dV,
|
| 320 |
+
dA=dA,
|
| 321 |
+
dD=dD,
|
| 322 |
+
batch_idx=grid_idx.batch_idx,
|
| 323 |
+
head_idx=grid_idx.head_idx,
|
| 324 |
+
head_kv_idx=grid_idx.head_kv_idx,
|
| 325 |
+
split_idx=grid_idx.split_idx,
|
| 326 |
+
stride_qb=stride_qb,
|
| 327 |
+
stride_qh=stride_qh,
|
| 328 |
+
stride_qm=stride_qm,
|
| 329 |
+
stride_kb=stride_kb,
|
| 330 |
+
stride_kh=stride_kh,
|
| 331 |
+
stride_kn=stride_kn,
|
| 332 |
+
stride_vb=stride_vb,
|
| 333 |
+
stride_vh=stride_vh,
|
| 334 |
+
stride_vn=stride_vn,
|
| 335 |
+
stride_ab=stride_ab,
|
| 336 |
+
stride_ah=stride_ah,
|
| 337 |
+
stride_db=stride_db,
|
| 338 |
+
stride_dh=stride_dh,
|
| 339 |
+
stride_dob=stride_dob,
|
| 340 |
+
stride_doh=stride_doh,
|
| 341 |
+
stride_dom=stride_dom,
|
| 342 |
+
stride_lb=stride_lb,
|
| 343 |
+
stride_lh=stride_lh,
|
| 344 |
+
stride_pb=stride_pb,
|
| 345 |
+
stride_ph=stride_ph,
|
| 346 |
+
stride_dqab=stride_dqab,
|
| 347 |
+
stride_dqah=stride_dqah,
|
| 348 |
+
stride_dqam=stride_dqam,
|
| 349 |
+
stride_dkb=stride_dkb,
|
| 350 |
+
stride_dkh=stride_dkh,
|
| 351 |
+
stride_dkn=stride_dkn,
|
| 352 |
+
stride_dks=stride_dks,
|
| 353 |
+
stride_dvb=stride_dvb,
|
| 354 |
+
stride_dvh=stride_dvh,
|
| 355 |
+
stride_dvn=stride_dvn,
|
| 356 |
+
stride_dvs=stride_dvs,
|
| 357 |
+
stride_dab=stride_dab,
|
| 358 |
+
stride_dah=stride_dah,
|
| 359 |
+
stride_ddb=stride_ddb,
|
| 360 |
+
stride_ddh=stride_ddh,
|
| 361 |
+
stride_dds=stride_dds,
|
| 362 |
+
IS_SPLIT_QO=IS_SPLIT_QO,
|
| 363 |
+
IS_GATED=True,
|
| 364 |
+
HAS_CU_SEQLENS_Q=HAS_CU_SEQLENS_Q,
|
| 365 |
+
HAS_CU_SEQLENS_K=HAS_CU_SEQLENS_K,
|
| 366 |
+
)
|
| 367 |
+
|
| 368 |
+
# Create block scheduler
|
| 369 |
+
block_sched = AttnBwdBlockScheduler.create(
|
| 370 |
+
config=config,
|
| 371 |
+
split_idx=grid_idx.split_idx,
|
| 372 |
+
num_splits=num_splits,
|
| 373 |
+
IS_CAUSAL=IS_CAUSAL,
|
| 374 |
+
IS_LOCAL=IS_LOCAL,
|
| 375 |
+
IS_SPLIT_QO=IS_SPLIT_QO,
|
| 376 |
+
)
|
| 377 |
+
|
| 378 |
+
# Create mask scheduler
|
| 379 |
+
mask_sched = AttnMaskScheduler.create(config, SWAP_AB=True)
|
| 380 |
+
|
| 381 |
+
# Create softmax scheduler
|
| 382 |
+
softmax_sched = SoftmaxScheduler.create(config)
|
| 383 |
+
|
| 384 |
+
# Early exit if no n_blocks to process
|
| 385 |
+
if grid_idx.n_block * TILE_N >= config.actual_seqlen_k:
|
| 386 |
+
return
|
| 387 |
+
|
| 388 |
+
# Create pointers
|
| 389 |
+
q_ptrs = ptrs_sched.make_q_ptrs(config)
|
| 390 |
+
k_ptrs = ptrs_sched.make_k_ptrs(config)
|
| 391 |
+
v_ptrs = ptrs_sched.make_v_ptrs(config)
|
| 392 |
+
a_ptrs = ptrs_sched.make_a_ptrs(config)
|
| 393 |
+
d_ptrs = ptrs_sched.make_d_ptrs(config)
|
| 394 |
+
do_ptrs = ptrs_sched.make_do_ptrs(config)
|
| 395 |
+
lse_ptrs = ptrs_sched.make_lse_ptrs(config)
|
| 396 |
+
dpsum_ptrs = ptrs_sched.make_dpsum_ptrs(config)
|
| 397 |
+
dq_accum_ptrs = ptrs_sched.make_dq_accum_ptrs(config)
|
| 398 |
+
dk_ptrs = ptrs_sched.make_dk_ptrs(config)
|
| 399 |
+
dv_ptrs = ptrs_sched.make_dv_ptrs(config)
|
| 400 |
+
da_ptrs = ptrs_sched.make_da_ptrs(config)
|
| 401 |
+
dd_ptrs = ptrs_sched.make_dd_ptrs(config)
|
| 402 |
+
|
| 403 |
+
# Initialize accumulators
|
| 404 |
+
gate_max = tl.full((), float("-inf"), dtype=tl.float32)
|
| 405 |
+
acc_dk = tl.zeros((TILE_N, TILE_K), dtype=tl.float32)
|
| 406 |
+
acc_dv = tl.zeros((TILE_N, TILE_K), dtype=tl.float32)
|
| 407 |
+
acc_dd = tl.zeros((TILE_N,), dtype=tl.float32)
|
| 408 |
+
|
| 409 |
+
# Load key tile
|
| 410 |
+
k_tile = ptrs_sched.load_k(config, k_ptrs)
|
| 411 |
+
|
| 412 |
+
# Load value tile
|
| 413 |
+
v_tile = ptrs_sched.load_v(config, v_ptrs)
|
| 414 |
+
|
| 415 |
+
# Load delta tile
|
| 416 |
+
d_tile = ptrs_sched.load_d(config, d_ptrs)
|
| 417 |
+
d_max = tl.max(d_tile)
|
| 418 |
+
d_min = tl.min(d_tile)
|
| 419 |
+
|
| 420 |
+
# Process m_blocks with causal masking
|
| 421 |
+
if IS_CAUSAL or IS_LOCAL:
|
| 422 |
+
for m_block in tl.range(
|
| 423 |
+
block_sched.m_block_min, block_sched.m_block_min_no_mask
|
| 424 |
+
):
|
| 425 |
+
(
|
| 426 |
+
acc_dk,
|
| 427 |
+
acc_dv,
|
| 428 |
+
acc_dd,
|
| 429 |
+
gate_max,
|
| 430 |
+
) = _bwd_inner_gated_kernel(
|
| 431 |
+
config=config,
|
| 432 |
+
ptrs_sched=ptrs_sched,
|
| 433 |
+
mask_sched=mask_sched,
|
| 434 |
+
softmax_sched=softmax_sched,
|
| 435 |
+
acc_dk=acc_dk,
|
| 436 |
+
acc_dv=acc_dv,
|
| 437 |
+
acc_dd=acc_dd,
|
| 438 |
+
k_tile=k_tile,
|
| 439 |
+
v_tile=v_tile,
|
| 440 |
+
d_tile=d_tile,
|
| 441 |
+
q_ptrs=q_ptrs,
|
| 442 |
+
a_ptrs=a_ptrs,
|
| 443 |
+
do_ptrs=do_ptrs,
|
| 444 |
+
dq_accum_ptrs=dq_accum_ptrs,
|
| 445 |
+
da_ptrs=da_ptrs,
|
| 446 |
+
lse_ptrs=lse_ptrs,
|
| 447 |
+
dpsum_ptrs=dpsum_ptrs,
|
| 448 |
+
d_max=d_max,
|
| 449 |
+
d_min=d_min,
|
| 450 |
+
gate_max=gate_max,
|
| 451 |
+
m_block=m_block,
|
| 452 |
+
IS_MASK=True,
|
| 453 |
+
MASK_CAUSAL=IS_CAUSAL,
|
| 454 |
+
MASK_LOCAL=True if IS_LOCAL else False,
|
| 455 |
+
MASK_SINK=False,
|
| 456 |
+
)
|
| 457 |
+
|
| 458 |
+
# Process m_blocks without masking
|
| 459 |
+
if not IS_LOCAL and block_sched.m_block_min_no_mask < block_sched.m_block_max:
|
| 460 |
+
for m_block in tl.range(
|
| 461 |
+
block_sched.m_block_min_no_mask, block_sched.m_block_max
|
| 462 |
+
):
|
| 463 |
+
(
|
| 464 |
+
acc_dk,
|
| 465 |
+
acc_dv,
|
| 466 |
+
acc_dd,
|
| 467 |
+
gate_max,
|
| 468 |
+
) = _bwd_inner_gated_kernel(
|
| 469 |
+
config=config,
|
| 470 |
+
ptrs_sched=ptrs_sched,
|
| 471 |
+
mask_sched=mask_sched,
|
| 472 |
+
softmax_sched=softmax_sched,
|
| 473 |
+
acc_dk=acc_dk,
|
| 474 |
+
acc_dv=acc_dv,
|
| 475 |
+
acc_dd=acc_dd,
|
| 476 |
+
k_tile=k_tile,
|
| 477 |
+
v_tile=v_tile,
|
| 478 |
+
d_tile=d_tile,
|
| 479 |
+
q_ptrs=q_ptrs,
|
| 480 |
+
a_ptrs=a_ptrs,
|
| 481 |
+
do_ptrs=do_ptrs,
|
| 482 |
+
dq_accum_ptrs=dq_accum_ptrs,
|
| 483 |
+
da_ptrs=da_ptrs,
|
| 484 |
+
lse_ptrs=lse_ptrs,
|
| 485 |
+
dpsum_ptrs=dpsum_ptrs,
|
| 486 |
+
d_max=d_max,
|
| 487 |
+
d_min=d_min,
|
| 488 |
+
gate_max=gate_max,
|
| 489 |
+
m_block=m_block,
|
| 490 |
+
IS_MASK=False,
|
| 491 |
+
MASK_CAUSAL=False,
|
| 492 |
+
MASK_LOCAL=False,
|
| 493 |
+
MASK_SINK=False,
|
| 494 |
+
)
|
| 495 |
+
|
| 496 |
+
if IS_LOCAL:
|
| 497 |
+
# Process m_blocks with local right masking
|
| 498 |
+
if block_sched.m_block_window_min < block_sched.m_block_window_min_no_mask:
|
| 499 |
+
for m_block in tl.range(
|
| 500 |
+
block_sched.m_block_window_min,
|
| 501 |
+
block_sched.m_block_window_min_no_mask,
|
| 502 |
+
):
|
| 503 |
+
(
|
| 504 |
+
acc_dk,
|
| 505 |
+
acc_dv,
|
| 506 |
+
acc_dd,
|
| 507 |
+
gate_max,
|
| 508 |
+
) = _bwd_inner_gated_kernel(
|
| 509 |
+
config=config,
|
| 510 |
+
ptrs_sched=ptrs_sched,
|
| 511 |
+
mask_sched=mask_sched,
|
| 512 |
+
softmax_sched=softmax_sched,
|
| 513 |
+
acc_dk=acc_dk,
|
| 514 |
+
acc_dv=acc_dv,
|
| 515 |
+
acc_dd=acc_dd,
|
| 516 |
+
k_tile=k_tile,
|
| 517 |
+
v_tile=v_tile,
|
| 518 |
+
d_tile=d_tile,
|
| 519 |
+
q_ptrs=q_ptrs,
|
| 520 |
+
a_ptrs=a_ptrs,
|
| 521 |
+
do_ptrs=do_ptrs,
|
| 522 |
+
dq_accum_ptrs=dq_accum_ptrs,
|
| 523 |
+
da_ptrs=da_ptrs,
|
| 524 |
+
lse_ptrs=lse_ptrs,
|
| 525 |
+
dpsum_ptrs=dpsum_ptrs,
|
| 526 |
+
d_max=d_max,
|
| 527 |
+
d_min=d_min,
|
| 528 |
+
gate_max=gate_max,
|
| 529 |
+
m_block=m_block,
|
| 530 |
+
IS_MASK=True,
|
| 531 |
+
MASK_CAUSAL=False,
|
| 532 |
+
MASK_LOCAL=True,
|
| 533 |
+
MASK_SINK=False,
|
| 534 |
+
)
|
| 535 |
+
|
| 536 |
+
# Process m_blocks without masking
|
| 537 |
+
if (
|
| 538 |
+
block_sched.m_block_window_min_no_mask
|
| 539 |
+
< block_sched.m_block_window_max_no_mask
|
| 540 |
+
):
|
| 541 |
+
for m_block in tl.range(
|
| 542 |
+
block_sched.m_block_window_min_no_mask,
|
| 543 |
+
block_sched.m_block_window_max_no_mask,
|
| 544 |
+
):
|
| 545 |
+
(
|
| 546 |
+
acc_dk,
|
| 547 |
+
acc_dv,
|
| 548 |
+
acc_dd,
|
| 549 |
+
gate_max,
|
| 550 |
+
) = _bwd_inner_gated_kernel(
|
| 551 |
+
config=config,
|
| 552 |
+
ptrs_sched=ptrs_sched,
|
| 553 |
+
mask_sched=mask_sched,
|
| 554 |
+
softmax_sched=softmax_sched,
|
| 555 |
+
acc_dk=acc_dk,
|
| 556 |
+
acc_dv=acc_dv,
|
| 557 |
+
acc_dd=acc_dd,
|
| 558 |
+
k_tile=k_tile,
|
| 559 |
+
v_tile=v_tile,
|
| 560 |
+
d_tile=d_tile,
|
| 561 |
+
q_ptrs=q_ptrs,
|
| 562 |
+
a_ptrs=a_ptrs,
|
| 563 |
+
do_ptrs=do_ptrs,
|
| 564 |
+
dq_accum_ptrs=dq_accum_ptrs,
|
| 565 |
+
da_ptrs=da_ptrs,
|
| 566 |
+
lse_ptrs=lse_ptrs,
|
| 567 |
+
dpsum_ptrs=dpsum_ptrs,
|
| 568 |
+
d_max=d_max,
|
| 569 |
+
d_min=d_min,
|
| 570 |
+
gate_max=gate_max,
|
| 571 |
+
m_block=m_block,
|
| 572 |
+
IS_MASK=False,
|
| 573 |
+
MASK_CAUSAL=False,
|
| 574 |
+
MASK_LOCAL=False,
|
| 575 |
+
MASK_SINK=False,
|
| 576 |
+
)
|
| 577 |
+
|
| 578 |
+
# Process m_blocks with local left masking
|
| 579 |
+
if block_sched.m_block_window_max_no_mask < block_sched.m_block_window_max:
|
| 580 |
+
for m_block in tl.range(
|
| 581 |
+
block_sched.m_block_window_max_no_mask,
|
| 582 |
+
block_sched.m_block_window_max,
|
| 583 |
+
):
|
| 584 |
+
(
|
| 585 |
+
acc_dk,
|
| 586 |
+
acc_dv,
|
| 587 |
+
acc_dd,
|
| 588 |
+
gate_max,
|
| 589 |
+
) = _bwd_inner_gated_kernel(
|
| 590 |
+
config=config,
|
| 591 |
+
ptrs_sched=ptrs_sched,
|
| 592 |
+
mask_sched=mask_sched,
|
| 593 |
+
softmax_sched=softmax_sched,
|
| 594 |
+
acc_dk=acc_dk,
|
| 595 |
+
acc_dv=acc_dv,
|
| 596 |
+
acc_dd=acc_dd,
|
| 597 |
+
k_tile=k_tile,
|
| 598 |
+
v_tile=v_tile,
|
| 599 |
+
d_tile=d_tile,
|
| 600 |
+
q_ptrs=q_ptrs,
|
| 601 |
+
a_ptrs=a_ptrs,
|
| 602 |
+
do_ptrs=do_ptrs,
|
| 603 |
+
dq_accum_ptrs=dq_accum_ptrs,
|
| 604 |
+
da_ptrs=da_ptrs,
|
| 605 |
+
lse_ptrs=lse_ptrs,
|
| 606 |
+
dpsum_ptrs=dpsum_ptrs,
|
| 607 |
+
d_max=d_max,
|
| 608 |
+
d_min=d_min,
|
| 609 |
+
gate_max=gate_max,
|
| 610 |
+
m_block=m_block,
|
| 611 |
+
IS_MASK=True,
|
| 612 |
+
MASK_CAUSAL=False,
|
| 613 |
+
MASK_LOCAL=True,
|
| 614 |
+
MASK_SINK=False,
|
| 615 |
+
)
|
| 616 |
+
|
| 617 |
+
# Process m_blocks with local sink masking
|
| 618 |
+
if block_sched.m_block_sink_max > block_sched.m_block_sink_min:
|
| 619 |
+
for m_block in tl.range(
|
| 620 |
+
block_sched.m_block_sink_min,
|
| 621 |
+
block_sched.m_block_sink_max,
|
| 622 |
+
):
|
| 623 |
+
(
|
| 624 |
+
acc_dk,
|
| 625 |
+
acc_dv,
|
| 626 |
+
acc_dd,
|
| 627 |
+
gate_max,
|
| 628 |
+
) = _bwd_inner_gated_kernel(
|
| 629 |
+
config=config,
|
| 630 |
+
ptrs_sched=ptrs_sched,
|
| 631 |
+
mask_sched=mask_sched,
|
| 632 |
+
softmax_sched=softmax_sched,
|
| 633 |
+
acc_dk=acc_dk,
|
| 634 |
+
acc_dv=acc_dv,
|
| 635 |
+
acc_dd=acc_dd,
|
| 636 |
+
k_tile=k_tile,
|
| 637 |
+
v_tile=v_tile,
|
| 638 |
+
d_tile=d_tile,
|
| 639 |
+
q_ptrs=q_ptrs,
|
| 640 |
+
a_ptrs=a_ptrs,
|
| 641 |
+
do_ptrs=do_ptrs,
|
| 642 |
+
dq_accum_ptrs=dq_accum_ptrs,
|
| 643 |
+
da_ptrs=da_ptrs,
|
| 644 |
+
lse_ptrs=lse_ptrs,
|
| 645 |
+
dpsum_ptrs=dpsum_ptrs,
|
| 646 |
+
d_max=d_max,
|
| 647 |
+
d_min=d_min,
|
| 648 |
+
gate_max=gate_max,
|
| 649 |
+
m_block=m_block,
|
| 650 |
+
IS_MASK=True,
|
| 651 |
+
MASK_CAUSAL=False,
|
| 652 |
+
MASK_LOCAL=True,
|
| 653 |
+
MASK_SINK=True,
|
| 654 |
+
)
|
| 655 |
+
|
| 656 |
+
# Store value gradients
|
| 657 |
+
ptrs_sched.store_dv(config, dv_ptrs, acc_dv)
|
| 658 |
+
|
| 659 |
+
# Scale delta gradients
|
| 660 |
+
acc_dd = acc_dd * softmax_scale
|
| 661 |
+
|
| 662 |
+
# Store delta gradients
|
| 663 |
+
ptrs_sched.store_dd(config, dd_ptrs, acc_dd)
|
| 664 |
+
|
| 665 |
+
# Scale key gradients
|
| 666 |
+
acc_dk = acc_dk * softmax_scale
|
| 667 |
+
|
| 668 |
+
# Store key gradients
|
| 669 |
+
ptrs_sched.store_dk(config, dk_ptrs, acc_dk)
|
| 670 |
+
|
| 671 |
+
|
| 672 |
+
_bwd_gated_kernel = cache_utils.wrap_kernel(_bwd_gated_kernel)
|
| 673 |
+
|
| 674 |
+
|
| 675 |
+
_bwd_gated_kernel_autotuned = None
|
| 676 |
+
|
| 677 |
+
|
| 678 |
+
def _get_autotuned_kernel():
|
| 679 |
+
global _bwd_gated_kernel_autotuned
|
| 680 |
+
if _bwd_gated_kernel_autotuned is None:
|
| 681 |
+
jit_kernel = _bwd_gated_kernel._kernel
|
| 682 |
+
autotuned = autotuner.make_bwd_gated_autotuned_kernel(jit_kernel)
|
| 683 |
+
_bwd_gated_kernel_autotuned = autotuner.AutotunedKernel(autotuned)
|
| 684 |
+
return _bwd_gated_kernel_autotuned
|
| 685 |
+
|
| 686 |
+
|
| 687 |
+
def _flash_gated_attn_backward(
|
| 688 |
+
query: torch.Tensor,
|
| 689 |
+
key: torch.Tensor,
|
| 690 |
+
value: torch.Tensor,
|
| 691 |
+
alpha: torch.Tensor,
|
| 692 |
+
delta: torch.Tensor,
|
| 693 |
+
out: torch.Tensor,
|
| 694 |
+
dout: torch.Tensor,
|
| 695 |
+
lse: torch.Tensor,
|
| 696 |
+
is_causal: bool = False,
|
| 697 |
+
softmax_scale: float = None,
|
| 698 |
+
query_scale: Optional[torch.Tensor] = None,
|
| 699 |
+
key_scale: Optional[torch.Tensor] = None,
|
| 700 |
+
value_scale: Optional[torch.Tensor] = None,
|
| 701 |
+
window_sizes: Optional[torch.Tensor] = None,
|
| 702 |
+
softmax_threshold: float = None,
|
| 703 |
+
gate_threshold: float = None,
|
| 704 |
+
is_logsigmoid_gate: bool = True,
|
| 705 |
+
is_adapt_gate: bool = True,
|
| 706 |
+
is_local: bool = False,
|
| 707 |
+
is_quant: bool = False,
|
| 708 |
+
is_split_qo: bool = False,
|
| 709 |
+
is_autotune: bool = False,
|
| 710 |
+
skip_checks: bool = False,
|
| 711 |
+
) -> Tuple[torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor]:
|
| 712 |
+
device = query.device
|
| 713 |
+
num_SMs = cache_utils.get_device_num_sms(device)
|
| 714 |
+
batch_size, seqlen_q, num_heads_q, head_dim = query.shape
|
| 715 |
+
_, seqlen_k, num_heads_kv, _ = key.shape
|
| 716 |
+
softmax_scale = (
|
| 717 |
+
softmax_scale if softmax_scale is not None else 1.0 / (head_dim**0.5)
|
| 718 |
+
)
|
| 719 |
+
softmax_threshold = (
|
| 720 |
+
softmax_threshold if softmax_threshold is not None else 1 / seqlen_k
|
| 721 |
+
)
|
| 722 |
+
gate_threshold = (
|
| 723 |
+
gate_threshold if gate_threshold is not None else head_dim / seqlen_k
|
| 724 |
+
)
|
| 725 |
+
qhead_per_kvhead = num_heads_q // num_heads_kv
|
| 726 |
+
if is_local and window_sizes is None:
|
| 727 |
+
window_sizes = utils.window_sizes_heuristic(seqlen_k, num_heads_kv, device)
|
| 728 |
+
elif not is_local:
|
| 729 |
+
window_sizes = torch.zeros((num_heads_kv, 4), dtype=torch.int32, device=device)
|
| 730 |
+
|
| 731 |
+
if not skip_checks:
|
| 732 |
+
assert_inputs.assert_bwd_inputs(
|
| 733 |
+
query,
|
| 734 |
+
key,
|
| 735 |
+
value,
|
| 736 |
+
out,
|
| 737 |
+
dout,
|
| 738 |
+
lse,
|
| 739 |
+
alpha=alpha,
|
| 740 |
+
delta=delta,
|
| 741 |
+
query_scale=query_scale,
|
| 742 |
+
key_scale=key_scale,
|
| 743 |
+
value_scale=value_scale,
|
| 744 |
+
window_sizes=window_sizes,
|
| 745 |
+
cu_seqlens_q=None,
|
| 746 |
+
cu_seqlens_k=None,
|
| 747 |
+
seqused_q=None,
|
| 748 |
+
seqused_k=None,
|
| 749 |
+
num_heads_q=num_heads_q,
|
| 750 |
+
num_heads_kv=num_heads_kv,
|
| 751 |
+
head_dim=head_dim,
|
| 752 |
+
is_quant=is_quant,
|
| 753 |
+
device=device,
|
| 754 |
+
)
|
| 755 |
+
|
| 756 |
+
TILE_K = max(triton.next_power_of_2(head_dim), 16)
|
| 757 |
+
|
| 758 |
+
launch_config = launch_template.load_launch_config(
|
| 759 |
+
device=device,
|
| 760 |
+
kernel_name="bwd_gated",
|
| 761 |
+
seqlen_q=seqlen_q,
|
| 762 |
+
seqlen_k=seqlen_k,
|
| 763 |
+
tile_k=TILE_K,
|
| 764 |
+
is_local=is_local,
|
| 765 |
+
qhead_per_kvhead=qhead_per_kvhead,
|
| 766 |
+
is_causal=is_causal,
|
| 767 |
+
is_quant=is_quant,
|
| 768 |
+
)
|
| 769 |
+
if launch_config is not None and not is_autotune:
|
| 770 |
+
kernel = _bwd_gated_kernel
|
| 771 |
+
TILE_M, TILE_N, num_warps, num_stages, num_ctas = launch_config
|
| 772 |
+
else:
|
| 773 |
+
kernel = _get_autotuned_kernel()
|
| 774 |
+
# Placeholder for pre-launch computations
|
| 775 |
+
TILE_M = TILE_N = 64
|
| 776 |
+
num_warps = num_stages = num_ctas = None
|
| 777 |
+
|
| 778 |
+
num_splits = (
|
| 779 |
+
utils.num_splits_heuristic(
|
| 780 |
+
seqlen_q=seqlen_k,
|
| 781 |
+
seqlen_k=seqlen_q,
|
| 782 |
+
num_SMs=num_SMs,
|
| 783 |
+
TILE_M=TILE_N,
|
| 784 |
+
TILE_N=TILE_M,
|
| 785 |
+
max_split_blocks=utils.max_split_blocks_from_window_sizes(
|
| 786 |
+
window_sizes, TILE_M
|
| 787 |
+
)
|
| 788 |
+
if is_local
|
| 789 |
+
else None,
|
| 790 |
+
)
|
| 791 |
+
if is_split_qo
|
| 792 |
+
else 1
|
| 793 |
+
)
|
| 794 |
+
|
| 795 |
+
seqlen_q_rounded = int(math.ceil(seqlen_q / 128) * 128)
|
| 796 |
+
head_dim_rounded = int(math.ceil(head_dim / 32) * 32)
|
| 797 |
+
|
| 798 |
+
if not is_quant:
|
| 799 |
+
query_scale = torch.ones(1, device=device, dtype=query.dtype)
|
| 800 |
+
key_scale = torch.ones(1, device=device, dtype=query.dtype)
|
| 801 |
+
value_scale = torch.ones(1, device=device, dtype=query.dtype)
|
| 802 |
+
|
| 803 |
+
dq = torch.empty_like(query, dtype=query_scale.dtype)
|
| 804 |
+
dk = torch.empty_like(key, dtype=key_scale.dtype)
|
| 805 |
+
dv = torch.empty_like(value, dtype=value_scale.dtype)
|
| 806 |
+
da = torch.empty_like(alpha)
|
| 807 |
+
dd = torch.empty_like(delta)
|
| 808 |
+
lse_log2 = torch.empty(
|
| 809 |
+
(batch_size, num_heads_q, seqlen_q_rounded),
|
| 810 |
+
dtype=torch.float32,
|
| 811 |
+
device=query.device,
|
| 812 |
+
)
|
| 813 |
+
dpsum = torch.empty(
|
| 814 |
+
(batch_size, num_heads_q, seqlen_q_rounded),
|
| 815 |
+
dtype=torch.float32,
|
| 816 |
+
device=query.device,
|
| 817 |
+
)
|
| 818 |
+
dq_accum = torch.empty(
|
| 819 |
+
(batch_size, num_heads_q, seqlen_q_rounded * head_dim_rounded),
|
| 820 |
+
dtype=torch.float32,
|
| 821 |
+
device=query.device,
|
| 822 |
+
)
|
| 823 |
+
dk_accum = torch.zeros(
|
| 824 |
+
(num_splits, batch_size, seqlen_k, num_heads_kv, head_dim)
|
| 825 |
+
if is_split_qo and num_splits > 1
|
| 826 |
+
else (batch_size, seqlen_k, num_heads_kv, head_dim),
|
| 827 |
+
dtype=torch.float32,
|
| 828 |
+
device=query.device,
|
| 829 |
+
)
|
| 830 |
+
dv_accum = torch.zeros(
|
| 831 |
+
(num_splits, batch_size, seqlen_k, num_heads_kv, head_dim)
|
| 832 |
+
if is_split_qo and num_splits > 1
|
| 833 |
+
else (batch_size, seqlen_k, num_heads_kv, head_dim),
|
| 834 |
+
dtype=torch.float32,
|
| 835 |
+
device=query.device,
|
| 836 |
+
)
|
| 837 |
+
da_accum = torch.zeros(
|
| 838 |
+
(batch_size, num_heads_q, seqlen_q_rounded),
|
| 839 |
+
dtype=torch.float32,
|
| 840 |
+
device=query.device,
|
| 841 |
+
)
|
| 842 |
+
dd_accum = torch.zeros(
|
| 843 |
+
(num_splits, batch_size, num_heads_kv, seqlen_k)
|
| 844 |
+
if is_split_qo and num_splits > 1
|
| 845 |
+
else (batch_size, num_heads_kv, seqlen_k),
|
| 846 |
+
dtype=torch.float32,
|
| 847 |
+
device=query.device,
|
| 848 |
+
)
|
| 849 |
+
|
| 850 |
+
flash_bwd_preprocess._flash_attn_bwd_preprocess(
|
| 851 |
+
out=out,
|
| 852 |
+
dout=dout,
|
| 853 |
+
dpsum=dpsum,
|
| 854 |
+
lse=lse,
|
| 855 |
+
lse_log2=lse_log2,
|
| 856 |
+
dq_accum=dq_accum,
|
| 857 |
+
head_dim_rounded=head_dim_rounded,
|
| 858 |
+
tile_m=TILE_M,
|
| 859 |
+
tile_k=TILE_K,
|
| 860 |
+
)
|
| 861 |
+
|
| 862 |
+
grid = launch_grid.get_bwd_grid(
|
| 863 |
+
seqlen_k=seqlen_k,
|
| 864 |
+
num_heads_q=num_heads_q,
|
| 865 |
+
batch_size=batch_size,
|
| 866 |
+
num_splits=num_splits,
|
| 867 |
+
)
|
| 868 |
+
|
| 869 |
+
triton.set_allocator(utils.alloc_fn)
|
| 870 |
+
|
| 871 |
+
kernel[grid](
|
| 872 |
+
query,
|
| 873 |
+
key,
|
| 874 |
+
value,
|
| 875 |
+
alpha,
|
| 876 |
+
delta,
|
| 877 |
+
dout,
|
| 878 |
+
lse_log2,
|
| 879 |
+
dpsum,
|
| 880 |
+
dq_accum,
|
| 881 |
+
dk_accum,
|
| 882 |
+
dv_accum,
|
| 883 |
+
da_accum,
|
| 884 |
+
dd_accum,
|
| 885 |
+
softmax_scale,
|
| 886 |
+
query_scale,
|
| 887 |
+
key_scale,
|
| 888 |
+
value_scale,
|
| 889 |
+
window_sizes,
|
| 890 |
+
softmax_threshold,
|
| 891 |
+
gate_threshold,
|
| 892 |
+
query.stride(0),
|
| 893 |
+
query.stride(-2),
|
| 894 |
+
query.stride(-3),
|
| 895 |
+
key.stride(0),
|
| 896 |
+
key.stride(-2),
|
| 897 |
+
key.stride(-3),
|
| 898 |
+
value.stride(0),
|
| 899 |
+
value.stride(-2),
|
| 900 |
+
value.stride(-3),
|
| 901 |
+
alpha.stride(0),
|
| 902 |
+
alpha.stride(-2),
|
| 903 |
+
delta.stride(0),
|
| 904 |
+
delta.stride(-2),
|
| 905 |
+
dout.stride(0),
|
| 906 |
+
dout.stride(-2),
|
| 907 |
+
dout.stride(-3),
|
| 908 |
+
lse_log2.stride(0),
|
| 909 |
+
lse_log2.stride(-2),
|
| 910 |
+
dpsum.stride(0),
|
| 911 |
+
dpsum.stride(-2),
|
| 912 |
+
dq_accum.stride(0),
|
| 913 |
+
dq_accum.stride(1),
|
| 914 |
+
head_dim_rounded,
|
| 915 |
+
dk_accum.stride(-4) if is_split_qo and num_splits > 1 else dk_accum.stride(0),
|
| 916 |
+
dk_accum.stride(-2),
|
| 917 |
+
dk_accum.stride(-3),
|
| 918 |
+
dk_accum.stride(0) if is_split_qo and num_splits > 1 else 0,
|
| 919 |
+
dv_accum.stride(-4) if is_split_qo and num_splits > 1 else dv_accum.stride(0),
|
| 920 |
+
dv_accum.stride(-2),
|
| 921 |
+
dv_accum.stride(-3),
|
| 922 |
+
dv_accum.stride(0) if is_split_qo and num_splits > 1 else 0,
|
| 923 |
+
da_accum.stride(0),
|
| 924 |
+
da_accum.stride(-2),
|
| 925 |
+
dd_accum.stride(-3) if is_split_qo and num_splits > 1 else dd_accum.stride(0),
|
| 926 |
+
dd_accum.stride(-2),
|
| 927 |
+
dd_accum.stride(0) if is_split_qo and num_splits > 1 else 0,
|
| 928 |
+
window_sizes.stride(0),
|
| 929 |
+
None,
|
| 930 |
+
None,
|
| 931 |
+
None,
|
| 932 |
+
None,
|
| 933 |
+
num_splits,
|
| 934 |
+
seqlen_q=seqlen_q,
|
| 935 |
+
seqlen_k=seqlen_k,
|
| 936 |
+
head_dim=head_dim,
|
| 937 |
+
SEQLEN_Q_CACHE=seqlen_q // 1024,
|
| 938 |
+
SEQLEN_K_CACHE=seqlen_k // 1024,
|
| 939 |
+
QHEAD_PER_KVHEAD=qhead_per_kvhead,
|
| 940 |
+
TILE_M=TILE_M,
|
| 941 |
+
TILE_N=TILE_N,
|
| 942 |
+
TILE_K=TILE_K,
|
| 943 |
+
IS_CAUSAL=is_causal,
|
| 944 |
+
IS_LOCAL=is_local,
|
| 945 |
+
IS_SPLIT_QO=is_split_qo and num_splits > 1,
|
| 946 |
+
IS_LOGSIGMOID_GATE=is_logsigmoid_gate,
|
| 947 |
+
IS_ADAPT_GATE=is_adapt_gate,
|
| 948 |
+
HAS_CU_SEQLENS_Q=False,
|
| 949 |
+
HAS_CU_SEQLENS_K=False,
|
| 950 |
+
HAS_SEQUSED_Q=False,
|
| 951 |
+
HAS_SEQUSED_K=False,
|
| 952 |
+
num_warps=num_warps,
|
| 953 |
+
num_stages=num_stages,
|
| 954 |
+
num_ctas=num_ctas,
|
| 955 |
+
)
|
| 956 |
+
|
| 957 |
+
if launch_config is None or is_autotune:
|
| 958 |
+
best = launch_template.extract_best_config(_get_autotuned_kernel())
|
| 959 |
+
if best is not None:
|
| 960 |
+
launch_template.store_launch_config(
|
| 961 |
+
device=device,
|
| 962 |
+
kernel_name="bwd_gated",
|
| 963 |
+
seqlen_q=seqlen_q,
|
| 964 |
+
seqlen_k=seqlen_k,
|
| 965 |
+
tile_k=TILE_K,
|
| 966 |
+
config=best,
|
| 967 |
+
is_local=is_local,
|
| 968 |
+
qhead_per_kvhead=qhead_per_kvhead,
|
| 969 |
+
is_causal=is_causal,
|
| 970 |
+
is_quant=is_quant,
|
| 971 |
+
)
|
| 972 |
+
|
| 973 |
+
flash_bwd_postprocess._flash_attn_bwd_postprocess(
|
| 974 |
+
dq_accum=dq_accum,
|
| 975 |
+
dq=dq,
|
| 976 |
+
da_accum=da_accum,
|
| 977 |
+
da=da,
|
| 978 |
+
scale=softmax_scale,
|
| 979 |
+
head_dim_rounded=head_dim_rounded,
|
| 980 |
+
tile_m=TILE_M,
|
| 981 |
+
tile_k=TILE_K,
|
| 982 |
+
)
|
| 983 |
+
|
| 984 |
+
if is_split_qo and num_splits > 1:
|
| 985 |
+
dk_accum = dk_accum.sum(dim=0)
|
| 986 |
+
dv_accum = dv_accum.sum(dim=0)
|
| 987 |
+
dd_accum = dd_accum.sum(dim=0)
|
| 988 |
+
|
| 989 |
+
dk.copy_(dk_accum)
|
| 990 |
+
dv.copy_(dv_accum)
|
| 991 |
+
dd.copy_(dd_accum)
|
| 992 |
+
|
| 993 |
+
return dq, dk, dv, da, dd
|
| 994 |
+
|
| 995 |
+
|
| 996 |
+
def _flash_gated_attn_varlen_backward(
|
| 997 |
+
query: torch.Tensor,
|
| 998 |
+
key: torch.Tensor,
|
| 999 |
+
value: torch.Tensor,
|
| 1000 |
+
alpha: torch.Tensor,
|
| 1001 |
+
delta: torch.Tensor,
|
| 1002 |
+
out: torch.Tensor,
|
| 1003 |
+
dout: torch.Tensor,
|
| 1004 |
+
lse: torch.Tensor,
|
| 1005 |
+
cu_seqlens_q: torch.Tensor,
|
| 1006 |
+
cu_seqlens_k: torch.Tensor,
|
| 1007 |
+
max_seqlen_q: Optional[int] = None,
|
| 1008 |
+
max_seqlen_k: Optional[int] = None,
|
| 1009 |
+
is_causal: bool = False,
|
| 1010 |
+
softmax_scale: float = None,
|
| 1011 |
+
query_scale: Optional[torch.Tensor] = None,
|
| 1012 |
+
key_scale: Optional[torch.Tensor] = None,
|
| 1013 |
+
value_scale: Optional[torch.Tensor] = None,
|
| 1014 |
+
window_sizes: Optional[torch.Tensor] = None,
|
| 1015 |
+
softmax_threshold: float = None,
|
| 1016 |
+
gate_threshold: float = None,
|
| 1017 |
+
is_logsigmoid_gate: bool = True,
|
| 1018 |
+
is_adapt_gate: bool = True,
|
| 1019 |
+
is_local: bool = False,
|
| 1020 |
+
is_quant: bool = False,
|
| 1021 |
+
seqused_q: Optional[torch.Tensor] = None,
|
| 1022 |
+
seqused_k: Optional[torch.Tensor] = None,
|
| 1023 |
+
is_split_qo: bool = False,
|
| 1024 |
+
is_autotune: bool = False,
|
| 1025 |
+
skip_checks: bool = False,
|
| 1026 |
+
) -> Tuple[torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor]:
|
| 1027 |
+
device = query.device
|
| 1028 |
+
num_SMs = cache_utils.get_device_num_sms(device)
|
| 1029 |
+
total_q, num_heads_q, head_dim = query.shape
|
| 1030 |
+
total_k, num_heads_kv, _ = key.shape
|
| 1031 |
+
batch_size = cu_seqlens_q.shape[0] - 1
|
| 1032 |
+
seqlen_q = max_seqlen_q
|
| 1033 |
+
seqlen_k = max_seqlen_k
|
| 1034 |
+
softmax_scale = (
|
| 1035 |
+
softmax_scale if softmax_scale is not None else 1.0 / (head_dim**0.5)
|
| 1036 |
+
)
|
| 1037 |
+
softmax_threshold = (
|
| 1038 |
+
softmax_threshold if softmax_threshold is not None else 1 / seqlen_k
|
| 1039 |
+
)
|
| 1040 |
+
gate_threshold = (
|
| 1041 |
+
gate_threshold if gate_threshold is not None else head_dim / seqlen_k
|
| 1042 |
+
)
|
| 1043 |
+
qhead_per_kvhead = num_heads_q // num_heads_kv
|
| 1044 |
+
if is_local and window_sizes is None:
|
| 1045 |
+
window_sizes = utils.window_sizes_heuristic(seqlen_k, num_heads_kv, device)
|
| 1046 |
+
elif not is_local:
|
| 1047 |
+
window_sizes = torch.zeros((num_heads_kv, 4), dtype=torch.int32, device=device)
|
| 1048 |
+
|
| 1049 |
+
if not skip_checks:
|
| 1050 |
+
assert_inputs.assert_bwd_inputs(
|
| 1051 |
+
query,
|
| 1052 |
+
key,
|
| 1053 |
+
value,
|
| 1054 |
+
out,
|
| 1055 |
+
dout,
|
| 1056 |
+
lse,
|
| 1057 |
+
alpha=alpha,
|
| 1058 |
+
delta=delta,
|
| 1059 |
+
query_scale=query_scale,
|
| 1060 |
+
key_scale=key_scale,
|
| 1061 |
+
value_scale=value_scale,
|
| 1062 |
+
window_sizes=window_sizes,
|
| 1063 |
+
cu_seqlens_q=cu_seqlens_q,
|
| 1064 |
+
cu_seqlens_k=cu_seqlens_k,
|
| 1065 |
+
seqused_q=seqused_q,
|
| 1066 |
+
seqused_k=seqused_k,
|
| 1067 |
+
num_heads_q=num_heads_q,
|
| 1068 |
+
num_heads_kv=num_heads_kv,
|
| 1069 |
+
head_dim=head_dim,
|
| 1070 |
+
is_quant=is_quant,
|
| 1071 |
+
device=device,
|
| 1072 |
+
)
|
| 1073 |
+
|
| 1074 |
+
TILE_K = max(triton.next_power_of_2(head_dim), 16)
|
| 1075 |
+
|
| 1076 |
+
launch_config = launch_template.load_launch_config(
|
| 1077 |
+
device=device,
|
| 1078 |
+
kernel_name="bwd_gated",
|
| 1079 |
+
seqlen_q=seqlen_q,
|
| 1080 |
+
seqlen_k=seqlen_k,
|
| 1081 |
+
tile_k=TILE_K,
|
| 1082 |
+
is_local=is_local,
|
| 1083 |
+
qhead_per_kvhead=qhead_per_kvhead,
|
| 1084 |
+
is_causal=is_causal,
|
| 1085 |
+
is_quant=is_quant,
|
| 1086 |
+
)
|
| 1087 |
+
if launch_config is not None and not is_autotune:
|
| 1088 |
+
kernel = _bwd_gated_kernel
|
| 1089 |
+
TILE_M, TILE_N, num_warps, num_stages, num_ctas = launch_config
|
| 1090 |
+
else:
|
| 1091 |
+
kernel = _get_autotuned_kernel()
|
| 1092 |
+
# Placeholder for pre-launch computations
|
| 1093 |
+
TILE_M = TILE_N = 64
|
| 1094 |
+
num_warps = num_stages = num_ctas = None
|
| 1095 |
+
|
| 1096 |
+
num_splits = (
|
| 1097 |
+
utils.num_splits_heuristic(
|
| 1098 |
+
seqlen_q=seqlen_k,
|
| 1099 |
+
seqlen_k=seqlen_q,
|
| 1100 |
+
num_SMs=num_SMs,
|
| 1101 |
+
TILE_M=TILE_N,
|
| 1102 |
+
TILE_N=TILE_M,
|
| 1103 |
+
max_split_blocks=utils.max_split_blocks_from_window_sizes(
|
| 1104 |
+
window_sizes, TILE_M
|
| 1105 |
+
)
|
| 1106 |
+
if is_local
|
| 1107 |
+
else None,
|
| 1108 |
+
)
|
| 1109 |
+
if is_split_qo
|
| 1110 |
+
else 1
|
| 1111 |
+
)
|
| 1112 |
+
|
| 1113 |
+
total_q_rounded_padded = int(math.ceil((total_q + batch_size * 128) / 128) * 128)
|
| 1114 |
+
head_dim_rounded = int(math.ceil(head_dim / 32) * 32)
|
| 1115 |
+
|
| 1116 |
+
if not is_quant:
|
| 1117 |
+
query_scale = torch.ones(1, device=device, dtype=query.dtype)
|
| 1118 |
+
key_scale = torch.ones(1, device=device, dtype=query.dtype)
|
| 1119 |
+
value_scale = torch.ones(1, device=device, dtype=query.dtype)
|
| 1120 |
+
|
| 1121 |
+
dq = torch.empty_like(query, dtype=query_scale.dtype)
|
| 1122 |
+
dk = torch.empty_like(key, dtype=key_scale.dtype)
|
| 1123 |
+
dv = torch.empty_like(value, dtype=value_scale.dtype)
|
| 1124 |
+
da = torch.empty_like(alpha)
|
| 1125 |
+
dd = torch.empty_like(delta)
|
| 1126 |
+
lse_log2 = torch.empty(
|
| 1127 |
+
(num_heads_q, total_q_rounded_padded),
|
| 1128 |
+
dtype=torch.float32,
|
| 1129 |
+
device=query.device,
|
| 1130 |
+
)
|
| 1131 |
+
dpsum = torch.empty(
|
| 1132 |
+
(num_heads_q, total_q_rounded_padded),
|
| 1133 |
+
dtype=torch.float32,
|
| 1134 |
+
device=query.device,
|
| 1135 |
+
)
|
| 1136 |
+
dq_accum = torch.empty(
|
| 1137 |
+
(num_heads_q, total_q_rounded_padded * head_dim_rounded),
|
| 1138 |
+
dtype=torch.float32,
|
| 1139 |
+
device=query.device,
|
| 1140 |
+
)
|
| 1141 |
+
dk_accum = torch.zeros(
|
| 1142 |
+
(num_splits, total_k, num_heads_kv, head_dim)
|
| 1143 |
+
if is_split_qo and num_splits > 1
|
| 1144 |
+
else (total_k, num_heads_kv, head_dim),
|
| 1145 |
+
dtype=torch.float32,
|
| 1146 |
+
device=query.device,
|
| 1147 |
+
)
|
| 1148 |
+
dv_accum = torch.zeros(
|
| 1149 |
+
(num_splits, total_k, num_heads_kv, head_dim)
|
| 1150 |
+
if is_split_qo and num_splits > 1
|
| 1151 |
+
else (total_k, num_heads_kv, head_dim),
|
| 1152 |
+
dtype=torch.float32,
|
| 1153 |
+
device=query.device,
|
| 1154 |
+
)
|
| 1155 |
+
da_accum = torch.zeros(
|
| 1156 |
+
(num_heads_q, total_q_rounded_padded),
|
| 1157 |
+
dtype=torch.float32,
|
| 1158 |
+
device=query.device,
|
| 1159 |
+
)
|
| 1160 |
+
dd_accum = torch.zeros(
|
| 1161 |
+
(num_splits, num_heads_kv, total_k)
|
| 1162 |
+
if is_split_qo and num_splits > 1
|
| 1163 |
+
else (num_heads_kv, total_k),
|
| 1164 |
+
dtype=torch.float32,
|
| 1165 |
+
device=query.device,
|
| 1166 |
+
)
|
| 1167 |
+
|
| 1168 |
+
flash_bwd_preprocess._flash_attn_bwd_preprocess(
|
| 1169 |
+
out=out,
|
| 1170 |
+
dout=dout,
|
| 1171 |
+
dpsum=dpsum,
|
| 1172 |
+
lse=lse,
|
| 1173 |
+
lse_log2=lse_log2,
|
| 1174 |
+
dq_accum=dq_accum,
|
| 1175 |
+
head_dim_rounded=head_dim_rounded,
|
| 1176 |
+
cu_seqlens_q=cu_seqlens_q,
|
| 1177 |
+
seqused_q=seqused_q,
|
| 1178 |
+
max_seqlen_q=max_seqlen_q,
|
| 1179 |
+
tile_m=TILE_M,
|
| 1180 |
+
tile_k=TILE_K,
|
| 1181 |
+
)
|
| 1182 |
+
|
| 1183 |
+
grid = launch_grid.get_bwd_grid(
|
| 1184 |
+
seqlen_k=seqlen_k,
|
| 1185 |
+
num_heads_q=num_heads_q,
|
| 1186 |
+
batch_size=batch_size,
|
| 1187 |
+
num_splits=num_splits,
|
| 1188 |
+
)
|
| 1189 |
+
|
| 1190 |
+
triton.set_allocator(utils.alloc_fn)
|
| 1191 |
+
|
| 1192 |
+
kernel[grid](
|
| 1193 |
+
query,
|
| 1194 |
+
key,
|
| 1195 |
+
value,
|
| 1196 |
+
alpha,
|
| 1197 |
+
delta,
|
| 1198 |
+
dout,
|
| 1199 |
+
lse_log2,
|
| 1200 |
+
dpsum,
|
| 1201 |
+
dq_accum,
|
| 1202 |
+
dk_accum,
|
| 1203 |
+
dv_accum,
|
| 1204 |
+
da_accum,
|
| 1205 |
+
dd_accum,
|
| 1206 |
+
softmax_scale,
|
| 1207 |
+
query_scale,
|
| 1208 |
+
key_scale,
|
| 1209 |
+
value_scale,
|
| 1210 |
+
window_sizes,
|
| 1211 |
+
softmax_threshold,
|
| 1212 |
+
gate_threshold,
|
| 1213 |
+
0,
|
| 1214 |
+
query.stride(-2),
|
| 1215 |
+
query.stride(0),
|
| 1216 |
+
0,
|
| 1217 |
+
key.stride(-2),
|
| 1218 |
+
key.stride(0),
|
| 1219 |
+
0,
|
| 1220 |
+
value.stride(-2),
|
| 1221 |
+
value.stride(0),
|
| 1222 |
+
0,
|
| 1223 |
+
alpha.stride(-2),
|
| 1224 |
+
0,
|
| 1225 |
+
delta.stride(-2),
|
| 1226 |
+
0,
|
| 1227 |
+
dout.stride(-2),
|
| 1228 |
+
dout.stride(0),
|
| 1229 |
+
0,
|
| 1230 |
+
lse_log2.stride(0),
|
| 1231 |
+
0,
|
| 1232 |
+
dpsum.stride(0),
|
| 1233 |
+
0,
|
| 1234 |
+
dq_accum.stride(0),
|
| 1235 |
+
head_dim_rounded,
|
| 1236 |
+
0,
|
| 1237 |
+
dk_accum.stride(-2),
|
| 1238 |
+
dk_accum.stride(-3),
|
| 1239 |
+
dk_accum.stride(0) if is_split_qo and num_splits > 1 else 0,
|
| 1240 |
+
0,
|
| 1241 |
+
dv_accum.stride(-2),
|
| 1242 |
+
dv_accum.stride(-3),
|
| 1243 |
+
dv_accum.stride(0) if is_split_qo and num_splits > 1 else 0,
|
| 1244 |
+
0,
|
| 1245 |
+
da_accum.stride(0),
|
| 1246 |
+
0,
|
| 1247 |
+
dd_accum.stride(-2),
|
| 1248 |
+
dd_accum.stride(0) if is_split_qo and num_splits > 1 else 0,
|
| 1249 |
+
window_sizes.stride(0),
|
| 1250 |
+
cu_seqlens_q,
|
| 1251 |
+
cu_seqlens_k,
|
| 1252 |
+
seqused_q,
|
| 1253 |
+
seqused_k,
|
| 1254 |
+
num_splits,
|
| 1255 |
+
seqlen_q=seqlen_q,
|
| 1256 |
+
seqlen_k=seqlen_k,
|
| 1257 |
+
head_dim=head_dim,
|
| 1258 |
+
SEQLEN_Q_CACHE=seqlen_q // 1024,
|
| 1259 |
+
SEQLEN_K_CACHE=seqlen_k // 1024,
|
| 1260 |
+
QHEAD_PER_KVHEAD=qhead_per_kvhead,
|
| 1261 |
+
TILE_M=TILE_M,
|
| 1262 |
+
TILE_N=TILE_N,
|
| 1263 |
+
TILE_K=TILE_K,
|
| 1264 |
+
IS_CAUSAL=is_causal,
|
| 1265 |
+
IS_LOCAL=is_local,
|
| 1266 |
+
IS_SPLIT_QO=is_split_qo and num_splits > 1,
|
| 1267 |
+
IS_LOGSIGMOID_GATE=is_logsigmoid_gate,
|
| 1268 |
+
IS_ADAPT_GATE=is_adapt_gate,
|
| 1269 |
+
HAS_CU_SEQLENS_Q=True,
|
| 1270 |
+
HAS_CU_SEQLENS_K=True,
|
| 1271 |
+
HAS_SEQUSED_Q=seqused_q is not None,
|
| 1272 |
+
HAS_SEQUSED_K=seqused_k is not None,
|
| 1273 |
+
num_warps=num_warps,
|
| 1274 |
+
num_stages=num_stages,
|
| 1275 |
+
num_ctas=num_ctas,
|
| 1276 |
+
)
|
| 1277 |
+
|
| 1278 |
+
if launch_config is None or is_autotune:
|
| 1279 |
+
best = launch_template.extract_best_config(_get_autotuned_kernel())
|
| 1280 |
+
if best is not None:
|
| 1281 |
+
launch_template.store_launch_config(
|
| 1282 |
+
device=device,
|
| 1283 |
+
kernel_name="bwd_gated",
|
| 1284 |
+
seqlen_q=seqlen_q,
|
| 1285 |
+
seqlen_k=seqlen_k,
|
| 1286 |
+
tile_k=TILE_K,
|
| 1287 |
+
config=best,
|
| 1288 |
+
is_local=is_local,
|
| 1289 |
+
qhead_per_kvhead=qhead_per_kvhead,
|
| 1290 |
+
is_causal=is_causal,
|
| 1291 |
+
is_quant=is_quant,
|
| 1292 |
+
)
|
| 1293 |
+
|
| 1294 |
+
flash_bwd_postprocess._flash_attn_bwd_postprocess(
|
| 1295 |
+
dq_accum=dq_accum,
|
| 1296 |
+
dq=dq,
|
| 1297 |
+
da_accum=da_accum,
|
| 1298 |
+
da=da,
|
| 1299 |
+
scale=softmax_scale,
|
| 1300 |
+
head_dim_rounded=head_dim_rounded,
|
| 1301 |
+
cu_seqlens_q=cu_seqlens_q,
|
| 1302 |
+
seqused_q=seqused_q,
|
| 1303 |
+
max_seqlen_q=max_seqlen_q,
|
| 1304 |
+
tile_m=TILE_M,
|
| 1305 |
+
tile_k=TILE_K,
|
| 1306 |
+
)
|
| 1307 |
+
|
| 1308 |
+
if is_split_qo and num_splits > 1:
|
| 1309 |
+
dk_accum = dk_accum.sum(dim=0)
|
| 1310 |
+
dv_accum = dv_accum.sum(dim=0)
|
| 1311 |
+
dd_accum = dd_accum.sum(dim=0)
|
| 1312 |
+
|
| 1313 |
+
dk.copy_(dk_accum)
|
| 1314 |
+
dv.copy_(dv_accum)
|
| 1315 |
+
dd.copy_(dd_accum)
|
| 1316 |
+
|
| 1317 |
+
return dq, dk, dv, da, dd
|
build/torch-cuda/flash_gated_dec.py
ADDED
|
@@ -0,0 +1,1218 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from typing import Tuple, Optional
|
| 2 |
+
|
| 3 |
+
import torch
|
| 4 |
+
import triton
|
| 5 |
+
import triton.language as tl
|
| 6 |
+
|
| 7 |
+
from . import (
|
| 8 |
+
assert_inputs,
|
| 9 |
+
utils,
|
| 10 |
+
cache_utils,
|
| 11 |
+
launch_template,
|
| 12 |
+
launch_grid,
|
| 13 |
+
flash_dec_combine,
|
| 14 |
+
kernel_repr,
|
| 15 |
+
autotuner,
|
| 16 |
+
)
|
| 17 |
+
from .scheduler import (
|
| 18 |
+
AttnDecGridIndex,
|
| 19 |
+
AttnDecConfig,
|
| 20 |
+
AttnDecBlockScheduler,
|
| 21 |
+
AttnDecPointerScheduler,
|
| 22 |
+
AttnMaskScheduler,
|
| 23 |
+
SoftmaxScheduler,
|
| 24 |
+
)
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
@triton.jit
|
| 28 |
+
def _dec_inner_gated_kernel(
|
| 29 |
+
config: AttnDecConfig,
|
| 30 |
+
ptrs_sched: AttnDecPointerScheduler,
|
| 31 |
+
mask_sched: AttnMaskScheduler,
|
| 32 |
+
softmax_sched: SoftmaxScheduler,
|
| 33 |
+
skip_gate_curr,
|
| 34 |
+
acc_s,
|
| 35 |
+
acc_o,
|
| 36 |
+
q_tile,
|
| 37 |
+
a_tile,
|
| 38 |
+
k_ptrs,
|
| 39 |
+
v_ptrs,
|
| 40 |
+
d_ptrs,
|
| 41 |
+
a_max,
|
| 42 |
+
a_min,
|
| 43 |
+
gate_max,
|
| 44 |
+
row_max,
|
| 45 |
+
row_sum,
|
| 46 |
+
n_block,
|
| 47 |
+
n_block_min,
|
| 48 |
+
IS_MASK: tl.constexpr,
|
| 49 |
+
MASK_LOCAL: tl.constexpr,
|
| 50 |
+
MASK_SINK: tl.constexpr,
|
| 51 |
+
CHECK_INF: tl.constexpr,
|
| 52 |
+
):
|
| 53 |
+
# Load next delta tile
|
| 54 |
+
d_tile = ptrs_sched.load_d(config, d_ptrs, n_block - 1)
|
| 55 |
+
d_max = tl.max(d_tile)
|
| 56 |
+
d_min = tl.min(d_tile)
|
| 57 |
+
|
| 58 |
+
skip_gate_next = True
|
| 59 |
+
if not skip_gate_curr:
|
| 60 |
+
if n_block > n_block_min:
|
| 61 |
+
# Check if any gates are active for next tile
|
| 62 |
+
gate_max, skip_gate_next = softmax_sched.online_gate(
|
| 63 |
+
a_max=a_max,
|
| 64 |
+
a_min=a_min,
|
| 65 |
+
d_max=d_max,
|
| 66 |
+
d_min=d_min,
|
| 67 |
+
gate_max=gate_max,
|
| 68 |
+
gate_threshold_log2=config.gate_threshold_log2,
|
| 69 |
+
)
|
| 70 |
+
|
| 71 |
+
if IS_MASK:
|
| 72 |
+
# Apply mask to attention scores
|
| 73 |
+
acc_s = mask_sched.apply_mask(
|
| 74 |
+
acc_s=acc_s,
|
| 75 |
+
iter_block=n_block,
|
| 76 |
+
MASK_LOCAL=MASK_LOCAL,
|
| 77 |
+
MASK_SINK=MASK_SINK,
|
| 78 |
+
)
|
| 79 |
+
|
| 80 |
+
# Apply online softmax
|
| 81 |
+
p, row_max, row_sum, row_scale, skip_softmax = (
|
| 82 |
+
softmax_sched.online_sparse_softmax(
|
| 83 |
+
acc_s=acc_s,
|
| 84 |
+
row_max=row_max,
|
| 85 |
+
row_sum=row_sum,
|
| 86 |
+
softmax_threshold_log2=config.softmax_threshold_log2,
|
| 87 |
+
CHECK_INF=CHECK_INF,
|
| 88 |
+
)
|
| 89 |
+
)
|
| 90 |
+
|
| 91 |
+
if not skip_softmax:
|
| 92 |
+
# Load value tile
|
| 93 |
+
v_tile = ptrs_sched.load_v(config, v_ptrs, n_block)
|
| 94 |
+
|
| 95 |
+
# Rescale output accumulator
|
| 96 |
+
acc_o = softmax_sched.rescale_o(
|
| 97 |
+
acc_o=acc_o,
|
| 98 |
+
row_scale=row_scale,
|
| 99 |
+
)
|
| 100 |
+
|
| 101 |
+
# Update output accumulator
|
| 102 |
+
acc_o += tl.dot(p.to(v_tile.dtype), v_tile)
|
| 103 |
+
|
| 104 |
+
else:
|
| 105 |
+
if n_block > n_block_min:
|
| 106 |
+
# Check if any gates are active for next tile
|
| 107 |
+
gate_max, skip_gate_next = softmax_sched.online_gate(
|
| 108 |
+
a_max=a_max,
|
| 109 |
+
a_min=a_min,
|
| 110 |
+
d_max=d_max,
|
| 111 |
+
d_min=d_min,
|
| 112 |
+
gate_max=gate_max,
|
| 113 |
+
gate_threshold_log2=config.gate_threshold_log2,
|
| 114 |
+
)
|
| 115 |
+
|
| 116 |
+
if not skip_gate_next:
|
| 117 |
+
# Compute attention gates for next tile
|
| 118 |
+
acc_s = a_tile[:, None] * d_tile[None, :]
|
| 119 |
+
if config.IS_LOGSIGMOID_GATE:
|
| 120 |
+
acc_s = softmax_sched.log_sigmoid(
|
| 121 |
+
acc_s=acc_s,
|
| 122 |
+
)
|
| 123 |
+
|
| 124 |
+
# Load next key tile
|
| 125 |
+
k_tile = ptrs_sched.load_k(config, k_ptrs, n_block - 1)
|
| 126 |
+
|
| 127 |
+
# Compute attention scores for next tile
|
| 128 |
+
acc_s += tl.dot(q_tile, k_tile.T)
|
| 129 |
+
|
| 130 |
+
return (
|
| 131 |
+
skip_gate_next,
|
| 132 |
+
acc_s,
|
| 133 |
+
acc_o,
|
| 134 |
+
gate_max,
|
| 135 |
+
row_max,
|
| 136 |
+
row_sum,
|
| 137 |
+
)
|
| 138 |
+
|
| 139 |
+
|
| 140 |
+
@triton.jit(repr=kernel_repr.dec_gated_repr)
|
| 141 |
+
def _dec_gated_kernel(
|
| 142 |
+
Q,
|
| 143 |
+
K,
|
| 144 |
+
V,
|
| 145 |
+
A,
|
| 146 |
+
D,
|
| 147 |
+
Out,
|
| 148 |
+
Lse,
|
| 149 |
+
softmax_scale,
|
| 150 |
+
query_scale,
|
| 151 |
+
key_scale,
|
| 152 |
+
value_scale,
|
| 153 |
+
softmax_threshold,
|
| 154 |
+
gate_threshold,
|
| 155 |
+
window_sizes,
|
| 156 |
+
stride_qb,
|
| 157 |
+
stride_qh,
|
| 158 |
+
stride_qm,
|
| 159 |
+
stride_kb,
|
| 160 |
+
stride_kh,
|
| 161 |
+
stride_kn,
|
| 162 |
+
stride_vb,
|
| 163 |
+
stride_vh,
|
| 164 |
+
stride_vn,
|
| 165 |
+
stride_ab,
|
| 166 |
+
stride_ah,
|
| 167 |
+
stride_db,
|
| 168 |
+
stride_dh,
|
| 169 |
+
stride_ob,
|
| 170 |
+
stride_oh,
|
| 171 |
+
stride_om,
|
| 172 |
+
stride_os,
|
| 173 |
+
stride_lb,
|
| 174 |
+
stride_lh,
|
| 175 |
+
stride_lm,
|
| 176 |
+
stride_ls,
|
| 177 |
+
stride_wh,
|
| 178 |
+
cu_seqlens_q,
|
| 179 |
+
cu_seqlens_k,
|
| 180 |
+
seqused_q,
|
| 181 |
+
seqused_k,
|
| 182 |
+
num_splits,
|
| 183 |
+
seqlen_q,
|
| 184 |
+
seqlen_k,
|
| 185 |
+
head_dim,
|
| 186 |
+
SEQLEN_Q_CACHE: tl.constexpr,
|
| 187 |
+
SEQLEN_K_CACHE: tl.constexpr,
|
| 188 |
+
QHEAD_PER_KVHEAD_PACKGQA: tl.constexpr,
|
| 189 |
+
TILE_M: tl.constexpr,
|
| 190 |
+
TILE_N: tl.constexpr,
|
| 191 |
+
TILE_K: tl.constexpr,
|
| 192 |
+
IS_LOCAL: tl.constexpr,
|
| 193 |
+
HAS_CU_SEQLENS_Q: tl.constexpr,
|
| 194 |
+
HAS_CU_SEQLENS_K: tl.constexpr,
|
| 195 |
+
HAS_SEQUSED_Q: tl.constexpr,
|
| 196 |
+
HAS_SEQUSED_K: tl.constexpr,
|
| 197 |
+
IS_LOGSIGMOID_GATE: tl.constexpr,
|
| 198 |
+
):
|
| 199 |
+
# Create grid index
|
| 200 |
+
grid_idx = AttnDecGridIndex.create(
|
| 201 |
+
num_splits=num_splits,
|
| 202 |
+
)
|
| 203 |
+
|
| 204 |
+
# Load window sizes
|
| 205 |
+
(
|
| 206 |
+
window_size_sink,
|
| 207 |
+
window_size_left,
|
| 208 |
+
window_size_right,
|
| 209 |
+
window_size_dist,
|
| 210 |
+
) = grid_idx.load_window_sizes(
|
| 211 |
+
window_sizes=window_sizes,
|
| 212 |
+
stride_wh=stride_wh,
|
| 213 |
+
IS_LOCAL=IS_LOCAL,
|
| 214 |
+
)
|
| 215 |
+
|
| 216 |
+
# Create config
|
| 217 |
+
config = AttnDecConfig.create(
|
| 218 |
+
softmax_scale=softmax_scale,
|
| 219 |
+
softmax_threshold=softmax_threshold,
|
| 220 |
+
gate_threshold=gate_threshold,
|
| 221 |
+
query_scale=query_scale,
|
| 222 |
+
key_scale=key_scale,
|
| 223 |
+
value_scale=value_scale,
|
| 224 |
+
batch_idx=grid_idx.batch_idx,
|
| 225 |
+
window_size_sink=window_size_sink,
|
| 226 |
+
window_size_left=window_size_left,
|
| 227 |
+
window_size_right=window_size_right,
|
| 228 |
+
window_size_dist=window_size_dist,
|
| 229 |
+
head_dim=head_dim,
|
| 230 |
+
cu_seqlens_q=cu_seqlens_q,
|
| 231 |
+
cu_seqlens_k=cu_seqlens_k,
|
| 232 |
+
seqused_q=seqused_q,
|
| 233 |
+
seqused_k=seqused_k,
|
| 234 |
+
seqlen_q=seqlen_q,
|
| 235 |
+
seqlen_k=seqlen_k,
|
| 236 |
+
QHEAD_PER_KVHEAD_PACKGQA=QHEAD_PER_KVHEAD_PACKGQA,
|
| 237 |
+
TILE_M=TILE_M,
|
| 238 |
+
TILE_N=TILE_N,
|
| 239 |
+
TILE_K=TILE_K,
|
| 240 |
+
IS_LOGSIGMOID_GATE=IS_LOGSIGMOID_GATE,
|
| 241 |
+
HAS_CU_SEQLENS_Q=HAS_CU_SEQLENS_Q,
|
| 242 |
+
HAS_CU_SEQLENS_K=HAS_CU_SEQLENS_K,
|
| 243 |
+
HAS_SEQUSED_Q=HAS_SEQUSED_Q,
|
| 244 |
+
HAS_SEQUSED_K=HAS_SEQUSED_K,
|
| 245 |
+
)
|
| 246 |
+
|
| 247 |
+
# Create pointer scheduler
|
| 248 |
+
ptrs_sched = AttnDecPointerScheduler.create(
|
| 249 |
+
config=config,
|
| 250 |
+
Q=Q,
|
| 251 |
+
K=K,
|
| 252 |
+
V=V,
|
| 253 |
+
A=A,
|
| 254 |
+
D=D,
|
| 255 |
+
Out=Out,
|
| 256 |
+
Lse=Lse,
|
| 257 |
+
batch_idx=grid_idx.batch_idx,
|
| 258 |
+
head_idx=grid_idx.head_idx,
|
| 259 |
+
head_kv_idx=grid_idx.head_kv_idx,
|
| 260 |
+
split_idx=grid_idx.split_idx,
|
| 261 |
+
stride_qb=stride_qb,
|
| 262 |
+
stride_qh=stride_qh,
|
| 263 |
+
stride_qm=stride_qm,
|
| 264 |
+
stride_kb=stride_kb,
|
| 265 |
+
stride_kh=stride_kh,
|
| 266 |
+
stride_kn=stride_kn,
|
| 267 |
+
stride_vb=stride_vb,
|
| 268 |
+
stride_vh=stride_vh,
|
| 269 |
+
stride_vn=stride_vn,
|
| 270 |
+
stride_ab=stride_ab,
|
| 271 |
+
stride_ah=stride_ah,
|
| 272 |
+
stride_db=stride_db,
|
| 273 |
+
stride_dh=stride_dh,
|
| 274 |
+
stride_ob=stride_ob,
|
| 275 |
+
stride_oh=stride_oh,
|
| 276 |
+
stride_om=stride_om,
|
| 277 |
+
stride_os=stride_os,
|
| 278 |
+
stride_lb=stride_lb,
|
| 279 |
+
stride_lh=stride_lh,
|
| 280 |
+
stride_lm=stride_lm,
|
| 281 |
+
stride_ls=stride_ls,
|
| 282 |
+
IS_GATED=True,
|
| 283 |
+
HAS_CU_SEQLENS_Q=HAS_CU_SEQLENS_Q,
|
| 284 |
+
HAS_CU_SEQLENS_K=HAS_CU_SEQLENS_K,
|
| 285 |
+
)
|
| 286 |
+
|
| 287 |
+
# Create block scheduler
|
| 288 |
+
block_sched = AttnDecBlockScheduler.create(
|
| 289 |
+
config=config,
|
| 290 |
+
split_idx=grid_idx.split_idx,
|
| 291 |
+
num_splits=num_splits,
|
| 292 |
+
IS_LOCAL=IS_LOCAL,
|
| 293 |
+
)
|
| 294 |
+
|
| 295 |
+
# Create mask scheduler
|
| 296 |
+
mask_sched = AttnMaskScheduler.create(config)
|
| 297 |
+
|
| 298 |
+
# Create softmax scheduler
|
| 299 |
+
softmax_sched = SoftmaxScheduler.create(config)
|
| 300 |
+
|
| 301 |
+
# Create pointers
|
| 302 |
+
out_ptrs = ptrs_sched.make_out_ptrs(config)
|
| 303 |
+
lse_ptrs = ptrs_sched.make_lse_ptrs(config)
|
| 304 |
+
|
| 305 |
+
# Early exit if no n_blocks to process
|
| 306 |
+
if block_sched.is_empty():
|
| 307 |
+
ptrs_sched.store_empty(config, out_ptrs, lse_ptrs, Out)
|
| 308 |
+
return
|
| 309 |
+
|
| 310 |
+
q_ptrs = ptrs_sched.make_q_ptrs(config)
|
| 311 |
+
k_ptrs = ptrs_sched.make_k_ptrs(config)
|
| 312 |
+
v_ptrs = ptrs_sched.make_v_ptrs(config)
|
| 313 |
+
a_ptrs = ptrs_sched.make_a_ptrs(config)
|
| 314 |
+
d_ptrs = ptrs_sched.make_d_ptrs(config)
|
| 315 |
+
|
| 316 |
+
# Initialize accumulators
|
| 317 |
+
gate_max = tl.full((), float("-inf"), dtype=tl.float32)
|
| 318 |
+
row_max = tl.full((TILE_M,), float("-inf"), dtype=tl.float32)
|
| 319 |
+
row_sum = tl.zeros((TILE_M,), dtype=tl.float32)
|
| 320 |
+
acc_o = tl.zeros((TILE_M, TILE_K), dtype=tl.float32)
|
| 321 |
+
|
| 322 |
+
# Load query tile
|
| 323 |
+
q_tile = ptrs_sched.load_q(config, q_ptrs)
|
| 324 |
+
|
| 325 |
+
# Load alpha tile
|
| 326 |
+
a_tile = ptrs_sched.load_a(config, a_ptrs)
|
| 327 |
+
a_max = tl.max(a_tile)
|
| 328 |
+
a_min = tl.min(a_tile)
|
| 329 |
+
|
| 330 |
+
# Load delta tile
|
| 331 |
+
d_tile = ptrs_sched.load_d(config, d_ptrs, block_sched.n_block_max - 1)
|
| 332 |
+
d_max = tl.max(d_tile)
|
| 333 |
+
d_min = tl.min(d_tile)
|
| 334 |
+
|
| 335 |
+
# Check if any gates are active for current tile
|
| 336 |
+
gate_max, skip_gate_curr = softmax_sched.online_gate(
|
| 337 |
+
a_max=a_max,
|
| 338 |
+
a_min=a_min,
|
| 339 |
+
d_max=d_max,
|
| 340 |
+
d_min=d_min,
|
| 341 |
+
gate_max=gate_max,
|
| 342 |
+
gate_threshold_log2=config.gate_threshold_log2,
|
| 343 |
+
)
|
| 344 |
+
|
| 345 |
+
# Initialize skip_gate_curr to False for the first iteration
|
| 346 |
+
skip_gate_curr = False
|
| 347 |
+
|
| 348 |
+
# Compute attention gates for first tile
|
| 349 |
+
acc_s = a_tile[:, None] * d_tile[None, :]
|
| 350 |
+
if IS_LOGSIGMOID_GATE:
|
| 351 |
+
acc_s = softmax_sched.log_sigmoid(
|
| 352 |
+
acc_s=acc_s,
|
| 353 |
+
)
|
| 354 |
+
|
| 355 |
+
# Load key tile
|
| 356 |
+
k_tile = ptrs_sched.load_k(config, k_ptrs, block_sched.n_block_max - 1)
|
| 357 |
+
|
| 358 |
+
# Compute attention scores for first tile
|
| 359 |
+
acc_s += tl.dot(q_tile, k_tile.T)
|
| 360 |
+
|
| 361 |
+
# Process n_blocks with seqlen masking
|
| 362 |
+
for n_block in tl.range(
|
| 363 |
+
block_sched.n_block_max - 1, block_sched.n_block_max_no_mask - 1, -1
|
| 364 |
+
):
|
| 365 |
+
(
|
| 366 |
+
skip_gate_curr,
|
| 367 |
+
acc_s,
|
| 368 |
+
acc_o,
|
| 369 |
+
gate_max,
|
| 370 |
+
row_max,
|
| 371 |
+
row_sum,
|
| 372 |
+
) = _dec_inner_gated_kernel(
|
| 373 |
+
config=config,
|
| 374 |
+
ptrs_sched=ptrs_sched,
|
| 375 |
+
mask_sched=mask_sched,
|
| 376 |
+
softmax_sched=softmax_sched,
|
| 377 |
+
skip_gate_curr=skip_gate_curr,
|
| 378 |
+
acc_s=acc_s,
|
| 379 |
+
acc_o=acc_o,
|
| 380 |
+
q_tile=q_tile,
|
| 381 |
+
a_tile=a_tile,
|
| 382 |
+
k_ptrs=k_ptrs,
|
| 383 |
+
v_ptrs=v_ptrs,
|
| 384 |
+
d_ptrs=d_ptrs,
|
| 385 |
+
a_max=a_max,
|
| 386 |
+
a_min=a_min,
|
| 387 |
+
gate_max=gate_max,
|
| 388 |
+
row_max=row_max,
|
| 389 |
+
row_sum=row_sum,
|
| 390 |
+
n_block=n_block,
|
| 391 |
+
n_block_min=block_sched.n_block_max_no_mask,
|
| 392 |
+
IS_MASK=True,
|
| 393 |
+
MASK_LOCAL=True if IS_LOCAL else False,
|
| 394 |
+
MASK_SINK=False,
|
| 395 |
+
CHECK_INF=True,
|
| 396 |
+
)
|
| 397 |
+
|
| 398 |
+
# Process n_blocks without masking
|
| 399 |
+
if not IS_LOCAL and block_sched.n_block_max_no_mask > block_sched.n_block_min:
|
| 400 |
+
# Load key tile
|
| 401 |
+
k_tile = ptrs_sched.load_k(config, k_ptrs, block_sched.n_block_max_no_mask - 1)
|
| 402 |
+
|
| 403 |
+
# Load delta tile
|
| 404 |
+
d_tile = ptrs_sched.load_d(config, d_ptrs, block_sched.n_block_max_no_mask - 1)
|
| 405 |
+
d_max = tl.max(d_tile)
|
| 406 |
+
d_min = tl.min(d_tile)
|
| 407 |
+
|
| 408 |
+
# Check if any gates are active for current tile
|
| 409 |
+
gate_max, skip_gate_curr = softmax_sched.online_gate(
|
| 410 |
+
a_max=a_max,
|
| 411 |
+
a_min=a_min,
|
| 412 |
+
d_max=d_max,
|
| 413 |
+
d_min=d_min,
|
| 414 |
+
gate_max=gate_max,
|
| 415 |
+
gate_threshold_log2=config.gate_threshold_log2,
|
| 416 |
+
)
|
| 417 |
+
|
| 418 |
+
# Compute attention gates
|
| 419 |
+
acc_s = a_tile[:, None] * d_tile[None, :]
|
| 420 |
+
if IS_LOGSIGMOID_GATE:
|
| 421 |
+
acc_s = softmax_sched.log_sigmoid(
|
| 422 |
+
acc_s=acc_s,
|
| 423 |
+
)
|
| 424 |
+
|
| 425 |
+
# Compute attention scores
|
| 426 |
+
acc_s += tl.dot(q_tile, k_tile.T)
|
| 427 |
+
|
| 428 |
+
for n_block in tl.range(
|
| 429 |
+
block_sched.n_block_max_no_mask - 1, block_sched.n_block_min - 1, -1
|
| 430 |
+
):
|
| 431 |
+
(
|
| 432 |
+
skip_gate_curr,
|
| 433 |
+
acc_s,
|
| 434 |
+
acc_o,
|
| 435 |
+
gate_max,
|
| 436 |
+
row_max,
|
| 437 |
+
row_sum,
|
| 438 |
+
) = _dec_inner_gated_kernel(
|
| 439 |
+
config=config,
|
| 440 |
+
ptrs_sched=ptrs_sched,
|
| 441 |
+
mask_sched=mask_sched,
|
| 442 |
+
softmax_sched=softmax_sched,
|
| 443 |
+
skip_gate_curr=skip_gate_curr,
|
| 444 |
+
acc_s=acc_s,
|
| 445 |
+
acc_o=acc_o,
|
| 446 |
+
q_tile=q_tile,
|
| 447 |
+
a_tile=a_tile,
|
| 448 |
+
k_ptrs=k_ptrs,
|
| 449 |
+
v_ptrs=v_ptrs,
|
| 450 |
+
d_ptrs=d_ptrs,
|
| 451 |
+
a_max=a_max,
|
| 452 |
+
a_min=a_min,
|
| 453 |
+
gate_max=gate_max,
|
| 454 |
+
row_max=row_max,
|
| 455 |
+
row_sum=row_sum,
|
| 456 |
+
n_block=n_block,
|
| 457 |
+
n_block_min=block_sched.n_block_min,
|
| 458 |
+
IS_MASK=False,
|
| 459 |
+
MASK_LOCAL=False,
|
| 460 |
+
MASK_SINK=False,
|
| 461 |
+
CHECK_INF=False,
|
| 462 |
+
)
|
| 463 |
+
|
| 464 |
+
if IS_LOCAL:
|
| 465 |
+
# Process n_blocks with local right masking
|
| 466 |
+
if block_sched.n_block_window_max > block_sched.n_block_window_max_no_mask:
|
| 467 |
+
# Load key tile
|
| 468 |
+
k_tile = ptrs_sched.load_k(
|
| 469 |
+
config, k_ptrs, block_sched.n_block_window_max - 1
|
| 470 |
+
)
|
| 471 |
+
|
| 472 |
+
# Load delta tile
|
| 473 |
+
d_tile = ptrs_sched.load_d(
|
| 474 |
+
config, d_ptrs, block_sched.n_block_window_max - 1
|
| 475 |
+
)
|
| 476 |
+
d_max = tl.max(d_tile)
|
| 477 |
+
d_min = tl.min(d_tile)
|
| 478 |
+
|
| 479 |
+
# Check if any gates are active for current tile
|
| 480 |
+
gate_max, skip_gate_curr = softmax_sched.online_gate(
|
| 481 |
+
a_max=a_max,
|
| 482 |
+
a_min=a_min,
|
| 483 |
+
d_max=d_max,
|
| 484 |
+
d_min=d_min,
|
| 485 |
+
gate_max=gate_max,
|
| 486 |
+
gate_threshold_log2=config.gate_threshold_log2,
|
| 487 |
+
)
|
| 488 |
+
|
| 489 |
+
# Compute attention gates
|
| 490 |
+
acc_s = a_tile[:, None] * d_tile[None, :]
|
| 491 |
+
if IS_LOGSIGMOID_GATE:
|
| 492 |
+
acc_s = softmax_sched.log_sigmoid(
|
| 493 |
+
acc_s=acc_s,
|
| 494 |
+
)
|
| 495 |
+
|
| 496 |
+
# Compute attention scores
|
| 497 |
+
acc_s += tl.dot(q_tile, k_tile.T)
|
| 498 |
+
|
| 499 |
+
for n_block in tl.range(
|
| 500 |
+
block_sched.n_block_window_max - 1,
|
| 501 |
+
block_sched.n_block_window_max_no_mask - 1,
|
| 502 |
+
-1,
|
| 503 |
+
):
|
| 504 |
+
(
|
| 505 |
+
skip_gate_curr,
|
| 506 |
+
acc_s,
|
| 507 |
+
acc_o,
|
| 508 |
+
gate_max,
|
| 509 |
+
row_max,
|
| 510 |
+
row_sum,
|
| 511 |
+
) = _dec_inner_gated_kernel(
|
| 512 |
+
config=config,
|
| 513 |
+
ptrs_sched=ptrs_sched,
|
| 514 |
+
mask_sched=mask_sched,
|
| 515 |
+
softmax_sched=softmax_sched,
|
| 516 |
+
skip_gate_curr=skip_gate_curr,
|
| 517 |
+
acc_s=acc_s,
|
| 518 |
+
acc_o=acc_o,
|
| 519 |
+
q_tile=q_tile,
|
| 520 |
+
a_tile=a_tile,
|
| 521 |
+
k_ptrs=k_ptrs,
|
| 522 |
+
v_ptrs=v_ptrs,
|
| 523 |
+
d_ptrs=d_ptrs,
|
| 524 |
+
a_max=a_max,
|
| 525 |
+
a_min=a_min,
|
| 526 |
+
gate_max=gate_max,
|
| 527 |
+
row_max=row_max,
|
| 528 |
+
row_sum=row_sum,
|
| 529 |
+
n_block=n_block,
|
| 530 |
+
n_block_min=block_sched.n_block_window_max_no_mask,
|
| 531 |
+
IS_MASK=True,
|
| 532 |
+
MASK_LOCAL=True,
|
| 533 |
+
MASK_SINK=False,
|
| 534 |
+
CHECK_INF=True,
|
| 535 |
+
)
|
| 536 |
+
|
| 537 |
+
# Process n_blocks without masking
|
| 538 |
+
if (
|
| 539 |
+
block_sched.n_block_window_max_no_mask
|
| 540 |
+
> block_sched.n_block_window_min_no_mask
|
| 541 |
+
):
|
| 542 |
+
# Load key tile
|
| 543 |
+
k_tile = ptrs_sched.load_k(
|
| 544 |
+
config, k_ptrs, block_sched.n_block_window_max_no_mask - 1
|
| 545 |
+
)
|
| 546 |
+
|
| 547 |
+
# Load delta tile
|
| 548 |
+
d_tile = ptrs_sched.load_d(
|
| 549 |
+
config, d_ptrs, block_sched.n_block_window_max_no_mask - 1
|
| 550 |
+
)
|
| 551 |
+
d_max = tl.max(d_tile)
|
| 552 |
+
d_min = tl.min(d_tile)
|
| 553 |
+
|
| 554 |
+
# Check if any gates are active for current tile
|
| 555 |
+
gate_max, skip_gate_curr = softmax_sched.online_gate(
|
| 556 |
+
a_max=a_max,
|
| 557 |
+
a_min=a_min,
|
| 558 |
+
d_max=d_max,
|
| 559 |
+
d_min=d_min,
|
| 560 |
+
gate_max=gate_max,
|
| 561 |
+
gate_threshold_log2=config.gate_threshold_log2,
|
| 562 |
+
)
|
| 563 |
+
|
| 564 |
+
# Compute attention gates
|
| 565 |
+
acc_s = a_tile[:, None] * d_tile[None, :]
|
| 566 |
+
if IS_LOGSIGMOID_GATE:
|
| 567 |
+
acc_s = softmax_sched.log_sigmoid(
|
| 568 |
+
acc_s=acc_s,
|
| 569 |
+
)
|
| 570 |
+
|
| 571 |
+
# Compute attention scores
|
| 572 |
+
acc_s += tl.dot(q_tile, k_tile.T)
|
| 573 |
+
|
| 574 |
+
for n_block in tl.range(
|
| 575 |
+
block_sched.n_block_window_max_no_mask - 1,
|
| 576 |
+
block_sched.n_block_window_min_no_mask - 1,
|
| 577 |
+
-1,
|
| 578 |
+
):
|
| 579 |
+
(
|
| 580 |
+
skip_gate_curr,
|
| 581 |
+
acc_s,
|
| 582 |
+
acc_o,
|
| 583 |
+
gate_max,
|
| 584 |
+
row_max,
|
| 585 |
+
row_sum,
|
| 586 |
+
) = _dec_inner_gated_kernel(
|
| 587 |
+
config=config,
|
| 588 |
+
ptrs_sched=ptrs_sched,
|
| 589 |
+
mask_sched=mask_sched,
|
| 590 |
+
softmax_sched=softmax_sched,
|
| 591 |
+
skip_gate_curr=skip_gate_curr,
|
| 592 |
+
acc_s=acc_s,
|
| 593 |
+
acc_o=acc_o,
|
| 594 |
+
q_tile=q_tile,
|
| 595 |
+
a_tile=a_tile,
|
| 596 |
+
k_ptrs=k_ptrs,
|
| 597 |
+
v_ptrs=v_ptrs,
|
| 598 |
+
d_ptrs=d_ptrs,
|
| 599 |
+
a_max=a_max,
|
| 600 |
+
a_min=a_min,
|
| 601 |
+
gate_max=gate_max,
|
| 602 |
+
row_max=row_max,
|
| 603 |
+
row_sum=row_sum,
|
| 604 |
+
n_block=n_block,
|
| 605 |
+
n_block_min=block_sched.n_block_window_min_no_mask,
|
| 606 |
+
IS_MASK=False,
|
| 607 |
+
MASK_LOCAL=False,
|
| 608 |
+
MASK_SINK=False,
|
| 609 |
+
CHECK_INF=False,
|
| 610 |
+
)
|
| 611 |
+
|
| 612 |
+
# Process n_blocks with local left masking
|
| 613 |
+
if block_sched.n_block_window_min_no_mask > block_sched.n_block_window_min:
|
| 614 |
+
# Load key tile
|
| 615 |
+
k_tile = ptrs_sched.load_k(
|
| 616 |
+
config, k_ptrs, block_sched.n_block_window_min_no_mask - 1
|
| 617 |
+
)
|
| 618 |
+
|
| 619 |
+
# Load delta tile
|
| 620 |
+
d_tile = ptrs_sched.load_d(
|
| 621 |
+
config, d_ptrs, block_sched.n_block_window_min_no_mask - 1
|
| 622 |
+
)
|
| 623 |
+
d_max = tl.max(d_tile)
|
| 624 |
+
d_min = tl.min(d_tile)
|
| 625 |
+
|
| 626 |
+
# Check if any gates are active for current tile
|
| 627 |
+
gate_max, skip_gate_curr = softmax_sched.online_gate(
|
| 628 |
+
a_max=a_max,
|
| 629 |
+
a_min=a_min,
|
| 630 |
+
d_max=d_max,
|
| 631 |
+
d_min=d_min,
|
| 632 |
+
gate_max=gate_max,
|
| 633 |
+
gate_threshold_log2=config.gate_threshold_log2,
|
| 634 |
+
)
|
| 635 |
+
|
| 636 |
+
# Compute attention gates
|
| 637 |
+
acc_s = a_tile[:, None] * d_tile[None, :]
|
| 638 |
+
if IS_LOGSIGMOID_GATE:
|
| 639 |
+
acc_s = softmax_sched.log_sigmoid(
|
| 640 |
+
acc_s=acc_s,
|
| 641 |
+
)
|
| 642 |
+
|
| 643 |
+
# Compute attention scores
|
| 644 |
+
acc_s += tl.dot(q_tile, k_tile.T)
|
| 645 |
+
|
| 646 |
+
for n_block in tl.range(
|
| 647 |
+
block_sched.n_block_window_min_no_mask - 1,
|
| 648 |
+
block_sched.n_block_window_min - 1,
|
| 649 |
+
-1,
|
| 650 |
+
):
|
| 651 |
+
(
|
| 652 |
+
skip_gate_curr,
|
| 653 |
+
acc_s,
|
| 654 |
+
acc_o,
|
| 655 |
+
gate_max,
|
| 656 |
+
row_max,
|
| 657 |
+
row_sum,
|
| 658 |
+
) = _dec_inner_gated_kernel(
|
| 659 |
+
config=config,
|
| 660 |
+
ptrs_sched=ptrs_sched,
|
| 661 |
+
mask_sched=mask_sched,
|
| 662 |
+
softmax_sched=softmax_sched,
|
| 663 |
+
skip_gate_curr=skip_gate_curr,
|
| 664 |
+
acc_s=acc_s,
|
| 665 |
+
acc_o=acc_o,
|
| 666 |
+
q_tile=q_tile,
|
| 667 |
+
a_tile=a_tile,
|
| 668 |
+
k_ptrs=k_ptrs,
|
| 669 |
+
v_ptrs=v_ptrs,
|
| 670 |
+
d_ptrs=d_ptrs,
|
| 671 |
+
a_max=a_max,
|
| 672 |
+
a_min=a_min,
|
| 673 |
+
gate_max=gate_max,
|
| 674 |
+
row_max=row_max,
|
| 675 |
+
row_sum=row_sum,
|
| 676 |
+
n_block=n_block,
|
| 677 |
+
n_block_min=block_sched.n_block_window_min,
|
| 678 |
+
IS_MASK=True,
|
| 679 |
+
MASK_LOCAL=True,
|
| 680 |
+
MASK_SINK=False,
|
| 681 |
+
CHECK_INF=True,
|
| 682 |
+
)
|
| 683 |
+
|
| 684 |
+
# Process n_blocks with local sink masking
|
| 685 |
+
if block_sched.n_block_sink_max > block_sched.n_block_sink_min:
|
| 686 |
+
# Load key tile
|
| 687 |
+
k_tile = ptrs_sched.load_k(config, k_ptrs, block_sched.n_block_sink_max - 1)
|
| 688 |
+
|
| 689 |
+
# Load delta tile
|
| 690 |
+
d_tile = ptrs_sched.load_d(config, d_ptrs, block_sched.n_block_sink_max - 1)
|
| 691 |
+
d_max = tl.max(d_tile)
|
| 692 |
+
d_min = tl.min(d_tile)
|
| 693 |
+
|
| 694 |
+
# Check if any gates are active for current tile
|
| 695 |
+
gate_max, skip_gate_curr = softmax_sched.online_gate(
|
| 696 |
+
a_max=a_max,
|
| 697 |
+
a_min=a_min,
|
| 698 |
+
d_max=d_max,
|
| 699 |
+
d_min=d_min,
|
| 700 |
+
gate_max=gate_max,
|
| 701 |
+
gate_threshold_log2=config.gate_threshold_log2,
|
| 702 |
+
)
|
| 703 |
+
|
| 704 |
+
# Compute attention gates
|
| 705 |
+
acc_s = a_tile[:, None] * d_tile[None, :]
|
| 706 |
+
if IS_LOGSIGMOID_GATE:
|
| 707 |
+
acc_s = softmax_sched.log_sigmoid(
|
| 708 |
+
acc_s=acc_s,
|
| 709 |
+
)
|
| 710 |
+
|
| 711 |
+
# Compute attention scores
|
| 712 |
+
acc_s += tl.dot(q_tile, k_tile.T)
|
| 713 |
+
|
| 714 |
+
for n_block in tl.range(
|
| 715 |
+
block_sched.n_block_sink_max - 1,
|
| 716 |
+
block_sched.n_block_sink_min - 1,
|
| 717 |
+
-1,
|
| 718 |
+
):
|
| 719 |
+
(
|
| 720 |
+
skip_gate_curr,
|
| 721 |
+
acc_s,
|
| 722 |
+
acc_o,
|
| 723 |
+
gate_max,
|
| 724 |
+
row_max,
|
| 725 |
+
row_sum,
|
| 726 |
+
) = _dec_inner_gated_kernel(
|
| 727 |
+
config=config,
|
| 728 |
+
ptrs_sched=ptrs_sched,
|
| 729 |
+
mask_sched=mask_sched,
|
| 730 |
+
softmax_sched=softmax_sched,
|
| 731 |
+
skip_gate_curr=skip_gate_curr,
|
| 732 |
+
acc_s=acc_s,
|
| 733 |
+
acc_o=acc_o,
|
| 734 |
+
q_tile=q_tile,
|
| 735 |
+
a_tile=a_tile,
|
| 736 |
+
k_ptrs=k_ptrs,
|
| 737 |
+
v_ptrs=v_ptrs,
|
| 738 |
+
d_ptrs=d_ptrs,
|
| 739 |
+
a_max=a_max,
|
| 740 |
+
a_min=a_min,
|
| 741 |
+
gate_max=gate_max,
|
| 742 |
+
row_max=row_max,
|
| 743 |
+
row_sum=row_sum,
|
| 744 |
+
n_block=n_block,
|
| 745 |
+
n_block_min=block_sched.n_block_sink_min,
|
| 746 |
+
IS_MASK=True,
|
| 747 |
+
MASK_LOCAL=True,
|
| 748 |
+
MASK_SINK=True,
|
| 749 |
+
CHECK_INF=True,
|
| 750 |
+
)
|
| 751 |
+
|
| 752 |
+
# Finalize softmax
|
| 753 |
+
row_scale, lse_tile = softmax_sched.finalize(
|
| 754 |
+
row_max=row_max,
|
| 755 |
+
row_sum=row_sum,
|
| 756 |
+
IS_LOG2=True,
|
| 757 |
+
)
|
| 758 |
+
|
| 759 |
+
# Store LSE
|
| 760 |
+
ptrs_sched.store_lse(config, lse_ptrs, lse_tile)
|
| 761 |
+
|
| 762 |
+
# Finalize rescale
|
| 763 |
+
acc_o = softmax_sched.rescale_o(
|
| 764 |
+
acc_o=acc_o,
|
| 765 |
+
row_scale=row_scale,
|
| 766 |
+
)
|
| 767 |
+
|
| 768 |
+
# Store output
|
| 769 |
+
ptrs_sched.store_out(config, out_ptrs, acc_o)
|
| 770 |
+
|
| 771 |
+
|
| 772 |
+
_dec_gated_kernel = cache_utils.wrap_kernel(_dec_gated_kernel)
|
| 773 |
+
|
| 774 |
+
|
| 775 |
+
_dec_gated_kernel_autotuned = None
|
| 776 |
+
|
| 777 |
+
|
| 778 |
+
def _get_autotuned_kernel():
|
| 779 |
+
global _dec_gated_kernel_autotuned
|
| 780 |
+
if _dec_gated_kernel_autotuned is None:
|
| 781 |
+
jit_kernel = _dec_gated_kernel._kernel
|
| 782 |
+
autotuned = autotuner.make_dec_gated_autotuned_kernel(jit_kernel)
|
| 783 |
+
_dec_gated_kernel_autotuned = autotuner.AutotunedKernel(autotuned)
|
| 784 |
+
return _dec_gated_kernel_autotuned
|
| 785 |
+
|
| 786 |
+
|
| 787 |
+
def _flash_gated_attn_decode(
|
| 788 |
+
query: torch.Tensor,
|
| 789 |
+
key: torch.Tensor,
|
| 790 |
+
value: torch.Tensor,
|
| 791 |
+
alpha: torch.Tensor,
|
| 792 |
+
delta: torch.Tensor,
|
| 793 |
+
softmax_scale: float = None,
|
| 794 |
+
softmax_threshold: float = None,
|
| 795 |
+
gate_threshold: float = None,
|
| 796 |
+
is_logsigmoid_gate: bool = True,
|
| 797 |
+
query_scale: Optional[torch.Tensor] = None,
|
| 798 |
+
key_scale: Optional[torch.Tensor] = None,
|
| 799 |
+
value_scale: Optional[torch.Tensor] = None,
|
| 800 |
+
window_sizes: Optional[torch.Tensor] = None,
|
| 801 |
+
is_local: bool = False,
|
| 802 |
+
is_quant: bool = False,
|
| 803 |
+
out: Optional[torch.Tensor] = None,
|
| 804 |
+
lse: Optional[torch.Tensor] = None,
|
| 805 |
+
is_autotune: bool = False,
|
| 806 |
+
skip_checks: bool = False,
|
| 807 |
+
) -> Tuple[torch.Tensor, torch.Tensor]:
|
| 808 |
+
device = query.device
|
| 809 |
+
num_SMs = cache_utils.get_device_num_sms(device)
|
| 810 |
+
batch_size, num_heads_q, head_dim = query.shape
|
| 811 |
+
_, seqlen_k, num_heads_kv, _ = key.shape
|
| 812 |
+
softmax_scale = (
|
| 813 |
+
softmax_scale if softmax_scale is not None else 1.0 / (head_dim**0.5)
|
| 814 |
+
)
|
| 815 |
+
softmax_threshold = (
|
| 816 |
+
softmax_threshold if softmax_threshold is not None else 1 / seqlen_k
|
| 817 |
+
)
|
| 818 |
+
gate_threshold = (
|
| 819 |
+
gate_threshold if gate_threshold is not None else head_dim / seqlen_k
|
| 820 |
+
)
|
| 821 |
+
qhead_per_kvhead = num_heads_q // num_heads_kv
|
| 822 |
+
if is_local and window_sizes is None:
|
| 823 |
+
window_sizes = utils.window_sizes_heuristic(seqlen_k, num_heads_kv, device)
|
| 824 |
+
elif not is_local:
|
| 825 |
+
window_sizes = torch.zeros((num_heads_kv, 4), dtype=torch.int32, device=device)
|
| 826 |
+
|
| 827 |
+
if not skip_checks:
|
| 828 |
+
assert_inputs.assert_dec_inputs(
|
| 829 |
+
query,
|
| 830 |
+
key,
|
| 831 |
+
value,
|
| 832 |
+
alpha=alpha,
|
| 833 |
+
delta=delta,
|
| 834 |
+
query_scale=query_scale,
|
| 835 |
+
key_scale=key_scale,
|
| 836 |
+
value_scale=value_scale,
|
| 837 |
+
window_sizes=window_sizes,
|
| 838 |
+
cu_seqlens_k=None,
|
| 839 |
+
seqused_k=None,
|
| 840 |
+
num_heads_q=num_heads_q,
|
| 841 |
+
num_heads_kv=num_heads_kv,
|
| 842 |
+
head_dim=head_dim,
|
| 843 |
+
is_quant=is_quant,
|
| 844 |
+
device=device,
|
| 845 |
+
)
|
| 846 |
+
|
| 847 |
+
TILE_K = max(triton.next_power_of_2(head_dim), 16)
|
| 848 |
+
|
| 849 |
+
launch_config = launch_template.load_launch_config(
|
| 850 |
+
device=device,
|
| 851 |
+
kernel_name="dec_gated",
|
| 852 |
+
seqlen_q=1,
|
| 853 |
+
seqlen_k=seqlen_k,
|
| 854 |
+
tile_k=TILE_K,
|
| 855 |
+
is_local=is_local,
|
| 856 |
+
qhead_per_kvhead=qhead_per_kvhead,
|
| 857 |
+
is_quant=is_quant,
|
| 858 |
+
)
|
| 859 |
+
if launch_config is not None and not is_autotune:
|
| 860 |
+
kernel = _dec_gated_kernel
|
| 861 |
+
TILE_M, TILE_N, num_warps, num_stages, num_ctas = launch_config
|
| 862 |
+
else:
|
| 863 |
+
kernel = _get_autotuned_kernel()
|
| 864 |
+
TILE_M = max(triton.next_power_of_2(qhead_per_kvhead), 16)
|
| 865 |
+
TILE_N = 128
|
| 866 |
+
num_warps = num_stages = num_ctas = None
|
| 867 |
+
|
| 868 |
+
num_splits = utils.num_splits_heuristic(
|
| 869 |
+
seqlen_q=qhead_per_kvhead,
|
| 870 |
+
seqlen_k=seqlen_k,
|
| 871 |
+
num_SMs=num_SMs,
|
| 872 |
+
TILE_M=TILE_M,
|
| 873 |
+
TILE_N=TILE_N,
|
| 874 |
+
max_split_blocks=utils.max_split_blocks_from_window_sizes(window_sizes, TILE_N)
|
| 875 |
+
if is_local
|
| 876 |
+
else None,
|
| 877 |
+
)
|
| 878 |
+
|
| 879 |
+
out_dtype = torch.bfloat16 if is_quant else query.dtype
|
| 880 |
+
out = (
|
| 881 |
+
out
|
| 882 |
+
if out is not None
|
| 883 |
+
else torch.empty(query.shape, dtype=out_dtype, device=device)
|
| 884 |
+
)
|
| 885 |
+
lse = (
|
| 886 |
+
lse
|
| 887 |
+
if lse is not None
|
| 888 |
+
else torch.empty((batch_size, num_heads_q), dtype=torch.float32, device=device)
|
| 889 |
+
)
|
| 890 |
+
|
| 891 |
+
out_partial = torch.empty(
|
| 892 |
+
(num_splits, batch_size, num_heads_q, head_dim),
|
| 893 |
+
dtype=torch.float32,
|
| 894 |
+
device=device,
|
| 895 |
+
)
|
| 896 |
+
lse_partial = torch.empty(
|
| 897 |
+
(num_splits, batch_size, num_heads_q),
|
| 898 |
+
dtype=torch.float32,
|
| 899 |
+
device=device,
|
| 900 |
+
)
|
| 901 |
+
|
| 902 |
+
if not is_quant:
|
| 903 |
+
query_scale = torch.ones(1, device=device, dtype=query.dtype)
|
| 904 |
+
key_scale = torch.ones(1, device=device, dtype=query.dtype)
|
| 905 |
+
value_scale = torch.ones(1, device=device, dtype=query.dtype)
|
| 906 |
+
|
| 907 |
+
grid = launch_grid.get_dec_grid(
|
| 908 |
+
batch_size=batch_size,
|
| 909 |
+
num_heads_kv=num_heads_kv,
|
| 910 |
+
num_splits=num_splits,
|
| 911 |
+
)
|
| 912 |
+
|
| 913 |
+
triton.set_allocator(utils.alloc_fn)
|
| 914 |
+
|
| 915 |
+
kernel[grid](
|
| 916 |
+
query,
|
| 917 |
+
key,
|
| 918 |
+
value,
|
| 919 |
+
alpha,
|
| 920 |
+
delta,
|
| 921 |
+
out_partial,
|
| 922 |
+
lse_partial,
|
| 923 |
+
softmax_scale,
|
| 924 |
+
query_scale,
|
| 925 |
+
key_scale,
|
| 926 |
+
value_scale,
|
| 927 |
+
softmax_threshold,
|
| 928 |
+
gate_threshold,
|
| 929 |
+
window_sizes,
|
| 930 |
+
query.stride(0),
|
| 931 |
+
query.stride(-2),
|
| 932 |
+
1,
|
| 933 |
+
key.stride(0),
|
| 934 |
+
key.stride(-2),
|
| 935 |
+
key.stride(-3),
|
| 936 |
+
value.stride(0),
|
| 937 |
+
value.stride(-2),
|
| 938 |
+
value.stride(-3),
|
| 939 |
+
alpha.stride(0),
|
| 940 |
+
1,
|
| 941 |
+
delta.stride(0),
|
| 942 |
+
delta.stride(-2),
|
| 943 |
+
out_partial.stride(1),
|
| 944 |
+
out_partial.stride(-2),
|
| 945 |
+
1,
|
| 946 |
+
out_partial.stride(0),
|
| 947 |
+
lse_partial.stride(1),
|
| 948 |
+
lse_partial.stride(-1),
|
| 949 |
+
1,
|
| 950 |
+
lse_partial.stride(0),
|
| 951 |
+
window_sizes.stride(0),
|
| 952 |
+
None,
|
| 953 |
+
None,
|
| 954 |
+
None,
|
| 955 |
+
None,
|
| 956 |
+
num_splits,
|
| 957 |
+
seqlen_q=1,
|
| 958 |
+
seqlen_k=seqlen_k,
|
| 959 |
+
head_dim=head_dim,
|
| 960 |
+
SEQLEN_Q_CACHE=0,
|
| 961 |
+
SEQLEN_K_CACHE=seqlen_k // 1024,
|
| 962 |
+
QHEAD_PER_KVHEAD_PACKGQA=qhead_per_kvhead,
|
| 963 |
+
TILE_M=TILE_M,
|
| 964 |
+
TILE_N=TILE_N,
|
| 965 |
+
TILE_K=TILE_K,
|
| 966 |
+
IS_LOCAL=is_local,
|
| 967 |
+
HAS_CU_SEQLENS_Q=False,
|
| 968 |
+
HAS_CU_SEQLENS_K=False,
|
| 969 |
+
HAS_SEQUSED_Q=False,
|
| 970 |
+
HAS_SEQUSED_K=False,
|
| 971 |
+
IS_LOGSIGMOID_GATE=is_logsigmoid_gate,
|
| 972 |
+
num_warps=num_warps,
|
| 973 |
+
num_stages=num_stages,
|
| 974 |
+
num_ctas=num_ctas,
|
| 975 |
+
)
|
| 976 |
+
|
| 977 |
+
if launch_config is None or is_autotune:
|
| 978 |
+
best = launch_template.extract_best_config(_get_autotuned_kernel())
|
| 979 |
+
if best is not None:
|
| 980 |
+
launch_template.store_launch_config(
|
| 981 |
+
device=device,
|
| 982 |
+
kernel_name="dec_gated",
|
| 983 |
+
seqlen_q=1,
|
| 984 |
+
seqlen_k=seqlen_k,
|
| 985 |
+
tile_k=TILE_K,
|
| 986 |
+
config=best,
|
| 987 |
+
is_local=is_local,
|
| 988 |
+
qhead_per_kvhead=qhead_per_kvhead,
|
| 989 |
+
is_quant=is_quant,
|
| 990 |
+
)
|
| 991 |
+
|
| 992 |
+
flash_dec_combine._flash_attn_dec_combine(
|
| 993 |
+
out_partial,
|
| 994 |
+
lse_partial,
|
| 995 |
+
out,
|
| 996 |
+
lse,
|
| 997 |
+
)
|
| 998 |
+
|
| 999 |
+
return out, lse
|
| 1000 |
+
|
| 1001 |
+
|
| 1002 |
+
def _flash_gated_attn_varlen_decode(
|
| 1003 |
+
query: torch.Tensor,
|
| 1004 |
+
key: torch.Tensor,
|
| 1005 |
+
value: torch.Tensor,
|
| 1006 |
+
alpha: torch.Tensor,
|
| 1007 |
+
delta: torch.Tensor,
|
| 1008 |
+
cu_seqlens_k: torch.Tensor,
|
| 1009 |
+
max_seqlen_k: int,
|
| 1010 |
+
softmax_scale: float = None,
|
| 1011 |
+
softmax_threshold: float = None,
|
| 1012 |
+
gate_threshold: float = None,
|
| 1013 |
+
is_logsigmoid_gate: bool = True,
|
| 1014 |
+
query_scale: Optional[torch.Tensor] = None,
|
| 1015 |
+
key_scale: Optional[torch.Tensor] = None,
|
| 1016 |
+
value_scale: Optional[torch.Tensor] = None,
|
| 1017 |
+
window_sizes: Optional[torch.Tensor] = None,
|
| 1018 |
+
is_local: bool = False,
|
| 1019 |
+
is_quant: bool = False,
|
| 1020 |
+
seqused_k: Optional[torch.Tensor] = None,
|
| 1021 |
+
out: Optional[torch.Tensor] = None,
|
| 1022 |
+
lse: Optional[torch.Tensor] = None,
|
| 1023 |
+
is_autotune: bool = False,
|
| 1024 |
+
skip_checks: bool = False,
|
| 1025 |
+
) -> Tuple[torch.Tensor, torch.Tensor]:
|
| 1026 |
+
device = query.device
|
| 1027 |
+
num_SMs = cache_utils.get_device_num_sms(device)
|
| 1028 |
+
batch_size, num_heads_q, head_dim = query.shape
|
| 1029 |
+
_, num_heads_kv, _ = key.shape
|
| 1030 |
+
seqlen_k = max_seqlen_k
|
| 1031 |
+
softmax_scale = (
|
| 1032 |
+
softmax_scale if softmax_scale is not None else 1.0 / (head_dim**0.5)
|
| 1033 |
+
)
|
| 1034 |
+
softmax_threshold = (
|
| 1035 |
+
softmax_threshold if softmax_threshold is not None else 1 / seqlen_k
|
| 1036 |
+
)
|
| 1037 |
+
gate_threshold = (
|
| 1038 |
+
gate_threshold if gate_threshold is not None else head_dim / seqlen_k
|
| 1039 |
+
)
|
| 1040 |
+
qhead_per_kvhead = num_heads_q // num_heads_kv
|
| 1041 |
+
if is_local and window_sizes is None:
|
| 1042 |
+
window_sizes = utils.window_sizes_heuristic(seqlen_k, num_heads_kv, device)
|
| 1043 |
+
elif not is_local:
|
| 1044 |
+
window_sizes = torch.zeros((num_heads_kv, 4), dtype=torch.int32, device=device)
|
| 1045 |
+
|
| 1046 |
+
if not skip_checks:
|
| 1047 |
+
assert_inputs.assert_dec_inputs(
|
| 1048 |
+
query,
|
| 1049 |
+
key,
|
| 1050 |
+
value,
|
| 1051 |
+
alpha=alpha,
|
| 1052 |
+
delta=delta,
|
| 1053 |
+
query_scale=query_scale,
|
| 1054 |
+
key_scale=key_scale,
|
| 1055 |
+
value_scale=value_scale,
|
| 1056 |
+
window_sizes=window_sizes,
|
| 1057 |
+
cu_seqlens_k=cu_seqlens_k,
|
| 1058 |
+
seqused_k=seqused_k,
|
| 1059 |
+
num_heads_q=num_heads_q,
|
| 1060 |
+
num_heads_kv=num_heads_kv,
|
| 1061 |
+
head_dim=head_dim,
|
| 1062 |
+
is_quant=is_quant,
|
| 1063 |
+
device=device,
|
| 1064 |
+
)
|
| 1065 |
+
|
| 1066 |
+
TILE_K = max(triton.next_power_of_2(head_dim), 16)
|
| 1067 |
+
|
| 1068 |
+
launch_config = launch_template.load_launch_config(
|
| 1069 |
+
device=device,
|
| 1070 |
+
kernel_name="dec_gated",
|
| 1071 |
+
seqlen_q=1,
|
| 1072 |
+
seqlen_k=seqlen_k,
|
| 1073 |
+
tile_k=TILE_K,
|
| 1074 |
+
is_local=is_local,
|
| 1075 |
+
qhead_per_kvhead=qhead_per_kvhead,
|
| 1076 |
+
is_quant=is_quant,
|
| 1077 |
+
)
|
| 1078 |
+
if launch_config is not None and not is_autotune:
|
| 1079 |
+
kernel = _dec_gated_kernel
|
| 1080 |
+
TILE_M, TILE_N, num_warps, num_stages, num_ctas = launch_config
|
| 1081 |
+
else:
|
| 1082 |
+
kernel = _get_autotuned_kernel()
|
| 1083 |
+
TILE_M = max(triton.next_power_of_2(qhead_per_kvhead), 16)
|
| 1084 |
+
TILE_N = 128
|
| 1085 |
+
num_warps = num_stages = num_ctas = None
|
| 1086 |
+
|
| 1087 |
+
num_splits = utils.num_splits_heuristic(
|
| 1088 |
+
seqlen_q=qhead_per_kvhead,
|
| 1089 |
+
seqlen_k=seqlen_k,
|
| 1090 |
+
num_SMs=num_SMs,
|
| 1091 |
+
TILE_M=TILE_M,
|
| 1092 |
+
TILE_N=TILE_N,
|
| 1093 |
+
max_split_blocks=utils.max_split_blocks_from_window_sizes(window_sizes, TILE_N)
|
| 1094 |
+
if is_local
|
| 1095 |
+
else None,
|
| 1096 |
+
)
|
| 1097 |
+
|
| 1098 |
+
out_dtype = torch.bfloat16 if is_quant else query.dtype
|
| 1099 |
+
out = (
|
| 1100 |
+
out
|
| 1101 |
+
if out is not None
|
| 1102 |
+
else torch.empty(query.shape, dtype=out_dtype, device=device)
|
| 1103 |
+
)
|
| 1104 |
+
lse = (
|
| 1105 |
+
lse
|
| 1106 |
+
if lse is not None
|
| 1107 |
+
else torch.empty((batch_size, num_heads_q), dtype=torch.float32, device=device)
|
| 1108 |
+
)
|
| 1109 |
+
|
| 1110 |
+
out_partial = torch.empty(
|
| 1111 |
+
(num_splits, batch_size, num_heads_q, head_dim),
|
| 1112 |
+
dtype=torch.float32,
|
| 1113 |
+
device=device,
|
| 1114 |
+
)
|
| 1115 |
+
lse_partial = torch.empty(
|
| 1116 |
+
(num_splits, batch_size, num_heads_q),
|
| 1117 |
+
dtype=torch.float32,
|
| 1118 |
+
device=device,
|
| 1119 |
+
)
|
| 1120 |
+
|
| 1121 |
+
if not is_quant:
|
| 1122 |
+
query_scale = torch.ones(1, device=device, dtype=query.dtype)
|
| 1123 |
+
key_scale = torch.ones(1, device=device, dtype=query.dtype)
|
| 1124 |
+
value_scale = torch.ones(1, device=device, dtype=query.dtype)
|
| 1125 |
+
|
| 1126 |
+
grid = launch_grid.get_dec_grid(
|
| 1127 |
+
batch_size=batch_size,
|
| 1128 |
+
num_heads_kv=num_heads_kv,
|
| 1129 |
+
num_splits=num_splits,
|
| 1130 |
+
)
|
| 1131 |
+
|
| 1132 |
+
triton.set_allocator(utils.alloc_fn)
|
| 1133 |
+
|
| 1134 |
+
kernel[grid](
|
| 1135 |
+
query,
|
| 1136 |
+
key,
|
| 1137 |
+
value,
|
| 1138 |
+
alpha,
|
| 1139 |
+
delta,
|
| 1140 |
+
out_partial,
|
| 1141 |
+
lse_partial,
|
| 1142 |
+
softmax_scale,
|
| 1143 |
+
query_scale,
|
| 1144 |
+
key_scale,
|
| 1145 |
+
value_scale,
|
| 1146 |
+
softmax_threshold,
|
| 1147 |
+
gate_threshold,
|
| 1148 |
+
window_sizes,
|
| 1149 |
+
query.stride(0),
|
| 1150 |
+
query.stride(-2),
|
| 1151 |
+
1,
|
| 1152 |
+
0,
|
| 1153 |
+
key.stride(-2),
|
| 1154 |
+
key.stride(0),
|
| 1155 |
+
0,
|
| 1156 |
+
value.stride(-2),
|
| 1157 |
+
value.stride(0),
|
| 1158 |
+
alpha.stride(0),
|
| 1159 |
+
1,
|
| 1160 |
+
0,
|
| 1161 |
+
delta.stride(-2),
|
| 1162 |
+
out_partial.stride(1),
|
| 1163 |
+
out_partial.stride(-2),
|
| 1164 |
+
1,
|
| 1165 |
+
out_partial.stride(0),
|
| 1166 |
+
lse_partial.stride(1),
|
| 1167 |
+
lse_partial.stride(-1),
|
| 1168 |
+
1,
|
| 1169 |
+
lse_partial.stride(0),
|
| 1170 |
+
window_sizes.stride(0),
|
| 1171 |
+
None,
|
| 1172 |
+
cu_seqlens_k,
|
| 1173 |
+
None,
|
| 1174 |
+
seqused_k,
|
| 1175 |
+
num_splits,
|
| 1176 |
+
seqlen_q=1,
|
| 1177 |
+
seqlen_k=seqlen_k,
|
| 1178 |
+
head_dim=head_dim,
|
| 1179 |
+
SEQLEN_Q_CACHE=0,
|
| 1180 |
+
SEQLEN_K_CACHE=seqlen_k // 1024,
|
| 1181 |
+
QHEAD_PER_KVHEAD_PACKGQA=qhead_per_kvhead,
|
| 1182 |
+
TILE_M=TILE_M,
|
| 1183 |
+
TILE_N=TILE_N,
|
| 1184 |
+
TILE_K=TILE_K,
|
| 1185 |
+
IS_LOCAL=is_local,
|
| 1186 |
+
HAS_CU_SEQLENS_Q=False,
|
| 1187 |
+
HAS_CU_SEQLENS_K=True,
|
| 1188 |
+
HAS_SEQUSED_Q=False,
|
| 1189 |
+
HAS_SEQUSED_K=seqused_k is not None,
|
| 1190 |
+
IS_LOGSIGMOID_GATE=is_logsigmoid_gate,
|
| 1191 |
+
num_warps=num_warps,
|
| 1192 |
+
num_stages=num_stages,
|
| 1193 |
+
num_ctas=num_ctas,
|
| 1194 |
+
)
|
| 1195 |
+
|
| 1196 |
+
if launch_config is None or is_autotune:
|
| 1197 |
+
best = launch_template.extract_best_config(_get_autotuned_kernel())
|
| 1198 |
+
if best is not None:
|
| 1199 |
+
launch_template.store_launch_config(
|
| 1200 |
+
device=device,
|
| 1201 |
+
kernel_name="dec_gated",
|
| 1202 |
+
seqlen_q=1,
|
| 1203 |
+
seqlen_k=seqlen_k,
|
| 1204 |
+
tile_k=TILE_K,
|
| 1205 |
+
config=best,
|
| 1206 |
+
is_local=is_local,
|
| 1207 |
+
qhead_per_kvhead=qhead_per_kvhead,
|
| 1208 |
+
is_quant=is_quant,
|
| 1209 |
+
)
|
| 1210 |
+
|
| 1211 |
+
flash_dec_combine._flash_attn_dec_combine(
|
| 1212 |
+
out_partial,
|
| 1213 |
+
lse_partial,
|
| 1214 |
+
out,
|
| 1215 |
+
lse,
|
| 1216 |
+
)
|
| 1217 |
+
|
| 1218 |
+
return out, lse
|
build/torch-cuda/flash_gated_fwd.py
ADDED
|
@@ -0,0 +1,1337 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from typing import Tuple, Optional
|
| 2 |
+
|
| 3 |
+
import torch
|
| 4 |
+
import triton
|
| 5 |
+
import triton.language as tl
|
| 6 |
+
|
| 7 |
+
from . import (
|
| 8 |
+
assert_inputs,
|
| 9 |
+
utils,
|
| 10 |
+
cache_utils,
|
| 11 |
+
launch_template,
|
| 12 |
+
launch_grid,
|
| 13 |
+
flash_fwd_combine,
|
| 14 |
+
kernel_repr,
|
| 15 |
+
autotuner,
|
| 16 |
+
)
|
| 17 |
+
from .scheduler import (
|
| 18 |
+
AttnFwdGridIndex,
|
| 19 |
+
AttnFwdConfig,
|
| 20 |
+
AttnFwdBlockScheduler,
|
| 21 |
+
AttnFwdPointerScheduler,
|
| 22 |
+
AttnMaskScheduler,
|
| 23 |
+
SoftmaxScheduler,
|
| 24 |
+
)
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
@triton.jit
|
| 28 |
+
def _fwd_inner_gated_kernel(
|
| 29 |
+
config: AttnFwdConfig,
|
| 30 |
+
ptrs_sched: AttnFwdPointerScheduler,
|
| 31 |
+
mask_sched: AttnMaskScheduler,
|
| 32 |
+
softmax_sched: SoftmaxScheduler,
|
| 33 |
+
skip_gate_curr,
|
| 34 |
+
acc_s,
|
| 35 |
+
acc_o,
|
| 36 |
+
q_tile,
|
| 37 |
+
a_tile,
|
| 38 |
+
k_ptrs,
|
| 39 |
+
v_ptrs,
|
| 40 |
+
d_ptrs,
|
| 41 |
+
a_max,
|
| 42 |
+
a_min,
|
| 43 |
+
gate_max,
|
| 44 |
+
row_max,
|
| 45 |
+
row_sum,
|
| 46 |
+
n_block,
|
| 47 |
+
n_block_min,
|
| 48 |
+
IS_MASK: tl.constexpr,
|
| 49 |
+
MASK_CAUSAL: tl.constexpr,
|
| 50 |
+
MASK_LOCAL: tl.constexpr,
|
| 51 |
+
MASK_SINK: tl.constexpr,
|
| 52 |
+
CHECK_INF: tl.constexpr,
|
| 53 |
+
):
|
| 54 |
+
skip_gate_next = True
|
| 55 |
+
if not skip_gate_curr:
|
| 56 |
+
if IS_MASK:
|
| 57 |
+
# Apply mask
|
| 58 |
+
acc_s = mask_sched.apply_mask(
|
| 59 |
+
acc_s=acc_s,
|
| 60 |
+
iter_block=n_block,
|
| 61 |
+
MASK_CAUSAL=MASK_CAUSAL,
|
| 62 |
+
MASK_LOCAL=MASK_LOCAL,
|
| 63 |
+
MASK_SINK=MASK_SINK,
|
| 64 |
+
)
|
| 65 |
+
|
| 66 |
+
# Apply online sparse softmax
|
| 67 |
+
p, row_max, row_sum, row_scale, skip_softmax = (
|
| 68 |
+
softmax_sched.online_sparse_softmax(
|
| 69 |
+
acc_s=acc_s,
|
| 70 |
+
row_max=row_max,
|
| 71 |
+
row_sum=row_sum,
|
| 72 |
+
softmax_threshold_log2=config.softmax_threshold_log2,
|
| 73 |
+
CHECK_INF=CHECK_INF,
|
| 74 |
+
)
|
| 75 |
+
)
|
| 76 |
+
|
| 77 |
+
if not skip_softmax:
|
| 78 |
+
# Load value tile
|
| 79 |
+
v_tile = ptrs_sched.load_v(config, v_ptrs, n_block)
|
| 80 |
+
|
| 81 |
+
# Rescale output accumulator
|
| 82 |
+
acc_o = softmax_sched.rescale_o(
|
| 83 |
+
acc_o=acc_o,
|
| 84 |
+
row_scale=row_scale,
|
| 85 |
+
)
|
| 86 |
+
|
| 87 |
+
# Update output accumulator
|
| 88 |
+
acc_o += tl.dot(p.to(v_tile.dtype), v_tile)
|
| 89 |
+
|
| 90 |
+
if n_block > n_block_min:
|
| 91 |
+
# Load next delta tile
|
| 92 |
+
d_tile = ptrs_sched.load_d(config, d_ptrs, n_block - 1)
|
| 93 |
+
d_max = tl.max(d_tile)
|
| 94 |
+
d_min = tl.min(d_tile)
|
| 95 |
+
|
| 96 |
+
# Check if any gates are active for next tile
|
| 97 |
+
gate_max, skip_gate_next = softmax_sched.online_gate(
|
| 98 |
+
a_max=a_max,
|
| 99 |
+
a_min=a_min,
|
| 100 |
+
d_max=d_max,
|
| 101 |
+
d_min=d_min,
|
| 102 |
+
gate_max=gate_max,
|
| 103 |
+
gate_threshold_log2=config.gate_threshold_log2,
|
| 104 |
+
)
|
| 105 |
+
|
| 106 |
+
if not skip_gate_next:
|
| 107 |
+
# Compute attention gates for next tile
|
| 108 |
+
acc_s = a_tile[:, None] * d_tile[None, :]
|
| 109 |
+
if config.IS_LOGSIGMOID_GATE:
|
| 110 |
+
acc_s = softmax_sched.log_sigmoid(
|
| 111 |
+
acc_s=acc_s,
|
| 112 |
+
)
|
| 113 |
+
|
| 114 |
+
# Load next key tile
|
| 115 |
+
k_tile = ptrs_sched.load_k(config, k_ptrs, n_block - 1)
|
| 116 |
+
|
| 117 |
+
# Compute attention scores for next tile
|
| 118 |
+
acc_s += tl.dot(q_tile, k_tile.T)
|
| 119 |
+
|
| 120 |
+
return (
|
| 121 |
+
skip_gate_next,
|
| 122 |
+
acc_s,
|
| 123 |
+
acc_o,
|
| 124 |
+
gate_max,
|
| 125 |
+
row_max,
|
| 126 |
+
row_sum,
|
| 127 |
+
)
|
| 128 |
+
|
| 129 |
+
|
| 130 |
+
@triton.jit(repr=kernel_repr.fwd_gated_repr)
|
| 131 |
+
def _fwd_gated_kernel(
|
| 132 |
+
Q,
|
| 133 |
+
K,
|
| 134 |
+
V,
|
| 135 |
+
A,
|
| 136 |
+
D,
|
| 137 |
+
Out,
|
| 138 |
+
Lse,
|
| 139 |
+
softmax_scale,
|
| 140 |
+
softmax_threshold,
|
| 141 |
+
gate_threshold,
|
| 142 |
+
query_scale,
|
| 143 |
+
key_scale,
|
| 144 |
+
value_scale,
|
| 145 |
+
window_sizes,
|
| 146 |
+
stride_qb,
|
| 147 |
+
stride_qh,
|
| 148 |
+
stride_qm,
|
| 149 |
+
stride_kb,
|
| 150 |
+
stride_kh,
|
| 151 |
+
stride_kn,
|
| 152 |
+
stride_vb,
|
| 153 |
+
stride_vh,
|
| 154 |
+
stride_vn,
|
| 155 |
+
stride_ab,
|
| 156 |
+
stride_ah,
|
| 157 |
+
stride_am,
|
| 158 |
+
stride_db,
|
| 159 |
+
stride_dh,
|
| 160 |
+
stride_dn,
|
| 161 |
+
stride_ob,
|
| 162 |
+
stride_oh,
|
| 163 |
+
stride_om,
|
| 164 |
+
stride_os,
|
| 165 |
+
stride_lb,
|
| 166 |
+
stride_lh,
|
| 167 |
+
stride_ls,
|
| 168 |
+
stride_wh,
|
| 169 |
+
cu_seqlens_q,
|
| 170 |
+
cu_seqlens_k,
|
| 171 |
+
seqused_q,
|
| 172 |
+
seqused_k,
|
| 173 |
+
num_splits,
|
| 174 |
+
seqlen_q,
|
| 175 |
+
seqlen_k,
|
| 176 |
+
head_dim,
|
| 177 |
+
SEQLEN_Q_CACHE: tl.constexpr,
|
| 178 |
+
SEQLEN_K_CACHE: tl.constexpr,
|
| 179 |
+
QHEAD_PER_KVHEAD: tl.constexpr,
|
| 180 |
+
PACK_GQA: tl.constexpr,
|
| 181 |
+
QHEAD_PER_KVHEAD_PACKGQA: tl.constexpr,
|
| 182 |
+
TILE_M: tl.constexpr,
|
| 183 |
+
TILE_N: tl.constexpr,
|
| 184 |
+
TILE_K: tl.constexpr,
|
| 185 |
+
IS_CAUSAL: tl.constexpr,
|
| 186 |
+
IS_LOCAL: tl.constexpr,
|
| 187 |
+
IS_SPLIT_KV: tl.constexpr,
|
| 188 |
+
IS_LOGSIGMOID_GATE: tl.constexpr,
|
| 189 |
+
IS_ADAPT_GATE: tl.constexpr,
|
| 190 |
+
HAS_CU_SEQLENS_Q: tl.constexpr,
|
| 191 |
+
HAS_CU_SEQLENS_K: tl.constexpr,
|
| 192 |
+
HAS_SEQUSED_Q: tl.constexpr,
|
| 193 |
+
HAS_SEQUSED_K: tl.constexpr,
|
| 194 |
+
):
|
| 195 |
+
# Create grid index
|
| 196 |
+
grid_idx = AttnFwdGridIndex.create(
|
| 197 |
+
num_splits=num_splits,
|
| 198 |
+
QHEAD_PER_KVHEAD=QHEAD_PER_KVHEAD,
|
| 199 |
+
IS_SPLIT_KV=IS_SPLIT_KV,
|
| 200 |
+
PACK_GQA=PACK_GQA,
|
| 201 |
+
)
|
| 202 |
+
|
| 203 |
+
# Load window sizes
|
| 204 |
+
(
|
| 205 |
+
window_size_sink,
|
| 206 |
+
window_size_left,
|
| 207 |
+
window_size_right,
|
| 208 |
+
window_size_dist,
|
| 209 |
+
) = grid_idx.load_window_sizes(
|
| 210 |
+
window_sizes=window_sizes,
|
| 211 |
+
stride_wh=stride_wh,
|
| 212 |
+
IS_LOCAL=IS_LOCAL,
|
| 213 |
+
)
|
| 214 |
+
|
| 215 |
+
# Create config
|
| 216 |
+
config = AttnFwdConfig.create(
|
| 217 |
+
softmax_scale=softmax_scale,
|
| 218 |
+
softmax_threshold=softmax_threshold,
|
| 219 |
+
gate_threshold=gate_threshold,
|
| 220 |
+
query_scale=query_scale,
|
| 221 |
+
key_scale=key_scale,
|
| 222 |
+
value_scale=value_scale,
|
| 223 |
+
m_block=grid_idx.m_block,
|
| 224 |
+
batch_idx=grid_idx.batch_idx,
|
| 225 |
+
window_size_sink=window_size_sink,
|
| 226 |
+
window_size_left=window_size_left,
|
| 227 |
+
window_size_right=window_size_right,
|
| 228 |
+
window_size_dist=window_size_dist,
|
| 229 |
+
head_dim=head_dim,
|
| 230 |
+
cu_seqlens_q=cu_seqlens_q,
|
| 231 |
+
cu_seqlens_k=cu_seqlens_k,
|
| 232 |
+
seqused_q=seqused_q,
|
| 233 |
+
seqused_k=seqused_k,
|
| 234 |
+
seqlen_q=seqlen_q,
|
| 235 |
+
seqlen_k=seqlen_k,
|
| 236 |
+
PACK_GQA=PACK_GQA,
|
| 237 |
+
QHEAD_PER_KVHEAD_PACKGQA=QHEAD_PER_KVHEAD_PACKGQA,
|
| 238 |
+
TILE_M=TILE_M,
|
| 239 |
+
TILE_N=TILE_N,
|
| 240 |
+
TILE_K=TILE_K,
|
| 241 |
+
IS_CAUSAL=IS_CAUSAL,
|
| 242 |
+
IS_LOGSIGMOID_GATE=IS_LOGSIGMOID_GATE,
|
| 243 |
+
IS_ADAPT_GATE=IS_ADAPT_GATE,
|
| 244 |
+
HAS_CU_SEQLENS_Q=HAS_CU_SEQLENS_Q,
|
| 245 |
+
HAS_CU_SEQLENS_K=HAS_CU_SEQLENS_K,
|
| 246 |
+
HAS_SEQUSED_Q=HAS_SEQUSED_Q,
|
| 247 |
+
HAS_SEQUSED_K=HAS_SEQUSED_K,
|
| 248 |
+
)
|
| 249 |
+
|
| 250 |
+
# Create pointer scheduler
|
| 251 |
+
ptrs_sched = AttnFwdPointerScheduler.create(
|
| 252 |
+
config=config,
|
| 253 |
+
Q=Q,
|
| 254 |
+
K=K,
|
| 255 |
+
V=V,
|
| 256 |
+
A=A,
|
| 257 |
+
D=D,
|
| 258 |
+
Out=Out,
|
| 259 |
+
Lse=Lse,
|
| 260 |
+
batch_idx=grid_idx.batch_idx,
|
| 261 |
+
head_idx=grid_idx.head_idx,
|
| 262 |
+
head_kv_idx=grid_idx.head_kv_idx,
|
| 263 |
+
split_idx=grid_idx.split_idx,
|
| 264 |
+
stride_qb=stride_qb,
|
| 265 |
+
stride_qh=stride_qh,
|
| 266 |
+
stride_qm=stride_qm,
|
| 267 |
+
stride_kb=stride_kb,
|
| 268 |
+
stride_kh=stride_kh,
|
| 269 |
+
stride_kn=stride_kn,
|
| 270 |
+
stride_vb=stride_vb,
|
| 271 |
+
stride_vh=stride_vh,
|
| 272 |
+
stride_vn=stride_vn,
|
| 273 |
+
stride_ab=stride_ab,
|
| 274 |
+
stride_ah=stride_ah,
|
| 275 |
+
stride_am=stride_am,
|
| 276 |
+
stride_db=stride_db,
|
| 277 |
+
stride_dh=stride_dh,
|
| 278 |
+
stride_dn=stride_dn,
|
| 279 |
+
stride_ob=stride_ob,
|
| 280 |
+
stride_oh=stride_oh,
|
| 281 |
+
stride_om=stride_om,
|
| 282 |
+
stride_os=stride_os,
|
| 283 |
+
stride_lb=stride_lb,
|
| 284 |
+
stride_lh=stride_lh,
|
| 285 |
+
stride_ls=stride_ls,
|
| 286 |
+
IS_SPLIT_KV=IS_SPLIT_KV,
|
| 287 |
+
IS_GATED=True,
|
| 288 |
+
HAS_CU_SEQLENS_Q=HAS_CU_SEQLENS_Q,
|
| 289 |
+
HAS_CU_SEQLENS_K=HAS_CU_SEQLENS_K,
|
| 290 |
+
)
|
| 291 |
+
|
| 292 |
+
# Create block scheduler
|
| 293 |
+
block_sched = AttnFwdBlockScheduler.create(
|
| 294 |
+
config=config,
|
| 295 |
+
split_idx=grid_idx.split_idx,
|
| 296 |
+
num_splits=num_splits,
|
| 297 |
+
IS_CAUSAL=IS_CAUSAL,
|
| 298 |
+
IS_LOCAL=IS_LOCAL,
|
| 299 |
+
IS_SPLIT_KV=IS_SPLIT_KV,
|
| 300 |
+
)
|
| 301 |
+
|
| 302 |
+
# Create mask scheduler
|
| 303 |
+
mask_sched = AttnMaskScheduler.create(config)
|
| 304 |
+
|
| 305 |
+
# Create softmax scheduler
|
| 306 |
+
softmax_sched = SoftmaxScheduler.create(config)
|
| 307 |
+
|
| 308 |
+
# Create pointers
|
| 309 |
+
out_ptrs = ptrs_sched.make_out_ptrs(config)
|
| 310 |
+
lse_ptrs = ptrs_sched.make_lse_ptrs(config)
|
| 311 |
+
|
| 312 |
+
# Early exit if no n_blocks to process
|
| 313 |
+
if block_sched.is_empty():
|
| 314 |
+
ptrs_sched.store_empty(config, out_ptrs, lse_ptrs, IS_SPLIT_KV=IS_SPLIT_KV)
|
| 315 |
+
return
|
| 316 |
+
|
| 317 |
+
q_ptrs = ptrs_sched.make_q_ptrs(config)
|
| 318 |
+
k_ptrs = ptrs_sched.make_k_ptrs(config)
|
| 319 |
+
v_ptrs = ptrs_sched.make_v_ptrs(config)
|
| 320 |
+
a_ptrs = ptrs_sched.make_a_ptrs(config)
|
| 321 |
+
d_ptrs = ptrs_sched.make_d_ptrs(config)
|
| 322 |
+
|
| 323 |
+
# Initialize accumulators
|
| 324 |
+
gate_max = tl.full((), float("-inf"), dtype=tl.float32)
|
| 325 |
+
row_max = tl.full((TILE_M,), float("-inf"), dtype=tl.float32)
|
| 326 |
+
row_sum = tl.zeros((TILE_M,), dtype=tl.float32)
|
| 327 |
+
acc_o = tl.zeros((TILE_M, TILE_K), dtype=tl.float32)
|
| 328 |
+
|
| 329 |
+
# Load query tile
|
| 330 |
+
q_tile = ptrs_sched.load_q(config, q_ptrs)
|
| 331 |
+
|
| 332 |
+
# Load alpha tile
|
| 333 |
+
a_tile = ptrs_sched.load_a(config, a_ptrs)
|
| 334 |
+
a_max = tl.max(a_tile)
|
| 335 |
+
a_min = tl.min(a_tile)
|
| 336 |
+
|
| 337 |
+
# Load key tile
|
| 338 |
+
k_tile = ptrs_sched.load_k(config, k_ptrs, block_sched.n_block_max - 1)
|
| 339 |
+
|
| 340 |
+
# Load delta tile
|
| 341 |
+
d_tile = ptrs_sched.load_d(config, d_ptrs, block_sched.n_block_max - 1)
|
| 342 |
+
d_max = tl.max(d_tile)
|
| 343 |
+
d_min = tl.min(d_tile)
|
| 344 |
+
|
| 345 |
+
# Check if any gates are active for current tile
|
| 346 |
+
gate_max, skip_gate_curr = softmax_sched.online_gate(
|
| 347 |
+
a_max=a_max,
|
| 348 |
+
a_min=a_min,
|
| 349 |
+
d_max=d_max,
|
| 350 |
+
d_min=d_min,
|
| 351 |
+
gate_max=gate_max,
|
| 352 |
+
gate_threshold_log2=config.gate_threshold_log2,
|
| 353 |
+
)
|
| 354 |
+
|
| 355 |
+
# Initialize skip_gate_curr to False for the first iteration
|
| 356 |
+
skip_gate_curr = False
|
| 357 |
+
|
| 358 |
+
# Compute attention gates for first tile
|
| 359 |
+
acc_s = a_tile[:, None] * d_tile[None, :]
|
| 360 |
+
if IS_LOGSIGMOID_GATE:
|
| 361 |
+
acc_s = softmax_sched.log_sigmoid(
|
| 362 |
+
acc_s=acc_s,
|
| 363 |
+
)
|
| 364 |
+
|
| 365 |
+
# Compute attention scores for first tile
|
| 366 |
+
acc_s += tl.dot(q_tile, k_tile.T)
|
| 367 |
+
|
| 368 |
+
# Process n_blocks with causal masking
|
| 369 |
+
if IS_CAUSAL or IS_LOCAL:
|
| 370 |
+
n_block_max_no_mask = block_sched.n_block_max_no_mask
|
| 371 |
+
for n_block in tl.range(
|
| 372 |
+
block_sched.n_block_max - 1, block_sched.n_block_max_no_mask - 1, -1
|
| 373 |
+
):
|
| 374 |
+
(
|
| 375 |
+
skip_gate_curr,
|
| 376 |
+
acc_s,
|
| 377 |
+
acc_o,
|
| 378 |
+
gate_max,
|
| 379 |
+
row_max,
|
| 380 |
+
row_sum,
|
| 381 |
+
) = _fwd_inner_gated_kernel(
|
| 382 |
+
config=config,
|
| 383 |
+
ptrs_sched=ptrs_sched,
|
| 384 |
+
mask_sched=mask_sched,
|
| 385 |
+
softmax_sched=softmax_sched,
|
| 386 |
+
skip_gate_curr=skip_gate_curr,
|
| 387 |
+
acc_s=acc_s,
|
| 388 |
+
acc_o=acc_o,
|
| 389 |
+
q_tile=q_tile,
|
| 390 |
+
a_tile=a_tile,
|
| 391 |
+
k_ptrs=k_ptrs,
|
| 392 |
+
v_ptrs=v_ptrs,
|
| 393 |
+
d_ptrs=d_ptrs,
|
| 394 |
+
a_max=a_max,
|
| 395 |
+
a_min=a_min,
|
| 396 |
+
gate_max=gate_max,
|
| 397 |
+
row_max=row_max,
|
| 398 |
+
row_sum=row_sum,
|
| 399 |
+
n_block=n_block,
|
| 400 |
+
n_block_min=block_sched.n_block_max_no_mask,
|
| 401 |
+
IS_MASK=True,
|
| 402 |
+
MASK_CAUSAL=IS_CAUSAL,
|
| 403 |
+
MASK_LOCAL=True if IS_LOCAL else False,
|
| 404 |
+
MASK_SINK=False,
|
| 405 |
+
CHECK_INF=True,
|
| 406 |
+
)
|
| 407 |
+
else:
|
| 408 |
+
# First iteration with seqlen masking
|
| 409 |
+
n_block = block_sched.n_block_max - 1
|
| 410 |
+
# Triton aggregate does not support attribute reassignment yet
|
| 411 |
+
# block_sched.n_block_max_no_mask = n_block
|
| 412 |
+
n_block_max_no_mask = n_block
|
| 413 |
+
|
| 414 |
+
(
|
| 415 |
+
skip_gate_curr,
|
| 416 |
+
acc_s,
|
| 417 |
+
acc_o,
|
| 418 |
+
gate_max,
|
| 419 |
+
row_max,
|
| 420 |
+
row_sum,
|
| 421 |
+
) = _fwd_inner_gated_kernel(
|
| 422 |
+
config=config,
|
| 423 |
+
ptrs_sched=ptrs_sched,
|
| 424 |
+
mask_sched=mask_sched,
|
| 425 |
+
softmax_sched=softmax_sched,
|
| 426 |
+
skip_gate_curr=skip_gate_curr,
|
| 427 |
+
acc_s=acc_s,
|
| 428 |
+
acc_o=acc_o,
|
| 429 |
+
q_tile=q_tile,
|
| 430 |
+
a_tile=a_tile,
|
| 431 |
+
k_ptrs=k_ptrs,
|
| 432 |
+
v_ptrs=v_ptrs,
|
| 433 |
+
d_ptrs=d_ptrs,
|
| 434 |
+
a_max=a_max,
|
| 435 |
+
a_min=a_min,
|
| 436 |
+
gate_max=gate_max,
|
| 437 |
+
row_max=row_max,
|
| 438 |
+
row_sum=row_sum,
|
| 439 |
+
n_block=n_block,
|
| 440 |
+
n_block_min=n_block,
|
| 441 |
+
IS_MASK=True,
|
| 442 |
+
MASK_CAUSAL=False,
|
| 443 |
+
MASK_LOCAL=False,
|
| 444 |
+
MASK_SINK=False,
|
| 445 |
+
CHECK_INF=True,
|
| 446 |
+
)
|
| 447 |
+
|
| 448 |
+
# Process n_blocks without masking
|
| 449 |
+
if not IS_LOCAL and n_block_max_no_mask > block_sched.n_block_min:
|
| 450 |
+
# Load key tile
|
| 451 |
+
k_tile = ptrs_sched.load_k(config, k_ptrs, n_block_max_no_mask - 1)
|
| 452 |
+
|
| 453 |
+
# Load delta tile
|
| 454 |
+
d_tile = ptrs_sched.load_d(config, d_ptrs, n_block_max_no_mask - 1)
|
| 455 |
+
d_max = tl.max(d_tile)
|
| 456 |
+
d_min = tl.min(d_tile)
|
| 457 |
+
|
| 458 |
+
# Check if any gates are active for current tile
|
| 459 |
+
gate_max, skip_gate_curr = softmax_sched.online_gate(
|
| 460 |
+
a_max=a_max,
|
| 461 |
+
a_min=a_min,
|
| 462 |
+
d_max=d_max,
|
| 463 |
+
d_min=d_min,
|
| 464 |
+
gate_max=gate_max,
|
| 465 |
+
gate_threshold_log2=config.gate_threshold_log2,
|
| 466 |
+
)
|
| 467 |
+
|
| 468 |
+
# Compute attention gates
|
| 469 |
+
acc_s = a_tile[:, None] * d_tile[None, :]
|
| 470 |
+
if IS_LOGSIGMOID_GATE:
|
| 471 |
+
acc_s = softmax_sched.log_sigmoid(
|
| 472 |
+
acc_s=acc_s,
|
| 473 |
+
)
|
| 474 |
+
|
| 475 |
+
# Compute attention scores
|
| 476 |
+
acc_s += tl.dot(q_tile, k_tile.T)
|
| 477 |
+
|
| 478 |
+
for n_block in tl.range(
|
| 479 |
+
n_block_max_no_mask - 1, block_sched.n_block_min - 1, -1
|
| 480 |
+
):
|
| 481 |
+
(
|
| 482 |
+
skip_gate_curr,
|
| 483 |
+
acc_s,
|
| 484 |
+
acc_o,
|
| 485 |
+
gate_max,
|
| 486 |
+
row_max,
|
| 487 |
+
row_sum,
|
| 488 |
+
) = _fwd_inner_gated_kernel(
|
| 489 |
+
config=config,
|
| 490 |
+
ptrs_sched=ptrs_sched,
|
| 491 |
+
mask_sched=mask_sched,
|
| 492 |
+
softmax_sched=softmax_sched,
|
| 493 |
+
skip_gate_curr=skip_gate_curr,
|
| 494 |
+
acc_s=acc_s,
|
| 495 |
+
acc_o=acc_o,
|
| 496 |
+
q_tile=q_tile,
|
| 497 |
+
a_tile=a_tile,
|
| 498 |
+
k_ptrs=k_ptrs,
|
| 499 |
+
v_ptrs=v_ptrs,
|
| 500 |
+
d_ptrs=d_ptrs,
|
| 501 |
+
a_max=a_max,
|
| 502 |
+
a_min=a_min,
|
| 503 |
+
gate_max=gate_max,
|
| 504 |
+
row_max=row_max,
|
| 505 |
+
row_sum=row_sum,
|
| 506 |
+
n_block=n_block,
|
| 507 |
+
n_block_min=block_sched.n_block_min,
|
| 508 |
+
IS_MASK=False,
|
| 509 |
+
MASK_CAUSAL=False,
|
| 510 |
+
MASK_LOCAL=False,
|
| 511 |
+
MASK_SINK=False,
|
| 512 |
+
CHECK_INF=False,
|
| 513 |
+
)
|
| 514 |
+
|
| 515 |
+
if IS_LOCAL:
|
| 516 |
+
# Process n_blocks with local right masking
|
| 517 |
+
if block_sched.n_block_window_max > block_sched.n_block_window_max_no_mask:
|
| 518 |
+
# Load key tile
|
| 519 |
+
k_tile = ptrs_sched.load_k(
|
| 520 |
+
config, k_ptrs, block_sched.n_block_window_max - 1
|
| 521 |
+
)
|
| 522 |
+
|
| 523 |
+
# Load delta tile
|
| 524 |
+
d_tile = ptrs_sched.load_d(
|
| 525 |
+
config, d_ptrs, block_sched.n_block_window_max - 1
|
| 526 |
+
)
|
| 527 |
+
d_max = tl.max(d_tile)
|
| 528 |
+
d_min = tl.min(d_tile)
|
| 529 |
+
|
| 530 |
+
# Check if any gates are active for current tile
|
| 531 |
+
gate_max, skip_gate_curr = softmax_sched.online_gate(
|
| 532 |
+
a_max=a_max,
|
| 533 |
+
a_min=a_min,
|
| 534 |
+
d_max=d_max,
|
| 535 |
+
d_min=d_min,
|
| 536 |
+
gate_max=gate_max,
|
| 537 |
+
gate_threshold_log2=config.gate_threshold_log2,
|
| 538 |
+
)
|
| 539 |
+
|
| 540 |
+
# Compute attention gates
|
| 541 |
+
acc_s = a_tile[:, None] * d_tile[None, :]
|
| 542 |
+
if IS_LOGSIGMOID_GATE:
|
| 543 |
+
acc_s = softmax_sched.log_sigmoid(
|
| 544 |
+
acc_s=acc_s,
|
| 545 |
+
)
|
| 546 |
+
|
| 547 |
+
# Compute attention scores
|
| 548 |
+
acc_s += tl.dot(q_tile, k_tile.T)
|
| 549 |
+
|
| 550 |
+
for n_block in tl.range(
|
| 551 |
+
block_sched.n_block_window_max - 1,
|
| 552 |
+
block_sched.n_block_window_max_no_mask - 1,
|
| 553 |
+
-1,
|
| 554 |
+
):
|
| 555 |
+
(
|
| 556 |
+
skip_gate_curr,
|
| 557 |
+
acc_s,
|
| 558 |
+
acc_o,
|
| 559 |
+
gate_max,
|
| 560 |
+
row_max,
|
| 561 |
+
row_sum,
|
| 562 |
+
) = _fwd_inner_gated_kernel(
|
| 563 |
+
config=config,
|
| 564 |
+
ptrs_sched=ptrs_sched,
|
| 565 |
+
mask_sched=mask_sched,
|
| 566 |
+
softmax_sched=softmax_sched,
|
| 567 |
+
skip_gate_curr=skip_gate_curr,
|
| 568 |
+
acc_s=acc_s,
|
| 569 |
+
acc_o=acc_o,
|
| 570 |
+
q_tile=q_tile,
|
| 571 |
+
a_tile=a_tile,
|
| 572 |
+
k_ptrs=k_ptrs,
|
| 573 |
+
v_ptrs=v_ptrs,
|
| 574 |
+
d_ptrs=d_ptrs,
|
| 575 |
+
a_max=a_max,
|
| 576 |
+
a_min=a_min,
|
| 577 |
+
gate_max=gate_max,
|
| 578 |
+
row_max=row_max,
|
| 579 |
+
row_sum=row_sum,
|
| 580 |
+
n_block=n_block,
|
| 581 |
+
n_block_min=block_sched.n_block_window_max_no_mask,
|
| 582 |
+
IS_MASK=True,
|
| 583 |
+
MASK_CAUSAL=False,
|
| 584 |
+
MASK_LOCAL=True,
|
| 585 |
+
MASK_SINK=False,
|
| 586 |
+
CHECK_INF=True,
|
| 587 |
+
)
|
| 588 |
+
|
| 589 |
+
# Process n_blocks without masking
|
| 590 |
+
if (
|
| 591 |
+
block_sched.n_block_window_max_no_mask
|
| 592 |
+
> block_sched.n_block_window_min_no_mask
|
| 593 |
+
):
|
| 594 |
+
# Load key tile
|
| 595 |
+
k_tile = ptrs_sched.load_k(
|
| 596 |
+
config, k_ptrs, block_sched.n_block_window_max_no_mask - 1
|
| 597 |
+
)
|
| 598 |
+
|
| 599 |
+
# Load delta tile
|
| 600 |
+
d_tile = ptrs_sched.load_d(
|
| 601 |
+
config, d_ptrs, block_sched.n_block_window_max_no_mask - 1
|
| 602 |
+
)
|
| 603 |
+
d_max = tl.max(d_tile)
|
| 604 |
+
d_min = tl.min(d_tile)
|
| 605 |
+
|
| 606 |
+
# Check if any gates are active for current tile
|
| 607 |
+
gate_max, skip_gate_curr = softmax_sched.online_gate(
|
| 608 |
+
a_max=a_max,
|
| 609 |
+
a_min=a_min,
|
| 610 |
+
d_max=d_max,
|
| 611 |
+
d_min=d_min,
|
| 612 |
+
gate_max=gate_max,
|
| 613 |
+
gate_threshold_log2=config.gate_threshold_log2,
|
| 614 |
+
)
|
| 615 |
+
|
| 616 |
+
# Compute attention gates
|
| 617 |
+
acc_s = a_tile[:, None] * d_tile[None, :]
|
| 618 |
+
if IS_LOGSIGMOID_GATE:
|
| 619 |
+
acc_s = softmax_sched.log_sigmoid(
|
| 620 |
+
acc_s=acc_s,
|
| 621 |
+
)
|
| 622 |
+
|
| 623 |
+
# Compute attention scores
|
| 624 |
+
acc_s += tl.dot(q_tile, k_tile.T)
|
| 625 |
+
|
| 626 |
+
for n_block in tl.range(
|
| 627 |
+
block_sched.n_block_window_max_no_mask - 1,
|
| 628 |
+
block_sched.n_block_window_min_no_mask - 1,
|
| 629 |
+
-1,
|
| 630 |
+
):
|
| 631 |
+
(
|
| 632 |
+
skip_gate_curr,
|
| 633 |
+
acc_s,
|
| 634 |
+
acc_o,
|
| 635 |
+
gate_max,
|
| 636 |
+
row_max,
|
| 637 |
+
row_sum,
|
| 638 |
+
) = _fwd_inner_gated_kernel(
|
| 639 |
+
config=config,
|
| 640 |
+
ptrs_sched=ptrs_sched,
|
| 641 |
+
mask_sched=mask_sched,
|
| 642 |
+
softmax_sched=softmax_sched,
|
| 643 |
+
skip_gate_curr=skip_gate_curr,
|
| 644 |
+
acc_s=acc_s,
|
| 645 |
+
acc_o=acc_o,
|
| 646 |
+
q_tile=q_tile,
|
| 647 |
+
a_tile=a_tile,
|
| 648 |
+
k_ptrs=k_ptrs,
|
| 649 |
+
v_ptrs=v_ptrs,
|
| 650 |
+
d_ptrs=d_ptrs,
|
| 651 |
+
a_max=a_max,
|
| 652 |
+
a_min=a_min,
|
| 653 |
+
gate_max=gate_max,
|
| 654 |
+
row_max=row_max,
|
| 655 |
+
row_sum=row_sum,
|
| 656 |
+
n_block=n_block,
|
| 657 |
+
n_block_min=block_sched.n_block_window_min_no_mask,
|
| 658 |
+
IS_MASK=False,
|
| 659 |
+
MASK_CAUSAL=False,
|
| 660 |
+
MASK_LOCAL=False,
|
| 661 |
+
MASK_SINK=False,
|
| 662 |
+
CHECK_INF=False,
|
| 663 |
+
)
|
| 664 |
+
|
| 665 |
+
# Process n_blocks with local left masking
|
| 666 |
+
if block_sched.n_block_window_min_no_mask > block_sched.n_block_window_min:
|
| 667 |
+
# Load key tile
|
| 668 |
+
k_tile = ptrs_sched.load_k(
|
| 669 |
+
config, k_ptrs, block_sched.n_block_window_min_no_mask - 1
|
| 670 |
+
)
|
| 671 |
+
|
| 672 |
+
# Load delta tile
|
| 673 |
+
d_tile = ptrs_sched.load_d(
|
| 674 |
+
config, d_ptrs, block_sched.n_block_window_min_no_mask - 1
|
| 675 |
+
)
|
| 676 |
+
d_max = tl.max(d_tile)
|
| 677 |
+
d_min = tl.min(d_tile)
|
| 678 |
+
|
| 679 |
+
# Check if any gates are active for current tile
|
| 680 |
+
gate_max, skip_gate_curr = softmax_sched.online_gate(
|
| 681 |
+
a_max=a_max,
|
| 682 |
+
a_min=a_min,
|
| 683 |
+
d_max=d_max,
|
| 684 |
+
d_min=d_min,
|
| 685 |
+
gate_max=gate_max,
|
| 686 |
+
gate_threshold_log2=config.gate_threshold_log2,
|
| 687 |
+
)
|
| 688 |
+
|
| 689 |
+
# Compute attention gates
|
| 690 |
+
acc_s = a_tile[:, None] * d_tile[None, :]
|
| 691 |
+
if IS_LOGSIGMOID_GATE:
|
| 692 |
+
acc_s = softmax_sched.log_sigmoid(
|
| 693 |
+
acc_s=acc_s,
|
| 694 |
+
)
|
| 695 |
+
|
| 696 |
+
# Compute attention scores
|
| 697 |
+
acc_s += tl.dot(q_tile, k_tile.T)
|
| 698 |
+
|
| 699 |
+
for n_block in tl.range(
|
| 700 |
+
block_sched.n_block_window_min_no_mask - 1,
|
| 701 |
+
block_sched.n_block_window_min - 1,
|
| 702 |
+
-1,
|
| 703 |
+
):
|
| 704 |
+
(
|
| 705 |
+
skip_gate_curr,
|
| 706 |
+
acc_s,
|
| 707 |
+
acc_o,
|
| 708 |
+
gate_max,
|
| 709 |
+
row_max,
|
| 710 |
+
row_sum,
|
| 711 |
+
) = _fwd_inner_gated_kernel(
|
| 712 |
+
config=config,
|
| 713 |
+
ptrs_sched=ptrs_sched,
|
| 714 |
+
mask_sched=mask_sched,
|
| 715 |
+
softmax_sched=softmax_sched,
|
| 716 |
+
skip_gate_curr=skip_gate_curr,
|
| 717 |
+
acc_s=acc_s,
|
| 718 |
+
acc_o=acc_o,
|
| 719 |
+
q_tile=q_tile,
|
| 720 |
+
a_tile=a_tile,
|
| 721 |
+
k_ptrs=k_ptrs,
|
| 722 |
+
v_ptrs=v_ptrs,
|
| 723 |
+
d_ptrs=d_ptrs,
|
| 724 |
+
a_max=a_max,
|
| 725 |
+
a_min=a_min,
|
| 726 |
+
gate_max=gate_max,
|
| 727 |
+
row_max=row_max,
|
| 728 |
+
row_sum=row_sum,
|
| 729 |
+
n_block=n_block,
|
| 730 |
+
n_block_min=block_sched.n_block_window_min,
|
| 731 |
+
IS_MASK=True,
|
| 732 |
+
MASK_CAUSAL=False,
|
| 733 |
+
MASK_LOCAL=True,
|
| 734 |
+
MASK_SINK=False,
|
| 735 |
+
CHECK_INF=True,
|
| 736 |
+
)
|
| 737 |
+
|
| 738 |
+
# Process n_blocks with local sink masking
|
| 739 |
+
if block_sched.n_block_sink_max > block_sched.n_block_sink_min:
|
| 740 |
+
# Load key tile
|
| 741 |
+
k_tile = ptrs_sched.load_k(config, k_ptrs, block_sched.n_block_sink_max - 1)
|
| 742 |
+
|
| 743 |
+
# Load delta tile
|
| 744 |
+
d_tile = ptrs_sched.load_d(config, d_ptrs, block_sched.n_block_sink_max - 1)
|
| 745 |
+
d_max = tl.max(d_tile)
|
| 746 |
+
d_min = tl.min(d_tile)
|
| 747 |
+
|
| 748 |
+
# Check if any gates are active for current tile
|
| 749 |
+
gate_max, skip_gate_curr = softmax_sched.online_gate(
|
| 750 |
+
a_max=a_max,
|
| 751 |
+
a_min=a_min,
|
| 752 |
+
d_max=d_max,
|
| 753 |
+
d_min=d_min,
|
| 754 |
+
gate_max=gate_max,
|
| 755 |
+
gate_threshold_log2=config.gate_threshold_log2,
|
| 756 |
+
)
|
| 757 |
+
|
| 758 |
+
# Compute attention gates
|
| 759 |
+
acc_s = a_tile[:, None] * d_tile[None, :]
|
| 760 |
+
if IS_LOGSIGMOID_GATE:
|
| 761 |
+
acc_s = softmax_sched.log_sigmoid(
|
| 762 |
+
acc_s=acc_s,
|
| 763 |
+
)
|
| 764 |
+
|
| 765 |
+
# Compute attention scores
|
| 766 |
+
acc_s += tl.dot(q_tile, k_tile.T)
|
| 767 |
+
|
| 768 |
+
for n_block in tl.range(
|
| 769 |
+
block_sched.n_block_sink_max - 1,
|
| 770 |
+
block_sched.n_block_sink_min - 1,
|
| 771 |
+
-1,
|
| 772 |
+
):
|
| 773 |
+
(
|
| 774 |
+
skip_gate_curr,
|
| 775 |
+
acc_s,
|
| 776 |
+
acc_o,
|
| 777 |
+
gate_max,
|
| 778 |
+
row_max,
|
| 779 |
+
row_sum,
|
| 780 |
+
) = _fwd_inner_gated_kernel(
|
| 781 |
+
config=config,
|
| 782 |
+
ptrs_sched=ptrs_sched,
|
| 783 |
+
mask_sched=mask_sched,
|
| 784 |
+
softmax_sched=softmax_sched,
|
| 785 |
+
skip_gate_curr=skip_gate_curr,
|
| 786 |
+
acc_s=acc_s,
|
| 787 |
+
acc_o=acc_o,
|
| 788 |
+
q_tile=q_tile,
|
| 789 |
+
a_tile=a_tile,
|
| 790 |
+
k_ptrs=k_ptrs,
|
| 791 |
+
v_ptrs=v_ptrs,
|
| 792 |
+
d_ptrs=d_ptrs,
|
| 793 |
+
a_max=a_max,
|
| 794 |
+
a_min=a_min,
|
| 795 |
+
gate_max=gate_max,
|
| 796 |
+
row_max=row_max,
|
| 797 |
+
row_sum=row_sum,
|
| 798 |
+
n_block=n_block,
|
| 799 |
+
n_block_min=block_sched.n_block_sink_min,
|
| 800 |
+
IS_MASK=True,
|
| 801 |
+
MASK_CAUSAL=False,
|
| 802 |
+
MASK_LOCAL=True,
|
| 803 |
+
MASK_SINK=True,
|
| 804 |
+
CHECK_INF=True,
|
| 805 |
+
)
|
| 806 |
+
|
| 807 |
+
# Finalize softmax
|
| 808 |
+
row_scale, lse_tile = softmax_sched.finalize(
|
| 809 |
+
row_max=row_max,
|
| 810 |
+
row_sum=row_sum,
|
| 811 |
+
IS_LOG2=IS_SPLIT_KV,
|
| 812 |
+
)
|
| 813 |
+
|
| 814 |
+
# Store LSE
|
| 815 |
+
ptrs_sched.store_lse(config, lse_ptrs, lse_tile)
|
| 816 |
+
|
| 817 |
+
# Finalize rescale
|
| 818 |
+
acc_o = softmax_sched.rescale_o(
|
| 819 |
+
acc_o=acc_o,
|
| 820 |
+
row_scale=row_scale,
|
| 821 |
+
)
|
| 822 |
+
|
| 823 |
+
# Store output
|
| 824 |
+
ptrs_sched.store_out(config, out_ptrs, acc_o, IS_SPLIT_KV=IS_SPLIT_KV)
|
| 825 |
+
|
| 826 |
+
|
| 827 |
+
_fwd_gated_kernel = cache_utils.wrap_kernel(_fwd_gated_kernel)
|
| 828 |
+
|
| 829 |
+
|
| 830 |
+
_fwd_gated_kernel_autotuned = None
|
| 831 |
+
|
| 832 |
+
|
| 833 |
+
def _get_autotuned_kernel():
|
| 834 |
+
global _fwd_gated_kernel_autotuned
|
| 835 |
+
if _fwd_gated_kernel_autotuned is None:
|
| 836 |
+
jit_kernel = _fwd_gated_kernel._kernel
|
| 837 |
+
autotuned = autotuner.make_fwd_gated_autotuned_kernel(jit_kernel)
|
| 838 |
+
_fwd_gated_kernel_autotuned = autotuner.AutotunedKernel(autotuned)
|
| 839 |
+
return _fwd_gated_kernel_autotuned
|
| 840 |
+
|
| 841 |
+
|
| 842 |
+
def _flash_gated_attn_forward(
|
| 843 |
+
query: torch.Tensor,
|
| 844 |
+
key: torch.Tensor,
|
| 845 |
+
value: torch.Tensor,
|
| 846 |
+
alpha: torch.Tensor,
|
| 847 |
+
delta: torch.Tensor,
|
| 848 |
+
is_causal: bool = False,
|
| 849 |
+
softmax_scale: float = None,
|
| 850 |
+
query_scale: Optional[torch.Tensor] = None,
|
| 851 |
+
key_scale: Optional[torch.Tensor] = None,
|
| 852 |
+
value_scale: Optional[torch.Tensor] = None,
|
| 853 |
+
window_sizes: Optional[torch.Tensor] = None,
|
| 854 |
+
softmax_threshold: float = None,
|
| 855 |
+
gate_threshold: float = None,
|
| 856 |
+
is_logsigmoid_gate: bool = False,
|
| 857 |
+
is_adapt_gate: bool = True,
|
| 858 |
+
is_local: bool = False,
|
| 859 |
+
is_quant: bool = False,
|
| 860 |
+
is_split_kv: bool = False,
|
| 861 |
+
pack_gqa: bool = False,
|
| 862 |
+
out: Optional[torch.Tensor] = None,
|
| 863 |
+
lse: Optional[torch.Tensor] = None,
|
| 864 |
+
is_autotune: bool = False,
|
| 865 |
+
skip_checks: bool = False,
|
| 866 |
+
) -> Tuple[torch.Tensor, torch.Tensor, float, float, float]:
|
| 867 |
+
device = query.device
|
| 868 |
+
num_SMs = cache_utils.get_device_num_sms(device)
|
| 869 |
+
batch_size, seqlen_q, num_heads_q, head_dim = query.shape
|
| 870 |
+
_, seqlen_k, num_heads_kv, _ = key.shape
|
| 871 |
+
softmax_scale = (
|
| 872 |
+
softmax_scale if softmax_scale is not None else 1.0 / (head_dim**0.5)
|
| 873 |
+
)
|
| 874 |
+
softmax_threshold = (
|
| 875 |
+
softmax_threshold if softmax_threshold is not None else 1 / seqlen_k
|
| 876 |
+
)
|
| 877 |
+
gate_threshold = (
|
| 878 |
+
gate_threshold if gate_threshold is not None else head_dim / seqlen_k
|
| 879 |
+
)
|
| 880 |
+
qhead_per_kvhead = num_heads_q // num_heads_kv
|
| 881 |
+
qhead_per_kvhead_packgqa = num_heads_q // num_heads_kv if pack_gqa else 1
|
| 882 |
+
if is_local and window_sizes is None:
|
| 883 |
+
window_sizes = utils.window_sizes_heuristic(seqlen_k, num_heads_kv, device)
|
| 884 |
+
elif not is_local:
|
| 885 |
+
window_sizes = torch.zeros((num_heads_kv, 4), dtype=torch.int32, device=device)
|
| 886 |
+
|
| 887 |
+
if not skip_checks:
|
| 888 |
+
assert_inputs.assert_fwd_inputs(
|
| 889 |
+
query,
|
| 890 |
+
key,
|
| 891 |
+
value,
|
| 892 |
+
query_scale=query_scale,
|
| 893 |
+
key_scale=key_scale,
|
| 894 |
+
value_scale=value_scale,
|
| 895 |
+
window_sizes=window_sizes,
|
| 896 |
+
alpha=alpha,
|
| 897 |
+
delta=delta,
|
| 898 |
+
cu_seqlens_q=None,
|
| 899 |
+
cu_seqlens_k=None,
|
| 900 |
+
seqused_q=None,
|
| 901 |
+
seqused_k=None,
|
| 902 |
+
num_heads_q=num_heads_q,
|
| 903 |
+
num_heads_kv=num_heads_kv,
|
| 904 |
+
head_dim=head_dim,
|
| 905 |
+
is_quant=is_quant,
|
| 906 |
+
device=device,
|
| 907 |
+
)
|
| 908 |
+
|
| 909 |
+
TILE_K = max(triton.next_power_of_2(head_dim), 16)
|
| 910 |
+
|
| 911 |
+
_kernel_name = f"fwd_gated{'_split' if is_split_kv else ''}"
|
| 912 |
+
launch_config = launch_template.load_launch_config(
|
| 913 |
+
device=device,
|
| 914 |
+
kernel_name=_kernel_name,
|
| 915 |
+
seqlen_q=seqlen_q,
|
| 916 |
+
seqlen_k=seqlen_k,
|
| 917 |
+
tile_k=TILE_K,
|
| 918 |
+
is_local=is_local,
|
| 919 |
+
qhead_per_kvhead=qhead_per_kvhead,
|
| 920 |
+
is_causal=is_causal,
|
| 921 |
+
pack_gqa=pack_gqa,
|
| 922 |
+
is_quant=is_quant,
|
| 923 |
+
)
|
| 924 |
+
if launch_config is not None and not is_autotune:
|
| 925 |
+
kernel = _fwd_gated_kernel
|
| 926 |
+
TILE_M, TILE_N, num_warps, num_stages, num_ctas = launch_config
|
| 927 |
+
else:
|
| 928 |
+
kernel = _get_autotuned_kernel()
|
| 929 |
+
# Placeholder for pre-launch computations
|
| 930 |
+
TILE_M = TILE_N = 64
|
| 931 |
+
num_warps = num_stages = num_ctas = None
|
| 932 |
+
|
| 933 |
+
num_splits = (
|
| 934 |
+
utils.num_splits_heuristic(
|
| 935 |
+
seqlen_q=seqlen_q * qhead_per_kvhead_packgqa,
|
| 936 |
+
seqlen_k=seqlen_k,
|
| 937 |
+
num_SMs=num_SMs,
|
| 938 |
+
TILE_M=TILE_M,
|
| 939 |
+
TILE_N=TILE_N,
|
| 940 |
+
max_split_blocks=utils.max_split_blocks_from_window_sizes(
|
| 941 |
+
window_sizes, TILE_N
|
| 942 |
+
)
|
| 943 |
+
if is_local
|
| 944 |
+
else None,
|
| 945 |
+
)
|
| 946 |
+
if is_split_kv
|
| 947 |
+
else 1
|
| 948 |
+
)
|
| 949 |
+
|
| 950 |
+
out_dtype = torch.bfloat16 if is_quant else query.dtype
|
| 951 |
+
out = out if out is not None else torch.zeros_like(query, dtype=out_dtype)
|
| 952 |
+
lse = (
|
| 953 |
+
lse
|
| 954 |
+
if lse is not None
|
| 955 |
+
else torch.zeros(
|
| 956 |
+
(batch_size, num_heads_q, seqlen_q),
|
| 957 |
+
device=query.device,
|
| 958 |
+
dtype=torch.float32,
|
| 959 |
+
)
|
| 960 |
+
)
|
| 961 |
+
|
| 962 |
+
if is_split_kv:
|
| 963 |
+
out_partial = torch.empty(
|
| 964 |
+
(num_splits, batch_size, seqlen_q, num_heads_q, head_dim),
|
| 965 |
+
dtype=torch.float32,
|
| 966 |
+
device=query.device,
|
| 967 |
+
)
|
| 968 |
+
lse_partial = torch.empty(
|
| 969 |
+
(num_splits, batch_size, num_heads_q, seqlen_q),
|
| 970 |
+
dtype=torch.float32,
|
| 971 |
+
device=query.device,
|
| 972 |
+
)
|
| 973 |
+
|
| 974 |
+
if not is_quant:
|
| 975 |
+
query_scale = torch.ones(1, device=device, dtype=query.dtype)
|
| 976 |
+
key_scale = torch.ones(1, device=device, dtype=query.dtype)
|
| 977 |
+
value_scale = torch.ones(1, device=device, dtype=query.dtype)
|
| 978 |
+
|
| 979 |
+
grid = launch_grid.get_fwd_grid(
|
| 980 |
+
batch_size=batch_size,
|
| 981 |
+
seqlen_q=seqlen_q,
|
| 982 |
+
num_heads_q=num_heads_q,
|
| 983 |
+
num_heads_kv=num_heads_kv,
|
| 984 |
+
pack_gqa=pack_gqa,
|
| 985 |
+
num_splits=num_splits,
|
| 986 |
+
)
|
| 987 |
+
|
| 988 |
+
triton.set_allocator(utils.alloc_fn)
|
| 989 |
+
|
| 990 |
+
kernel[grid](
|
| 991 |
+
query,
|
| 992 |
+
key,
|
| 993 |
+
value,
|
| 994 |
+
alpha,
|
| 995 |
+
delta,
|
| 996 |
+
out if not is_split_kv else out_partial,
|
| 997 |
+
lse if not is_split_kv else lse_partial,
|
| 998 |
+
softmax_scale,
|
| 999 |
+
softmax_threshold,
|
| 1000 |
+
gate_threshold,
|
| 1001 |
+
query_scale,
|
| 1002 |
+
key_scale,
|
| 1003 |
+
value_scale,
|
| 1004 |
+
window_sizes,
|
| 1005 |
+
query.stride(0),
|
| 1006 |
+
query.stride(-2),
|
| 1007 |
+
query.stride(-3),
|
| 1008 |
+
key.stride(0),
|
| 1009 |
+
key.stride(-2),
|
| 1010 |
+
key.stride(-3),
|
| 1011 |
+
value.stride(0),
|
| 1012 |
+
value.stride(-2),
|
| 1013 |
+
value.stride(-3),
|
| 1014 |
+
alpha.stride(0),
|
| 1015 |
+
alpha.stride(-2),
|
| 1016 |
+
alpha.stride(-1),
|
| 1017 |
+
delta.stride(0),
|
| 1018 |
+
delta.stride(-2),
|
| 1019 |
+
delta.stride(-1),
|
| 1020 |
+
out.stride(0) if not is_split_kv else out_partial.stride(1),
|
| 1021 |
+
out.stride(-2) if not is_split_kv else out_partial.stride(-2),
|
| 1022 |
+
out.stride(-3) if not is_split_kv else out_partial.stride(-3),
|
| 1023 |
+
0 if not is_split_kv else out_partial.stride(0),
|
| 1024 |
+
lse.stride(0) if not is_split_kv else lse_partial.stride(1),
|
| 1025 |
+
lse.stride(-2) if not is_split_kv else lse_partial.stride(-2),
|
| 1026 |
+
0 if not is_split_kv else lse_partial.stride(0),
|
| 1027 |
+
window_sizes.stride(0),
|
| 1028 |
+
None,
|
| 1029 |
+
None,
|
| 1030 |
+
None,
|
| 1031 |
+
None,
|
| 1032 |
+
num_splits,
|
| 1033 |
+
seqlen_q=seqlen_q,
|
| 1034 |
+
seqlen_k=seqlen_k,
|
| 1035 |
+
head_dim=head_dim,
|
| 1036 |
+
SEQLEN_Q_CACHE=seqlen_q // 1024,
|
| 1037 |
+
SEQLEN_K_CACHE=seqlen_k // 1024,
|
| 1038 |
+
QHEAD_PER_KVHEAD=qhead_per_kvhead,
|
| 1039 |
+
PACK_GQA=pack_gqa,
|
| 1040 |
+
QHEAD_PER_KVHEAD_PACKGQA=qhead_per_kvhead_packgqa,
|
| 1041 |
+
TILE_M=TILE_M,
|
| 1042 |
+
TILE_N=TILE_N,
|
| 1043 |
+
TILE_K=TILE_K,
|
| 1044 |
+
IS_CAUSAL=is_causal,
|
| 1045 |
+
IS_LOCAL=is_local,
|
| 1046 |
+
IS_SPLIT_KV=is_split_kv,
|
| 1047 |
+
IS_LOGSIGMOID_GATE=is_logsigmoid_gate,
|
| 1048 |
+
IS_ADAPT_GATE=is_adapt_gate,
|
| 1049 |
+
HAS_CU_SEQLENS_Q=False,
|
| 1050 |
+
HAS_CU_SEQLENS_K=False,
|
| 1051 |
+
HAS_SEQUSED_Q=False,
|
| 1052 |
+
HAS_SEQUSED_K=False,
|
| 1053 |
+
num_warps=num_warps,
|
| 1054 |
+
num_stages=num_stages,
|
| 1055 |
+
num_ctas=num_ctas,
|
| 1056 |
+
)
|
| 1057 |
+
|
| 1058 |
+
if launch_config is None or is_autotune:
|
| 1059 |
+
best = launch_template.extract_best_config(_get_autotuned_kernel())
|
| 1060 |
+
if best is not None:
|
| 1061 |
+
launch_template.store_launch_config(
|
| 1062 |
+
device=device,
|
| 1063 |
+
kernel_name=_kernel_name,
|
| 1064 |
+
seqlen_q=seqlen_q,
|
| 1065 |
+
seqlen_k=seqlen_k,
|
| 1066 |
+
tile_k=TILE_K,
|
| 1067 |
+
config=best,
|
| 1068 |
+
is_local=is_local,
|
| 1069 |
+
qhead_per_kvhead=qhead_per_kvhead,
|
| 1070 |
+
is_causal=is_causal,
|
| 1071 |
+
pack_gqa=pack_gqa,
|
| 1072 |
+
is_quant=is_quant,
|
| 1073 |
+
)
|
| 1074 |
+
|
| 1075 |
+
if is_split_kv:
|
| 1076 |
+
flash_fwd_combine._flash_attn_fwd_combine(
|
| 1077 |
+
out_partial,
|
| 1078 |
+
lse_partial,
|
| 1079 |
+
out,
|
| 1080 |
+
lse,
|
| 1081 |
+
)
|
| 1082 |
+
|
| 1083 |
+
return out, lse, softmax_scale, softmax_threshold, gate_threshold
|
| 1084 |
+
|
| 1085 |
+
|
| 1086 |
+
def _flash_gated_attn_varlen_forward(
|
| 1087 |
+
query: torch.Tensor,
|
| 1088 |
+
key: torch.Tensor,
|
| 1089 |
+
value: torch.Tensor,
|
| 1090 |
+
alpha: torch.Tensor,
|
| 1091 |
+
delta: torch.Tensor,
|
| 1092 |
+
cu_seqlens_q: torch.Tensor,
|
| 1093 |
+
cu_seqlens_k: torch.Tensor,
|
| 1094 |
+
max_seqlen_q: int,
|
| 1095 |
+
max_seqlen_k: int,
|
| 1096 |
+
is_causal: bool = False,
|
| 1097 |
+
softmax_scale: float = None,
|
| 1098 |
+
query_scale: Optional[torch.Tensor] = None,
|
| 1099 |
+
key_scale: Optional[torch.Tensor] = None,
|
| 1100 |
+
value_scale: Optional[torch.Tensor] = None,
|
| 1101 |
+
window_sizes: Optional[torch.Tensor] = None,
|
| 1102 |
+
softmax_threshold: float = None,
|
| 1103 |
+
gate_threshold: float = None,
|
| 1104 |
+
is_logsigmoid_gate: bool = True,
|
| 1105 |
+
is_adapt_gate: bool = True,
|
| 1106 |
+
is_local: bool = False,
|
| 1107 |
+
is_quant: bool = False,
|
| 1108 |
+
is_split_kv: bool = False,
|
| 1109 |
+
pack_gqa: bool = False,
|
| 1110 |
+
seqused_q: Optional[torch.Tensor] = None,
|
| 1111 |
+
seqused_k: Optional[torch.Tensor] = None,
|
| 1112 |
+
out: Optional[torch.Tensor] = None,
|
| 1113 |
+
lse: Optional[torch.Tensor] = None,
|
| 1114 |
+
is_autotune: bool = False,
|
| 1115 |
+
skip_checks: bool = False,
|
| 1116 |
+
) -> Tuple[torch.Tensor, torch.Tensor, float, float, float]:
|
| 1117 |
+
device = query.device
|
| 1118 |
+
num_SMs = cache_utils.get_device_num_sms(device)
|
| 1119 |
+
total_seqlen_q, num_heads_q, head_dim = query.shape
|
| 1120 |
+
_, num_heads_kv, _ = key.shape
|
| 1121 |
+
batch_size = cu_seqlens_q.shape[0] - 1
|
| 1122 |
+
seqlen_q = max_seqlen_q
|
| 1123 |
+
seqlen_k = max_seqlen_k
|
| 1124 |
+
softmax_scale = (
|
| 1125 |
+
softmax_scale if softmax_scale is not None else 1.0 / (head_dim**0.5)
|
| 1126 |
+
)
|
| 1127 |
+
softmax_threshold = (
|
| 1128 |
+
softmax_threshold if softmax_threshold is not None else 1 / seqlen_k
|
| 1129 |
+
)
|
| 1130 |
+
gate_threshold = (
|
| 1131 |
+
gate_threshold if gate_threshold is not None else head_dim / seqlen_k
|
| 1132 |
+
)
|
| 1133 |
+
qhead_per_kvhead = num_heads_q // num_heads_kv
|
| 1134 |
+
qhead_per_kvhead_packgqa = num_heads_q // num_heads_kv if pack_gqa else 1
|
| 1135 |
+
if is_local and window_sizes is None:
|
| 1136 |
+
window_sizes = utils.window_sizes_heuristic(seqlen_k, num_heads_kv, device)
|
| 1137 |
+
elif not is_local:
|
| 1138 |
+
window_sizes = torch.zeros((num_heads_kv, 4), dtype=torch.int32, device=device)
|
| 1139 |
+
|
| 1140 |
+
if not skip_checks:
|
| 1141 |
+
assert_inputs.assert_fwd_inputs(
|
| 1142 |
+
query,
|
| 1143 |
+
key,
|
| 1144 |
+
value,
|
| 1145 |
+
query_scale=query_scale,
|
| 1146 |
+
key_scale=key_scale,
|
| 1147 |
+
value_scale=value_scale,
|
| 1148 |
+
window_sizes=window_sizes,
|
| 1149 |
+
alpha=alpha,
|
| 1150 |
+
delta=delta,
|
| 1151 |
+
cu_seqlens_q=cu_seqlens_q,
|
| 1152 |
+
cu_seqlens_k=cu_seqlens_k,
|
| 1153 |
+
seqused_q=seqused_q,
|
| 1154 |
+
seqused_k=seqused_k,
|
| 1155 |
+
num_heads_q=num_heads_q,
|
| 1156 |
+
num_heads_kv=num_heads_kv,
|
| 1157 |
+
head_dim=head_dim,
|
| 1158 |
+
is_quant=is_quant,
|
| 1159 |
+
device=device,
|
| 1160 |
+
)
|
| 1161 |
+
|
| 1162 |
+
TILE_K = max(triton.next_power_of_2(head_dim), 16)
|
| 1163 |
+
|
| 1164 |
+
_kernel_name = f"fwd_gated{'_split' if is_split_kv else ''}"
|
| 1165 |
+
launch_config = launch_template.load_launch_config(
|
| 1166 |
+
device=device,
|
| 1167 |
+
kernel_name=_kernel_name,
|
| 1168 |
+
seqlen_q=seqlen_q,
|
| 1169 |
+
seqlen_k=seqlen_k,
|
| 1170 |
+
tile_k=TILE_K,
|
| 1171 |
+
is_local=is_local,
|
| 1172 |
+
qhead_per_kvhead=qhead_per_kvhead,
|
| 1173 |
+
is_causal=is_causal,
|
| 1174 |
+
pack_gqa=pack_gqa,
|
| 1175 |
+
is_quant=is_quant,
|
| 1176 |
+
)
|
| 1177 |
+
if launch_config is not None and not is_autotune:
|
| 1178 |
+
kernel = _fwd_gated_kernel
|
| 1179 |
+
TILE_M, TILE_N, num_warps, num_stages, num_ctas = launch_config
|
| 1180 |
+
else:
|
| 1181 |
+
kernel = _get_autotuned_kernel()
|
| 1182 |
+
# Placeholder for pre-launch computations
|
| 1183 |
+
TILE_M = TILE_N = 64
|
| 1184 |
+
num_warps = num_stages = num_ctas = None
|
| 1185 |
+
|
| 1186 |
+
num_splits = (
|
| 1187 |
+
utils.num_splits_heuristic(
|
| 1188 |
+
seqlen_q=seqlen_q * qhead_per_kvhead_packgqa,
|
| 1189 |
+
seqlen_k=seqlen_k,
|
| 1190 |
+
num_SMs=num_SMs,
|
| 1191 |
+
TILE_M=TILE_M,
|
| 1192 |
+
TILE_N=TILE_N,
|
| 1193 |
+
max_split_blocks=utils.max_split_blocks_from_window_sizes(
|
| 1194 |
+
window_sizes, TILE_N
|
| 1195 |
+
)
|
| 1196 |
+
if is_local
|
| 1197 |
+
else None,
|
| 1198 |
+
)
|
| 1199 |
+
if is_split_kv
|
| 1200 |
+
else 1
|
| 1201 |
+
)
|
| 1202 |
+
|
| 1203 |
+
out_dtype = torch.bfloat16 if is_quant else query.dtype
|
| 1204 |
+
out = out if out is not None else torch.zeros_like(query, dtype=out_dtype)
|
| 1205 |
+
lse = (
|
| 1206 |
+
lse
|
| 1207 |
+
if lse is not None
|
| 1208 |
+
else torch.empty(
|
| 1209 |
+
(num_heads_q, total_seqlen_q),
|
| 1210 |
+
dtype=torch.float32,
|
| 1211 |
+
device=query.device,
|
| 1212 |
+
)
|
| 1213 |
+
)
|
| 1214 |
+
|
| 1215 |
+
if is_split_kv:
|
| 1216 |
+
out_partial = torch.empty(
|
| 1217 |
+
(num_splits, total_seqlen_q, num_heads_q, head_dim),
|
| 1218 |
+
dtype=torch.float32,
|
| 1219 |
+
device=query.device,
|
| 1220 |
+
)
|
| 1221 |
+
lse_partial = torch.empty(
|
| 1222 |
+
(num_splits, num_heads_q, total_seqlen_q),
|
| 1223 |
+
dtype=torch.float32,
|
| 1224 |
+
device=query.device,
|
| 1225 |
+
)
|
| 1226 |
+
|
| 1227 |
+
if not is_quant:
|
| 1228 |
+
query_scale = torch.ones(1, device=device, dtype=query.dtype)
|
| 1229 |
+
key_scale = torch.ones(1, device=device, dtype=query.dtype)
|
| 1230 |
+
value_scale = torch.ones(1, device=device, dtype=query.dtype)
|
| 1231 |
+
|
| 1232 |
+
grid = launch_grid.get_fwd_grid(
|
| 1233 |
+
batch_size=batch_size,
|
| 1234 |
+
seqlen_q=seqlen_q,
|
| 1235 |
+
num_heads_q=num_heads_q,
|
| 1236 |
+
num_heads_kv=num_heads_kv,
|
| 1237 |
+
pack_gqa=pack_gqa,
|
| 1238 |
+
num_splits=num_splits,
|
| 1239 |
+
)
|
| 1240 |
+
|
| 1241 |
+
triton.set_allocator(utils.alloc_fn)
|
| 1242 |
+
|
| 1243 |
+
kernel[grid](
|
| 1244 |
+
query,
|
| 1245 |
+
key,
|
| 1246 |
+
value,
|
| 1247 |
+
alpha,
|
| 1248 |
+
delta,
|
| 1249 |
+
out if not is_split_kv else out_partial,
|
| 1250 |
+
lse if not is_split_kv else lse_partial,
|
| 1251 |
+
softmax_scale,
|
| 1252 |
+
softmax_threshold,
|
| 1253 |
+
gate_threshold,
|
| 1254 |
+
query_scale,
|
| 1255 |
+
key_scale,
|
| 1256 |
+
value_scale,
|
| 1257 |
+
window_sizes,
|
| 1258 |
+
0,
|
| 1259 |
+
query.stride(-2),
|
| 1260 |
+
query.stride(0),
|
| 1261 |
+
0,
|
| 1262 |
+
key.stride(-2),
|
| 1263 |
+
key.stride(0),
|
| 1264 |
+
0,
|
| 1265 |
+
value.stride(-2),
|
| 1266 |
+
value.stride(0),
|
| 1267 |
+
0,
|
| 1268 |
+
alpha.stride(0),
|
| 1269 |
+
alpha.stride(-1),
|
| 1270 |
+
0,
|
| 1271 |
+
delta.stride(0),
|
| 1272 |
+
delta.stride(-1),
|
| 1273 |
+
0,
|
| 1274 |
+
out.stride(-2) if not is_split_kv else out_partial.stride(-2),
|
| 1275 |
+
out.stride(0) if not is_split_kv else out_partial.stride(-3),
|
| 1276 |
+
0 if not is_split_kv else out_partial.stride(0),
|
| 1277 |
+
0,
|
| 1278 |
+
lse.stride(-2) if not is_split_kv else lse_partial.stride(-2),
|
| 1279 |
+
0 if not is_split_kv else lse_partial.stride(0),
|
| 1280 |
+
window_sizes.stride(0),
|
| 1281 |
+
cu_seqlens_q,
|
| 1282 |
+
cu_seqlens_k,
|
| 1283 |
+
seqused_q,
|
| 1284 |
+
seqused_k,
|
| 1285 |
+
num_splits,
|
| 1286 |
+
seqlen_q=seqlen_q,
|
| 1287 |
+
seqlen_k=seqlen_k,
|
| 1288 |
+
head_dim=head_dim,
|
| 1289 |
+
SEQLEN_Q_CACHE=seqlen_q // 1024,
|
| 1290 |
+
SEQLEN_K_CACHE=seqlen_k // 1024,
|
| 1291 |
+
QHEAD_PER_KVHEAD=qhead_per_kvhead,
|
| 1292 |
+
PACK_GQA=pack_gqa,
|
| 1293 |
+
QHEAD_PER_KVHEAD_PACKGQA=qhead_per_kvhead_packgqa,
|
| 1294 |
+
TILE_M=TILE_M,
|
| 1295 |
+
TILE_N=TILE_N,
|
| 1296 |
+
TILE_K=TILE_K,
|
| 1297 |
+
IS_CAUSAL=is_causal,
|
| 1298 |
+
IS_LOCAL=is_local,
|
| 1299 |
+
IS_SPLIT_KV=is_split_kv,
|
| 1300 |
+
IS_LOGSIGMOID_GATE=is_logsigmoid_gate,
|
| 1301 |
+
IS_ADAPT_GATE=is_adapt_gate,
|
| 1302 |
+
HAS_CU_SEQLENS_Q=True,
|
| 1303 |
+
HAS_CU_SEQLENS_K=True,
|
| 1304 |
+
HAS_SEQUSED_Q=seqused_q is not None,
|
| 1305 |
+
HAS_SEQUSED_K=seqused_k is not None,
|
| 1306 |
+
num_warps=num_warps,
|
| 1307 |
+
num_stages=num_stages,
|
| 1308 |
+
num_ctas=num_ctas,
|
| 1309 |
+
)
|
| 1310 |
+
|
| 1311 |
+
if launch_config is None or is_autotune:
|
| 1312 |
+
best = launch_template.extract_best_config(_get_autotuned_kernel())
|
| 1313 |
+
if best is not None:
|
| 1314 |
+
launch_template.store_launch_config(
|
| 1315 |
+
device=device,
|
| 1316 |
+
kernel_name=_kernel_name,
|
| 1317 |
+
seqlen_q=seqlen_q,
|
| 1318 |
+
seqlen_k=seqlen_k,
|
| 1319 |
+
tile_k=TILE_K,
|
| 1320 |
+
config=best,
|
| 1321 |
+
is_local=is_local,
|
| 1322 |
+
qhead_per_kvhead=qhead_per_kvhead,
|
| 1323 |
+
is_causal=is_causal,
|
| 1324 |
+
pack_gqa=pack_gqa,
|
| 1325 |
+
is_quant=is_quant,
|
| 1326 |
+
)
|
| 1327 |
+
|
| 1328 |
+
if is_split_kv:
|
| 1329 |
+
flash_fwd_combine._flash_attn_fwd_combine(
|
| 1330 |
+
out_partial,
|
| 1331 |
+
lse_partial,
|
| 1332 |
+
out,
|
| 1333 |
+
lse,
|
| 1334 |
+
cu_seqlens_q=cu_seqlens_q,
|
| 1335 |
+
)
|
| 1336 |
+
|
| 1337 |
+
return out, lse, softmax_scale, softmax_threshold, gate_threshold
|
build/torch-cuda/flash_proj_bwd.py
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
from triton_kernels.matmul_ogs import matmul_ogs, PrecisionConfig
|
| 3 |
+
|
| 4 |
+
|
| 5 |
+
def _flash_proj_backward(
|
| 6 |
+
dout: torch.Tensor,
|
| 7 |
+
input: torch.Tensor,
|
| 8 |
+
weight: torch.Tensor,
|
| 9 |
+
) -> tuple[torch.Tensor, torch.Tensor]:
|
| 10 |
+
batch_size, seqlen, out_features = dout.shape
|
| 11 |
+
in_features = input.shape[-1]
|
| 12 |
+
|
| 13 |
+
dy = dout.view(batch_size * seqlen, out_features)
|
| 14 |
+
x = input.view(batch_size * seqlen, in_features)
|
| 15 |
+
|
| 16 |
+
precision_config = PrecisionConfig(out_dtype=dy.dtype)
|
| 17 |
+
|
| 18 |
+
dx = matmul_ogs(dy, weight, None, precision_config=precision_config)
|
| 19 |
+
dw = matmul_ogs(dy.T, x, None, precision_config=precision_config)
|
| 20 |
+
|
| 21 |
+
return dx.view(batch_size, seqlen, in_features), dw
|
build/torch-cuda/flash_proj_fwd.py
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
from triton_kernels.matmul_ogs import matmul_ogs, PrecisionConfig, FlexCtx
|
| 3 |
+
from triton_kernels.numerics import InFlexData, OutFlexData
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
def _flash_proj_forward(
|
| 7 |
+
input: torch.Tensor,
|
| 8 |
+
weight: torch.Tensor,
|
| 9 |
+
out_dtype: torch.dtype = torch.float8_e5m2,
|
| 10 |
+
prev_scale: torch.Tensor | None = None,
|
| 11 |
+
) -> tuple[torch.Tensor, torch.Tensor]:
|
| 12 |
+
batch_size, seqlen, in_features = input.shape
|
| 13 |
+
out_features = weight.shape[0]
|
| 14 |
+
device = input.device
|
| 15 |
+
|
| 16 |
+
x = input.view(batch_size * seqlen, in_features)
|
| 17 |
+
|
| 18 |
+
if prev_scale is None:
|
| 19 |
+
expected_scale = torch.tensor([1.0], dtype=torch.float32, device=device)
|
| 20 |
+
else:
|
| 21 |
+
expected_scale = prev_scale.view(1).float()
|
| 22 |
+
|
| 23 |
+
actual_scale = torch.zeros(1, dtype=torch.float32, device=device)
|
| 24 |
+
|
| 25 |
+
out_flex = OutFlexData(
|
| 26 |
+
dtype=out_dtype,
|
| 27 |
+
expected_scale=expected_scale,
|
| 28 |
+
actual_scale=actual_scale,
|
| 29 |
+
checksum_scale=None,
|
| 30 |
+
)
|
| 31 |
+
|
| 32 |
+
precision_config = PrecisionConfig(
|
| 33 |
+
flex_ctx=FlexCtx(
|
| 34 |
+
lhs_data=InFlexData(),
|
| 35 |
+
rhs_data=InFlexData(),
|
| 36 |
+
out_data=out_flex,
|
| 37 |
+
),
|
| 38 |
+
out_dtype=out_dtype,
|
| 39 |
+
)
|
| 40 |
+
|
| 41 |
+
out = matmul_ogs(x, weight.T, None, precision_config=precision_config)
|
| 42 |
+
|
| 43 |
+
descale = precision_config.flex_ctx.out_data.actual_scale.clone()
|
| 44 |
+
|
| 45 |
+
return out.view(batch_size, seqlen, out_features), descale
|
build/torch-cuda/flash_sparse_attention/__init__.py
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import ctypes
|
| 2 |
+
import importlib.util
|
| 3 |
+
import sys
|
| 4 |
+
from pathlib import Path
|
| 5 |
+
from types import ModuleType
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
def _import_from_path(file_path: Path) -> ModuleType:
|
| 9 |
+
# We cannot use the module name as-is, after adding it to `sys.modules`,
|
| 10 |
+
# it would also be used for other imports. So, we make a module name that
|
| 11 |
+
# depends on the path for it to be unique using the hex-encoded hash of
|
| 12 |
+
# the path.
|
| 13 |
+
path_hash = "{:x}".format(ctypes.c_size_t(hash(file_path.absolute())).value)
|
| 14 |
+
module_name = path_hash
|
| 15 |
+
spec = importlib.util.spec_from_file_location(module_name, file_path)
|
| 16 |
+
if spec is None:
|
| 17 |
+
raise ImportError(f"Cannot load spec for {module_name} from {file_path}")
|
| 18 |
+
module = importlib.util.module_from_spec(spec)
|
| 19 |
+
if module is None:
|
| 20 |
+
raise ImportError(f"Cannot load module {module_name} from spec")
|
| 21 |
+
sys.modules[module_name] = module
|
| 22 |
+
spec.loader.exec_module(module) # type: ignore
|
| 23 |
+
return module
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
globals().update(vars(_import_from_path(Path(__file__).parent.parent / "__init__.py")))
|
build/torch-cuda/flash_sparse_bwd.py
ADDED
|
@@ -0,0 +1,1041 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from typing import Optional, Tuple
|
| 2 |
+
|
| 3 |
+
import math
|
| 4 |
+
import torch
|
| 5 |
+
import triton
|
| 6 |
+
import triton.language as tl
|
| 7 |
+
|
| 8 |
+
from . import (
|
| 9 |
+
assert_inputs,
|
| 10 |
+
utils,
|
| 11 |
+
cache_utils,
|
| 12 |
+
launch_template,
|
| 13 |
+
launch_grid,
|
| 14 |
+
activations,
|
| 15 |
+
flash_bwd_preprocess,
|
| 16 |
+
flash_bwd_postprocess,
|
| 17 |
+
kernel_repr,
|
| 18 |
+
autotuner,
|
| 19 |
+
)
|
| 20 |
+
from .scheduler import (
|
| 21 |
+
AttnBwdGridIndex,
|
| 22 |
+
AttnBwdConfig,
|
| 23 |
+
AttnBwdBlockScheduler,
|
| 24 |
+
AttnBwdPointerScheduler,
|
| 25 |
+
AttnMaskScheduler,
|
| 26 |
+
)
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
@triton.jit
|
| 30 |
+
def _bwd_inner_sparse_kernel(
|
| 31 |
+
config,
|
| 32 |
+
ptrs_sched,
|
| 33 |
+
mask_sched,
|
| 34 |
+
acc_dk,
|
| 35 |
+
acc_dv,
|
| 36 |
+
k_tile,
|
| 37 |
+
v_tile,
|
| 38 |
+
q_ptrs,
|
| 39 |
+
do_ptrs,
|
| 40 |
+
dq_accum_ptrs,
|
| 41 |
+
lse_ptrs,
|
| 42 |
+
dpsum_ptrs,
|
| 43 |
+
m_block,
|
| 44 |
+
IS_MASK: tl.constexpr,
|
| 45 |
+
MASK_CAUSAL: tl.constexpr,
|
| 46 |
+
MASK_LOCAL: tl.constexpr,
|
| 47 |
+
MASK_SINK: tl.constexpr,
|
| 48 |
+
):
|
| 49 |
+
# Load query tile
|
| 50 |
+
q_tile = ptrs_sched.load_q(config, q_ptrs, m_block)
|
| 51 |
+
|
| 52 |
+
# Compute attention scores
|
| 53 |
+
acc_s = tl.dot(k_tile, q_tile.T)
|
| 54 |
+
|
| 55 |
+
if IS_MASK:
|
| 56 |
+
# Apply mask to attention scores
|
| 57 |
+
acc_s = mask_sched.apply_mask(
|
| 58 |
+
acc_s=acc_s,
|
| 59 |
+
iter_block=m_block,
|
| 60 |
+
MASK_CAUSAL=MASK_CAUSAL,
|
| 61 |
+
MASK_LOCAL=MASK_LOCAL,
|
| 62 |
+
MASK_SINK=MASK_SINK,
|
| 63 |
+
)
|
| 64 |
+
|
| 65 |
+
# Load LSE
|
| 66 |
+
lse_log2 = ptrs_sched.load_lse(config, lse_ptrs, m_block)
|
| 67 |
+
|
| 68 |
+
# Compute attention weights in log2-domain
|
| 69 |
+
p_log2 = acc_s * config.softmax_scale_log2 - lse_log2[None, :]
|
| 70 |
+
|
| 71 |
+
# Compute softmax threshold for this m_block
|
| 72 |
+
softmax_threshold_log2 = config.get_softmax_threshold_log2(
|
| 73 |
+
m_block=m_block,
|
| 74 |
+
)
|
| 75 |
+
|
| 76 |
+
# Update skip condition based on threshold
|
| 77 |
+
skip_softmax = tl.max(p_log2 - softmax_threshold_log2[None, :]) < 0.0
|
| 78 |
+
|
| 79 |
+
if not skip_softmax:
|
| 80 |
+
# Compute attention weights
|
| 81 |
+
p = activations.exp2(p_log2).to(q_tile.dtype)
|
| 82 |
+
|
| 83 |
+
# Load output gradients tile
|
| 84 |
+
do_tile = ptrs_sched.load_do(config, do_ptrs, m_block)
|
| 85 |
+
|
| 86 |
+
# Compute value gradients
|
| 87 |
+
acc_dv += tl.dot(p, do_tile)
|
| 88 |
+
|
| 89 |
+
# Compute attention weight gradients
|
| 90 |
+
acc_dp = tl.dot(v_tile, tl.trans(do_tile))
|
| 91 |
+
|
| 92 |
+
# Load dpsum
|
| 93 |
+
dpsum = ptrs_sched.load_dpsum(config, dpsum_ptrs, m_block)
|
| 94 |
+
|
| 95 |
+
# Compute attention score gradients
|
| 96 |
+
ds = p * (acc_dp - dpsum[None, :]).to(q_tile.dtype)
|
| 97 |
+
|
| 98 |
+
# Compute query gradients
|
| 99 |
+
dq = tl.dot(tl.trans(ds), k_tile)
|
| 100 |
+
|
| 101 |
+
# Store query gradients
|
| 102 |
+
ptrs_sched.store_dq(config, dq_accum_ptrs, m_block, dq)
|
| 103 |
+
|
| 104 |
+
# Compute key gradients
|
| 105 |
+
acc_dk += tl.dot(ds, q_tile)
|
| 106 |
+
|
| 107 |
+
return acc_dk, acc_dv
|
| 108 |
+
|
| 109 |
+
|
| 110 |
+
@triton.jit(repr=kernel_repr.bwd_sparse_repr)
|
| 111 |
+
def _bwd_sparse_kernel(
|
| 112 |
+
Q,
|
| 113 |
+
K,
|
| 114 |
+
V,
|
| 115 |
+
dO,
|
| 116 |
+
LSELog2,
|
| 117 |
+
dPsum,
|
| 118 |
+
dQaccum,
|
| 119 |
+
dK,
|
| 120 |
+
dV,
|
| 121 |
+
softmax_scale,
|
| 122 |
+
query_scale,
|
| 123 |
+
key_scale,
|
| 124 |
+
value_scale,
|
| 125 |
+
softmax_threshold,
|
| 126 |
+
window_sizes,
|
| 127 |
+
stride_qb,
|
| 128 |
+
stride_qh,
|
| 129 |
+
stride_qm,
|
| 130 |
+
stride_kb,
|
| 131 |
+
stride_kh,
|
| 132 |
+
stride_kn,
|
| 133 |
+
stride_vb,
|
| 134 |
+
stride_vh,
|
| 135 |
+
stride_vn,
|
| 136 |
+
stride_dob,
|
| 137 |
+
stride_doh,
|
| 138 |
+
stride_dom,
|
| 139 |
+
stride_lb,
|
| 140 |
+
stride_lh,
|
| 141 |
+
stride_pb,
|
| 142 |
+
stride_ph,
|
| 143 |
+
stride_dqab,
|
| 144 |
+
stride_dqah,
|
| 145 |
+
stride_dqam,
|
| 146 |
+
stride_dkb,
|
| 147 |
+
stride_dkh,
|
| 148 |
+
stride_dkn,
|
| 149 |
+
stride_dks,
|
| 150 |
+
stride_dvb,
|
| 151 |
+
stride_dvh,
|
| 152 |
+
stride_dvn,
|
| 153 |
+
stride_dvs,
|
| 154 |
+
stride_wh,
|
| 155 |
+
cu_seqlens_q,
|
| 156 |
+
cu_seqlens_k,
|
| 157 |
+
seqused_q,
|
| 158 |
+
seqused_k,
|
| 159 |
+
num_splits,
|
| 160 |
+
seqlen_q,
|
| 161 |
+
seqlen_k,
|
| 162 |
+
head_dim,
|
| 163 |
+
SEQLEN_Q_CACHE: tl.constexpr,
|
| 164 |
+
SEQLEN_K_CACHE: tl.constexpr,
|
| 165 |
+
QHEAD_PER_KVHEAD: tl.constexpr,
|
| 166 |
+
TILE_M: tl.constexpr,
|
| 167 |
+
TILE_N: tl.constexpr,
|
| 168 |
+
TILE_K: tl.constexpr,
|
| 169 |
+
IS_CAUSAL: tl.constexpr,
|
| 170 |
+
IS_LOCAL: tl.constexpr,
|
| 171 |
+
IS_SPLIT_QO: tl.constexpr,
|
| 172 |
+
HAS_CU_SEQLENS_Q: tl.constexpr,
|
| 173 |
+
HAS_CU_SEQLENS_K: tl.constexpr,
|
| 174 |
+
HAS_SEQUSED_Q: tl.constexpr,
|
| 175 |
+
HAS_SEQUSED_K: tl.constexpr,
|
| 176 |
+
):
|
| 177 |
+
# Create grid index
|
| 178 |
+
grid_idx = AttnBwdGridIndex.create(
|
| 179 |
+
num_splits=num_splits,
|
| 180 |
+
QHEAD_PER_KVHEAD=QHEAD_PER_KVHEAD,
|
| 181 |
+
IS_SPLIT_QO=IS_SPLIT_QO,
|
| 182 |
+
)
|
| 183 |
+
|
| 184 |
+
# Load window sizes
|
| 185 |
+
(
|
| 186 |
+
window_size_sink,
|
| 187 |
+
window_size_left,
|
| 188 |
+
window_size_right,
|
| 189 |
+
window_size_dist,
|
| 190 |
+
) = grid_idx.load_window_sizes(
|
| 191 |
+
window_sizes=window_sizes,
|
| 192 |
+
stride_wh=stride_wh,
|
| 193 |
+
IS_LOCAL=IS_LOCAL,
|
| 194 |
+
)
|
| 195 |
+
|
| 196 |
+
# Create config
|
| 197 |
+
config = AttnBwdConfig.create(
|
| 198 |
+
softmax_scale=softmax_scale,
|
| 199 |
+
softmax_threshold=softmax_threshold,
|
| 200 |
+
query_scale=query_scale,
|
| 201 |
+
key_scale=key_scale,
|
| 202 |
+
value_scale=value_scale,
|
| 203 |
+
n_block=grid_idx.n_block,
|
| 204 |
+
batch_idx=grid_idx.batch_idx,
|
| 205 |
+
window_size_sink=window_size_sink,
|
| 206 |
+
window_size_left=window_size_left,
|
| 207 |
+
window_size_right=window_size_right,
|
| 208 |
+
window_size_dist=window_size_dist,
|
| 209 |
+
head_dim=head_dim,
|
| 210 |
+
cu_seqlens_q=cu_seqlens_q,
|
| 211 |
+
cu_seqlens_k=cu_seqlens_k,
|
| 212 |
+
seqused_q=seqused_q,
|
| 213 |
+
seqused_k=seqused_k,
|
| 214 |
+
seqlen_q=seqlen_q,
|
| 215 |
+
seqlen_k=seqlen_k,
|
| 216 |
+
QHEAD_PER_KVHEAD=QHEAD_PER_KVHEAD,
|
| 217 |
+
TILE_M=TILE_M,
|
| 218 |
+
TILE_N=TILE_N,
|
| 219 |
+
TILE_K=TILE_K,
|
| 220 |
+
IS_CAUSAL=IS_CAUSAL,
|
| 221 |
+
HAS_CU_SEQLENS_Q=HAS_CU_SEQLENS_Q,
|
| 222 |
+
HAS_CU_SEQLENS_K=HAS_CU_SEQLENS_K,
|
| 223 |
+
HAS_SEQUSED_Q=HAS_SEQUSED_Q,
|
| 224 |
+
HAS_SEQUSED_K=HAS_SEQUSED_K,
|
| 225 |
+
)
|
| 226 |
+
|
| 227 |
+
# Create pointer scheduler
|
| 228 |
+
ptrs_sched = AttnBwdPointerScheduler.create(
|
| 229 |
+
config=config,
|
| 230 |
+
Q=Q,
|
| 231 |
+
K=K,
|
| 232 |
+
V=V,
|
| 233 |
+
dO=dO,
|
| 234 |
+
LSELog2=LSELog2,
|
| 235 |
+
dPsum=dPsum,
|
| 236 |
+
dQaccum=dQaccum,
|
| 237 |
+
dK=dK,
|
| 238 |
+
dV=dV,
|
| 239 |
+
batch_idx=grid_idx.batch_idx,
|
| 240 |
+
head_idx=grid_idx.head_idx,
|
| 241 |
+
head_kv_idx=grid_idx.head_kv_idx,
|
| 242 |
+
split_idx=grid_idx.split_idx,
|
| 243 |
+
stride_qb=stride_qb,
|
| 244 |
+
stride_qh=stride_qh,
|
| 245 |
+
stride_qm=stride_qm,
|
| 246 |
+
stride_kb=stride_kb,
|
| 247 |
+
stride_kh=stride_kh,
|
| 248 |
+
stride_kn=stride_kn,
|
| 249 |
+
stride_vb=stride_vb,
|
| 250 |
+
stride_vh=stride_vh,
|
| 251 |
+
stride_vn=stride_vn,
|
| 252 |
+
stride_dob=stride_dob,
|
| 253 |
+
stride_doh=stride_doh,
|
| 254 |
+
stride_dom=stride_dom,
|
| 255 |
+
stride_lb=stride_lb,
|
| 256 |
+
stride_lh=stride_lh,
|
| 257 |
+
stride_pb=stride_pb,
|
| 258 |
+
stride_ph=stride_ph,
|
| 259 |
+
stride_dqab=stride_dqab,
|
| 260 |
+
stride_dqah=stride_dqah,
|
| 261 |
+
stride_dqam=stride_dqam,
|
| 262 |
+
stride_dkb=stride_dkb,
|
| 263 |
+
stride_dkh=stride_dkh,
|
| 264 |
+
stride_dkn=stride_dkn,
|
| 265 |
+
stride_dks=stride_dks,
|
| 266 |
+
stride_dvb=stride_dvb,
|
| 267 |
+
stride_dvh=stride_dvh,
|
| 268 |
+
stride_dvn=stride_dvn,
|
| 269 |
+
stride_dvs=stride_dvs,
|
| 270 |
+
IS_SPLIT_QO=IS_SPLIT_QO,
|
| 271 |
+
HAS_CU_SEQLENS_Q=HAS_CU_SEQLENS_Q,
|
| 272 |
+
HAS_CU_SEQLENS_K=HAS_CU_SEQLENS_K,
|
| 273 |
+
)
|
| 274 |
+
|
| 275 |
+
# Create block scheduler
|
| 276 |
+
block_sched = AttnBwdBlockScheduler.create(
|
| 277 |
+
config=config,
|
| 278 |
+
split_idx=grid_idx.split_idx,
|
| 279 |
+
num_splits=num_splits,
|
| 280 |
+
IS_CAUSAL=IS_CAUSAL,
|
| 281 |
+
IS_LOCAL=IS_LOCAL,
|
| 282 |
+
IS_SPLIT_QO=IS_SPLIT_QO,
|
| 283 |
+
)
|
| 284 |
+
|
| 285 |
+
# Create mask scheduler
|
| 286 |
+
mask_sched = AttnMaskScheduler.create(config, SWAP_AB=True)
|
| 287 |
+
|
| 288 |
+
# Early exit if no n_blocks to process
|
| 289 |
+
if grid_idx.n_block * TILE_N >= config.actual_seqlen_k:
|
| 290 |
+
return
|
| 291 |
+
|
| 292 |
+
# Create pointers
|
| 293 |
+
q_ptrs = ptrs_sched.make_q_ptrs(config)
|
| 294 |
+
k_ptrs = ptrs_sched.make_k_ptrs(config)
|
| 295 |
+
v_ptrs = ptrs_sched.make_v_ptrs(config)
|
| 296 |
+
do_ptrs = ptrs_sched.make_do_ptrs(config)
|
| 297 |
+
lse_ptrs = ptrs_sched.make_lse_ptrs(config)
|
| 298 |
+
dpsum_ptrs = ptrs_sched.make_dpsum_ptrs(config)
|
| 299 |
+
dq_accum_ptrs = ptrs_sched.make_dq_accum_ptrs(config)
|
| 300 |
+
dk_ptrs = ptrs_sched.make_dk_ptrs(config)
|
| 301 |
+
dv_ptrs = ptrs_sched.make_dv_ptrs(config)
|
| 302 |
+
|
| 303 |
+
# Initialize accumulators
|
| 304 |
+
acc_dk = tl.zeros((TILE_N, TILE_K), dtype=tl.float32)
|
| 305 |
+
acc_dv = tl.zeros((TILE_N, TILE_K), dtype=tl.float32)
|
| 306 |
+
|
| 307 |
+
# Load key tile
|
| 308 |
+
k_tile = ptrs_sched.load_k(config, k_ptrs)
|
| 309 |
+
|
| 310 |
+
# Load value tile
|
| 311 |
+
v_tile = ptrs_sched.load_v(config, v_ptrs)
|
| 312 |
+
|
| 313 |
+
# Process m_blocks with causal masking
|
| 314 |
+
if IS_CAUSAL or IS_LOCAL:
|
| 315 |
+
for m_block in tl.range(
|
| 316 |
+
block_sched.m_block_min, block_sched.m_block_min_no_mask
|
| 317 |
+
):
|
| 318 |
+
acc_dk, acc_dv = _bwd_inner_sparse_kernel(
|
| 319 |
+
config=config,
|
| 320 |
+
ptrs_sched=ptrs_sched,
|
| 321 |
+
mask_sched=mask_sched,
|
| 322 |
+
acc_dk=acc_dk,
|
| 323 |
+
acc_dv=acc_dv,
|
| 324 |
+
k_tile=k_tile,
|
| 325 |
+
v_tile=v_tile,
|
| 326 |
+
q_ptrs=q_ptrs,
|
| 327 |
+
do_ptrs=do_ptrs,
|
| 328 |
+
dq_accum_ptrs=dq_accum_ptrs,
|
| 329 |
+
lse_ptrs=lse_ptrs,
|
| 330 |
+
dpsum_ptrs=dpsum_ptrs,
|
| 331 |
+
m_block=m_block,
|
| 332 |
+
IS_MASK=True,
|
| 333 |
+
MASK_CAUSAL=IS_CAUSAL,
|
| 334 |
+
MASK_LOCAL=True if IS_LOCAL else False,
|
| 335 |
+
MASK_SINK=False,
|
| 336 |
+
)
|
| 337 |
+
|
| 338 |
+
# Process m_blocks without masking
|
| 339 |
+
if not IS_LOCAL and block_sched.m_block_min_no_mask < block_sched.m_block_max:
|
| 340 |
+
for m_block in tl.range(
|
| 341 |
+
block_sched.m_block_min_no_mask, block_sched.m_block_max
|
| 342 |
+
):
|
| 343 |
+
acc_dk, acc_dv = _bwd_inner_sparse_kernel(
|
| 344 |
+
config=config,
|
| 345 |
+
ptrs_sched=ptrs_sched,
|
| 346 |
+
mask_sched=mask_sched,
|
| 347 |
+
acc_dk=acc_dk,
|
| 348 |
+
acc_dv=acc_dv,
|
| 349 |
+
k_tile=k_tile,
|
| 350 |
+
v_tile=v_tile,
|
| 351 |
+
q_ptrs=q_ptrs,
|
| 352 |
+
do_ptrs=do_ptrs,
|
| 353 |
+
dq_accum_ptrs=dq_accum_ptrs,
|
| 354 |
+
lse_ptrs=lse_ptrs,
|
| 355 |
+
dpsum_ptrs=dpsum_ptrs,
|
| 356 |
+
m_block=m_block,
|
| 357 |
+
IS_MASK=False,
|
| 358 |
+
MASK_CAUSAL=False,
|
| 359 |
+
MASK_LOCAL=False,
|
| 360 |
+
MASK_SINK=False,
|
| 361 |
+
)
|
| 362 |
+
|
| 363 |
+
if IS_LOCAL:
|
| 364 |
+
# Process m_blocks with local right masking
|
| 365 |
+
if block_sched.m_block_window_min < block_sched.m_block_window_min_no_mask:
|
| 366 |
+
for m_block in tl.range(
|
| 367 |
+
block_sched.m_block_window_min,
|
| 368 |
+
block_sched.m_block_window_min_no_mask,
|
| 369 |
+
):
|
| 370 |
+
acc_dk, acc_dv = _bwd_inner_sparse_kernel(
|
| 371 |
+
config=config,
|
| 372 |
+
ptrs_sched=ptrs_sched,
|
| 373 |
+
mask_sched=mask_sched,
|
| 374 |
+
acc_dk=acc_dk,
|
| 375 |
+
acc_dv=acc_dv,
|
| 376 |
+
k_tile=k_tile,
|
| 377 |
+
v_tile=v_tile,
|
| 378 |
+
q_ptrs=q_ptrs,
|
| 379 |
+
do_ptrs=do_ptrs,
|
| 380 |
+
dq_accum_ptrs=dq_accum_ptrs,
|
| 381 |
+
lse_ptrs=lse_ptrs,
|
| 382 |
+
dpsum_ptrs=dpsum_ptrs,
|
| 383 |
+
m_block=m_block,
|
| 384 |
+
IS_MASK=True,
|
| 385 |
+
MASK_CAUSAL=False,
|
| 386 |
+
MASK_LOCAL=True,
|
| 387 |
+
MASK_SINK=False,
|
| 388 |
+
)
|
| 389 |
+
|
| 390 |
+
# Process m_blocks without masking
|
| 391 |
+
if (
|
| 392 |
+
block_sched.m_block_window_min_no_mask
|
| 393 |
+
< block_sched.m_block_window_max_no_mask
|
| 394 |
+
):
|
| 395 |
+
for m_block in tl.range(
|
| 396 |
+
block_sched.m_block_window_min_no_mask,
|
| 397 |
+
block_sched.m_block_window_max_no_mask,
|
| 398 |
+
):
|
| 399 |
+
acc_dk, acc_dv = _bwd_inner_sparse_kernel(
|
| 400 |
+
config=config,
|
| 401 |
+
ptrs_sched=ptrs_sched,
|
| 402 |
+
mask_sched=mask_sched,
|
| 403 |
+
acc_dk=acc_dk,
|
| 404 |
+
acc_dv=acc_dv,
|
| 405 |
+
k_tile=k_tile,
|
| 406 |
+
v_tile=v_tile,
|
| 407 |
+
q_ptrs=q_ptrs,
|
| 408 |
+
do_ptrs=do_ptrs,
|
| 409 |
+
dq_accum_ptrs=dq_accum_ptrs,
|
| 410 |
+
lse_ptrs=lse_ptrs,
|
| 411 |
+
dpsum_ptrs=dpsum_ptrs,
|
| 412 |
+
m_block=m_block,
|
| 413 |
+
IS_MASK=False,
|
| 414 |
+
MASK_CAUSAL=False,
|
| 415 |
+
MASK_LOCAL=False,
|
| 416 |
+
MASK_SINK=False,
|
| 417 |
+
)
|
| 418 |
+
|
| 419 |
+
# Process m_blocks with local left masking
|
| 420 |
+
if block_sched.m_block_window_max_no_mask < block_sched.m_block_window_max:
|
| 421 |
+
for m_block in tl.range(
|
| 422 |
+
block_sched.m_block_window_max_no_mask,
|
| 423 |
+
block_sched.m_block_window_max,
|
| 424 |
+
):
|
| 425 |
+
acc_dk, acc_dv = _bwd_inner_sparse_kernel(
|
| 426 |
+
config=config,
|
| 427 |
+
ptrs_sched=ptrs_sched,
|
| 428 |
+
mask_sched=mask_sched,
|
| 429 |
+
acc_dk=acc_dk,
|
| 430 |
+
acc_dv=acc_dv,
|
| 431 |
+
k_tile=k_tile,
|
| 432 |
+
v_tile=v_tile,
|
| 433 |
+
q_ptrs=q_ptrs,
|
| 434 |
+
do_ptrs=do_ptrs,
|
| 435 |
+
dq_accum_ptrs=dq_accum_ptrs,
|
| 436 |
+
lse_ptrs=lse_ptrs,
|
| 437 |
+
dpsum_ptrs=dpsum_ptrs,
|
| 438 |
+
m_block=m_block,
|
| 439 |
+
IS_MASK=True,
|
| 440 |
+
MASK_CAUSAL=False,
|
| 441 |
+
MASK_LOCAL=True,
|
| 442 |
+
MASK_SINK=False,
|
| 443 |
+
)
|
| 444 |
+
|
| 445 |
+
# Process m_blocks with local sink masking
|
| 446 |
+
if block_sched.m_block_sink_max > block_sched.m_block_sink_min:
|
| 447 |
+
for m_block in tl.range(
|
| 448 |
+
block_sched.m_block_sink_min,
|
| 449 |
+
block_sched.m_block_sink_max,
|
| 450 |
+
):
|
| 451 |
+
acc_dk, acc_dv = _bwd_inner_sparse_kernel(
|
| 452 |
+
config=config,
|
| 453 |
+
ptrs_sched=ptrs_sched,
|
| 454 |
+
mask_sched=mask_sched,
|
| 455 |
+
acc_dk=acc_dk,
|
| 456 |
+
acc_dv=acc_dv,
|
| 457 |
+
k_tile=k_tile,
|
| 458 |
+
v_tile=v_tile,
|
| 459 |
+
q_ptrs=q_ptrs,
|
| 460 |
+
do_ptrs=do_ptrs,
|
| 461 |
+
dq_accum_ptrs=dq_accum_ptrs,
|
| 462 |
+
lse_ptrs=lse_ptrs,
|
| 463 |
+
dpsum_ptrs=dpsum_ptrs,
|
| 464 |
+
m_block=m_block,
|
| 465 |
+
IS_MASK=True,
|
| 466 |
+
MASK_CAUSAL=False,
|
| 467 |
+
MASK_LOCAL=True,
|
| 468 |
+
MASK_SINK=True,
|
| 469 |
+
)
|
| 470 |
+
|
| 471 |
+
# Store value gradients
|
| 472 |
+
ptrs_sched.store_dv(config, dv_ptrs, acc_dv)
|
| 473 |
+
|
| 474 |
+
# Scale key gradients
|
| 475 |
+
acc_dk = acc_dk * softmax_scale
|
| 476 |
+
|
| 477 |
+
# Store key gradients
|
| 478 |
+
ptrs_sched.store_dk(config, dk_ptrs, acc_dk)
|
| 479 |
+
|
| 480 |
+
|
| 481 |
+
_bwd_sparse_kernel = cache_utils.wrap_kernel(_bwd_sparse_kernel)
|
| 482 |
+
|
| 483 |
+
|
| 484 |
+
_bwd_sparse_kernel_autotuned = None
|
| 485 |
+
|
| 486 |
+
|
| 487 |
+
def _get_autotuned_kernel():
|
| 488 |
+
global _bwd_sparse_kernel_autotuned
|
| 489 |
+
if _bwd_sparse_kernel_autotuned is None:
|
| 490 |
+
jit_kernel = _bwd_sparse_kernel._kernel
|
| 491 |
+
autotuned = autotuner.make_bwd_sparse_autotuned_kernel(jit_kernel)
|
| 492 |
+
_bwd_sparse_kernel_autotuned = autotuner.AutotunedKernel(autotuned)
|
| 493 |
+
return _bwd_sparse_kernel_autotuned
|
| 494 |
+
|
| 495 |
+
|
| 496 |
+
def _flash_sparse_attn_backward(
|
| 497 |
+
query: torch.Tensor,
|
| 498 |
+
key: torch.Tensor,
|
| 499 |
+
value: torch.Tensor,
|
| 500 |
+
out: torch.Tensor,
|
| 501 |
+
dout: torch.Tensor,
|
| 502 |
+
lse: torch.Tensor,
|
| 503 |
+
is_causal: bool = False,
|
| 504 |
+
softmax_scale: float = None,
|
| 505 |
+
query_scale: Optional[torch.Tensor] = None,
|
| 506 |
+
key_scale: Optional[torch.Tensor] = None,
|
| 507 |
+
value_scale: Optional[torch.Tensor] = None,
|
| 508 |
+
window_sizes: Optional[torch.Tensor] = None,
|
| 509 |
+
softmax_threshold: float = None,
|
| 510 |
+
is_local: bool = False,
|
| 511 |
+
is_quant: bool = False,
|
| 512 |
+
is_split_qo: bool = False,
|
| 513 |
+
is_autotune: bool = False,
|
| 514 |
+
skip_checks: bool = False,
|
| 515 |
+
) -> Tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
|
| 516 |
+
device = query.device
|
| 517 |
+
num_SMs = cache_utils.get_device_num_sms(device)
|
| 518 |
+
batch_size, seqlen_q, num_heads_q, head_dim = query.shape
|
| 519 |
+
_, seqlen_k, num_heads_kv, _ = key.shape
|
| 520 |
+
softmax_scale = (
|
| 521 |
+
softmax_scale if softmax_scale is not None else 1.0 / (head_dim**0.5)
|
| 522 |
+
)
|
| 523 |
+
softmax_threshold = (
|
| 524 |
+
softmax_threshold if softmax_threshold is not None else 1 / seqlen_k
|
| 525 |
+
)
|
| 526 |
+
qhead_per_kvhead = num_heads_q // num_heads_kv
|
| 527 |
+
if is_local and window_sizes is None:
|
| 528 |
+
window_sizes = utils.window_sizes_heuristic(seqlen_k, num_heads_kv, device)
|
| 529 |
+
elif not is_local:
|
| 530 |
+
window_sizes = torch.zeros((num_heads_kv, 4), dtype=torch.int32, device=device)
|
| 531 |
+
|
| 532 |
+
if not skip_checks:
|
| 533 |
+
assert_inputs.assert_bwd_inputs(
|
| 534 |
+
query,
|
| 535 |
+
key,
|
| 536 |
+
value,
|
| 537 |
+
out,
|
| 538 |
+
dout,
|
| 539 |
+
lse,
|
| 540 |
+
query_scale=query_scale,
|
| 541 |
+
key_scale=key_scale,
|
| 542 |
+
value_scale=value_scale,
|
| 543 |
+
window_sizes=window_sizes,
|
| 544 |
+
cu_seqlens_q=None,
|
| 545 |
+
cu_seqlens_k=None,
|
| 546 |
+
seqused_q=None,
|
| 547 |
+
seqused_k=None,
|
| 548 |
+
num_heads_q=num_heads_q,
|
| 549 |
+
num_heads_kv=num_heads_kv,
|
| 550 |
+
head_dim=head_dim,
|
| 551 |
+
is_quant=is_quant,
|
| 552 |
+
device=device,
|
| 553 |
+
)
|
| 554 |
+
|
| 555 |
+
TILE_K = max(triton.next_power_of_2(head_dim), 16)
|
| 556 |
+
|
| 557 |
+
launch_config = launch_template.load_launch_config(
|
| 558 |
+
device=device,
|
| 559 |
+
kernel_name="bwd_sparse",
|
| 560 |
+
seqlen_q=seqlen_q,
|
| 561 |
+
seqlen_k=seqlen_k,
|
| 562 |
+
tile_k=TILE_K,
|
| 563 |
+
is_local=is_local,
|
| 564 |
+
qhead_per_kvhead=qhead_per_kvhead,
|
| 565 |
+
is_causal=is_causal,
|
| 566 |
+
is_quant=is_quant,
|
| 567 |
+
)
|
| 568 |
+
if launch_config is not None and not is_autotune:
|
| 569 |
+
kernel = _bwd_sparse_kernel
|
| 570 |
+
TILE_M, TILE_N, num_warps, num_stages, num_ctas = launch_config
|
| 571 |
+
else:
|
| 572 |
+
kernel = _get_autotuned_kernel()
|
| 573 |
+
# Placeholder for pre-launch computations
|
| 574 |
+
TILE_M = TILE_N = 64
|
| 575 |
+
num_warps = num_stages = num_ctas = None
|
| 576 |
+
|
| 577 |
+
num_splits = (
|
| 578 |
+
utils.num_splits_heuristic(
|
| 579 |
+
seqlen_q=seqlen_k,
|
| 580 |
+
seqlen_k=seqlen_q,
|
| 581 |
+
num_SMs=num_SMs,
|
| 582 |
+
TILE_M=TILE_N,
|
| 583 |
+
TILE_N=TILE_M,
|
| 584 |
+
max_split_blocks=utils.max_split_blocks_from_window_sizes(
|
| 585 |
+
window_sizes, TILE_M
|
| 586 |
+
)
|
| 587 |
+
if is_local
|
| 588 |
+
else None,
|
| 589 |
+
)
|
| 590 |
+
if is_split_qo
|
| 591 |
+
else 1
|
| 592 |
+
)
|
| 593 |
+
|
| 594 |
+
seqlen_q_rounded = int(math.ceil(seqlen_q / 128) * 128)
|
| 595 |
+
head_dim_rounded = int(math.ceil(head_dim / 32) * 32)
|
| 596 |
+
|
| 597 |
+
if not is_quant:
|
| 598 |
+
query_scale = torch.ones(1, device=device, dtype=query.dtype)
|
| 599 |
+
key_scale = torch.ones(1, device=device, dtype=query.dtype)
|
| 600 |
+
value_scale = torch.ones(1, device=device, dtype=query.dtype)
|
| 601 |
+
|
| 602 |
+
dq = torch.empty_like(query, dtype=query_scale.dtype)
|
| 603 |
+
dk = torch.empty_like(key, dtype=key_scale.dtype)
|
| 604 |
+
dv = torch.empty_like(value, dtype=value_scale.dtype)
|
| 605 |
+
lse_log2 = torch.empty(
|
| 606 |
+
(batch_size, num_heads_q, seqlen_q_rounded),
|
| 607 |
+
dtype=torch.float32,
|
| 608 |
+
device=query.device,
|
| 609 |
+
)
|
| 610 |
+
dpsum = torch.empty(
|
| 611 |
+
(batch_size, num_heads_q, seqlen_q_rounded),
|
| 612 |
+
dtype=torch.float32,
|
| 613 |
+
device=query.device,
|
| 614 |
+
)
|
| 615 |
+
dq_accum = torch.empty(
|
| 616 |
+
(batch_size, num_heads_q, seqlen_q_rounded * head_dim_rounded),
|
| 617 |
+
dtype=torch.float32,
|
| 618 |
+
device=query.device,
|
| 619 |
+
)
|
| 620 |
+
dk_accum = torch.zeros(
|
| 621 |
+
(num_splits, batch_size, seqlen_k, num_heads_kv, head_dim)
|
| 622 |
+
if is_split_qo and num_splits > 1
|
| 623 |
+
else (batch_size, seqlen_k, num_heads_kv, head_dim),
|
| 624 |
+
dtype=torch.float32,
|
| 625 |
+
device=query.device,
|
| 626 |
+
)
|
| 627 |
+
dv_accum = torch.zeros(
|
| 628 |
+
(num_splits, batch_size, seqlen_k, num_heads_kv, head_dim)
|
| 629 |
+
if is_split_qo and num_splits > 1
|
| 630 |
+
else (batch_size, seqlen_k, num_heads_kv, head_dim),
|
| 631 |
+
dtype=torch.float32,
|
| 632 |
+
device=query.device,
|
| 633 |
+
)
|
| 634 |
+
|
| 635 |
+
flash_bwd_preprocess._flash_attn_bwd_preprocess(
|
| 636 |
+
out=out,
|
| 637 |
+
dout=dout,
|
| 638 |
+
dpsum=dpsum,
|
| 639 |
+
lse=lse,
|
| 640 |
+
lse_log2=lse_log2,
|
| 641 |
+
dq_accum=dq_accum,
|
| 642 |
+
head_dim_rounded=head_dim_rounded,
|
| 643 |
+
tile_m=TILE_M,
|
| 644 |
+
tile_k=TILE_K,
|
| 645 |
+
)
|
| 646 |
+
|
| 647 |
+
grid = launch_grid.get_bwd_grid(
|
| 648 |
+
seqlen_k=seqlen_k,
|
| 649 |
+
num_heads_q=num_heads_q,
|
| 650 |
+
batch_size=batch_size,
|
| 651 |
+
num_splits=num_splits,
|
| 652 |
+
)
|
| 653 |
+
|
| 654 |
+
triton.set_allocator(utils.alloc_fn)
|
| 655 |
+
|
| 656 |
+
kernel[grid](
|
| 657 |
+
query,
|
| 658 |
+
key,
|
| 659 |
+
value,
|
| 660 |
+
dout,
|
| 661 |
+
lse_log2,
|
| 662 |
+
dpsum,
|
| 663 |
+
dq_accum,
|
| 664 |
+
dk_accum,
|
| 665 |
+
dv_accum,
|
| 666 |
+
softmax_scale,
|
| 667 |
+
query_scale,
|
| 668 |
+
key_scale,
|
| 669 |
+
value_scale,
|
| 670 |
+
softmax_threshold,
|
| 671 |
+
window_sizes,
|
| 672 |
+
query.stride(0),
|
| 673 |
+
query.stride(-2),
|
| 674 |
+
query.stride(-3),
|
| 675 |
+
key.stride(0),
|
| 676 |
+
key.stride(-2),
|
| 677 |
+
key.stride(-3),
|
| 678 |
+
value.stride(0),
|
| 679 |
+
value.stride(-2),
|
| 680 |
+
value.stride(-3),
|
| 681 |
+
dout.stride(0),
|
| 682 |
+
dout.stride(-2),
|
| 683 |
+
dout.stride(-3),
|
| 684 |
+
lse_log2.stride(0),
|
| 685 |
+
lse_log2.stride(-2),
|
| 686 |
+
dpsum.stride(0),
|
| 687 |
+
dpsum.stride(-2),
|
| 688 |
+
dq_accum.stride(0),
|
| 689 |
+
dq_accum.stride(1),
|
| 690 |
+
head_dim_rounded,
|
| 691 |
+
dk_accum.stride(-4) if is_split_qo and num_splits > 1 else dk_accum.stride(0),
|
| 692 |
+
dk_accum.stride(-2),
|
| 693 |
+
dk_accum.stride(-3),
|
| 694 |
+
dk_accum.stride(0) if is_split_qo and num_splits > 1 else 0,
|
| 695 |
+
dv_accum.stride(-4) if is_split_qo and num_splits > 1 else dv_accum.stride(0),
|
| 696 |
+
dv_accum.stride(-2),
|
| 697 |
+
dv_accum.stride(-3),
|
| 698 |
+
dv_accum.stride(0) if is_split_qo and num_splits > 1 else 0,
|
| 699 |
+
window_sizes.stride(0),
|
| 700 |
+
None,
|
| 701 |
+
None,
|
| 702 |
+
None,
|
| 703 |
+
None,
|
| 704 |
+
num_splits,
|
| 705 |
+
seqlen_q=seqlen_q,
|
| 706 |
+
seqlen_k=seqlen_k,
|
| 707 |
+
head_dim=head_dim,
|
| 708 |
+
SEQLEN_Q_CACHE=seqlen_q // 1024,
|
| 709 |
+
SEQLEN_K_CACHE=seqlen_k // 1024,
|
| 710 |
+
QHEAD_PER_KVHEAD=qhead_per_kvhead,
|
| 711 |
+
TILE_M=TILE_M,
|
| 712 |
+
TILE_N=TILE_N,
|
| 713 |
+
TILE_K=TILE_K,
|
| 714 |
+
IS_CAUSAL=is_causal,
|
| 715 |
+
IS_LOCAL=is_local,
|
| 716 |
+
IS_SPLIT_QO=is_split_qo and num_splits > 1,
|
| 717 |
+
HAS_CU_SEQLENS_Q=False,
|
| 718 |
+
HAS_CU_SEQLENS_K=False,
|
| 719 |
+
HAS_SEQUSED_Q=False,
|
| 720 |
+
HAS_SEQUSED_K=False,
|
| 721 |
+
num_warps=num_warps,
|
| 722 |
+
num_stages=num_stages,
|
| 723 |
+
num_ctas=num_ctas,
|
| 724 |
+
)
|
| 725 |
+
|
| 726 |
+
if launch_config is None or is_autotune:
|
| 727 |
+
best = launch_template.extract_best_config(_get_autotuned_kernel())
|
| 728 |
+
if best is not None:
|
| 729 |
+
launch_template.store_launch_config(
|
| 730 |
+
device=device,
|
| 731 |
+
kernel_name="bwd_sparse",
|
| 732 |
+
seqlen_q=seqlen_q,
|
| 733 |
+
seqlen_k=seqlen_k,
|
| 734 |
+
tile_k=TILE_K,
|
| 735 |
+
config=best,
|
| 736 |
+
is_local=is_local,
|
| 737 |
+
qhead_per_kvhead=qhead_per_kvhead,
|
| 738 |
+
is_causal=is_causal,
|
| 739 |
+
is_quant=is_quant,
|
| 740 |
+
)
|
| 741 |
+
|
| 742 |
+
flash_bwd_postprocess._flash_attn_bwd_postprocess(
|
| 743 |
+
dq_accum=dq_accum,
|
| 744 |
+
dq=dq,
|
| 745 |
+
scale=softmax_scale,
|
| 746 |
+
head_dim_rounded=head_dim_rounded,
|
| 747 |
+
tile_m=TILE_M,
|
| 748 |
+
tile_k=TILE_K,
|
| 749 |
+
)
|
| 750 |
+
|
| 751 |
+
if is_split_qo and num_splits > 1:
|
| 752 |
+
dk_accum = dk_accum.sum(dim=0)
|
| 753 |
+
dv_accum = dv_accum.sum(dim=0)
|
| 754 |
+
|
| 755 |
+
dk.copy_(dk_accum)
|
| 756 |
+
dv.copy_(dv_accum)
|
| 757 |
+
|
| 758 |
+
return dq, dk, dv
|
| 759 |
+
|
| 760 |
+
|
| 761 |
+
def _flash_sparse_attn_varlen_backward(
|
| 762 |
+
query: torch.Tensor,
|
| 763 |
+
key: torch.Tensor,
|
| 764 |
+
value: torch.Tensor,
|
| 765 |
+
out: torch.Tensor,
|
| 766 |
+
dout: torch.Tensor,
|
| 767 |
+
lse: torch.Tensor,
|
| 768 |
+
cu_seqlens_q: torch.Tensor,
|
| 769 |
+
cu_seqlens_k: torch.Tensor,
|
| 770 |
+
max_seqlen_q: Optional[int] = None,
|
| 771 |
+
max_seqlen_k: Optional[int] = None,
|
| 772 |
+
is_causal: bool = False,
|
| 773 |
+
softmax_scale: float = None,
|
| 774 |
+
query_scale: Optional[torch.Tensor] = None,
|
| 775 |
+
key_scale: Optional[torch.Tensor] = None,
|
| 776 |
+
value_scale: Optional[torch.Tensor] = None,
|
| 777 |
+
window_sizes: Optional[torch.Tensor] = None,
|
| 778 |
+
softmax_threshold: float = None,
|
| 779 |
+
is_local: bool = False,
|
| 780 |
+
is_quant: bool = False,
|
| 781 |
+
seqused_q: Optional[torch.Tensor] = None,
|
| 782 |
+
seqused_k: Optional[torch.Tensor] = None,
|
| 783 |
+
is_split_qo: bool = False,
|
| 784 |
+
is_autotune: bool = False,
|
| 785 |
+
skip_checks: bool = False,
|
| 786 |
+
) -> Tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
|
| 787 |
+
device = query.device
|
| 788 |
+
num_SMs = cache_utils.get_device_num_sms(device)
|
| 789 |
+
total_q, num_heads_q, head_dim = query.shape
|
| 790 |
+
total_k, num_heads_kv, _ = key.shape
|
| 791 |
+
batch_size = cu_seqlens_q.shape[0] - 1
|
| 792 |
+
seqlen_q = max_seqlen_q
|
| 793 |
+
seqlen_k = max_seqlen_k
|
| 794 |
+
softmax_scale = (
|
| 795 |
+
softmax_scale if softmax_scale is not None else 1.0 / (head_dim**0.5)
|
| 796 |
+
)
|
| 797 |
+
softmax_threshold = (
|
| 798 |
+
softmax_threshold if softmax_threshold is not None else 1 / seqlen_k
|
| 799 |
+
)
|
| 800 |
+
qhead_per_kvhead = num_heads_q // num_heads_kv
|
| 801 |
+
if is_local and window_sizes is None:
|
| 802 |
+
window_sizes = utils.window_sizes_heuristic(seqlen_k, num_heads_kv, device)
|
| 803 |
+
elif not is_local:
|
| 804 |
+
window_sizes = torch.zeros((num_heads_kv, 4), dtype=torch.int32, device=device)
|
| 805 |
+
|
| 806 |
+
if not skip_checks:
|
| 807 |
+
assert_inputs.assert_bwd_inputs(
|
| 808 |
+
query,
|
| 809 |
+
key,
|
| 810 |
+
value,
|
| 811 |
+
out,
|
| 812 |
+
dout,
|
| 813 |
+
lse,
|
| 814 |
+
query_scale=query_scale,
|
| 815 |
+
key_scale=key_scale,
|
| 816 |
+
value_scale=value_scale,
|
| 817 |
+
window_sizes=window_sizes,
|
| 818 |
+
cu_seqlens_q=cu_seqlens_q,
|
| 819 |
+
cu_seqlens_k=cu_seqlens_k,
|
| 820 |
+
seqused_q=seqused_q,
|
| 821 |
+
seqused_k=seqused_k,
|
| 822 |
+
num_heads_q=num_heads_q,
|
| 823 |
+
num_heads_kv=num_heads_kv,
|
| 824 |
+
head_dim=head_dim,
|
| 825 |
+
is_quant=is_quant,
|
| 826 |
+
device=device,
|
| 827 |
+
)
|
| 828 |
+
|
| 829 |
+
TILE_K = max(triton.next_power_of_2(head_dim), 16)
|
| 830 |
+
|
| 831 |
+
launch_config = launch_template.load_launch_config(
|
| 832 |
+
device=device,
|
| 833 |
+
kernel_name="bwd_sparse",
|
| 834 |
+
seqlen_q=seqlen_q,
|
| 835 |
+
seqlen_k=seqlen_k,
|
| 836 |
+
tile_k=TILE_K,
|
| 837 |
+
is_local=is_local,
|
| 838 |
+
qhead_per_kvhead=qhead_per_kvhead,
|
| 839 |
+
is_causal=is_causal,
|
| 840 |
+
is_quant=is_quant,
|
| 841 |
+
)
|
| 842 |
+
if launch_config is not None and not is_autotune:
|
| 843 |
+
kernel = _bwd_sparse_kernel
|
| 844 |
+
TILE_M, TILE_N, num_warps, num_stages, num_ctas = launch_config
|
| 845 |
+
else:
|
| 846 |
+
kernel = _get_autotuned_kernel()
|
| 847 |
+
# Placeholder for pre-launch computations
|
| 848 |
+
TILE_M = TILE_N = 64
|
| 849 |
+
num_warps = num_stages = num_ctas = None
|
| 850 |
+
|
| 851 |
+
num_splits = (
|
| 852 |
+
utils.num_splits_heuristic(
|
| 853 |
+
seqlen_q=seqlen_k,
|
| 854 |
+
seqlen_k=seqlen_q,
|
| 855 |
+
num_SMs=num_SMs,
|
| 856 |
+
TILE_M=TILE_N,
|
| 857 |
+
TILE_N=TILE_M,
|
| 858 |
+
max_split_blocks=utils.max_split_blocks_from_window_sizes(
|
| 859 |
+
window_sizes, TILE_M
|
| 860 |
+
)
|
| 861 |
+
if is_local
|
| 862 |
+
else None,
|
| 863 |
+
)
|
| 864 |
+
if is_split_qo
|
| 865 |
+
else 1
|
| 866 |
+
)
|
| 867 |
+
|
| 868 |
+
total_q_rounded_padded = int(math.ceil((total_q + batch_size * 128) / 128) * 128)
|
| 869 |
+
head_dim_rounded = int(math.ceil(head_dim / 32) * 32)
|
| 870 |
+
|
| 871 |
+
if not is_quant:
|
| 872 |
+
query_scale = torch.ones(1, device=device, dtype=query.dtype)
|
| 873 |
+
key_scale = torch.ones(1, device=device, dtype=query.dtype)
|
| 874 |
+
value_scale = torch.ones(1, device=device, dtype=query.dtype)
|
| 875 |
+
|
| 876 |
+
dq = torch.empty_like(query, dtype=query_scale.dtype)
|
| 877 |
+
dk = torch.empty_like(key, dtype=key_scale.dtype)
|
| 878 |
+
dv = torch.empty_like(value, dtype=value_scale.dtype)
|
| 879 |
+
lse_log2 = torch.empty(
|
| 880 |
+
num_heads_q,
|
| 881 |
+
total_q_rounded_padded,
|
| 882 |
+
dtype=torch.float32,
|
| 883 |
+
device=query.device,
|
| 884 |
+
)
|
| 885 |
+
dpsum = torch.empty(
|
| 886 |
+
num_heads_q,
|
| 887 |
+
total_q_rounded_padded,
|
| 888 |
+
dtype=torch.float32,
|
| 889 |
+
device=query.device,
|
| 890 |
+
)
|
| 891 |
+
dq_accum = torch.empty(
|
| 892 |
+
num_heads_q,
|
| 893 |
+
total_q_rounded_padded * head_dim_rounded,
|
| 894 |
+
dtype=torch.float32,
|
| 895 |
+
device=query.device,
|
| 896 |
+
)
|
| 897 |
+
dk_accum = torch.zeros(
|
| 898 |
+
(num_splits, total_k, num_heads_kv, head_dim)
|
| 899 |
+
if is_split_qo and num_splits > 1
|
| 900 |
+
else (total_k, num_heads_kv, head_dim),
|
| 901 |
+
dtype=torch.float32,
|
| 902 |
+
device=query.device,
|
| 903 |
+
)
|
| 904 |
+
dv_accum = torch.zeros(
|
| 905 |
+
(num_splits, total_k, num_heads_kv, head_dim)
|
| 906 |
+
if is_split_qo and num_splits > 1
|
| 907 |
+
else (total_k, num_heads_kv, head_dim),
|
| 908 |
+
dtype=torch.float32,
|
| 909 |
+
device=query.device,
|
| 910 |
+
)
|
| 911 |
+
|
| 912 |
+
flash_bwd_preprocess._flash_attn_bwd_preprocess(
|
| 913 |
+
out=out,
|
| 914 |
+
dout=dout,
|
| 915 |
+
dpsum=dpsum,
|
| 916 |
+
lse=lse,
|
| 917 |
+
lse_log2=lse_log2,
|
| 918 |
+
dq_accum=dq_accum,
|
| 919 |
+
head_dim_rounded=head_dim_rounded,
|
| 920 |
+
cu_seqlens_q=cu_seqlens_q,
|
| 921 |
+
seqused_q=seqused_q,
|
| 922 |
+
max_seqlen_q=max_seqlen_q,
|
| 923 |
+
tile_m=TILE_M,
|
| 924 |
+
tile_k=TILE_K,
|
| 925 |
+
)
|
| 926 |
+
|
| 927 |
+
grid = launch_grid.get_bwd_grid(
|
| 928 |
+
seqlen_k=seqlen_k,
|
| 929 |
+
num_heads_q=num_heads_q,
|
| 930 |
+
batch_size=batch_size,
|
| 931 |
+
num_splits=num_splits,
|
| 932 |
+
)
|
| 933 |
+
|
| 934 |
+
triton.set_allocator(utils.alloc_fn)
|
| 935 |
+
|
| 936 |
+
kernel[grid](
|
| 937 |
+
query,
|
| 938 |
+
key,
|
| 939 |
+
value,
|
| 940 |
+
dout,
|
| 941 |
+
lse_log2,
|
| 942 |
+
dpsum,
|
| 943 |
+
dq_accum,
|
| 944 |
+
dk_accum,
|
| 945 |
+
dv_accum,
|
| 946 |
+
softmax_scale,
|
| 947 |
+
query_scale,
|
| 948 |
+
key_scale,
|
| 949 |
+
value_scale,
|
| 950 |
+
softmax_threshold,
|
| 951 |
+
window_sizes,
|
| 952 |
+
0,
|
| 953 |
+
query.stride(-2),
|
| 954 |
+
query.stride(0),
|
| 955 |
+
0,
|
| 956 |
+
key.stride(-2),
|
| 957 |
+
key.stride(0),
|
| 958 |
+
0,
|
| 959 |
+
value.stride(-2),
|
| 960 |
+
value.stride(0),
|
| 961 |
+
0,
|
| 962 |
+
dout.stride(-2),
|
| 963 |
+
dout.stride(0),
|
| 964 |
+
0,
|
| 965 |
+
lse_log2.stride(0),
|
| 966 |
+
0,
|
| 967 |
+
dpsum.stride(0),
|
| 968 |
+
0,
|
| 969 |
+
dq_accum.stride(0),
|
| 970 |
+
head_dim_rounded,
|
| 971 |
+
0,
|
| 972 |
+
dk_accum.stride(-2),
|
| 973 |
+
dk_accum.stride(-3),
|
| 974 |
+
dk_accum.stride(0) if is_split_qo and num_splits > 1 else 0,
|
| 975 |
+
0,
|
| 976 |
+
dv_accum.stride(-2),
|
| 977 |
+
dv_accum.stride(-3),
|
| 978 |
+
dv_accum.stride(0) if is_split_qo and num_splits > 1 else 0,
|
| 979 |
+
window_sizes.stride(0),
|
| 980 |
+
cu_seqlens_q,
|
| 981 |
+
cu_seqlens_k,
|
| 982 |
+
seqused_q,
|
| 983 |
+
seqused_k,
|
| 984 |
+
num_splits,
|
| 985 |
+
seqlen_q=seqlen_q,
|
| 986 |
+
seqlen_k=seqlen_k,
|
| 987 |
+
head_dim=head_dim,
|
| 988 |
+
SEQLEN_Q_CACHE=seqlen_q // 1024,
|
| 989 |
+
SEQLEN_K_CACHE=seqlen_k // 1024,
|
| 990 |
+
QHEAD_PER_KVHEAD=qhead_per_kvhead,
|
| 991 |
+
TILE_M=TILE_M,
|
| 992 |
+
TILE_N=TILE_N,
|
| 993 |
+
TILE_K=TILE_K,
|
| 994 |
+
IS_CAUSAL=is_causal,
|
| 995 |
+
IS_LOCAL=is_local,
|
| 996 |
+
IS_SPLIT_QO=is_split_qo and num_splits > 1,
|
| 997 |
+
HAS_CU_SEQLENS_Q=True,
|
| 998 |
+
HAS_CU_SEQLENS_K=True,
|
| 999 |
+
HAS_SEQUSED_Q=seqused_q is not None,
|
| 1000 |
+
HAS_SEQUSED_K=seqused_k is not None,
|
| 1001 |
+
num_warps=num_warps,
|
| 1002 |
+
num_stages=num_stages,
|
| 1003 |
+
num_ctas=num_ctas,
|
| 1004 |
+
)
|
| 1005 |
+
|
| 1006 |
+
if launch_config is None or is_autotune:
|
| 1007 |
+
best = launch_template.extract_best_config(_get_autotuned_kernel())
|
| 1008 |
+
if best is not None:
|
| 1009 |
+
launch_template.store_launch_config(
|
| 1010 |
+
device=device,
|
| 1011 |
+
kernel_name="bwd_sparse",
|
| 1012 |
+
seqlen_q=seqlen_q,
|
| 1013 |
+
seqlen_k=seqlen_k,
|
| 1014 |
+
tile_k=TILE_K,
|
| 1015 |
+
config=best,
|
| 1016 |
+
is_local=is_local,
|
| 1017 |
+
qhead_per_kvhead=qhead_per_kvhead,
|
| 1018 |
+
is_causal=is_causal,
|
| 1019 |
+
is_quant=is_quant,
|
| 1020 |
+
)
|
| 1021 |
+
|
| 1022 |
+
flash_bwd_postprocess._flash_attn_bwd_postprocess(
|
| 1023 |
+
dq_accum=dq_accum,
|
| 1024 |
+
dq=dq,
|
| 1025 |
+
scale=softmax_scale,
|
| 1026 |
+
head_dim_rounded=head_dim_rounded,
|
| 1027 |
+
cu_seqlens_q=cu_seqlens_q,
|
| 1028 |
+
seqused_q=seqused_q,
|
| 1029 |
+
max_seqlen_q=max_seqlen_q,
|
| 1030 |
+
tile_m=TILE_M,
|
| 1031 |
+
tile_k=TILE_K,
|
| 1032 |
+
)
|
| 1033 |
+
|
| 1034 |
+
if is_split_qo and num_splits > 1:
|
| 1035 |
+
dk_accum = dk_accum.sum(dim=0)
|
| 1036 |
+
dv_accum = dv_accum.sum(dim=0)
|
| 1037 |
+
|
| 1038 |
+
dk.copy_(dk_accum)
|
| 1039 |
+
dv.copy_(dv_accum)
|
| 1040 |
+
|
| 1041 |
+
return dq, dk, dv
|
build/torch-cuda/flash_sparse_dec.py
ADDED
|
@@ -0,0 +1,871 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from typing import Tuple, Optional
|
| 2 |
+
|
| 3 |
+
import torch
|
| 4 |
+
import triton
|
| 5 |
+
import triton.language as tl
|
| 6 |
+
|
| 7 |
+
from . import (
|
| 8 |
+
assert_inputs,
|
| 9 |
+
utils,
|
| 10 |
+
cache_utils,
|
| 11 |
+
launch_template,
|
| 12 |
+
launch_grid,
|
| 13 |
+
flash_dec_combine,
|
| 14 |
+
kernel_repr,
|
| 15 |
+
autotuner,
|
| 16 |
+
)
|
| 17 |
+
from .scheduler import (
|
| 18 |
+
AttnDecGridIndex,
|
| 19 |
+
AttnDecConfig,
|
| 20 |
+
AttnDecBlockScheduler,
|
| 21 |
+
AttnDecPointerScheduler,
|
| 22 |
+
AttnMaskScheduler,
|
| 23 |
+
SoftmaxScheduler,
|
| 24 |
+
)
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
@triton.jit
|
| 28 |
+
def _dec_inner_sparse_kernel(
|
| 29 |
+
config: AttnDecConfig,
|
| 30 |
+
ptrs_sched: AttnDecPointerScheduler,
|
| 31 |
+
mask_sched: AttnMaskScheduler,
|
| 32 |
+
softmax_sched: SoftmaxScheduler,
|
| 33 |
+
q_tile,
|
| 34 |
+
k_tile,
|
| 35 |
+
k_ptrs,
|
| 36 |
+
v_ptrs,
|
| 37 |
+
acc_o,
|
| 38 |
+
row_max,
|
| 39 |
+
row_sum,
|
| 40 |
+
n_block,
|
| 41 |
+
n_block_min,
|
| 42 |
+
IS_MASK: tl.constexpr,
|
| 43 |
+
MASK_LOCAL: tl.constexpr,
|
| 44 |
+
MASK_SINK: tl.constexpr,
|
| 45 |
+
CHECK_INF: tl.constexpr,
|
| 46 |
+
):
|
| 47 |
+
# Compute attention scores
|
| 48 |
+
acc_s = tl.dot(q_tile, k_tile.T)
|
| 49 |
+
|
| 50 |
+
# Prefetch next key tile
|
| 51 |
+
if n_block > n_block_min:
|
| 52 |
+
# Load next key tile
|
| 53 |
+
k_tile = ptrs_sched.load_k(config, k_ptrs, n_block - 1)
|
| 54 |
+
|
| 55 |
+
if IS_MASK:
|
| 56 |
+
# Apply mask to attention scores
|
| 57 |
+
acc_s = mask_sched.apply_mask(
|
| 58 |
+
acc_s=acc_s,
|
| 59 |
+
iter_block=n_block,
|
| 60 |
+
MASK_LOCAL=MASK_LOCAL,
|
| 61 |
+
MASK_SINK=MASK_SINK,
|
| 62 |
+
)
|
| 63 |
+
|
| 64 |
+
# Apply online sparse softmax
|
| 65 |
+
p, row_max, row_sum, row_scale, skip_softmax = softmax_sched.online_sparse_softmax(
|
| 66 |
+
acc_s=acc_s,
|
| 67 |
+
row_max=row_max,
|
| 68 |
+
row_sum=row_sum,
|
| 69 |
+
softmax_threshold_log2=config.softmax_threshold_log2,
|
| 70 |
+
CHECK_INF=CHECK_INF,
|
| 71 |
+
)
|
| 72 |
+
|
| 73 |
+
if not skip_softmax:
|
| 74 |
+
# Load value tile
|
| 75 |
+
v_tile = ptrs_sched.load_v(config, v_ptrs, n_block)
|
| 76 |
+
|
| 77 |
+
# Rescale output accumulator
|
| 78 |
+
acc_o = softmax_sched.rescale_o(
|
| 79 |
+
acc_o=acc_o,
|
| 80 |
+
row_scale=row_scale,
|
| 81 |
+
)
|
| 82 |
+
|
| 83 |
+
# Update output accumulator
|
| 84 |
+
acc_o += tl.dot(p.to(v_tile.dtype), v_tile)
|
| 85 |
+
|
| 86 |
+
return k_tile, acc_o, row_max, row_sum
|
| 87 |
+
|
| 88 |
+
|
| 89 |
+
@triton.jit(repr=kernel_repr.dec_sparse_repr)
|
| 90 |
+
def _dec_sparse_kernel(
|
| 91 |
+
Q,
|
| 92 |
+
K,
|
| 93 |
+
V,
|
| 94 |
+
Out,
|
| 95 |
+
Lse,
|
| 96 |
+
softmax_scale,
|
| 97 |
+
query_scale,
|
| 98 |
+
key_scale,
|
| 99 |
+
value_scale,
|
| 100 |
+
softmax_threshold,
|
| 101 |
+
window_sizes,
|
| 102 |
+
stride_qb,
|
| 103 |
+
stride_qh,
|
| 104 |
+
stride_qm,
|
| 105 |
+
stride_kb,
|
| 106 |
+
stride_kh,
|
| 107 |
+
stride_kn,
|
| 108 |
+
stride_vb,
|
| 109 |
+
stride_vh,
|
| 110 |
+
stride_vn,
|
| 111 |
+
stride_ob,
|
| 112 |
+
stride_oh,
|
| 113 |
+
stride_om,
|
| 114 |
+
stride_os,
|
| 115 |
+
stride_lb,
|
| 116 |
+
stride_lh,
|
| 117 |
+
stride_lm,
|
| 118 |
+
stride_ls,
|
| 119 |
+
stride_wh,
|
| 120 |
+
cu_seqlens_q,
|
| 121 |
+
cu_seqlens_k,
|
| 122 |
+
seqused_q,
|
| 123 |
+
seqused_k,
|
| 124 |
+
num_splits,
|
| 125 |
+
seqlen_q,
|
| 126 |
+
seqlen_k,
|
| 127 |
+
head_dim,
|
| 128 |
+
SEQLEN_Q_CACHE: tl.constexpr,
|
| 129 |
+
SEQLEN_K_CACHE: tl.constexpr,
|
| 130 |
+
QHEAD_PER_KVHEAD_PACKGQA: tl.constexpr,
|
| 131 |
+
TILE_M: tl.constexpr,
|
| 132 |
+
TILE_N: tl.constexpr,
|
| 133 |
+
TILE_K: tl.constexpr,
|
| 134 |
+
IS_LOCAL: tl.constexpr,
|
| 135 |
+
HAS_CU_SEQLENS_Q: tl.constexpr,
|
| 136 |
+
HAS_CU_SEQLENS_K: tl.constexpr,
|
| 137 |
+
HAS_SEQUSED_Q: tl.constexpr,
|
| 138 |
+
HAS_SEQUSED_K: tl.constexpr,
|
| 139 |
+
):
|
| 140 |
+
# Create grid index
|
| 141 |
+
grid_idx = AttnDecGridIndex.create(
|
| 142 |
+
num_splits=num_splits,
|
| 143 |
+
)
|
| 144 |
+
|
| 145 |
+
# Load window sizes
|
| 146 |
+
(
|
| 147 |
+
window_size_sink,
|
| 148 |
+
window_size_left,
|
| 149 |
+
window_size_right,
|
| 150 |
+
window_size_dist,
|
| 151 |
+
) = grid_idx.load_window_sizes(
|
| 152 |
+
window_sizes=window_sizes,
|
| 153 |
+
stride_wh=stride_wh,
|
| 154 |
+
IS_LOCAL=IS_LOCAL,
|
| 155 |
+
)
|
| 156 |
+
|
| 157 |
+
# Create config
|
| 158 |
+
config = AttnDecConfig.create(
|
| 159 |
+
softmax_scale=softmax_scale,
|
| 160 |
+
softmax_threshold=softmax_threshold,
|
| 161 |
+
query_scale=query_scale,
|
| 162 |
+
key_scale=key_scale,
|
| 163 |
+
value_scale=value_scale,
|
| 164 |
+
batch_idx=grid_idx.batch_idx,
|
| 165 |
+
window_size_sink=window_size_sink,
|
| 166 |
+
window_size_left=window_size_left,
|
| 167 |
+
window_size_right=window_size_right,
|
| 168 |
+
window_size_dist=window_size_dist,
|
| 169 |
+
head_dim=head_dim,
|
| 170 |
+
cu_seqlens_q=cu_seqlens_q,
|
| 171 |
+
cu_seqlens_k=cu_seqlens_k,
|
| 172 |
+
seqused_q=seqused_q,
|
| 173 |
+
seqused_k=seqused_k,
|
| 174 |
+
seqlen_q=seqlen_q,
|
| 175 |
+
seqlen_k=seqlen_k,
|
| 176 |
+
QHEAD_PER_KVHEAD_PACKGQA=QHEAD_PER_KVHEAD_PACKGQA,
|
| 177 |
+
TILE_M=TILE_M,
|
| 178 |
+
TILE_N=TILE_N,
|
| 179 |
+
TILE_K=TILE_K,
|
| 180 |
+
HAS_CU_SEQLENS_Q=HAS_CU_SEQLENS_Q,
|
| 181 |
+
HAS_CU_SEQLENS_K=HAS_CU_SEQLENS_K,
|
| 182 |
+
HAS_SEQUSED_Q=HAS_SEQUSED_Q,
|
| 183 |
+
HAS_SEQUSED_K=HAS_SEQUSED_K,
|
| 184 |
+
)
|
| 185 |
+
|
| 186 |
+
# Create pointer scheduler
|
| 187 |
+
ptrs_sched = AttnDecPointerScheduler.create(
|
| 188 |
+
config=config,
|
| 189 |
+
Q=Q,
|
| 190 |
+
K=K,
|
| 191 |
+
V=V,
|
| 192 |
+
Out=Out,
|
| 193 |
+
Lse=Lse,
|
| 194 |
+
batch_idx=grid_idx.batch_idx,
|
| 195 |
+
head_idx=grid_idx.head_idx,
|
| 196 |
+
head_kv_idx=grid_idx.head_kv_idx,
|
| 197 |
+
split_idx=grid_idx.split_idx,
|
| 198 |
+
stride_qb=stride_qb,
|
| 199 |
+
stride_qh=stride_qh,
|
| 200 |
+
stride_qm=stride_qm,
|
| 201 |
+
stride_kb=stride_kb,
|
| 202 |
+
stride_kh=stride_kh,
|
| 203 |
+
stride_kn=stride_kn,
|
| 204 |
+
stride_vb=stride_vb,
|
| 205 |
+
stride_vh=stride_vh,
|
| 206 |
+
stride_vn=stride_vn,
|
| 207 |
+
stride_ob=stride_ob,
|
| 208 |
+
stride_oh=stride_oh,
|
| 209 |
+
stride_om=stride_om,
|
| 210 |
+
stride_os=stride_os,
|
| 211 |
+
stride_lb=stride_lb,
|
| 212 |
+
stride_lh=stride_lh,
|
| 213 |
+
stride_lm=stride_lm,
|
| 214 |
+
stride_ls=stride_ls,
|
| 215 |
+
HAS_CU_SEQLENS_Q=HAS_CU_SEQLENS_Q,
|
| 216 |
+
HAS_CU_SEQLENS_K=HAS_CU_SEQLENS_K,
|
| 217 |
+
)
|
| 218 |
+
|
| 219 |
+
# Create block scheduler
|
| 220 |
+
block_sched = AttnDecBlockScheduler.create(
|
| 221 |
+
config=config,
|
| 222 |
+
split_idx=grid_idx.split_idx,
|
| 223 |
+
num_splits=num_splits,
|
| 224 |
+
IS_LOCAL=IS_LOCAL,
|
| 225 |
+
)
|
| 226 |
+
|
| 227 |
+
# Create mask scheduler
|
| 228 |
+
mask_sched = AttnMaskScheduler.create(config)
|
| 229 |
+
|
| 230 |
+
# Create softmax scheduler
|
| 231 |
+
softmax_sched = SoftmaxScheduler.create(config)
|
| 232 |
+
|
| 233 |
+
# Create pointers
|
| 234 |
+
out_ptrs = ptrs_sched.make_out_ptrs(config)
|
| 235 |
+
lse_ptrs = ptrs_sched.make_lse_ptrs(config)
|
| 236 |
+
|
| 237 |
+
# Early exit if no n_blocks to process
|
| 238 |
+
if block_sched.is_empty():
|
| 239 |
+
ptrs_sched.store_empty(config, out_ptrs, lse_ptrs, Out)
|
| 240 |
+
return
|
| 241 |
+
|
| 242 |
+
q_ptrs = ptrs_sched.make_q_ptrs(config)
|
| 243 |
+
k_ptrs = ptrs_sched.make_k_ptrs(config)
|
| 244 |
+
v_ptrs = ptrs_sched.make_v_ptrs(config)
|
| 245 |
+
|
| 246 |
+
# Initialize accumulators
|
| 247 |
+
row_max = tl.full((TILE_M,), float("-inf"), dtype=tl.float32)
|
| 248 |
+
row_sum = tl.zeros((TILE_M,), dtype=tl.float32)
|
| 249 |
+
acc_o = tl.zeros((TILE_M, TILE_K), dtype=tl.float32)
|
| 250 |
+
|
| 251 |
+
# Load query tile
|
| 252 |
+
q_tile = ptrs_sched.load_q(config, q_ptrs)
|
| 253 |
+
|
| 254 |
+
# Load key tile
|
| 255 |
+
k_tile = ptrs_sched.load_k(config, k_ptrs, block_sched.n_block_max - 1)
|
| 256 |
+
|
| 257 |
+
# Process n_blocks with seqlen masking
|
| 258 |
+
for n_block in tl.range(
|
| 259 |
+
block_sched.n_block_max - 1, block_sched.n_block_max_no_mask - 1, -1
|
| 260 |
+
):
|
| 261 |
+
k_tile, acc_o, row_max, row_sum = _dec_inner_sparse_kernel(
|
| 262 |
+
config=config,
|
| 263 |
+
ptrs_sched=ptrs_sched,
|
| 264 |
+
mask_sched=mask_sched,
|
| 265 |
+
softmax_sched=softmax_sched,
|
| 266 |
+
q_tile=q_tile,
|
| 267 |
+
k_tile=k_tile,
|
| 268 |
+
k_ptrs=k_ptrs,
|
| 269 |
+
v_ptrs=v_ptrs,
|
| 270 |
+
acc_o=acc_o,
|
| 271 |
+
row_max=row_max,
|
| 272 |
+
row_sum=row_sum,
|
| 273 |
+
n_block=n_block,
|
| 274 |
+
n_block_min=block_sched.n_block_max_no_mask,
|
| 275 |
+
IS_MASK=True,
|
| 276 |
+
MASK_LOCAL=True if IS_LOCAL else False,
|
| 277 |
+
MASK_SINK=False,
|
| 278 |
+
CHECK_INF=True,
|
| 279 |
+
)
|
| 280 |
+
|
| 281 |
+
# Process n_blocks without masking
|
| 282 |
+
if not IS_LOCAL and block_sched.n_block_max_no_mask > block_sched.n_block_min:
|
| 283 |
+
# Load key tile
|
| 284 |
+
k_tile = ptrs_sched.load_k(config, k_ptrs, block_sched.n_block_max_no_mask - 1)
|
| 285 |
+
|
| 286 |
+
for n_block in tl.range(
|
| 287 |
+
block_sched.n_block_max_no_mask - 1, block_sched.n_block_min - 1, -1
|
| 288 |
+
):
|
| 289 |
+
k_tile, acc_o, row_max, row_sum = _dec_inner_sparse_kernel(
|
| 290 |
+
config=config,
|
| 291 |
+
ptrs_sched=ptrs_sched,
|
| 292 |
+
mask_sched=mask_sched,
|
| 293 |
+
softmax_sched=softmax_sched,
|
| 294 |
+
q_tile=q_tile,
|
| 295 |
+
k_tile=k_tile,
|
| 296 |
+
k_ptrs=k_ptrs,
|
| 297 |
+
v_ptrs=v_ptrs,
|
| 298 |
+
acc_o=acc_o,
|
| 299 |
+
row_max=row_max,
|
| 300 |
+
row_sum=row_sum,
|
| 301 |
+
n_block=n_block,
|
| 302 |
+
n_block_min=block_sched.n_block_min,
|
| 303 |
+
IS_MASK=False,
|
| 304 |
+
MASK_LOCAL=False,
|
| 305 |
+
MASK_SINK=False,
|
| 306 |
+
CHECK_INF=False,
|
| 307 |
+
)
|
| 308 |
+
|
| 309 |
+
if IS_LOCAL:
|
| 310 |
+
# Process n_blocks with local right masking
|
| 311 |
+
if block_sched.n_block_window_max > block_sched.n_block_window_max_no_mask:
|
| 312 |
+
# Load key tile
|
| 313 |
+
k_tile = ptrs_sched.load_k(
|
| 314 |
+
config, k_ptrs, block_sched.n_block_window_max - 1
|
| 315 |
+
)
|
| 316 |
+
|
| 317 |
+
for n_block in tl.range(
|
| 318 |
+
block_sched.n_block_window_max - 1,
|
| 319 |
+
block_sched.n_block_window_max_no_mask - 1,
|
| 320 |
+
-1,
|
| 321 |
+
):
|
| 322 |
+
k_tile, acc_o, row_max, row_sum = _dec_inner_sparse_kernel(
|
| 323 |
+
config=config,
|
| 324 |
+
ptrs_sched=ptrs_sched,
|
| 325 |
+
mask_sched=mask_sched,
|
| 326 |
+
softmax_sched=softmax_sched,
|
| 327 |
+
q_tile=q_tile,
|
| 328 |
+
k_tile=k_tile,
|
| 329 |
+
k_ptrs=k_ptrs,
|
| 330 |
+
v_ptrs=v_ptrs,
|
| 331 |
+
acc_o=acc_o,
|
| 332 |
+
row_max=row_max,
|
| 333 |
+
row_sum=row_sum,
|
| 334 |
+
n_block=n_block,
|
| 335 |
+
n_block_min=block_sched.n_block_window_max_no_mask,
|
| 336 |
+
IS_MASK=True,
|
| 337 |
+
MASK_LOCAL=True,
|
| 338 |
+
MASK_SINK=False,
|
| 339 |
+
CHECK_INF=True,
|
| 340 |
+
)
|
| 341 |
+
|
| 342 |
+
# Process n_blocks without masking
|
| 343 |
+
if (
|
| 344 |
+
block_sched.n_block_window_max_no_mask
|
| 345 |
+
> block_sched.n_block_window_min_no_mask
|
| 346 |
+
):
|
| 347 |
+
# Load key tile
|
| 348 |
+
k_tile = ptrs_sched.load_k(
|
| 349 |
+
config, k_ptrs, block_sched.n_block_window_max_no_mask - 1
|
| 350 |
+
)
|
| 351 |
+
|
| 352 |
+
for n_block in tl.range(
|
| 353 |
+
block_sched.n_block_window_max_no_mask - 1,
|
| 354 |
+
block_sched.n_block_window_min_no_mask - 1,
|
| 355 |
+
-1,
|
| 356 |
+
):
|
| 357 |
+
k_tile, acc_o, row_max, row_sum = _dec_inner_sparse_kernel(
|
| 358 |
+
config=config,
|
| 359 |
+
ptrs_sched=ptrs_sched,
|
| 360 |
+
mask_sched=mask_sched,
|
| 361 |
+
softmax_sched=softmax_sched,
|
| 362 |
+
q_tile=q_tile,
|
| 363 |
+
k_tile=k_tile,
|
| 364 |
+
k_ptrs=k_ptrs,
|
| 365 |
+
v_ptrs=v_ptrs,
|
| 366 |
+
acc_o=acc_o,
|
| 367 |
+
row_max=row_max,
|
| 368 |
+
row_sum=row_sum,
|
| 369 |
+
n_block=n_block,
|
| 370 |
+
n_block_min=block_sched.n_block_window_min_no_mask,
|
| 371 |
+
IS_MASK=False,
|
| 372 |
+
MASK_LOCAL=False,
|
| 373 |
+
MASK_SINK=False,
|
| 374 |
+
CHECK_INF=False,
|
| 375 |
+
)
|
| 376 |
+
|
| 377 |
+
# Process n_blocks with local left masking
|
| 378 |
+
if block_sched.n_block_window_min_no_mask > block_sched.n_block_window_min:
|
| 379 |
+
# Load key tile
|
| 380 |
+
k_tile = ptrs_sched.load_k(
|
| 381 |
+
config, k_ptrs, block_sched.n_block_window_min_no_mask - 1
|
| 382 |
+
)
|
| 383 |
+
|
| 384 |
+
for n_block in tl.range(
|
| 385 |
+
block_sched.n_block_window_min_no_mask - 1,
|
| 386 |
+
block_sched.n_block_window_min - 1,
|
| 387 |
+
-1,
|
| 388 |
+
):
|
| 389 |
+
k_tile, acc_o, row_max, row_sum = _dec_inner_sparse_kernel(
|
| 390 |
+
config=config,
|
| 391 |
+
ptrs_sched=ptrs_sched,
|
| 392 |
+
mask_sched=mask_sched,
|
| 393 |
+
softmax_sched=softmax_sched,
|
| 394 |
+
q_tile=q_tile,
|
| 395 |
+
k_tile=k_tile,
|
| 396 |
+
k_ptrs=k_ptrs,
|
| 397 |
+
v_ptrs=v_ptrs,
|
| 398 |
+
acc_o=acc_o,
|
| 399 |
+
row_max=row_max,
|
| 400 |
+
row_sum=row_sum,
|
| 401 |
+
n_block=n_block,
|
| 402 |
+
n_block_min=block_sched.n_block_window_min,
|
| 403 |
+
IS_MASK=True,
|
| 404 |
+
MASK_LOCAL=True,
|
| 405 |
+
MASK_SINK=False,
|
| 406 |
+
CHECK_INF=True,
|
| 407 |
+
)
|
| 408 |
+
|
| 409 |
+
# Process n_blocks with local sink masking
|
| 410 |
+
if block_sched.n_block_sink_max > block_sched.n_block_sink_min:
|
| 411 |
+
# Load key tile
|
| 412 |
+
k_tile = ptrs_sched.load_k(config, k_ptrs, block_sched.n_block_sink_max - 1)
|
| 413 |
+
|
| 414 |
+
for n_block in tl.range(
|
| 415 |
+
block_sched.n_block_sink_max - 1,
|
| 416 |
+
block_sched.n_block_sink_min - 1,
|
| 417 |
+
-1,
|
| 418 |
+
):
|
| 419 |
+
k_tile, acc_o, row_max, row_sum = _dec_inner_sparse_kernel(
|
| 420 |
+
config=config,
|
| 421 |
+
ptrs_sched=ptrs_sched,
|
| 422 |
+
mask_sched=mask_sched,
|
| 423 |
+
softmax_sched=softmax_sched,
|
| 424 |
+
q_tile=q_tile,
|
| 425 |
+
k_tile=k_tile,
|
| 426 |
+
k_ptrs=k_ptrs,
|
| 427 |
+
v_ptrs=v_ptrs,
|
| 428 |
+
acc_o=acc_o,
|
| 429 |
+
row_max=row_max,
|
| 430 |
+
row_sum=row_sum,
|
| 431 |
+
n_block=n_block,
|
| 432 |
+
n_block_min=block_sched.n_block_sink_min,
|
| 433 |
+
IS_MASK=True,
|
| 434 |
+
MASK_LOCAL=True,
|
| 435 |
+
MASK_SINK=True,
|
| 436 |
+
CHECK_INF=True,
|
| 437 |
+
)
|
| 438 |
+
|
| 439 |
+
# Finalize softmax
|
| 440 |
+
row_scale, lse_tile = softmax_sched.finalize(
|
| 441 |
+
row_max=row_max,
|
| 442 |
+
row_sum=row_sum,
|
| 443 |
+
IS_LOG2=True,
|
| 444 |
+
)
|
| 445 |
+
|
| 446 |
+
# Store LSE
|
| 447 |
+
ptrs_sched.store_lse(config, lse_ptrs, lse_tile)
|
| 448 |
+
|
| 449 |
+
# Finalize rescale
|
| 450 |
+
acc_o = softmax_sched.rescale_o(
|
| 451 |
+
acc_o=acc_o,
|
| 452 |
+
row_scale=row_scale,
|
| 453 |
+
)
|
| 454 |
+
|
| 455 |
+
# Store output
|
| 456 |
+
ptrs_sched.store_out(config, out_ptrs, acc_o)
|
| 457 |
+
|
| 458 |
+
|
| 459 |
+
_dec_sparse_kernel = cache_utils.wrap_kernel(_dec_sparse_kernel)
|
| 460 |
+
|
| 461 |
+
|
| 462 |
+
_dec_sparse_kernel_autotuned = None
|
| 463 |
+
|
| 464 |
+
|
| 465 |
+
def _get_autotuned_kernel():
|
| 466 |
+
global _dec_sparse_kernel_autotuned
|
| 467 |
+
if _dec_sparse_kernel_autotuned is None:
|
| 468 |
+
jit_kernel = _dec_sparse_kernel._kernel
|
| 469 |
+
autotuned = autotuner.make_dec_sparse_autotuned_kernel(jit_kernel)
|
| 470 |
+
_dec_sparse_kernel_autotuned = autotuner.AutotunedKernel(autotuned)
|
| 471 |
+
return _dec_sparse_kernel_autotuned
|
| 472 |
+
|
| 473 |
+
|
| 474 |
+
def _flash_sparse_attn_decode(
|
| 475 |
+
query: torch.Tensor,
|
| 476 |
+
key: torch.Tensor,
|
| 477 |
+
value: torch.Tensor,
|
| 478 |
+
softmax_scale: float = None,
|
| 479 |
+
softmax_threshold: float = None,
|
| 480 |
+
query_scale: Optional[torch.Tensor] = None,
|
| 481 |
+
key_scale: Optional[torch.Tensor] = None,
|
| 482 |
+
value_scale: Optional[torch.Tensor] = None,
|
| 483 |
+
window_sizes: Optional[torch.Tensor] = None,
|
| 484 |
+
is_local: bool = False,
|
| 485 |
+
is_quant: bool = False,
|
| 486 |
+
out: Optional[torch.Tensor] = None,
|
| 487 |
+
lse: Optional[torch.Tensor] = None,
|
| 488 |
+
is_autotune: bool = False,
|
| 489 |
+
skip_checks: bool = False,
|
| 490 |
+
) -> Tuple[torch.Tensor, torch.Tensor]:
|
| 491 |
+
device = query.device
|
| 492 |
+
num_SMs = cache_utils.get_device_num_sms(device)
|
| 493 |
+
batch_size, num_heads_q, head_dim = query.shape
|
| 494 |
+
_, seqlen_k, num_heads_kv, _ = key.shape
|
| 495 |
+
softmax_scale = (
|
| 496 |
+
softmax_scale if softmax_scale is not None else 1.0 / (head_dim**0.5)
|
| 497 |
+
)
|
| 498 |
+
softmax_threshold = (
|
| 499 |
+
softmax_threshold if softmax_threshold is not None else 1 / seqlen_k
|
| 500 |
+
)
|
| 501 |
+
qhead_per_kvhead = num_heads_q // num_heads_kv
|
| 502 |
+
if is_local and window_sizes is None:
|
| 503 |
+
window_sizes = utils.window_sizes_heuristic(seqlen_k, num_heads_kv, device)
|
| 504 |
+
elif not is_local:
|
| 505 |
+
window_sizes = torch.zeros((num_heads_kv, 4), dtype=torch.int32, device=device)
|
| 506 |
+
|
| 507 |
+
if not skip_checks:
|
| 508 |
+
assert_inputs.assert_dec_inputs(
|
| 509 |
+
query,
|
| 510 |
+
key,
|
| 511 |
+
value,
|
| 512 |
+
query_scale=query_scale,
|
| 513 |
+
key_scale=key_scale,
|
| 514 |
+
value_scale=value_scale,
|
| 515 |
+
window_sizes=window_sizes,
|
| 516 |
+
cu_seqlens_k=None,
|
| 517 |
+
seqused_k=None,
|
| 518 |
+
num_heads_q=num_heads_q,
|
| 519 |
+
num_heads_kv=num_heads_kv,
|
| 520 |
+
head_dim=head_dim,
|
| 521 |
+
is_quant=is_quant,
|
| 522 |
+
device=device,
|
| 523 |
+
)
|
| 524 |
+
|
| 525 |
+
TILE_K = max(triton.next_power_of_2(head_dim), 16)
|
| 526 |
+
|
| 527 |
+
launch_config = launch_template.load_launch_config(
|
| 528 |
+
device=device,
|
| 529 |
+
kernel_name="dec_sparse",
|
| 530 |
+
seqlen_q=1,
|
| 531 |
+
seqlen_k=seqlen_k,
|
| 532 |
+
tile_k=TILE_K,
|
| 533 |
+
is_local=is_local,
|
| 534 |
+
qhead_per_kvhead=qhead_per_kvhead,
|
| 535 |
+
is_quant=is_quant,
|
| 536 |
+
)
|
| 537 |
+
if launch_config is not None and not is_autotune:
|
| 538 |
+
kernel = _dec_sparse_kernel
|
| 539 |
+
TILE_M, TILE_N, num_warps, num_stages, num_ctas = launch_config
|
| 540 |
+
else:
|
| 541 |
+
kernel = _get_autotuned_kernel()
|
| 542 |
+
TILE_M = max(triton.next_power_of_2(qhead_per_kvhead), 16)
|
| 543 |
+
TILE_N = 128
|
| 544 |
+
num_warps = num_stages = num_ctas = None
|
| 545 |
+
|
| 546 |
+
num_splits = utils.num_splits_heuristic(
|
| 547 |
+
seqlen_q=qhead_per_kvhead,
|
| 548 |
+
seqlen_k=seqlen_k,
|
| 549 |
+
num_SMs=num_SMs,
|
| 550 |
+
TILE_M=TILE_M,
|
| 551 |
+
TILE_N=TILE_N,
|
| 552 |
+
max_split_blocks=utils.max_split_blocks_from_window_sizes(window_sizes, TILE_N)
|
| 553 |
+
if is_local
|
| 554 |
+
else None,
|
| 555 |
+
)
|
| 556 |
+
|
| 557 |
+
out_dtype = torch.bfloat16 if is_quant else query.dtype
|
| 558 |
+
out = (
|
| 559 |
+
out
|
| 560 |
+
if out is not None
|
| 561 |
+
else torch.empty(query.shape, dtype=out_dtype, device=device)
|
| 562 |
+
)
|
| 563 |
+
lse = (
|
| 564 |
+
lse
|
| 565 |
+
if lse is not None
|
| 566 |
+
else torch.empty((batch_size, num_heads_q), dtype=torch.float32, device=device)
|
| 567 |
+
)
|
| 568 |
+
|
| 569 |
+
out_partial = torch.empty(
|
| 570 |
+
(num_splits, batch_size, num_heads_q, head_dim),
|
| 571 |
+
dtype=torch.float32,
|
| 572 |
+
device=device,
|
| 573 |
+
)
|
| 574 |
+
lse_partial = torch.empty(
|
| 575 |
+
(num_splits, batch_size, num_heads_q),
|
| 576 |
+
dtype=torch.float32,
|
| 577 |
+
device=device,
|
| 578 |
+
)
|
| 579 |
+
|
| 580 |
+
if not is_quant:
|
| 581 |
+
query_scale = torch.ones(1, device=device, dtype=query.dtype)
|
| 582 |
+
key_scale = torch.ones(1, device=device, dtype=query.dtype)
|
| 583 |
+
value_scale = torch.ones(1, device=device, dtype=query.dtype)
|
| 584 |
+
|
| 585 |
+
grid = launch_grid.get_dec_grid(
|
| 586 |
+
batch_size=batch_size,
|
| 587 |
+
num_heads_kv=num_heads_kv,
|
| 588 |
+
num_splits=num_splits,
|
| 589 |
+
)
|
| 590 |
+
|
| 591 |
+
triton.set_allocator(utils.alloc_fn)
|
| 592 |
+
|
| 593 |
+
kernel[grid](
|
| 594 |
+
query,
|
| 595 |
+
key,
|
| 596 |
+
value,
|
| 597 |
+
out_partial,
|
| 598 |
+
lse_partial,
|
| 599 |
+
softmax_scale,
|
| 600 |
+
query_scale,
|
| 601 |
+
key_scale,
|
| 602 |
+
value_scale,
|
| 603 |
+
softmax_threshold,
|
| 604 |
+
window_sizes,
|
| 605 |
+
query.stride(0),
|
| 606 |
+
query.stride(-2),
|
| 607 |
+
1,
|
| 608 |
+
key.stride(0),
|
| 609 |
+
key.stride(-2),
|
| 610 |
+
key.stride(-3),
|
| 611 |
+
value.stride(0),
|
| 612 |
+
value.stride(-2),
|
| 613 |
+
value.stride(-3),
|
| 614 |
+
out_partial.stride(1),
|
| 615 |
+
out_partial.stride(-2),
|
| 616 |
+
1,
|
| 617 |
+
out_partial.stride(0),
|
| 618 |
+
lse_partial.stride(1),
|
| 619 |
+
lse_partial.stride(-1),
|
| 620 |
+
1,
|
| 621 |
+
lse_partial.stride(0),
|
| 622 |
+
window_sizes.stride(0),
|
| 623 |
+
None,
|
| 624 |
+
None,
|
| 625 |
+
None,
|
| 626 |
+
None,
|
| 627 |
+
num_splits,
|
| 628 |
+
seqlen_q=1,
|
| 629 |
+
seqlen_k=seqlen_k,
|
| 630 |
+
head_dim=head_dim,
|
| 631 |
+
SEQLEN_Q_CACHE=0,
|
| 632 |
+
SEQLEN_K_CACHE=seqlen_k // 1024,
|
| 633 |
+
QHEAD_PER_KVHEAD_PACKGQA=qhead_per_kvhead,
|
| 634 |
+
TILE_M=TILE_M,
|
| 635 |
+
TILE_N=TILE_N,
|
| 636 |
+
TILE_K=TILE_K,
|
| 637 |
+
IS_LOCAL=is_local,
|
| 638 |
+
HAS_CU_SEQLENS_Q=False,
|
| 639 |
+
HAS_CU_SEQLENS_K=False,
|
| 640 |
+
HAS_SEQUSED_Q=False,
|
| 641 |
+
HAS_SEQUSED_K=False,
|
| 642 |
+
num_warps=num_warps,
|
| 643 |
+
num_stages=num_stages,
|
| 644 |
+
num_ctas=num_ctas,
|
| 645 |
+
)
|
| 646 |
+
|
| 647 |
+
if launch_config is None or is_autotune:
|
| 648 |
+
best = launch_template.extract_best_config(_get_autotuned_kernel())
|
| 649 |
+
if best is not None:
|
| 650 |
+
launch_template.store_launch_config(
|
| 651 |
+
device=device,
|
| 652 |
+
kernel_name="dec_sparse",
|
| 653 |
+
seqlen_q=1,
|
| 654 |
+
seqlen_k=seqlen_k,
|
| 655 |
+
tile_k=TILE_K,
|
| 656 |
+
config=best,
|
| 657 |
+
is_local=is_local,
|
| 658 |
+
qhead_per_kvhead=qhead_per_kvhead,
|
| 659 |
+
is_quant=is_quant,
|
| 660 |
+
)
|
| 661 |
+
|
| 662 |
+
flash_dec_combine._flash_attn_dec_combine(
|
| 663 |
+
out_partial,
|
| 664 |
+
lse_partial,
|
| 665 |
+
out,
|
| 666 |
+
lse,
|
| 667 |
+
)
|
| 668 |
+
|
| 669 |
+
return out, lse
|
| 670 |
+
|
| 671 |
+
|
| 672 |
+
def _flash_sparse_attn_varlen_decode(
|
| 673 |
+
query: torch.Tensor,
|
| 674 |
+
key: torch.Tensor,
|
| 675 |
+
value: torch.Tensor,
|
| 676 |
+
cu_seqlens_k: torch.Tensor,
|
| 677 |
+
max_seqlen_k: int,
|
| 678 |
+
softmax_scale: float = None,
|
| 679 |
+
softmax_threshold: float = None,
|
| 680 |
+
query_scale: Optional[torch.Tensor] = None,
|
| 681 |
+
key_scale: Optional[torch.Tensor] = None,
|
| 682 |
+
value_scale: Optional[torch.Tensor] = None,
|
| 683 |
+
window_sizes: Optional[torch.Tensor] = None,
|
| 684 |
+
is_local: bool = False,
|
| 685 |
+
is_quant: bool = False,
|
| 686 |
+
seqused_k: Optional[torch.Tensor] = None,
|
| 687 |
+
out: Optional[torch.Tensor] = None,
|
| 688 |
+
lse: Optional[torch.Tensor] = None,
|
| 689 |
+
is_autotune: bool = False,
|
| 690 |
+
skip_checks: bool = False,
|
| 691 |
+
) -> Tuple[torch.Tensor, torch.Tensor]:
|
| 692 |
+
device = query.device
|
| 693 |
+
num_SMs = cache_utils.get_device_num_sms(device)
|
| 694 |
+
batch_size, num_heads_q, head_dim = query.shape
|
| 695 |
+
_, num_heads_kv, _ = key.shape
|
| 696 |
+
seqlen_k = max_seqlen_k
|
| 697 |
+
softmax_scale = (
|
| 698 |
+
softmax_scale if softmax_scale is not None else 1.0 / (head_dim**0.5)
|
| 699 |
+
)
|
| 700 |
+
softmax_threshold = (
|
| 701 |
+
softmax_threshold if softmax_threshold is not None else 1 / seqlen_k
|
| 702 |
+
)
|
| 703 |
+
qhead_per_kvhead = num_heads_q // num_heads_kv
|
| 704 |
+
if is_local and window_sizes is None:
|
| 705 |
+
window_sizes = utils.window_sizes_heuristic(seqlen_k, num_heads_kv, device)
|
| 706 |
+
elif not is_local:
|
| 707 |
+
window_sizes = torch.zeros((num_heads_kv, 4), dtype=torch.int32, device=device)
|
| 708 |
+
|
| 709 |
+
if not skip_checks:
|
| 710 |
+
assert_inputs.assert_dec_inputs(
|
| 711 |
+
query,
|
| 712 |
+
key,
|
| 713 |
+
value,
|
| 714 |
+
query_scale=query_scale,
|
| 715 |
+
key_scale=key_scale,
|
| 716 |
+
value_scale=value_scale,
|
| 717 |
+
window_sizes=window_sizes,
|
| 718 |
+
cu_seqlens_k=cu_seqlens_k,
|
| 719 |
+
seqused_k=seqused_k,
|
| 720 |
+
num_heads_q=num_heads_q,
|
| 721 |
+
num_heads_kv=num_heads_kv,
|
| 722 |
+
head_dim=head_dim,
|
| 723 |
+
is_quant=is_quant,
|
| 724 |
+
device=device,
|
| 725 |
+
)
|
| 726 |
+
|
| 727 |
+
TILE_K = max(triton.next_power_of_2(head_dim), 16)
|
| 728 |
+
|
| 729 |
+
launch_config = launch_template.load_launch_config(
|
| 730 |
+
device=device,
|
| 731 |
+
kernel_name="dec_sparse",
|
| 732 |
+
seqlen_q=1,
|
| 733 |
+
seqlen_k=seqlen_k,
|
| 734 |
+
tile_k=TILE_K,
|
| 735 |
+
is_local=is_local,
|
| 736 |
+
qhead_per_kvhead=qhead_per_kvhead,
|
| 737 |
+
is_quant=is_quant,
|
| 738 |
+
)
|
| 739 |
+
if launch_config is not None and not is_autotune:
|
| 740 |
+
kernel = _dec_sparse_kernel
|
| 741 |
+
TILE_M, TILE_N, num_warps, num_stages, num_ctas = launch_config
|
| 742 |
+
else:
|
| 743 |
+
kernel = _get_autotuned_kernel()
|
| 744 |
+
TILE_M = max(triton.next_power_of_2(qhead_per_kvhead), 16)
|
| 745 |
+
TILE_N = 128
|
| 746 |
+
num_warps = num_stages = num_ctas = None
|
| 747 |
+
|
| 748 |
+
num_splits = utils.num_splits_heuristic(
|
| 749 |
+
seqlen_q=qhead_per_kvhead,
|
| 750 |
+
seqlen_k=seqlen_k,
|
| 751 |
+
num_SMs=num_SMs,
|
| 752 |
+
TILE_M=TILE_M,
|
| 753 |
+
TILE_N=TILE_N,
|
| 754 |
+
max_split_blocks=utils.max_split_blocks_from_window_sizes(window_sizes, TILE_N)
|
| 755 |
+
if is_local
|
| 756 |
+
else None,
|
| 757 |
+
)
|
| 758 |
+
|
| 759 |
+
out_dtype = torch.bfloat16 if is_quant else query.dtype
|
| 760 |
+
out = (
|
| 761 |
+
out
|
| 762 |
+
if out is not None
|
| 763 |
+
else torch.empty(query.shape, dtype=out_dtype, device=device)
|
| 764 |
+
)
|
| 765 |
+
lse = (
|
| 766 |
+
lse
|
| 767 |
+
if lse is not None
|
| 768 |
+
else torch.empty((batch_size, num_heads_q), dtype=torch.float32, device=device)
|
| 769 |
+
)
|
| 770 |
+
|
| 771 |
+
out_partial = torch.empty(
|
| 772 |
+
(num_splits, batch_size, num_heads_q, head_dim),
|
| 773 |
+
dtype=torch.float32,
|
| 774 |
+
device=device,
|
| 775 |
+
)
|
| 776 |
+
lse_partial = torch.empty(
|
| 777 |
+
(num_splits, batch_size, num_heads_q),
|
| 778 |
+
dtype=torch.float32,
|
| 779 |
+
device=device,
|
| 780 |
+
)
|
| 781 |
+
|
| 782 |
+
if not is_quant:
|
| 783 |
+
query_scale = torch.ones(1, device=device, dtype=query.dtype)
|
| 784 |
+
key_scale = torch.ones(1, device=device, dtype=query.dtype)
|
| 785 |
+
value_scale = torch.ones(1, device=device, dtype=query.dtype)
|
| 786 |
+
|
| 787 |
+
grid = launch_grid.get_dec_grid(
|
| 788 |
+
batch_size=batch_size,
|
| 789 |
+
num_heads_kv=num_heads_kv,
|
| 790 |
+
num_splits=num_splits,
|
| 791 |
+
)
|
| 792 |
+
|
| 793 |
+
triton.set_allocator(utils.alloc_fn)
|
| 794 |
+
|
| 795 |
+
kernel[grid](
|
| 796 |
+
query,
|
| 797 |
+
key,
|
| 798 |
+
value,
|
| 799 |
+
out_partial,
|
| 800 |
+
lse_partial,
|
| 801 |
+
softmax_scale,
|
| 802 |
+
query_scale,
|
| 803 |
+
key_scale,
|
| 804 |
+
value_scale,
|
| 805 |
+
softmax_threshold,
|
| 806 |
+
window_sizes,
|
| 807 |
+
query.stride(0),
|
| 808 |
+
query.stride(-2),
|
| 809 |
+
1,
|
| 810 |
+
0,
|
| 811 |
+
key.stride(-2),
|
| 812 |
+
key.stride(0),
|
| 813 |
+
0,
|
| 814 |
+
value.stride(-2),
|
| 815 |
+
value.stride(0),
|
| 816 |
+
out_partial.stride(1),
|
| 817 |
+
out_partial.stride(-2),
|
| 818 |
+
1,
|
| 819 |
+
out_partial.stride(0),
|
| 820 |
+
lse_partial.stride(1),
|
| 821 |
+
lse_partial.stride(-1),
|
| 822 |
+
1,
|
| 823 |
+
lse_partial.stride(0),
|
| 824 |
+
window_sizes.stride(0),
|
| 825 |
+
None,
|
| 826 |
+
cu_seqlens_k,
|
| 827 |
+
None,
|
| 828 |
+
seqused_k,
|
| 829 |
+
num_splits,
|
| 830 |
+
seqlen_q=1,
|
| 831 |
+
seqlen_k=seqlen_k,
|
| 832 |
+
head_dim=head_dim,
|
| 833 |
+
SEQLEN_Q_CACHE=0,
|
| 834 |
+
SEQLEN_K_CACHE=seqlen_k // 1024,
|
| 835 |
+
QHEAD_PER_KVHEAD_PACKGQA=qhead_per_kvhead,
|
| 836 |
+
TILE_M=TILE_M,
|
| 837 |
+
TILE_N=TILE_N,
|
| 838 |
+
TILE_K=TILE_K,
|
| 839 |
+
IS_LOCAL=is_local,
|
| 840 |
+
HAS_CU_SEQLENS_Q=False,
|
| 841 |
+
HAS_CU_SEQLENS_K=True,
|
| 842 |
+
HAS_SEQUSED_Q=False,
|
| 843 |
+
HAS_SEQUSED_K=seqused_k is not None,
|
| 844 |
+
num_warps=num_warps,
|
| 845 |
+
num_stages=num_stages,
|
| 846 |
+
num_ctas=num_ctas,
|
| 847 |
+
)
|
| 848 |
+
|
| 849 |
+
if launch_config is None or is_autotune:
|
| 850 |
+
best = launch_template.extract_best_config(_get_autotuned_kernel())
|
| 851 |
+
if best is not None:
|
| 852 |
+
launch_template.store_launch_config(
|
| 853 |
+
device=device,
|
| 854 |
+
kernel_name="dec_sparse",
|
| 855 |
+
seqlen_q=1,
|
| 856 |
+
seqlen_k=seqlen_k,
|
| 857 |
+
tile_k=TILE_K,
|
| 858 |
+
config=best,
|
| 859 |
+
is_local=is_local,
|
| 860 |
+
qhead_per_kvhead=qhead_per_kvhead,
|
| 861 |
+
is_quant=is_quant,
|
| 862 |
+
)
|
| 863 |
+
|
| 864 |
+
flash_dec_combine._flash_attn_dec_combine(
|
| 865 |
+
out_partial,
|
| 866 |
+
lse_partial,
|
| 867 |
+
out,
|
| 868 |
+
lse,
|
| 869 |
+
)
|
| 870 |
+
|
| 871 |
+
return out, lse
|
build/torch-cuda/flash_sparse_fwd.py
ADDED
|
@@ -0,0 +1,974 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from typing import Tuple, Optional
|
| 2 |
+
|
| 3 |
+
import torch
|
| 4 |
+
import triton
|
| 5 |
+
import triton.language as tl
|
| 6 |
+
|
| 7 |
+
from . import (
|
| 8 |
+
assert_inputs,
|
| 9 |
+
utils,
|
| 10 |
+
cache_utils,
|
| 11 |
+
launch_template,
|
| 12 |
+
launch_grid,
|
| 13 |
+
flash_fwd_combine,
|
| 14 |
+
kernel_repr,
|
| 15 |
+
autotuner,
|
| 16 |
+
)
|
| 17 |
+
from .scheduler import (
|
| 18 |
+
AttnFwdGridIndex,
|
| 19 |
+
AttnFwdConfig,
|
| 20 |
+
AttnFwdBlockScheduler,
|
| 21 |
+
AttnFwdPointerScheduler,
|
| 22 |
+
AttnMaskScheduler,
|
| 23 |
+
SoftmaxScheduler,
|
| 24 |
+
)
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
@triton.jit
|
| 28 |
+
def _fwd_inner_sparse_kernel(
|
| 29 |
+
config: AttnFwdConfig,
|
| 30 |
+
ptrs_sched: AttnFwdPointerScheduler,
|
| 31 |
+
mask_sched: AttnMaskScheduler,
|
| 32 |
+
softmax_sched: SoftmaxScheduler,
|
| 33 |
+
q_tile,
|
| 34 |
+
k_tile,
|
| 35 |
+
k_ptrs,
|
| 36 |
+
v_ptrs,
|
| 37 |
+
acc_o,
|
| 38 |
+
row_max,
|
| 39 |
+
row_sum,
|
| 40 |
+
n_block,
|
| 41 |
+
n_block_min,
|
| 42 |
+
IS_MASK: tl.constexpr,
|
| 43 |
+
MASK_CAUSAL: tl.constexpr,
|
| 44 |
+
MASK_LOCAL: tl.constexpr,
|
| 45 |
+
MASK_SINK: tl.constexpr,
|
| 46 |
+
CHECK_INF: tl.constexpr,
|
| 47 |
+
):
|
| 48 |
+
# Compute attention scores
|
| 49 |
+
acc_s = tl.dot(q_tile, k_tile.T)
|
| 50 |
+
|
| 51 |
+
# Prefetch next key tile
|
| 52 |
+
if n_block > n_block_min:
|
| 53 |
+
# Load next key tile
|
| 54 |
+
k_tile = ptrs_sched.load_k(config, k_ptrs, n_block - 1)
|
| 55 |
+
|
| 56 |
+
if IS_MASK:
|
| 57 |
+
# Apply mask to attention scores
|
| 58 |
+
acc_s = mask_sched.apply_mask(
|
| 59 |
+
acc_s=acc_s,
|
| 60 |
+
iter_block=n_block,
|
| 61 |
+
MASK_CAUSAL=MASK_CAUSAL,
|
| 62 |
+
MASK_LOCAL=MASK_LOCAL,
|
| 63 |
+
MASK_SINK=MASK_SINK,
|
| 64 |
+
)
|
| 65 |
+
|
| 66 |
+
# Apply online sparse softmax
|
| 67 |
+
p, row_max, row_sum, row_scale, skip_softmax = softmax_sched.online_sparse_softmax(
|
| 68 |
+
acc_s=acc_s,
|
| 69 |
+
row_max=row_max,
|
| 70 |
+
row_sum=row_sum,
|
| 71 |
+
softmax_threshold_log2=config.softmax_threshold_log2,
|
| 72 |
+
CHECK_INF=CHECK_INF,
|
| 73 |
+
)
|
| 74 |
+
|
| 75 |
+
if not skip_softmax:
|
| 76 |
+
# Load value tile
|
| 77 |
+
v_tile = ptrs_sched.load_v(config, v_ptrs, n_block)
|
| 78 |
+
|
| 79 |
+
# Rescale output accumulator
|
| 80 |
+
acc_o = softmax_sched.rescale_o(
|
| 81 |
+
acc_o=acc_o,
|
| 82 |
+
row_scale=row_scale,
|
| 83 |
+
)
|
| 84 |
+
|
| 85 |
+
# Update output accumulator
|
| 86 |
+
acc_o += tl.dot(p.to(v_tile.dtype), v_tile)
|
| 87 |
+
|
| 88 |
+
return k_tile, acc_o, row_max, row_sum
|
| 89 |
+
|
| 90 |
+
|
| 91 |
+
@triton.jit(repr=kernel_repr.fwd_sparse_repr)
|
| 92 |
+
def _fwd_sparse_kernel(
|
| 93 |
+
Q,
|
| 94 |
+
K,
|
| 95 |
+
V,
|
| 96 |
+
Out,
|
| 97 |
+
Lse,
|
| 98 |
+
softmax_scale,
|
| 99 |
+
softmax_threshold,
|
| 100 |
+
query_scale,
|
| 101 |
+
key_scale,
|
| 102 |
+
value_scale,
|
| 103 |
+
window_sizes,
|
| 104 |
+
stride_qb,
|
| 105 |
+
stride_qh,
|
| 106 |
+
stride_qm,
|
| 107 |
+
stride_kb,
|
| 108 |
+
stride_kh,
|
| 109 |
+
stride_kn,
|
| 110 |
+
stride_vb,
|
| 111 |
+
stride_vh,
|
| 112 |
+
stride_vn,
|
| 113 |
+
stride_ob,
|
| 114 |
+
stride_oh,
|
| 115 |
+
stride_om,
|
| 116 |
+
stride_os,
|
| 117 |
+
stride_lb,
|
| 118 |
+
stride_lh,
|
| 119 |
+
stride_ls,
|
| 120 |
+
stride_wh,
|
| 121 |
+
cu_seqlens_q,
|
| 122 |
+
cu_seqlens_k,
|
| 123 |
+
seqused_q,
|
| 124 |
+
seqused_k,
|
| 125 |
+
num_splits,
|
| 126 |
+
seqlen_q,
|
| 127 |
+
seqlen_k,
|
| 128 |
+
head_dim,
|
| 129 |
+
SEQLEN_Q_CACHE: tl.constexpr,
|
| 130 |
+
SEQLEN_K_CACHE: tl.constexpr,
|
| 131 |
+
QHEAD_PER_KVHEAD: tl.constexpr,
|
| 132 |
+
PACK_GQA: tl.constexpr,
|
| 133 |
+
QHEAD_PER_KVHEAD_PACKGQA: tl.constexpr,
|
| 134 |
+
TILE_M: tl.constexpr,
|
| 135 |
+
TILE_N: tl.constexpr,
|
| 136 |
+
TILE_K: tl.constexpr,
|
| 137 |
+
IS_CAUSAL: tl.constexpr,
|
| 138 |
+
IS_LOCAL: tl.constexpr,
|
| 139 |
+
IS_SPLIT_KV: tl.constexpr,
|
| 140 |
+
HAS_CU_SEQLENS_Q: tl.constexpr,
|
| 141 |
+
HAS_CU_SEQLENS_K: tl.constexpr,
|
| 142 |
+
HAS_SEQUSED_Q: tl.constexpr,
|
| 143 |
+
HAS_SEQUSED_K: tl.constexpr,
|
| 144 |
+
):
|
| 145 |
+
# Create grid index
|
| 146 |
+
grid_idx = AttnFwdGridIndex.create(
|
| 147 |
+
num_splits=num_splits,
|
| 148 |
+
QHEAD_PER_KVHEAD=QHEAD_PER_KVHEAD,
|
| 149 |
+
IS_SPLIT_KV=IS_SPLIT_KV,
|
| 150 |
+
PACK_GQA=PACK_GQA,
|
| 151 |
+
)
|
| 152 |
+
|
| 153 |
+
# Load window sizes
|
| 154 |
+
(
|
| 155 |
+
window_size_sink,
|
| 156 |
+
window_size_left,
|
| 157 |
+
window_size_right,
|
| 158 |
+
window_size_dist,
|
| 159 |
+
) = grid_idx.load_window_sizes(
|
| 160 |
+
window_sizes=window_sizes,
|
| 161 |
+
stride_wh=stride_wh,
|
| 162 |
+
IS_LOCAL=IS_LOCAL,
|
| 163 |
+
)
|
| 164 |
+
|
| 165 |
+
# Create config
|
| 166 |
+
config = AttnFwdConfig.create(
|
| 167 |
+
softmax_scale=softmax_scale,
|
| 168 |
+
softmax_threshold=softmax_threshold,
|
| 169 |
+
query_scale=query_scale,
|
| 170 |
+
key_scale=key_scale,
|
| 171 |
+
value_scale=value_scale,
|
| 172 |
+
m_block=grid_idx.m_block,
|
| 173 |
+
batch_idx=grid_idx.batch_idx,
|
| 174 |
+
window_size_sink=window_size_sink,
|
| 175 |
+
window_size_left=window_size_left,
|
| 176 |
+
window_size_right=window_size_right,
|
| 177 |
+
window_size_dist=window_size_dist,
|
| 178 |
+
head_dim=head_dim,
|
| 179 |
+
cu_seqlens_q=cu_seqlens_q,
|
| 180 |
+
cu_seqlens_k=cu_seqlens_k,
|
| 181 |
+
seqused_q=seqused_q,
|
| 182 |
+
seqused_k=seqused_k,
|
| 183 |
+
seqlen_q=seqlen_q,
|
| 184 |
+
seqlen_k=seqlen_k,
|
| 185 |
+
PACK_GQA=PACK_GQA,
|
| 186 |
+
QHEAD_PER_KVHEAD_PACKGQA=QHEAD_PER_KVHEAD_PACKGQA,
|
| 187 |
+
TILE_M=TILE_M,
|
| 188 |
+
TILE_N=TILE_N,
|
| 189 |
+
TILE_K=TILE_K,
|
| 190 |
+
HAS_CU_SEQLENS_Q=HAS_CU_SEQLENS_Q,
|
| 191 |
+
HAS_CU_SEQLENS_K=HAS_CU_SEQLENS_K,
|
| 192 |
+
HAS_SEQUSED_Q=HAS_SEQUSED_Q,
|
| 193 |
+
HAS_SEQUSED_K=HAS_SEQUSED_K,
|
| 194 |
+
)
|
| 195 |
+
|
| 196 |
+
# Create pointer scheduler
|
| 197 |
+
ptrs_sched = AttnFwdPointerScheduler.create(
|
| 198 |
+
config=config,
|
| 199 |
+
Q=Q,
|
| 200 |
+
K=K,
|
| 201 |
+
V=V,
|
| 202 |
+
Out=Out,
|
| 203 |
+
Lse=Lse,
|
| 204 |
+
batch_idx=grid_idx.batch_idx,
|
| 205 |
+
head_idx=grid_idx.head_idx,
|
| 206 |
+
head_kv_idx=grid_idx.head_kv_idx,
|
| 207 |
+
split_idx=grid_idx.split_idx,
|
| 208 |
+
stride_qb=stride_qb,
|
| 209 |
+
stride_qh=stride_qh,
|
| 210 |
+
stride_qm=stride_qm,
|
| 211 |
+
stride_kb=stride_kb,
|
| 212 |
+
stride_kh=stride_kh,
|
| 213 |
+
stride_kn=stride_kn,
|
| 214 |
+
stride_vb=stride_vb,
|
| 215 |
+
stride_vh=stride_vh,
|
| 216 |
+
stride_vn=stride_vn,
|
| 217 |
+
stride_ob=stride_ob,
|
| 218 |
+
stride_oh=stride_oh,
|
| 219 |
+
stride_om=stride_om,
|
| 220 |
+
stride_os=stride_os,
|
| 221 |
+
stride_lb=stride_lb,
|
| 222 |
+
stride_lh=stride_lh,
|
| 223 |
+
stride_ls=stride_ls,
|
| 224 |
+
IS_SPLIT_KV=IS_SPLIT_KV,
|
| 225 |
+
HAS_CU_SEQLENS_Q=HAS_CU_SEQLENS_Q,
|
| 226 |
+
HAS_CU_SEQLENS_K=HAS_CU_SEQLENS_K,
|
| 227 |
+
)
|
| 228 |
+
|
| 229 |
+
# Create block scheduler
|
| 230 |
+
block_sched = AttnFwdBlockScheduler.create(
|
| 231 |
+
config=config,
|
| 232 |
+
split_idx=grid_idx.split_idx,
|
| 233 |
+
num_splits=num_splits,
|
| 234 |
+
IS_CAUSAL=IS_CAUSAL,
|
| 235 |
+
IS_LOCAL=IS_LOCAL,
|
| 236 |
+
IS_SPLIT_KV=IS_SPLIT_KV,
|
| 237 |
+
)
|
| 238 |
+
|
| 239 |
+
# Create mask scheduler
|
| 240 |
+
mask_sched = AttnMaskScheduler.create(config)
|
| 241 |
+
|
| 242 |
+
# Create softmax scheduler
|
| 243 |
+
softmax_sched = SoftmaxScheduler.create(config)
|
| 244 |
+
|
| 245 |
+
# Create pointers
|
| 246 |
+
out_ptrs = ptrs_sched.make_out_ptrs(config)
|
| 247 |
+
lse_ptrs = ptrs_sched.make_lse_ptrs(config)
|
| 248 |
+
|
| 249 |
+
# Early exit if no n_blocks to process
|
| 250 |
+
if block_sched.is_empty():
|
| 251 |
+
ptrs_sched.store_empty(config, out_ptrs, lse_ptrs, IS_SPLIT_KV=IS_SPLIT_KV)
|
| 252 |
+
return
|
| 253 |
+
|
| 254 |
+
q_ptrs = ptrs_sched.make_q_ptrs(config)
|
| 255 |
+
k_ptrs = ptrs_sched.make_k_ptrs(config)
|
| 256 |
+
v_ptrs = ptrs_sched.make_v_ptrs(config)
|
| 257 |
+
|
| 258 |
+
# Initialize accumulators
|
| 259 |
+
row_max = tl.full((TILE_M,), float("-inf"), dtype=tl.float32)
|
| 260 |
+
row_sum = tl.zeros((TILE_M,), dtype=tl.float32)
|
| 261 |
+
acc_o = tl.zeros((TILE_M, TILE_K), dtype=tl.float32)
|
| 262 |
+
|
| 263 |
+
# Load query tile
|
| 264 |
+
q_tile = ptrs_sched.load_q(config, q_ptrs)
|
| 265 |
+
|
| 266 |
+
# Load key tile
|
| 267 |
+
k_tile = ptrs_sched.load_k(config, k_ptrs, block_sched.n_block_max - 1)
|
| 268 |
+
|
| 269 |
+
# Process n_blocks with causal masking
|
| 270 |
+
if IS_CAUSAL or IS_LOCAL:
|
| 271 |
+
n_block_max_no_mask = block_sched.n_block_max_no_mask
|
| 272 |
+
for n_block in tl.range(
|
| 273 |
+
block_sched.n_block_max - 1, block_sched.n_block_max_no_mask - 1, -1
|
| 274 |
+
):
|
| 275 |
+
k_tile, acc_o, row_max, row_sum = _fwd_inner_sparse_kernel(
|
| 276 |
+
config=config,
|
| 277 |
+
ptrs_sched=ptrs_sched,
|
| 278 |
+
mask_sched=mask_sched,
|
| 279 |
+
softmax_sched=softmax_sched,
|
| 280 |
+
q_tile=q_tile,
|
| 281 |
+
k_tile=k_tile,
|
| 282 |
+
k_ptrs=k_ptrs,
|
| 283 |
+
v_ptrs=v_ptrs,
|
| 284 |
+
acc_o=acc_o,
|
| 285 |
+
row_max=row_max,
|
| 286 |
+
row_sum=row_sum,
|
| 287 |
+
n_block=n_block,
|
| 288 |
+
n_block_min=block_sched.n_block_max_no_mask,
|
| 289 |
+
IS_MASK=True,
|
| 290 |
+
MASK_CAUSAL=IS_CAUSAL,
|
| 291 |
+
MASK_LOCAL=True if IS_LOCAL else False,
|
| 292 |
+
MASK_SINK=False,
|
| 293 |
+
CHECK_INF=True,
|
| 294 |
+
)
|
| 295 |
+
else:
|
| 296 |
+
# First iteration with seqlen masking
|
| 297 |
+
n_block = block_sched.n_block_max - 1
|
| 298 |
+
# Triton aggregate does not support attribute reassignment yet
|
| 299 |
+
# block_sched.n_block_max_no_mask = n_block
|
| 300 |
+
n_block_max_no_mask = n_block
|
| 301 |
+
|
| 302 |
+
k_tile, acc_o, row_max, row_sum = _fwd_inner_sparse_kernel(
|
| 303 |
+
config=config,
|
| 304 |
+
ptrs_sched=ptrs_sched,
|
| 305 |
+
mask_sched=mask_sched,
|
| 306 |
+
softmax_sched=softmax_sched,
|
| 307 |
+
q_tile=q_tile,
|
| 308 |
+
k_tile=k_tile,
|
| 309 |
+
k_ptrs=k_ptrs,
|
| 310 |
+
v_ptrs=v_ptrs,
|
| 311 |
+
acc_o=acc_o,
|
| 312 |
+
row_max=row_max,
|
| 313 |
+
row_sum=row_sum,
|
| 314 |
+
n_block=n_block,
|
| 315 |
+
n_block_min=n_block,
|
| 316 |
+
IS_MASK=True,
|
| 317 |
+
MASK_CAUSAL=False,
|
| 318 |
+
MASK_LOCAL=False,
|
| 319 |
+
MASK_SINK=False,
|
| 320 |
+
CHECK_INF=True,
|
| 321 |
+
)
|
| 322 |
+
|
| 323 |
+
# Process n_blocks without masking
|
| 324 |
+
if not IS_LOCAL and n_block_max_no_mask > block_sched.n_block_min:
|
| 325 |
+
# Load key tile
|
| 326 |
+
k_tile = ptrs_sched.load_k(config, k_ptrs, n_block_max_no_mask - 1)
|
| 327 |
+
|
| 328 |
+
for n_block in tl.range(
|
| 329 |
+
n_block_max_no_mask - 1, block_sched.n_block_min - 1, -1
|
| 330 |
+
):
|
| 331 |
+
k_tile, acc_o, row_max, row_sum = _fwd_inner_sparse_kernel(
|
| 332 |
+
config=config,
|
| 333 |
+
ptrs_sched=ptrs_sched,
|
| 334 |
+
mask_sched=mask_sched,
|
| 335 |
+
softmax_sched=softmax_sched,
|
| 336 |
+
q_tile=q_tile,
|
| 337 |
+
k_tile=k_tile,
|
| 338 |
+
k_ptrs=k_ptrs,
|
| 339 |
+
v_ptrs=v_ptrs,
|
| 340 |
+
acc_o=acc_o,
|
| 341 |
+
row_max=row_max,
|
| 342 |
+
row_sum=row_sum,
|
| 343 |
+
n_block=n_block,
|
| 344 |
+
n_block_min=block_sched.n_block_min,
|
| 345 |
+
IS_MASK=False,
|
| 346 |
+
MASK_CAUSAL=False,
|
| 347 |
+
MASK_LOCAL=False,
|
| 348 |
+
MASK_SINK=False,
|
| 349 |
+
CHECK_INF=False,
|
| 350 |
+
)
|
| 351 |
+
|
| 352 |
+
if IS_LOCAL:
|
| 353 |
+
# Process n_blocks with local right masking
|
| 354 |
+
if block_sched.n_block_window_max > block_sched.n_block_window_max_no_mask:
|
| 355 |
+
# Load key tile
|
| 356 |
+
k_tile = ptrs_sched.load_k(
|
| 357 |
+
config, k_ptrs, block_sched.n_block_window_max - 1
|
| 358 |
+
)
|
| 359 |
+
|
| 360 |
+
for n_block in tl.range(
|
| 361 |
+
block_sched.n_block_window_max - 1,
|
| 362 |
+
block_sched.n_block_window_max_no_mask - 1,
|
| 363 |
+
-1,
|
| 364 |
+
):
|
| 365 |
+
k_tile, acc_o, row_max, row_sum = _fwd_inner_sparse_kernel(
|
| 366 |
+
config=config,
|
| 367 |
+
ptrs_sched=ptrs_sched,
|
| 368 |
+
mask_sched=mask_sched,
|
| 369 |
+
softmax_sched=softmax_sched,
|
| 370 |
+
q_tile=q_tile,
|
| 371 |
+
k_tile=k_tile,
|
| 372 |
+
k_ptrs=k_ptrs,
|
| 373 |
+
v_ptrs=v_ptrs,
|
| 374 |
+
acc_o=acc_o,
|
| 375 |
+
row_max=row_max,
|
| 376 |
+
row_sum=row_sum,
|
| 377 |
+
n_block=n_block,
|
| 378 |
+
n_block_min=block_sched.n_block_window_max_no_mask,
|
| 379 |
+
IS_MASK=True,
|
| 380 |
+
MASK_CAUSAL=False,
|
| 381 |
+
MASK_LOCAL=True,
|
| 382 |
+
MASK_SINK=False,
|
| 383 |
+
CHECK_INF=True,
|
| 384 |
+
)
|
| 385 |
+
|
| 386 |
+
# Process n_blocks without masking
|
| 387 |
+
if (
|
| 388 |
+
block_sched.n_block_window_max_no_mask
|
| 389 |
+
> block_sched.n_block_window_min_no_mask
|
| 390 |
+
):
|
| 391 |
+
# Load key tile
|
| 392 |
+
k_tile = ptrs_sched.load_k(
|
| 393 |
+
config, k_ptrs, block_sched.n_block_window_max_no_mask - 1
|
| 394 |
+
)
|
| 395 |
+
|
| 396 |
+
for n_block in tl.range(
|
| 397 |
+
block_sched.n_block_window_max_no_mask - 1,
|
| 398 |
+
block_sched.n_block_window_min_no_mask - 1,
|
| 399 |
+
-1,
|
| 400 |
+
):
|
| 401 |
+
k_tile, acc_o, row_max, row_sum = _fwd_inner_sparse_kernel(
|
| 402 |
+
config=config,
|
| 403 |
+
ptrs_sched=ptrs_sched,
|
| 404 |
+
mask_sched=mask_sched,
|
| 405 |
+
softmax_sched=softmax_sched,
|
| 406 |
+
q_tile=q_tile,
|
| 407 |
+
k_tile=k_tile,
|
| 408 |
+
k_ptrs=k_ptrs,
|
| 409 |
+
v_ptrs=v_ptrs,
|
| 410 |
+
acc_o=acc_o,
|
| 411 |
+
row_max=row_max,
|
| 412 |
+
row_sum=row_sum,
|
| 413 |
+
n_block=n_block,
|
| 414 |
+
n_block_min=block_sched.n_block_window_min_no_mask,
|
| 415 |
+
IS_MASK=False,
|
| 416 |
+
MASK_CAUSAL=False,
|
| 417 |
+
MASK_LOCAL=False,
|
| 418 |
+
MASK_SINK=False,
|
| 419 |
+
CHECK_INF=False,
|
| 420 |
+
)
|
| 421 |
+
|
| 422 |
+
# Process n_blocks with local left masking
|
| 423 |
+
if block_sched.n_block_window_min_no_mask > block_sched.n_block_window_min:
|
| 424 |
+
# Load key tile
|
| 425 |
+
k_tile = ptrs_sched.load_k(
|
| 426 |
+
config, k_ptrs, block_sched.n_block_window_min_no_mask - 1
|
| 427 |
+
)
|
| 428 |
+
|
| 429 |
+
for n_block in tl.range(
|
| 430 |
+
block_sched.n_block_window_min_no_mask - 1,
|
| 431 |
+
block_sched.n_block_window_min - 1,
|
| 432 |
+
-1,
|
| 433 |
+
):
|
| 434 |
+
k_tile, acc_o, row_max, row_sum = _fwd_inner_sparse_kernel(
|
| 435 |
+
config=config,
|
| 436 |
+
ptrs_sched=ptrs_sched,
|
| 437 |
+
mask_sched=mask_sched,
|
| 438 |
+
softmax_sched=softmax_sched,
|
| 439 |
+
q_tile=q_tile,
|
| 440 |
+
k_tile=k_tile,
|
| 441 |
+
k_ptrs=k_ptrs,
|
| 442 |
+
v_ptrs=v_ptrs,
|
| 443 |
+
acc_o=acc_o,
|
| 444 |
+
row_max=row_max,
|
| 445 |
+
row_sum=row_sum,
|
| 446 |
+
n_block=n_block,
|
| 447 |
+
n_block_min=block_sched.n_block_window_min,
|
| 448 |
+
IS_MASK=True,
|
| 449 |
+
MASK_CAUSAL=False,
|
| 450 |
+
MASK_LOCAL=True,
|
| 451 |
+
MASK_SINK=False,
|
| 452 |
+
CHECK_INF=True,
|
| 453 |
+
)
|
| 454 |
+
|
| 455 |
+
# Process n_blocks with local sink masking
|
| 456 |
+
if block_sched.n_block_sink_max > block_sched.n_block_sink_min:
|
| 457 |
+
# Load key tile
|
| 458 |
+
k_tile = ptrs_sched.load_k(config, k_ptrs, block_sched.n_block_sink_max - 1)
|
| 459 |
+
|
| 460 |
+
for n_block in tl.range(
|
| 461 |
+
block_sched.n_block_sink_max - 1,
|
| 462 |
+
block_sched.n_block_sink_min - 1,
|
| 463 |
+
-1,
|
| 464 |
+
):
|
| 465 |
+
k_tile, acc_o, row_max, row_sum = _fwd_inner_sparse_kernel(
|
| 466 |
+
config=config,
|
| 467 |
+
ptrs_sched=ptrs_sched,
|
| 468 |
+
mask_sched=mask_sched,
|
| 469 |
+
softmax_sched=softmax_sched,
|
| 470 |
+
q_tile=q_tile,
|
| 471 |
+
k_tile=k_tile,
|
| 472 |
+
k_ptrs=k_ptrs,
|
| 473 |
+
v_ptrs=v_ptrs,
|
| 474 |
+
acc_o=acc_o,
|
| 475 |
+
row_max=row_max,
|
| 476 |
+
row_sum=row_sum,
|
| 477 |
+
n_block=n_block,
|
| 478 |
+
n_block_min=block_sched.n_block_sink_min,
|
| 479 |
+
IS_MASK=True,
|
| 480 |
+
MASK_CAUSAL=False,
|
| 481 |
+
MASK_LOCAL=True,
|
| 482 |
+
MASK_SINK=True,
|
| 483 |
+
CHECK_INF=True,
|
| 484 |
+
)
|
| 485 |
+
|
| 486 |
+
# Finalize softmax
|
| 487 |
+
row_scale, lse_tile = softmax_sched.finalize(
|
| 488 |
+
row_max=row_max,
|
| 489 |
+
row_sum=row_sum,
|
| 490 |
+
IS_LOG2=IS_SPLIT_KV,
|
| 491 |
+
)
|
| 492 |
+
|
| 493 |
+
# Store LSE
|
| 494 |
+
ptrs_sched.store_lse(config, lse_ptrs, lse_tile)
|
| 495 |
+
|
| 496 |
+
# Finalize rescale
|
| 497 |
+
acc_o = softmax_sched.rescale_o(
|
| 498 |
+
acc_o=acc_o,
|
| 499 |
+
row_scale=row_scale,
|
| 500 |
+
)
|
| 501 |
+
|
| 502 |
+
# Store output
|
| 503 |
+
ptrs_sched.store_out(config, out_ptrs, acc_o, IS_SPLIT_KV=IS_SPLIT_KV)
|
| 504 |
+
|
| 505 |
+
|
| 506 |
+
_fwd_sparse_kernel = cache_utils.wrap_kernel(_fwd_sparse_kernel)
|
| 507 |
+
|
| 508 |
+
|
| 509 |
+
_fwd_sparse_kernel_autotuned = None
|
| 510 |
+
|
| 511 |
+
|
| 512 |
+
def _get_autotuned_kernel():
|
| 513 |
+
global _fwd_sparse_kernel_autotuned
|
| 514 |
+
if _fwd_sparse_kernel_autotuned is None:
|
| 515 |
+
jit_kernel = _fwd_sparse_kernel._kernel
|
| 516 |
+
autotuned = autotuner.make_fwd_sparse_autotuned_kernel(jit_kernel)
|
| 517 |
+
_fwd_sparse_kernel_autotuned = autotuner.AutotunedKernel(autotuned)
|
| 518 |
+
return _fwd_sparse_kernel_autotuned
|
| 519 |
+
|
| 520 |
+
|
| 521 |
+
def _flash_sparse_attn_forward(
|
| 522 |
+
query: torch.Tensor,
|
| 523 |
+
key: torch.Tensor,
|
| 524 |
+
value: torch.Tensor,
|
| 525 |
+
is_causal: bool = False,
|
| 526 |
+
softmax_scale: float = None,
|
| 527 |
+
query_scale: Optional[torch.Tensor] = None,
|
| 528 |
+
key_scale: Optional[torch.Tensor] = None,
|
| 529 |
+
value_scale: Optional[torch.Tensor] = None,
|
| 530 |
+
window_sizes: Optional[torch.Tensor] = None,
|
| 531 |
+
softmax_threshold: float = None,
|
| 532 |
+
is_local: bool = False,
|
| 533 |
+
is_quant: bool = False,
|
| 534 |
+
is_split_kv: bool = False,
|
| 535 |
+
pack_gqa: bool = False,
|
| 536 |
+
out: Optional[torch.Tensor] = None,
|
| 537 |
+
lse: Optional[torch.Tensor] = None,
|
| 538 |
+
is_autotune: bool = False,
|
| 539 |
+
skip_checks: bool = False,
|
| 540 |
+
) -> Tuple[torch.Tensor, torch.Tensor, float, float]:
|
| 541 |
+
device = query.device
|
| 542 |
+
num_SMs = cache_utils.get_device_num_sms(device)
|
| 543 |
+
batch_size, seqlen_q, num_heads_q, head_dim = query.shape
|
| 544 |
+
_, seqlen_k, num_heads_kv, _ = key.shape
|
| 545 |
+
softmax_scale = (
|
| 546 |
+
softmax_scale if softmax_scale is not None else 1.0 / (head_dim**0.5)
|
| 547 |
+
)
|
| 548 |
+
softmax_threshold = (
|
| 549 |
+
softmax_threshold if softmax_threshold is not None else 1 / seqlen_k
|
| 550 |
+
)
|
| 551 |
+
qhead_per_kvhead = num_heads_q // num_heads_kv
|
| 552 |
+
qhead_per_kvhead_packgqa = num_heads_q // num_heads_kv if pack_gqa else 1
|
| 553 |
+
if is_local and window_sizes is None:
|
| 554 |
+
window_sizes = utils.window_sizes_heuristic(seqlen_k, num_heads_kv, device)
|
| 555 |
+
elif not is_local:
|
| 556 |
+
window_sizes = torch.zeros((num_heads_kv, 4), dtype=torch.int32, device=device)
|
| 557 |
+
|
| 558 |
+
if not skip_checks:
|
| 559 |
+
assert_inputs.assert_fwd_inputs(
|
| 560 |
+
query,
|
| 561 |
+
key,
|
| 562 |
+
value,
|
| 563 |
+
query_scale=query_scale,
|
| 564 |
+
key_scale=key_scale,
|
| 565 |
+
value_scale=value_scale,
|
| 566 |
+
window_sizes=window_sizes,
|
| 567 |
+
cu_seqlens_q=None,
|
| 568 |
+
cu_seqlens_k=None,
|
| 569 |
+
seqused_q=None,
|
| 570 |
+
seqused_k=None,
|
| 571 |
+
num_heads_q=num_heads_q,
|
| 572 |
+
num_heads_kv=num_heads_kv,
|
| 573 |
+
head_dim=head_dim,
|
| 574 |
+
is_quant=is_quant,
|
| 575 |
+
device=device,
|
| 576 |
+
)
|
| 577 |
+
|
| 578 |
+
TILE_K = max(triton.next_power_of_2(head_dim), 16)
|
| 579 |
+
|
| 580 |
+
_kernel_name = f"fwd_sparse{'_split' if is_split_kv else ''}"
|
| 581 |
+
launch_config = launch_template.load_launch_config(
|
| 582 |
+
device=device,
|
| 583 |
+
kernel_name=_kernel_name,
|
| 584 |
+
seqlen_q=seqlen_q,
|
| 585 |
+
seqlen_k=seqlen_k,
|
| 586 |
+
tile_k=TILE_K,
|
| 587 |
+
is_local=is_local,
|
| 588 |
+
qhead_per_kvhead=qhead_per_kvhead,
|
| 589 |
+
is_causal=is_causal,
|
| 590 |
+
pack_gqa=pack_gqa,
|
| 591 |
+
is_quant=is_quant,
|
| 592 |
+
)
|
| 593 |
+
if launch_config is not None and not is_autotune:
|
| 594 |
+
kernel = _fwd_sparse_kernel
|
| 595 |
+
TILE_M, TILE_N, num_warps, num_stages, num_ctas = launch_config
|
| 596 |
+
else:
|
| 597 |
+
kernel = _get_autotuned_kernel()
|
| 598 |
+
# Placeholder for pre-launch computations
|
| 599 |
+
TILE_M = TILE_N = 64
|
| 600 |
+
num_warps = num_stages = num_ctas = None
|
| 601 |
+
|
| 602 |
+
num_splits = (
|
| 603 |
+
utils.num_splits_heuristic(
|
| 604 |
+
seqlen_q=seqlen_q * qhead_per_kvhead_packgqa,
|
| 605 |
+
seqlen_k=seqlen_k,
|
| 606 |
+
num_SMs=num_SMs,
|
| 607 |
+
TILE_M=TILE_M,
|
| 608 |
+
TILE_N=TILE_N,
|
| 609 |
+
max_split_blocks=utils.max_split_blocks_from_window_sizes(
|
| 610 |
+
window_sizes, TILE_N
|
| 611 |
+
)
|
| 612 |
+
if is_local
|
| 613 |
+
else None,
|
| 614 |
+
)
|
| 615 |
+
if is_split_kv
|
| 616 |
+
else 1
|
| 617 |
+
)
|
| 618 |
+
|
| 619 |
+
out_dtype = torch.bfloat16 if is_quant else query.dtype
|
| 620 |
+
out = out if out is not None else torch.empty_like(query, dtype=out_dtype)
|
| 621 |
+
lse = (
|
| 622 |
+
lse
|
| 623 |
+
if lse is not None
|
| 624 |
+
else torch.empty(
|
| 625 |
+
(batch_size, num_heads_q, seqlen_q),
|
| 626 |
+
dtype=torch.float32,
|
| 627 |
+
device=query.device,
|
| 628 |
+
)
|
| 629 |
+
)
|
| 630 |
+
|
| 631 |
+
if is_split_kv:
|
| 632 |
+
out_partial = torch.empty(
|
| 633 |
+
(num_splits, batch_size, seqlen_q, num_heads_q, head_dim),
|
| 634 |
+
dtype=torch.float32,
|
| 635 |
+
device=query.device,
|
| 636 |
+
)
|
| 637 |
+
lse_partial = torch.empty(
|
| 638 |
+
(num_splits, batch_size, num_heads_q, seqlen_q),
|
| 639 |
+
dtype=torch.float32,
|
| 640 |
+
device=query.device,
|
| 641 |
+
)
|
| 642 |
+
|
| 643 |
+
if not is_quant:
|
| 644 |
+
query_scale = torch.ones(1, device=device, dtype=query.dtype)
|
| 645 |
+
key_scale = torch.ones(1, device=device, dtype=query.dtype)
|
| 646 |
+
value_scale = torch.ones(1, device=device, dtype=query.dtype)
|
| 647 |
+
|
| 648 |
+
grid = launch_grid.get_fwd_grid(
|
| 649 |
+
batch_size=batch_size,
|
| 650 |
+
seqlen_q=seqlen_q,
|
| 651 |
+
num_heads_q=num_heads_q,
|
| 652 |
+
num_heads_kv=num_heads_kv,
|
| 653 |
+
pack_gqa=pack_gqa,
|
| 654 |
+
num_splits=num_splits,
|
| 655 |
+
)
|
| 656 |
+
|
| 657 |
+
triton.set_allocator(utils.alloc_fn)
|
| 658 |
+
|
| 659 |
+
kernel[grid](
|
| 660 |
+
query,
|
| 661 |
+
key,
|
| 662 |
+
value,
|
| 663 |
+
out if not is_split_kv else out_partial,
|
| 664 |
+
lse if not is_split_kv else lse_partial,
|
| 665 |
+
softmax_scale,
|
| 666 |
+
softmax_threshold,
|
| 667 |
+
query_scale,
|
| 668 |
+
key_scale,
|
| 669 |
+
value_scale,
|
| 670 |
+
window_sizes,
|
| 671 |
+
query.stride(0),
|
| 672 |
+
query.stride(-2),
|
| 673 |
+
query.stride(-3),
|
| 674 |
+
key.stride(0),
|
| 675 |
+
key.stride(-2),
|
| 676 |
+
key.stride(-3),
|
| 677 |
+
value.stride(0),
|
| 678 |
+
value.stride(-2),
|
| 679 |
+
value.stride(-3),
|
| 680 |
+
out.stride(0) if not is_split_kv else out_partial.stride(1),
|
| 681 |
+
out.stride(-2) if not is_split_kv else out_partial.stride(-2),
|
| 682 |
+
out.stride(-3) if not is_split_kv else out_partial.stride(-3),
|
| 683 |
+
0 if not is_split_kv else out_partial.stride(0),
|
| 684 |
+
lse.stride(0) if not is_split_kv else lse_partial.stride(1),
|
| 685 |
+
lse.stride(-2) if not is_split_kv else lse_partial.stride(-2),
|
| 686 |
+
0 if not is_split_kv else lse_partial.stride(0),
|
| 687 |
+
window_sizes.stride(0),
|
| 688 |
+
None,
|
| 689 |
+
None,
|
| 690 |
+
None,
|
| 691 |
+
None,
|
| 692 |
+
num_splits,
|
| 693 |
+
seqlen_q=seqlen_q,
|
| 694 |
+
seqlen_k=seqlen_k,
|
| 695 |
+
head_dim=head_dim,
|
| 696 |
+
SEQLEN_Q_CACHE=seqlen_q // 1024,
|
| 697 |
+
SEQLEN_K_CACHE=seqlen_k // 1024,
|
| 698 |
+
QHEAD_PER_KVHEAD=qhead_per_kvhead,
|
| 699 |
+
PACK_GQA=pack_gqa,
|
| 700 |
+
QHEAD_PER_KVHEAD_PACKGQA=qhead_per_kvhead_packgqa,
|
| 701 |
+
TILE_M=TILE_M,
|
| 702 |
+
TILE_N=TILE_N,
|
| 703 |
+
TILE_K=TILE_K,
|
| 704 |
+
IS_CAUSAL=is_causal,
|
| 705 |
+
IS_LOCAL=is_local,
|
| 706 |
+
IS_SPLIT_KV=is_split_kv,
|
| 707 |
+
HAS_CU_SEQLENS_Q=False,
|
| 708 |
+
HAS_CU_SEQLENS_K=False,
|
| 709 |
+
HAS_SEQUSED_Q=False,
|
| 710 |
+
HAS_SEQUSED_K=False,
|
| 711 |
+
num_warps=num_warps,
|
| 712 |
+
num_stages=num_stages,
|
| 713 |
+
num_ctas=num_ctas,
|
| 714 |
+
)
|
| 715 |
+
|
| 716 |
+
if launch_config is None or is_autotune:
|
| 717 |
+
best = launch_template.extract_best_config(_get_autotuned_kernel())
|
| 718 |
+
if best is not None:
|
| 719 |
+
launch_template.store_launch_config(
|
| 720 |
+
device=device,
|
| 721 |
+
kernel_name=_kernel_name,
|
| 722 |
+
seqlen_q=seqlen_q,
|
| 723 |
+
seqlen_k=seqlen_k,
|
| 724 |
+
tile_k=TILE_K,
|
| 725 |
+
config=best,
|
| 726 |
+
is_local=is_local,
|
| 727 |
+
qhead_per_kvhead=qhead_per_kvhead,
|
| 728 |
+
is_causal=is_causal,
|
| 729 |
+
pack_gqa=pack_gqa,
|
| 730 |
+
is_quant=is_quant,
|
| 731 |
+
)
|
| 732 |
+
|
| 733 |
+
if is_split_kv:
|
| 734 |
+
flash_fwd_combine._flash_attn_fwd_combine(
|
| 735 |
+
out_partial,
|
| 736 |
+
lse_partial,
|
| 737 |
+
out,
|
| 738 |
+
lse,
|
| 739 |
+
)
|
| 740 |
+
|
| 741 |
+
return out, lse, softmax_scale, softmax_threshold
|
| 742 |
+
|
| 743 |
+
|
| 744 |
+
def _flash_sparse_attn_varlen_forward(
|
| 745 |
+
query: torch.Tensor,
|
| 746 |
+
key: torch.Tensor,
|
| 747 |
+
value: torch.Tensor,
|
| 748 |
+
cu_seqlens_q: torch.Tensor,
|
| 749 |
+
cu_seqlens_k: torch.Tensor,
|
| 750 |
+
max_seqlen_q: int,
|
| 751 |
+
max_seqlen_k: int,
|
| 752 |
+
is_causal: bool = False,
|
| 753 |
+
softmax_scale: float = None,
|
| 754 |
+
query_scale: Optional[torch.Tensor] = None,
|
| 755 |
+
key_scale: Optional[torch.Tensor] = None,
|
| 756 |
+
value_scale: Optional[torch.Tensor] = None,
|
| 757 |
+
window_sizes: Optional[torch.Tensor] = None,
|
| 758 |
+
softmax_threshold: float = None,
|
| 759 |
+
is_local: bool = False,
|
| 760 |
+
is_quant: bool = False,
|
| 761 |
+
is_split_kv: bool = False,
|
| 762 |
+
pack_gqa: bool = False,
|
| 763 |
+
seqused_q: Optional[torch.Tensor] = None,
|
| 764 |
+
seqused_k: Optional[torch.Tensor] = None,
|
| 765 |
+
out: Optional[torch.Tensor] = None,
|
| 766 |
+
lse: Optional[torch.Tensor] = None,
|
| 767 |
+
is_autotune: bool = False,
|
| 768 |
+
skip_checks: bool = False,
|
| 769 |
+
) -> Tuple[torch.Tensor, torch.Tensor, float, float]:
|
| 770 |
+
device = query.device
|
| 771 |
+
num_SMs = cache_utils.get_device_num_sms(device)
|
| 772 |
+
total_seqlen_q, num_heads_q, head_dim = query.shape
|
| 773 |
+
_, num_heads_kv, _ = key.shape
|
| 774 |
+
batch_size = cu_seqlens_q.shape[0] - 1
|
| 775 |
+
seqlen_q = max_seqlen_q
|
| 776 |
+
seqlen_k = max_seqlen_k
|
| 777 |
+
softmax_scale = (
|
| 778 |
+
softmax_scale if softmax_scale is not None else 1.0 / (head_dim**0.5)
|
| 779 |
+
)
|
| 780 |
+
softmax_threshold = (
|
| 781 |
+
softmax_threshold if softmax_threshold is not None else 1 / seqlen_k
|
| 782 |
+
)
|
| 783 |
+
qhead_per_kvhead = num_heads_q // num_heads_kv
|
| 784 |
+
qhead_per_kvhead_packgqa = num_heads_q // num_heads_kv if pack_gqa else 1
|
| 785 |
+
if is_local and window_sizes is None:
|
| 786 |
+
window_sizes = utils.window_sizes_heuristic(seqlen_k, num_heads_kv, device)
|
| 787 |
+
elif not is_local:
|
| 788 |
+
window_sizes = torch.zeros((num_heads_kv, 4), dtype=torch.int32, device=device)
|
| 789 |
+
|
| 790 |
+
if not skip_checks:
|
| 791 |
+
assert_inputs.assert_fwd_inputs(
|
| 792 |
+
query,
|
| 793 |
+
key,
|
| 794 |
+
value,
|
| 795 |
+
query_scale=query_scale,
|
| 796 |
+
key_scale=key_scale,
|
| 797 |
+
value_scale=value_scale,
|
| 798 |
+
window_sizes=window_sizes,
|
| 799 |
+
cu_seqlens_q=cu_seqlens_q,
|
| 800 |
+
cu_seqlens_k=cu_seqlens_k,
|
| 801 |
+
seqused_q=seqused_q,
|
| 802 |
+
seqused_k=seqused_k,
|
| 803 |
+
num_heads_q=num_heads_q,
|
| 804 |
+
num_heads_kv=num_heads_kv,
|
| 805 |
+
head_dim=head_dim,
|
| 806 |
+
is_quant=is_quant,
|
| 807 |
+
device=device,
|
| 808 |
+
)
|
| 809 |
+
|
| 810 |
+
TILE_K = max(triton.next_power_of_2(head_dim), 16)
|
| 811 |
+
|
| 812 |
+
_kernel_name = f"fwd_sparse{'_split' if is_split_kv else ''}"
|
| 813 |
+
launch_config = launch_template.load_launch_config(
|
| 814 |
+
device=device,
|
| 815 |
+
kernel_name=_kernel_name,
|
| 816 |
+
seqlen_q=seqlen_q,
|
| 817 |
+
seqlen_k=seqlen_k,
|
| 818 |
+
tile_k=TILE_K,
|
| 819 |
+
is_local=is_local,
|
| 820 |
+
qhead_per_kvhead=qhead_per_kvhead,
|
| 821 |
+
is_causal=is_causal,
|
| 822 |
+
pack_gqa=pack_gqa,
|
| 823 |
+
is_quant=is_quant,
|
| 824 |
+
)
|
| 825 |
+
if launch_config is not None and not is_autotune:
|
| 826 |
+
kernel = _fwd_sparse_kernel
|
| 827 |
+
TILE_M, TILE_N, num_warps, num_stages, num_ctas = launch_config
|
| 828 |
+
else:
|
| 829 |
+
kernel = _get_autotuned_kernel()
|
| 830 |
+
# Placeholder for pre-launch computations
|
| 831 |
+
TILE_M = TILE_N = 64
|
| 832 |
+
num_warps = num_stages = num_ctas = None
|
| 833 |
+
|
| 834 |
+
num_splits = (
|
| 835 |
+
utils.num_splits_heuristic(
|
| 836 |
+
seqlen_q=seqlen_q * qhead_per_kvhead_packgqa,
|
| 837 |
+
seqlen_k=seqlen_k,
|
| 838 |
+
num_SMs=num_SMs,
|
| 839 |
+
TILE_M=TILE_M,
|
| 840 |
+
TILE_N=TILE_N,
|
| 841 |
+
max_split_blocks=utils.max_split_blocks_from_window_sizes(
|
| 842 |
+
window_sizes, TILE_N
|
| 843 |
+
)
|
| 844 |
+
if is_local
|
| 845 |
+
else None,
|
| 846 |
+
)
|
| 847 |
+
if is_split_kv
|
| 848 |
+
else 1
|
| 849 |
+
)
|
| 850 |
+
|
| 851 |
+
out_dtype = torch.bfloat16 if is_quant else query.dtype
|
| 852 |
+
out = out if out is not None else torch.empty_like(query, dtype=out_dtype)
|
| 853 |
+
lse = (
|
| 854 |
+
lse
|
| 855 |
+
if lse is not None
|
| 856 |
+
else torch.empty(
|
| 857 |
+
(num_heads_q, total_seqlen_q),
|
| 858 |
+
dtype=torch.float32,
|
| 859 |
+
device=query.device,
|
| 860 |
+
)
|
| 861 |
+
)
|
| 862 |
+
|
| 863 |
+
if is_split_kv:
|
| 864 |
+
out_partial = torch.empty(
|
| 865 |
+
(num_splits, total_seqlen_q, num_heads_q, head_dim),
|
| 866 |
+
dtype=torch.float32,
|
| 867 |
+
device=query.device,
|
| 868 |
+
)
|
| 869 |
+
lse_partial = torch.empty(
|
| 870 |
+
(num_splits, num_heads_q, total_seqlen_q),
|
| 871 |
+
dtype=torch.float32,
|
| 872 |
+
device=query.device,
|
| 873 |
+
)
|
| 874 |
+
|
| 875 |
+
if not is_quant:
|
| 876 |
+
query_scale = torch.ones(1, device=device, dtype=query.dtype)
|
| 877 |
+
key_scale = torch.ones(1, device=device, dtype=query.dtype)
|
| 878 |
+
value_scale = torch.ones(1, device=device, dtype=query.dtype)
|
| 879 |
+
|
| 880 |
+
grid = launch_grid.get_fwd_grid(
|
| 881 |
+
batch_size=batch_size,
|
| 882 |
+
seqlen_q=seqlen_q,
|
| 883 |
+
num_heads_q=num_heads_q,
|
| 884 |
+
num_heads_kv=num_heads_kv,
|
| 885 |
+
pack_gqa=pack_gqa,
|
| 886 |
+
num_splits=num_splits,
|
| 887 |
+
)
|
| 888 |
+
|
| 889 |
+
triton.set_allocator(utils.alloc_fn)
|
| 890 |
+
|
| 891 |
+
kernel[grid](
|
| 892 |
+
query,
|
| 893 |
+
key,
|
| 894 |
+
value,
|
| 895 |
+
out if not is_split_kv else out_partial,
|
| 896 |
+
lse if not is_split_kv else lse_partial,
|
| 897 |
+
softmax_scale,
|
| 898 |
+
softmax_threshold,
|
| 899 |
+
query_scale,
|
| 900 |
+
key_scale,
|
| 901 |
+
value_scale,
|
| 902 |
+
window_sizes,
|
| 903 |
+
0,
|
| 904 |
+
query.stride(-2),
|
| 905 |
+
query.stride(0),
|
| 906 |
+
0,
|
| 907 |
+
key.stride(-2),
|
| 908 |
+
key.stride(0),
|
| 909 |
+
0,
|
| 910 |
+
value.stride(-2),
|
| 911 |
+
value.stride(0),
|
| 912 |
+
0,
|
| 913 |
+
out.stride(-2) if not is_split_kv else out_partial.stride(-2),
|
| 914 |
+
out.stride(0) if not is_split_kv else out_partial.stride(-3),
|
| 915 |
+
0 if not is_split_kv else out_partial.stride(0),
|
| 916 |
+
0,
|
| 917 |
+
lse.stride(-2) if not is_split_kv else lse_partial.stride(-2),
|
| 918 |
+
0 if not is_split_kv else lse_partial.stride(0),
|
| 919 |
+
window_sizes.stride(0),
|
| 920 |
+
cu_seqlens_q,
|
| 921 |
+
cu_seqlens_k,
|
| 922 |
+
seqused_q,
|
| 923 |
+
seqused_k,
|
| 924 |
+
num_splits,
|
| 925 |
+
seqlen_q=seqlen_q,
|
| 926 |
+
seqlen_k=seqlen_k,
|
| 927 |
+
head_dim=head_dim,
|
| 928 |
+
SEQLEN_Q_CACHE=seqlen_q // 1024,
|
| 929 |
+
SEQLEN_K_CACHE=seqlen_k // 1024,
|
| 930 |
+
QHEAD_PER_KVHEAD=qhead_per_kvhead,
|
| 931 |
+
PACK_GQA=pack_gqa,
|
| 932 |
+
QHEAD_PER_KVHEAD_PACKGQA=qhead_per_kvhead_packgqa,
|
| 933 |
+
TILE_M=TILE_M,
|
| 934 |
+
TILE_N=TILE_N,
|
| 935 |
+
TILE_K=TILE_K,
|
| 936 |
+
IS_CAUSAL=is_causal,
|
| 937 |
+
IS_LOCAL=is_local,
|
| 938 |
+
IS_SPLIT_KV=is_split_kv,
|
| 939 |
+
HAS_CU_SEQLENS_Q=True,
|
| 940 |
+
HAS_CU_SEQLENS_K=True,
|
| 941 |
+
HAS_SEQUSED_Q=seqused_q is not None,
|
| 942 |
+
HAS_SEQUSED_K=seqused_k is not None,
|
| 943 |
+
num_warps=num_warps,
|
| 944 |
+
num_stages=num_stages,
|
| 945 |
+
num_ctas=num_ctas,
|
| 946 |
+
)
|
| 947 |
+
|
| 948 |
+
if launch_config is None or is_autotune:
|
| 949 |
+
best = launch_template.extract_best_config(_get_autotuned_kernel())
|
| 950 |
+
if best is not None:
|
| 951 |
+
launch_template.store_launch_config(
|
| 952 |
+
device=device,
|
| 953 |
+
kernel_name=_kernel_name,
|
| 954 |
+
seqlen_q=seqlen_q,
|
| 955 |
+
seqlen_k=seqlen_k,
|
| 956 |
+
tile_k=TILE_K,
|
| 957 |
+
config=best,
|
| 958 |
+
is_local=is_local,
|
| 959 |
+
qhead_per_kvhead=qhead_per_kvhead,
|
| 960 |
+
is_causal=is_causal,
|
| 961 |
+
pack_gqa=pack_gqa,
|
| 962 |
+
is_quant=is_quant,
|
| 963 |
+
)
|
| 964 |
+
|
| 965 |
+
if is_split_kv:
|
| 966 |
+
flash_fwd_combine._flash_attn_fwd_combine(
|
| 967 |
+
out_partial,
|
| 968 |
+
lse_partial,
|
| 969 |
+
out,
|
| 970 |
+
lse,
|
| 971 |
+
cu_seqlens_q=cu_seqlens_q,
|
| 972 |
+
)
|
| 973 |
+
|
| 974 |
+
return out, lse, softmax_scale, softmax_threshold
|
build/torch-cuda/interface.py
ADDED
|
@@ -0,0 +1,1912 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from typing import Optional, Tuple, Union
|
| 2 |
+
|
| 3 |
+
import torch
|
| 4 |
+
|
| 5 |
+
from .flash_dense_fwd import (
|
| 6 |
+
_flash_dense_attn_forward,
|
| 7 |
+
_flash_dense_attn_varlen_forward,
|
| 8 |
+
)
|
| 9 |
+
from .flash_dense_bwd import (
|
| 10 |
+
_flash_dense_attn_backward,
|
| 11 |
+
_flash_dense_attn_varlen_backward,
|
| 12 |
+
)
|
| 13 |
+
from .flash_dense_dec import (
|
| 14 |
+
_flash_dense_attn_decode,
|
| 15 |
+
_flash_dense_attn_varlen_decode,
|
| 16 |
+
)
|
| 17 |
+
from .flash_sparse_fwd import (
|
| 18 |
+
_flash_sparse_attn_forward,
|
| 19 |
+
_flash_sparse_attn_varlen_forward,
|
| 20 |
+
)
|
| 21 |
+
from .flash_sparse_bwd import (
|
| 22 |
+
_flash_sparse_attn_backward,
|
| 23 |
+
_flash_sparse_attn_varlen_backward,
|
| 24 |
+
)
|
| 25 |
+
from .flash_sparse_dec import (
|
| 26 |
+
_flash_sparse_attn_decode,
|
| 27 |
+
_flash_sparse_attn_varlen_decode,
|
| 28 |
+
)
|
| 29 |
+
from .flash_gated_fwd import (
|
| 30 |
+
_flash_gated_attn_forward,
|
| 31 |
+
_flash_gated_attn_varlen_forward,
|
| 32 |
+
)
|
| 33 |
+
from .flash_gated_bwd import (
|
| 34 |
+
_flash_gated_attn_backward,
|
| 35 |
+
_flash_gated_attn_varlen_backward,
|
| 36 |
+
)
|
| 37 |
+
from .flash_gated_dec import (
|
| 38 |
+
_flash_gated_attn_decode,
|
| 39 |
+
_flash_gated_attn_varlen_decode,
|
| 40 |
+
)
|
| 41 |
+
from .flash_proj_fwd import _flash_proj_forward
|
| 42 |
+
from .flash_proj_bwd import _flash_proj_backward
|
| 43 |
+
from .utils import ensure_contiguous
|
| 44 |
+
from . import quant
|
| 45 |
+
|
| 46 |
+
|
| 47 |
+
class FlashDenseAttnFunc(torch.autograd.Function):
|
| 48 |
+
@staticmethod
|
| 49 |
+
@ensure_contiguous
|
| 50 |
+
def forward(
|
| 51 |
+
ctx,
|
| 52 |
+
query: torch.Tensor,
|
| 53 |
+
key: torch.Tensor,
|
| 54 |
+
value: torch.Tensor,
|
| 55 |
+
is_causal: bool = False,
|
| 56 |
+
softmax_scale: Optional[float] = None,
|
| 57 |
+
query_scale: Optional[torch.Tensor] = None,
|
| 58 |
+
key_scale: Optional[torch.Tensor] = None,
|
| 59 |
+
value_scale: Optional[torch.Tensor] = None,
|
| 60 |
+
window_sizes: Optional[torch.Tensor] = None,
|
| 61 |
+
is_local: bool = False,
|
| 62 |
+
is_quant: bool = False,
|
| 63 |
+
is_split_kv: bool = False,
|
| 64 |
+
is_split_qo: bool = False,
|
| 65 |
+
pack_gqa: bool = False,
|
| 66 |
+
out: Optional[torch.Tensor] = None,
|
| 67 |
+
lse: Optional[torch.Tensor] = None,
|
| 68 |
+
is_autotune: bool = False,
|
| 69 |
+
skip_checks: bool = False,
|
| 70 |
+
return_lse: bool = False,
|
| 71 |
+
):
|
| 72 |
+
# Set is_causal to False if sequence length is 1 to avoid unnecessary masking overhead
|
| 73 |
+
is_causal = False if query.shape[1] == 1 else is_causal
|
| 74 |
+
|
| 75 |
+
if window_sizes is not None:
|
| 76 |
+
is_local = True
|
| 77 |
+
|
| 78 |
+
if is_quant and (
|
| 79 |
+
query_scale is None or key_scale is None or value_scale is None
|
| 80 |
+
):
|
| 81 |
+
query, query_scale = quant.quantize_fp8(query)
|
| 82 |
+
key, key_scale = quant.quantize_fp8(key)
|
| 83 |
+
value, value_scale = quant.quantize_fp8(value)
|
| 84 |
+
|
| 85 |
+
out, lse, softmax_scale = _flash_dense_attn_forward(
|
| 86 |
+
query=query,
|
| 87 |
+
key=key,
|
| 88 |
+
value=value,
|
| 89 |
+
is_causal=is_causal,
|
| 90 |
+
softmax_scale=softmax_scale,
|
| 91 |
+
query_scale=query_scale,
|
| 92 |
+
key_scale=key_scale,
|
| 93 |
+
value_scale=value_scale,
|
| 94 |
+
window_sizes=window_sizes,
|
| 95 |
+
is_local=is_local,
|
| 96 |
+
is_quant=is_quant,
|
| 97 |
+
is_split_kv=is_split_kv,
|
| 98 |
+
pack_gqa=pack_gqa,
|
| 99 |
+
out=out,
|
| 100 |
+
lse=lse,
|
| 101 |
+
is_autotune=is_autotune,
|
| 102 |
+
skip_checks=skip_checks,
|
| 103 |
+
)
|
| 104 |
+
|
| 105 |
+
ctx.save_for_backward(query, key, value, out, lse)
|
| 106 |
+
ctx.is_causal = is_causal
|
| 107 |
+
ctx.softmax_scale = softmax_scale
|
| 108 |
+
ctx.query_scale = query_scale
|
| 109 |
+
ctx.key_scale = key_scale
|
| 110 |
+
ctx.value_scale = value_scale
|
| 111 |
+
ctx.window_sizes = window_sizes
|
| 112 |
+
ctx.is_local = is_local
|
| 113 |
+
ctx.is_quant = is_quant
|
| 114 |
+
ctx.is_split_qo = is_split_qo
|
| 115 |
+
ctx.is_autotune = is_autotune
|
| 116 |
+
ctx.skip_checks = skip_checks
|
| 117 |
+
|
| 118 |
+
if return_lse:
|
| 119 |
+
# LSE gradient is not supported yet
|
| 120 |
+
ctx.mark_non_differentiable(lse)
|
| 121 |
+
return out, lse
|
| 122 |
+
return out
|
| 123 |
+
|
| 124 |
+
@staticmethod
|
| 125 |
+
@ensure_contiguous
|
| 126 |
+
def backward(ctx, dout: torch.Tensor, *args):
|
| 127 |
+
query, key, value, out, lse = ctx.saved_tensors
|
| 128 |
+
|
| 129 |
+
dq, dk, dv = _flash_dense_attn_backward(
|
| 130 |
+
query=query,
|
| 131 |
+
key=key,
|
| 132 |
+
value=value,
|
| 133 |
+
out=out,
|
| 134 |
+
dout=dout,
|
| 135 |
+
lse=lse,
|
| 136 |
+
is_causal=ctx.is_causal,
|
| 137 |
+
softmax_scale=ctx.softmax_scale,
|
| 138 |
+
query_scale=ctx.query_scale,
|
| 139 |
+
key_scale=ctx.key_scale,
|
| 140 |
+
value_scale=ctx.value_scale,
|
| 141 |
+
window_sizes=ctx.window_sizes,
|
| 142 |
+
is_local=ctx.is_local,
|
| 143 |
+
is_quant=ctx.is_quant,
|
| 144 |
+
is_split_qo=ctx.is_split_qo,
|
| 145 |
+
is_autotune=ctx.is_autotune,
|
| 146 |
+
skip_checks=ctx.skip_checks,
|
| 147 |
+
)
|
| 148 |
+
|
| 149 |
+
return dq, dk, dv, *((None,) * 30)
|
| 150 |
+
|
| 151 |
+
|
| 152 |
+
class FlashDenseAttnVarlenFunc(torch.autograd.Function):
|
| 153 |
+
@staticmethod
|
| 154 |
+
@ensure_contiguous
|
| 155 |
+
def forward(
|
| 156 |
+
ctx,
|
| 157 |
+
query: torch.Tensor,
|
| 158 |
+
key: torch.Tensor,
|
| 159 |
+
value: torch.Tensor,
|
| 160 |
+
cu_seqlens_q: torch.Tensor,
|
| 161 |
+
cu_seqlens_k: torch.Tensor,
|
| 162 |
+
max_seqlen_q: int,
|
| 163 |
+
max_seqlen_k: int,
|
| 164 |
+
is_causal: bool = False,
|
| 165 |
+
softmax_scale: Optional[float] = None,
|
| 166 |
+
query_scale: Optional[torch.Tensor] = None,
|
| 167 |
+
key_scale: Optional[torch.Tensor] = None,
|
| 168 |
+
value_scale: Optional[torch.Tensor] = None,
|
| 169 |
+
window_sizes: Optional[torch.Tensor] = None,
|
| 170 |
+
is_local: bool = False,
|
| 171 |
+
is_quant: bool = False,
|
| 172 |
+
is_split_kv: bool = False,
|
| 173 |
+
is_split_qo: bool = False,
|
| 174 |
+
pack_gqa: bool = False,
|
| 175 |
+
seqused_q: Optional[torch.Tensor] = None,
|
| 176 |
+
seqused_k: Optional[torch.Tensor] = None,
|
| 177 |
+
out: Optional[torch.Tensor] = None,
|
| 178 |
+
lse: Optional[torch.Tensor] = None,
|
| 179 |
+
is_autotune: bool = False,
|
| 180 |
+
skip_checks: bool = False,
|
| 181 |
+
return_lse: bool = False,
|
| 182 |
+
):
|
| 183 |
+
# Set is_causal to False if sequence length is 1 to avoid unnecessary masking overhead
|
| 184 |
+
is_causal = False if max_seqlen_q == 1 else is_causal
|
| 185 |
+
|
| 186 |
+
if window_sizes is not None:
|
| 187 |
+
is_local = True
|
| 188 |
+
|
| 189 |
+
if is_quant and (
|
| 190 |
+
query_scale is None or key_scale is None or value_scale is None
|
| 191 |
+
):
|
| 192 |
+
query, query_scale = quant.quantize_fp8(query)
|
| 193 |
+
key, key_scale = quant.quantize_fp8(key)
|
| 194 |
+
value, value_scale = quant.quantize_fp8(value)
|
| 195 |
+
|
| 196 |
+
out, lse, softmax_scale = _flash_dense_attn_varlen_forward(
|
| 197 |
+
query=query,
|
| 198 |
+
key=key,
|
| 199 |
+
value=value,
|
| 200 |
+
cu_seqlens_q=cu_seqlens_q,
|
| 201 |
+
cu_seqlens_k=cu_seqlens_k,
|
| 202 |
+
max_seqlen_q=max_seqlen_q,
|
| 203 |
+
max_seqlen_k=max_seqlen_k,
|
| 204 |
+
is_causal=is_causal,
|
| 205 |
+
softmax_scale=softmax_scale,
|
| 206 |
+
query_scale=query_scale,
|
| 207 |
+
key_scale=key_scale,
|
| 208 |
+
value_scale=value_scale,
|
| 209 |
+
window_sizes=window_sizes,
|
| 210 |
+
is_local=is_local,
|
| 211 |
+
is_quant=is_quant,
|
| 212 |
+
is_split_kv=is_split_kv,
|
| 213 |
+
pack_gqa=pack_gqa,
|
| 214 |
+
seqused_q=seqused_q,
|
| 215 |
+
seqused_k=seqused_k,
|
| 216 |
+
out=out,
|
| 217 |
+
lse=lse,
|
| 218 |
+
is_autotune=is_autotune,
|
| 219 |
+
skip_checks=skip_checks,
|
| 220 |
+
)
|
| 221 |
+
|
| 222 |
+
ctx.save_for_backward(
|
| 223 |
+
query,
|
| 224 |
+
key,
|
| 225 |
+
value,
|
| 226 |
+
out,
|
| 227 |
+
lse,
|
| 228 |
+
cu_seqlens_q,
|
| 229 |
+
cu_seqlens_k,
|
| 230 |
+
seqused_q,
|
| 231 |
+
seqused_k,
|
| 232 |
+
)
|
| 233 |
+
ctx.max_seqlen_q = max_seqlen_q
|
| 234 |
+
ctx.max_seqlen_k = max_seqlen_k
|
| 235 |
+
ctx.is_causal = is_causal
|
| 236 |
+
ctx.softmax_scale = softmax_scale
|
| 237 |
+
ctx.query_scale = query_scale
|
| 238 |
+
ctx.key_scale = key_scale
|
| 239 |
+
ctx.value_scale = value_scale
|
| 240 |
+
ctx.window_sizes = window_sizes
|
| 241 |
+
ctx.is_local = is_local
|
| 242 |
+
ctx.is_quant = is_quant
|
| 243 |
+
ctx.is_split_qo = is_split_qo
|
| 244 |
+
ctx.is_autotune = is_autotune
|
| 245 |
+
ctx.skip_checks = skip_checks
|
| 246 |
+
|
| 247 |
+
if return_lse:
|
| 248 |
+
# LSE gradient is not supported yet
|
| 249 |
+
ctx.mark_non_differentiable(lse)
|
| 250 |
+
return out, lse
|
| 251 |
+
return out
|
| 252 |
+
|
| 253 |
+
@staticmethod
|
| 254 |
+
@ensure_contiguous
|
| 255 |
+
def backward(ctx, dout: torch.Tensor, *args):
|
| 256 |
+
(
|
| 257 |
+
query,
|
| 258 |
+
key,
|
| 259 |
+
value,
|
| 260 |
+
out,
|
| 261 |
+
lse,
|
| 262 |
+
cu_seqlens_q,
|
| 263 |
+
cu_seqlens_k,
|
| 264 |
+
seqused_q,
|
| 265 |
+
seqused_k,
|
| 266 |
+
) = ctx.saved_tensors
|
| 267 |
+
|
| 268 |
+
dq, dk, dv = _flash_dense_attn_varlen_backward(
|
| 269 |
+
query=query,
|
| 270 |
+
key=key,
|
| 271 |
+
value=value,
|
| 272 |
+
out=out,
|
| 273 |
+
dout=dout,
|
| 274 |
+
lse=lse,
|
| 275 |
+
cu_seqlens_q=cu_seqlens_q,
|
| 276 |
+
cu_seqlens_k=cu_seqlens_k,
|
| 277 |
+
max_seqlen_q=ctx.max_seqlen_q,
|
| 278 |
+
max_seqlen_k=ctx.max_seqlen_k,
|
| 279 |
+
is_causal=ctx.is_causal,
|
| 280 |
+
softmax_scale=ctx.softmax_scale,
|
| 281 |
+
query_scale=ctx.query_scale,
|
| 282 |
+
key_scale=ctx.key_scale,
|
| 283 |
+
value_scale=ctx.value_scale,
|
| 284 |
+
window_sizes=ctx.window_sizes,
|
| 285 |
+
is_local=ctx.is_local,
|
| 286 |
+
is_quant=ctx.is_quant,
|
| 287 |
+
is_split_qo=ctx.is_split_qo,
|
| 288 |
+
seqused_q=seqused_q,
|
| 289 |
+
seqused_k=seqused_k,
|
| 290 |
+
is_autotune=ctx.is_autotune,
|
| 291 |
+
skip_checks=ctx.skip_checks,
|
| 292 |
+
)
|
| 293 |
+
|
| 294 |
+
return dq, dk, dv, *((None,) * 30)
|
| 295 |
+
|
| 296 |
+
|
| 297 |
+
class FlashSparseAttnFunc(torch.autograd.Function):
|
| 298 |
+
@staticmethod
|
| 299 |
+
@ensure_contiguous
|
| 300 |
+
def forward(
|
| 301 |
+
ctx,
|
| 302 |
+
query: torch.Tensor,
|
| 303 |
+
key: torch.Tensor,
|
| 304 |
+
value: torch.Tensor,
|
| 305 |
+
is_causal: bool = False,
|
| 306 |
+
softmax_scale: Optional[float] = None,
|
| 307 |
+
query_scale: Optional[torch.Tensor] = None,
|
| 308 |
+
key_scale: Optional[torch.Tensor] = None,
|
| 309 |
+
value_scale: Optional[torch.Tensor] = None,
|
| 310 |
+
window_sizes: Optional[torch.Tensor] = None,
|
| 311 |
+
softmax_threshold: Optional[float] = None,
|
| 312 |
+
is_local: bool = False,
|
| 313 |
+
is_quant: bool = False,
|
| 314 |
+
is_split_kv: bool = False,
|
| 315 |
+
is_split_qo: bool = False,
|
| 316 |
+
pack_gqa: bool = False,
|
| 317 |
+
out: Optional[torch.Tensor] = None,
|
| 318 |
+
lse: Optional[torch.Tensor] = None,
|
| 319 |
+
is_autotune: bool = False,
|
| 320 |
+
skip_checks: bool = False,
|
| 321 |
+
return_lse: bool = False,
|
| 322 |
+
):
|
| 323 |
+
# Set is_causal to False if sequence length is 1 to avoid unnecessary masking overhead
|
| 324 |
+
is_causal = False if query.shape[1] == 1 else is_causal
|
| 325 |
+
|
| 326 |
+
if window_sizes is not None:
|
| 327 |
+
is_local = True
|
| 328 |
+
|
| 329 |
+
if is_quant and (
|
| 330 |
+
query_scale is None or key_scale is None or value_scale is None
|
| 331 |
+
):
|
| 332 |
+
query, query_scale = quant.quantize_fp8(query)
|
| 333 |
+
key, key_scale = quant.quantize_fp8(key)
|
| 334 |
+
value, value_scale = quant.quantize_fp8(value)
|
| 335 |
+
|
| 336 |
+
out, lse, softmax_scale, softmax_threshold = _flash_sparse_attn_forward(
|
| 337 |
+
query=query,
|
| 338 |
+
key=key,
|
| 339 |
+
value=value,
|
| 340 |
+
is_causal=is_causal,
|
| 341 |
+
softmax_scale=softmax_scale,
|
| 342 |
+
query_scale=query_scale,
|
| 343 |
+
key_scale=key_scale,
|
| 344 |
+
value_scale=value_scale,
|
| 345 |
+
window_sizes=window_sizes,
|
| 346 |
+
softmax_threshold=softmax_threshold,
|
| 347 |
+
is_local=is_local,
|
| 348 |
+
is_quant=is_quant,
|
| 349 |
+
is_split_kv=is_split_kv,
|
| 350 |
+
pack_gqa=pack_gqa,
|
| 351 |
+
out=out,
|
| 352 |
+
lse=lse,
|
| 353 |
+
is_autotune=is_autotune,
|
| 354 |
+
skip_checks=skip_checks,
|
| 355 |
+
)
|
| 356 |
+
|
| 357 |
+
ctx.save_for_backward(query, key, value, out, lse)
|
| 358 |
+
ctx.is_causal = is_causal
|
| 359 |
+
ctx.softmax_scale = softmax_scale
|
| 360 |
+
ctx.query_scale = query_scale
|
| 361 |
+
ctx.key_scale = key_scale
|
| 362 |
+
ctx.value_scale = value_scale
|
| 363 |
+
ctx.window_sizes = window_sizes
|
| 364 |
+
ctx.softmax_threshold = softmax_threshold
|
| 365 |
+
ctx.is_local = is_local
|
| 366 |
+
ctx.is_quant = is_quant
|
| 367 |
+
ctx.is_split_qo = is_split_qo
|
| 368 |
+
ctx.is_autotune = is_autotune
|
| 369 |
+
ctx.skip_checks = skip_checks
|
| 370 |
+
|
| 371 |
+
if return_lse:
|
| 372 |
+
# LSE gradient is not supported yet
|
| 373 |
+
ctx.mark_non_differentiable(lse)
|
| 374 |
+
return out, lse
|
| 375 |
+
return out
|
| 376 |
+
|
| 377 |
+
@staticmethod
|
| 378 |
+
@ensure_contiguous
|
| 379 |
+
def backward(ctx, dout: torch.Tensor, *args):
|
| 380 |
+
query, key, value, out, lse = ctx.saved_tensors
|
| 381 |
+
|
| 382 |
+
dq, dk, dv = _flash_sparse_attn_backward(
|
| 383 |
+
query=query,
|
| 384 |
+
key=key,
|
| 385 |
+
value=value,
|
| 386 |
+
out=out,
|
| 387 |
+
dout=dout,
|
| 388 |
+
lse=lse,
|
| 389 |
+
is_causal=ctx.is_causal,
|
| 390 |
+
softmax_scale=ctx.softmax_scale,
|
| 391 |
+
query_scale=ctx.query_scale,
|
| 392 |
+
key_scale=ctx.key_scale,
|
| 393 |
+
value_scale=ctx.value_scale,
|
| 394 |
+
window_sizes=ctx.window_sizes,
|
| 395 |
+
softmax_threshold=ctx.softmax_threshold,
|
| 396 |
+
is_local=ctx.is_local,
|
| 397 |
+
is_quant=ctx.is_quant,
|
| 398 |
+
is_split_qo=ctx.is_split_qo,
|
| 399 |
+
is_autotune=ctx.is_autotune,
|
| 400 |
+
skip_checks=ctx.skip_checks,
|
| 401 |
+
)
|
| 402 |
+
|
| 403 |
+
return dq, dk, dv, *((None,) * 30)
|
| 404 |
+
|
| 405 |
+
|
| 406 |
+
class FlashSparseAttnVarlenFunc(torch.autograd.Function):
|
| 407 |
+
@staticmethod
|
| 408 |
+
@ensure_contiguous
|
| 409 |
+
def forward(
|
| 410 |
+
ctx,
|
| 411 |
+
query: torch.Tensor,
|
| 412 |
+
key: torch.Tensor,
|
| 413 |
+
value: torch.Tensor,
|
| 414 |
+
cu_seqlens_q: torch.Tensor,
|
| 415 |
+
cu_seqlens_k: torch.Tensor,
|
| 416 |
+
max_seqlen_q: int,
|
| 417 |
+
max_seqlen_k: int,
|
| 418 |
+
is_causal: bool = False,
|
| 419 |
+
softmax_scale: Optional[float] = None,
|
| 420 |
+
query_scale: Optional[torch.Tensor] = None,
|
| 421 |
+
key_scale: Optional[torch.Tensor] = None,
|
| 422 |
+
value_scale: Optional[torch.Tensor] = None,
|
| 423 |
+
window_sizes: Optional[torch.Tensor] = None,
|
| 424 |
+
softmax_threshold: Optional[float] = None,
|
| 425 |
+
is_local: bool = False,
|
| 426 |
+
is_quant: bool = False,
|
| 427 |
+
is_split_kv: bool = False,
|
| 428 |
+
is_split_qo: bool = False,
|
| 429 |
+
pack_gqa: bool = False,
|
| 430 |
+
seqused_q: Optional[torch.Tensor] = None,
|
| 431 |
+
seqused_k: Optional[torch.Tensor] = None,
|
| 432 |
+
out: Optional[torch.Tensor] = None,
|
| 433 |
+
lse: Optional[torch.Tensor] = None,
|
| 434 |
+
is_autotune: bool = False,
|
| 435 |
+
skip_checks: bool = False,
|
| 436 |
+
return_lse: bool = False,
|
| 437 |
+
):
|
| 438 |
+
# Set is_causal to False if sequence length is 1 to avoid unnecessary masking overhead
|
| 439 |
+
is_causal = False if max_seqlen_q == 1 else is_causal
|
| 440 |
+
|
| 441 |
+
if window_sizes is not None:
|
| 442 |
+
is_local = True
|
| 443 |
+
|
| 444 |
+
if is_quant and (
|
| 445 |
+
query_scale is None or key_scale is None or value_scale is None
|
| 446 |
+
):
|
| 447 |
+
query, query_scale = quant.quantize_fp8(query)
|
| 448 |
+
key, key_scale = quant.quantize_fp8(key)
|
| 449 |
+
value, value_scale = quant.quantize_fp8(value)
|
| 450 |
+
|
| 451 |
+
out, lse, softmax_scale, softmax_threshold = _flash_sparse_attn_varlen_forward(
|
| 452 |
+
query=query,
|
| 453 |
+
key=key,
|
| 454 |
+
value=value,
|
| 455 |
+
cu_seqlens_q=cu_seqlens_q,
|
| 456 |
+
cu_seqlens_k=cu_seqlens_k,
|
| 457 |
+
max_seqlen_q=max_seqlen_q,
|
| 458 |
+
max_seqlen_k=max_seqlen_k,
|
| 459 |
+
is_causal=is_causal,
|
| 460 |
+
softmax_scale=softmax_scale,
|
| 461 |
+
query_scale=query_scale,
|
| 462 |
+
key_scale=key_scale,
|
| 463 |
+
value_scale=value_scale,
|
| 464 |
+
window_sizes=window_sizes,
|
| 465 |
+
softmax_threshold=softmax_threshold,
|
| 466 |
+
is_local=is_local,
|
| 467 |
+
is_quant=is_quant,
|
| 468 |
+
is_split_kv=is_split_kv,
|
| 469 |
+
pack_gqa=pack_gqa,
|
| 470 |
+
seqused_q=seqused_q,
|
| 471 |
+
seqused_k=seqused_k,
|
| 472 |
+
out=out,
|
| 473 |
+
lse=lse,
|
| 474 |
+
is_autotune=is_autotune,
|
| 475 |
+
skip_checks=skip_checks,
|
| 476 |
+
)
|
| 477 |
+
|
| 478 |
+
ctx.save_for_backward(
|
| 479 |
+
query,
|
| 480 |
+
key,
|
| 481 |
+
value,
|
| 482 |
+
out,
|
| 483 |
+
lse,
|
| 484 |
+
cu_seqlens_q,
|
| 485 |
+
cu_seqlens_k,
|
| 486 |
+
seqused_q,
|
| 487 |
+
seqused_k,
|
| 488 |
+
)
|
| 489 |
+
ctx.max_seqlen_q = max_seqlen_q
|
| 490 |
+
ctx.max_seqlen_k = max_seqlen_k
|
| 491 |
+
ctx.is_causal = is_causal
|
| 492 |
+
ctx.softmax_scale = softmax_scale
|
| 493 |
+
ctx.query_scale = query_scale
|
| 494 |
+
ctx.key_scale = key_scale
|
| 495 |
+
ctx.value_scale = value_scale
|
| 496 |
+
ctx.window_sizes = window_sizes
|
| 497 |
+
ctx.softmax_threshold = softmax_threshold
|
| 498 |
+
ctx.is_local = is_local
|
| 499 |
+
ctx.is_quant = is_quant
|
| 500 |
+
ctx.is_split_qo = is_split_qo
|
| 501 |
+
ctx.is_autotune = is_autotune
|
| 502 |
+
ctx.skip_checks = skip_checks
|
| 503 |
+
|
| 504 |
+
if return_lse:
|
| 505 |
+
# LSE gradient is not supported yet
|
| 506 |
+
ctx.mark_non_differentiable(lse)
|
| 507 |
+
return out, lse
|
| 508 |
+
return out
|
| 509 |
+
|
| 510 |
+
@staticmethod
|
| 511 |
+
@ensure_contiguous
|
| 512 |
+
def backward(ctx, dout: torch.Tensor, *args):
|
| 513 |
+
(
|
| 514 |
+
query,
|
| 515 |
+
key,
|
| 516 |
+
value,
|
| 517 |
+
out,
|
| 518 |
+
lse,
|
| 519 |
+
cu_seqlens_q,
|
| 520 |
+
cu_seqlens_k,
|
| 521 |
+
seqused_q,
|
| 522 |
+
seqused_k,
|
| 523 |
+
) = ctx.saved_tensors
|
| 524 |
+
|
| 525 |
+
dq, dk, dv = _flash_sparse_attn_varlen_backward(
|
| 526 |
+
query=query,
|
| 527 |
+
key=key,
|
| 528 |
+
value=value,
|
| 529 |
+
out=out,
|
| 530 |
+
dout=dout,
|
| 531 |
+
lse=lse,
|
| 532 |
+
cu_seqlens_q=cu_seqlens_q,
|
| 533 |
+
cu_seqlens_k=cu_seqlens_k,
|
| 534 |
+
max_seqlen_q=ctx.max_seqlen_q,
|
| 535 |
+
max_seqlen_k=ctx.max_seqlen_k,
|
| 536 |
+
is_causal=ctx.is_causal,
|
| 537 |
+
softmax_scale=ctx.softmax_scale,
|
| 538 |
+
query_scale=ctx.query_scale,
|
| 539 |
+
key_scale=ctx.key_scale,
|
| 540 |
+
value_scale=ctx.value_scale,
|
| 541 |
+
window_sizes=ctx.window_sizes,
|
| 542 |
+
softmax_threshold=ctx.softmax_threshold,
|
| 543 |
+
is_local=ctx.is_local,
|
| 544 |
+
is_quant=ctx.is_quant,
|
| 545 |
+
seqused_q=seqused_q,
|
| 546 |
+
seqused_k=seqused_k,
|
| 547 |
+
is_split_qo=ctx.is_split_qo,
|
| 548 |
+
is_autotune=ctx.is_autotune,
|
| 549 |
+
skip_checks=ctx.skip_checks,
|
| 550 |
+
)
|
| 551 |
+
|
| 552 |
+
return dq, dk, dv, *((None,) * 30)
|
| 553 |
+
|
| 554 |
+
|
| 555 |
+
class FlashGatedAttnFunc(torch.autograd.Function):
|
| 556 |
+
@staticmethod
|
| 557 |
+
@ensure_contiguous
|
| 558 |
+
def forward(
|
| 559 |
+
ctx,
|
| 560 |
+
query: torch.Tensor,
|
| 561 |
+
key: torch.Tensor,
|
| 562 |
+
value: torch.Tensor,
|
| 563 |
+
alpha: torch.Tensor,
|
| 564 |
+
delta: torch.Tensor,
|
| 565 |
+
is_causal: bool = False,
|
| 566 |
+
softmax_scale: Optional[float] = None,
|
| 567 |
+
query_scale: Optional[torch.Tensor] = None,
|
| 568 |
+
key_scale: Optional[torch.Tensor] = None,
|
| 569 |
+
value_scale: Optional[torch.Tensor] = None,
|
| 570 |
+
window_sizes: Optional[torch.Tensor] = None,
|
| 571 |
+
softmax_threshold: Optional[float] = None,
|
| 572 |
+
gate_threshold: Optional[float] = None,
|
| 573 |
+
is_logsigmoid_gate: bool = True,
|
| 574 |
+
is_adapt_gate: bool = True,
|
| 575 |
+
is_local: bool = False,
|
| 576 |
+
is_quant: bool = False,
|
| 577 |
+
is_split_kv: bool = False,
|
| 578 |
+
is_split_qo: bool = False,
|
| 579 |
+
pack_gqa: bool = False,
|
| 580 |
+
out: Optional[torch.Tensor] = None,
|
| 581 |
+
lse: Optional[torch.Tensor] = None,
|
| 582 |
+
is_autotune: bool = False,
|
| 583 |
+
skip_checks: bool = False,
|
| 584 |
+
return_lse: bool = False,
|
| 585 |
+
):
|
| 586 |
+
# Set is_causal to False if sequence length is 1 to avoid unnecessary masking overhead
|
| 587 |
+
is_causal = False if query.shape[1] == 1 else is_causal
|
| 588 |
+
|
| 589 |
+
if window_sizes is not None:
|
| 590 |
+
is_local = True
|
| 591 |
+
|
| 592 |
+
if is_quant and (
|
| 593 |
+
query_scale is None or key_scale is None or value_scale is None
|
| 594 |
+
):
|
| 595 |
+
query, query_scale = quant.quantize_fp8(query)
|
| 596 |
+
key, key_scale = quant.quantize_fp8(key)
|
| 597 |
+
value, value_scale = quant.quantize_fp8(value)
|
| 598 |
+
|
| 599 |
+
# Transpose for make_tensor_descriptor
|
| 600 |
+
alpha = alpha.transpose(-2, -1).contiguous()
|
| 601 |
+
delta = delta.transpose(-2, -1).contiguous()
|
| 602 |
+
|
| 603 |
+
out, lse, softmax_scale, softmax_threshold, gate_threshold = (
|
| 604 |
+
_flash_gated_attn_forward(
|
| 605 |
+
query=query,
|
| 606 |
+
key=key,
|
| 607 |
+
value=value,
|
| 608 |
+
alpha=alpha,
|
| 609 |
+
delta=delta,
|
| 610 |
+
is_causal=is_causal,
|
| 611 |
+
softmax_scale=softmax_scale,
|
| 612 |
+
query_scale=query_scale,
|
| 613 |
+
key_scale=key_scale,
|
| 614 |
+
value_scale=value_scale,
|
| 615 |
+
window_sizes=window_sizes,
|
| 616 |
+
softmax_threshold=softmax_threshold,
|
| 617 |
+
gate_threshold=gate_threshold,
|
| 618 |
+
is_logsigmoid_gate=is_logsigmoid_gate,
|
| 619 |
+
is_adapt_gate=is_adapt_gate,
|
| 620 |
+
is_local=is_local,
|
| 621 |
+
is_quant=is_quant,
|
| 622 |
+
is_split_kv=is_split_kv,
|
| 623 |
+
pack_gqa=pack_gqa,
|
| 624 |
+
out=out,
|
| 625 |
+
lse=lse,
|
| 626 |
+
is_autotune=is_autotune,
|
| 627 |
+
skip_checks=skip_checks,
|
| 628 |
+
)
|
| 629 |
+
)
|
| 630 |
+
|
| 631 |
+
ctx.save_for_backward(query, key, value, alpha, delta, out, lse)
|
| 632 |
+
ctx.is_causal = is_causal
|
| 633 |
+
ctx.softmax_scale = softmax_scale
|
| 634 |
+
ctx.query_scale = query_scale
|
| 635 |
+
ctx.key_scale = key_scale
|
| 636 |
+
ctx.value_scale = value_scale
|
| 637 |
+
ctx.window_sizes = window_sizes
|
| 638 |
+
ctx.softmax_threshold = softmax_threshold
|
| 639 |
+
ctx.gate_threshold = gate_threshold
|
| 640 |
+
ctx.is_logsigmoid_gate = is_logsigmoid_gate
|
| 641 |
+
ctx.is_adapt_gate = is_adapt_gate
|
| 642 |
+
ctx.is_local = is_local
|
| 643 |
+
ctx.is_quant = is_quant
|
| 644 |
+
ctx.is_split_qo = is_split_qo
|
| 645 |
+
ctx.is_autotune = is_autotune
|
| 646 |
+
ctx.skip_checks = skip_checks
|
| 647 |
+
|
| 648 |
+
if return_lse:
|
| 649 |
+
# LSE gradient is not supported yet
|
| 650 |
+
ctx.mark_non_differentiable(lse)
|
| 651 |
+
return out, lse
|
| 652 |
+
return out
|
| 653 |
+
|
| 654 |
+
@staticmethod
|
| 655 |
+
@ensure_contiguous
|
| 656 |
+
def backward(ctx, dout: torch.Tensor, *args):
|
| 657 |
+
query, key, value, alpha, delta, out, lse = ctx.saved_tensors
|
| 658 |
+
|
| 659 |
+
dq, dk, dv, da, dd = _flash_gated_attn_backward(
|
| 660 |
+
query=query,
|
| 661 |
+
key=key,
|
| 662 |
+
value=value,
|
| 663 |
+
alpha=alpha,
|
| 664 |
+
delta=delta,
|
| 665 |
+
out=out,
|
| 666 |
+
dout=dout,
|
| 667 |
+
lse=lse,
|
| 668 |
+
is_causal=ctx.is_causal,
|
| 669 |
+
softmax_scale=ctx.softmax_scale,
|
| 670 |
+
query_scale=ctx.query_scale,
|
| 671 |
+
key_scale=ctx.key_scale,
|
| 672 |
+
value_scale=ctx.value_scale,
|
| 673 |
+
window_sizes=ctx.window_sizes,
|
| 674 |
+
softmax_threshold=ctx.softmax_threshold,
|
| 675 |
+
gate_threshold=ctx.gate_threshold,
|
| 676 |
+
is_logsigmoid_gate=ctx.is_logsigmoid_gate,
|
| 677 |
+
is_adapt_gate=ctx.is_adapt_gate,
|
| 678 |
+
is_local=ctx.is_local,
|
| 679 |
+
is_quant=ctx.is_quant,
|
| 680 |
+
is_split_qo=ctx.is_split_qo,
|
| 681 |
+
is_autotune=ctx.is_autotune,
|
| 682 |
+
skip_checks=ctx.skip_checks,
|
| 683 |
+
)
|
| 684 |
+
|
| 685 |
+
# Transpose back to original layout
|
| 686 |
+
da = da.transpose(-2, -1).contiguous()
|
| 687 |
+
dd = dd.transpose(-2, -1).contiguous()
|
| 688 |
+
|
| 689 |
+
return dq, dk, dv, da, dd, *((None,) * 30)
|
| 690 |
+
|
| 691 |
+
|
| 692 |
+
class FlashGatedAttnVarlenFunc(torch.autograd.Function):
|
| 693 |
+
@staticmethod
|
| 694 |
+
@ensure_contiguous
|
| 695 |
+
def forward(
|
| 696 |
+
ctx,
|
| 697 |
+
query: torch.Tensor,
|
| 698 |
+
key: torch.Tensor,
|
| 699 |
+
value: torch.Tensor,
|
| 700 |
+
alpha: torch.Tensor,
|
| 701 |
+
delta: torch.Tensor,
|
| 702 |
+
cu_seqlens_q: torch.Tensor,
|
| 703 |
+
cu_seqlens_k: torch.Tensor,
|
| 704 |
+
max_seqlen_q: int,
|
| 705 |
+
max_seqlen_k: int,
|
| 706 |
+
is_causal: bool = False,
|
| 707 |
+
softmax_scale: Optional[float] = None,
|
| 708 |
+
query_scale: Optional[torch.Tensor] = None,
|
| 709 |
+
key_scale: Optional[torch.Tensor] = None,
|
| 710 |
+
value_scale: Optional[torch.Tensor] = None,
|
| 711 |
+
window_sizes: Optional[torch.Tensor] = None,
|
| 712 |
+
softmax_threshold: Optional[float] = None,
|
| 713 |
+
gate_threshold: Optional[float] = None,
|
| 714 |
+
is_logsigmoid_gate: bool = True,
|
| 715 |
+
is_adapt_gate: bool = True,
|
| 716 |
+
is_local: bool = False,
|
| 717 |
+
is_quant: bool = False,
|
| 718 |
+
is_split_kv: bool = False,
|
| 719 |
+
is_split_qo: bool = False,
|
| 720 |
+
pack_gqa: bool = False,
|
| 721 |
+
seqused_q: Optional[torch.Tensor] = None,
|
| 722 |
+
seqused_k: Optional[torch.Tensor] = None,
|
| 723 |
+
out: Optional[torch.Tensor] = None,
|
| 724 |
+
lse: Optional[torch.Tensor] = None,
|
| 725 |
+
is_autotune: bool = False,
|
| 726 |
+
skip_checks: bool = False,
|
| 727 |
+
return_lse: bool = False,
|
| 728 |
+
):
|
| 729 |
+
# Set is_causal to False if sequence length is 1 to avoid unnecessary masking overhead
|
| 730 |
+
is_causal = False if max_seqlen_q == 1 else is_causal
|
| 731 |
+
|
| 732 |
+
if window_sizes is not None:
|
| 733 |
+
is_local = True
|
| 734 |
+
|
| 735 |
+
if is_quant and (
|
| 736 |
+
query_scale is None or key_scale is None or value_scale is None
|
| 737 |
+
):
|
| 738 |
+
query, query_scale = quant.quantize_fp8(query)
|
| 739 |
+
key, key_scale = quant.quantize_fp8(key)
|
| 740 |
+
value, value_scale = quant.quantize_fp8(value)
|
| 741 |
+
|
| 742 |
+
# Transpose for make_tensor_descriptor
|
| 743 |
+
alpha = alpha.transpose(-2, -1).contiguous()
|
| 744 |
+
delta = delta.transpose(-2, -1).contiguous()
|
| 745 |
+
|
| 746 |
+
out, lse, softmax_scale, softmax_threshold, gate_threshold = (
|
| 747 |
+
_flash_gated_attn_varlen_forward(
|
| 748 |
+
query=query,
|
| 749 |
+
key=key,
|
| 750 |
+
value=value,
|
| 751 |
+
alpha=alpha,
|
| 752 |
+
delta=delta,
|
| 753 |
+
cu_seqlens_q=cu_seqlens_q,
|
| 754 |
+
cu_seqlens_k=cu_seqlens_k,
|
| 755 |
+
max_seqlen_q=max_seqlen_q,
|
| 756 |
+
max_seqlen_k=max_seqlen_k,
|
| 757 |
+
is_causal=is_causal,
|
| 758 |
+
softmax_scale=softmax_scale,
|
| 759 |
+
query_scale=query_scale,
|
| 760 |
+
key_scale=key_scale,
|
| 761 |
+
value_scale=value_scale,
|
| 762 |
+
window_sizes=window_sizes,
|
| 763 |
+
softmax_threshold=softmax_threshold,
|
| 764 |
+
gate_threshold=gate_threshold,
|
| 765 |
+
is_logsigmoid_gate=is_logsigmoid_gate,
|
| 766 |
+
is_adapt_gate=is_adapt_gate,
|
| 767 |
+
is_local=is_local,
|
| 768 |
+
is_quant=is_quant,
|
| 769 |
+
is_split_kv=is_split_kv,
|
| 770 |
+
pack_gqa=pack_gqa,
|
| 771 |
+
seqused_q=seqused_q,
|
| 772 |
+
seqused_k=seqused_k,
|
| 773 |
+
out=out,
|
| 774 |
+
lse=lse,
|
| 775 |
+
is_autotune=is_autotune,
|
| 776 |
+
skip_checks=skip_checks,
|
| 777 |
+
)
|
| 778 |
+
)
|
| 779 |
+
|
| 780 |
+
ctx.save_for_backward(
|
| 781 |
+
query,
|
| 782 |
+
key,
|
| 783 |
+
value,
|
| 784 |
+
alpha,
|
| 785 |
+
delta,
|
| 786 |
+
out,
|
| 787 |
+
lse,
|
| 788 |
+
cu_seqlens_q,
|
| 789 |
+
cu_seqlens_k,
|
| 790 |
+
seqused_q,
|
| 791 |
+
seqused_k,
|
| 792 |
+
)
|
| 793 |
+
ctx.max_seqlen_q = max_seqlen_q
|
| 794 |
+
ctx.max_seqlen_k = max_seqlen_k
|
| 795 |
+
ctx.is_causal = is_causal
|
| 796 |
+
ctx.softmax_scale = softmax_scale
|
| 797 |
+
ctx.query_scale = query_scale
|
| 798 |
+
ctx.key_scale = key_scale
|
| 799 |
+
ctx.value_scale = value_scale
|
| 800 |
+
ctx.window_sizes = window_sizes
|
| 801 |
+
ctx.softmax_threshold = softmax_threshold
|
| 802 |
+
ctx.gate_threshold = gate_threshold
|
| 803 |
+
ctx.is_logsigmoid_gate = is_logsigmoid_gate
|
| 804 |
+
ctx.is_adapt_gate = is_adapt_gate
|
| 805 |
+
ctx.is_local = is_local
|
| 806 |
+
ctx.is_quant = is_quant
|
| 807 |
+
ctx.is_split_qo = is_split_qo
|
| 808 |
+
ctx.is_autotune = is_autotune
|
| 809 |
+
ctx.skip_checks = skip_checks
|
| 810 |
+
|
| 811 |
+
if return_lse:
|
| 812 |
+
# LSE gradient is not supported yet
|
| 813 |
+
ctx.mark_non_differentiable(lse)
|
| 814 |
+
return out, lse
|
| 815 |
+
return out
|
| 816 |
+
|
| 817 |
+
@staticmethod
|
| 818 |
+
@ensure_contiguous
|
| 819 |
+
def backward(ctx, dout: torch.Tensor, *args):
|
| 820 |
+
(
|
| 821 |
+
query,
|
| 822 |
+
key,
|
| 823 |
+
value,
|
| 824 |
+
alpha,
|
| 825 |
+
delta,
|
| 826 |
+
out,
|
| 827 |
+
lse,
|
| 828 |
+
cu_seqlens_q,
|
| 829 |
+
cu_seqlens_k,
|
| 830 |
+
seqused_q,
|
| 831 |
+
seqused_k,
|
| 832 |
+
) = ctx.saved_tensors
|
| 833 |
+
|
| 834 |
+
dq, dk, dv, da, dd = _flash_gated_attn_varlen_backward(
|
| 835 |
+
query=query,
|
| 836 |
+
key=key,
|
| 837 |
+
value=value,
|
| 838 |
+
alpha=alpha,
|
| 839 |
+
delta=delta,
|
| 840 |
+
out=out,
|
| 841 |
+
dout=dout,
|
| 842 |
+
lse=lse,
|
| 843 |
+
softmax_scale=ctx.softmax_scale,
|
| 844 |
+
query_scale=ctx.query_scale,
|
| 845 |
+
key_scale=ctx.key_scale,
|
| 846 |
+
value_scale=ctx.value_scale,
|
| 847 |
+
window_sizes=ctx.window_sizes,
|
| 848 |
+
softmax_threshold=ctx.softmax_threshold,
|
| 849 |
+
cu_seqlens_q=cu_seqlens_q,
|
| 850 |
+
cu_seqlens_k=cu_seqlens_k,
|
| 851 |
+
max_seqlen_q=ctx.max_seqlen_q,
|
| 852 |
+
max_seqlen_k=ctx.max_seqlen_k,
|
| 853 |
+
is_causal=ctx.is_causal,
|
| 854 |
+
gate_threshold=ctx.gate_threshold,
|
| 855 |
+
is_logsigmoid_gate=ctx.is_logsigmoid_gate,
|
| 856 |
+
is_adapt_gate=ctx.is_adapt_gate,
|
| 857 |
+
is_local=ctx.is_local,
|
| 858 |
+
is_quant=ctx.is_quant,
|
| 859 |
+
seqused_q=seqused_q,
|
| 860 |
+
seqused_k=seqused_k,
|
| 861 |
+
is_split_qo=ctx.is_split_qo,
|
| 862 |
+
is_autotune=ctx.is_autotune,
|
| 863 |
+
skip_checks=ctx.skip_checks,
|
| 864 |
+
)
|
| 865 |
+
|
| 866 |
+
# Transpose back to original layout
|
| 867 |
+
da = da.transpose(-2, -1).contiguous()
|
| 868 |
+
dd = dd.transpose(-2, -1).contiguous()
|
| 869 |
+
|
| 870 |
+
return dq, dk, dv, da, dd, *((None,) * 30)
|
| 871 |
+
|
| 872 |
+
|
| 873 |
+
class FlashProjFunc(torch.autograd.Function):
|
| 874 |
+
@staticmethod
|
| 875 |
+
def forward(
|
| 876 |
+
ctx,
|
| 877 |
+
input: torch.Tensor,
|
| 878 |
+
weight: torch.Tensor,
|
| 879 |
+
out_dtype: torch.dtype = torch.float8_e4m3fn,
|
| 880 |
+
prev_scale: Optional[torch.Tensor] = None,
|
| 881 |
+
):
|
| 882 |
+
out, descale = _flash_proj_forward(
|
| 883 |
+
input=input,
|
| 884 |
+
weight=weight,
|
| 885 |
+
out_dtype=out_dtype,
|
| 886 |
+
prev_scale=prev_scale,
|
| 887 |
+
)
|
| 888 |
+
|
| 889 |
+
ctx.save_for_backward(input, weight)
|
| 890 |
+
|
| 891 |
+
return out, descale
|
| 892 |
+
|
| 893 |
+
@staticmethod
|
| 894 |
+
def backward(ctx, dout: torch.Tensor, _dscale: torch.Tensor):
|
| 895 |
+
input, weight = ctx.saved_tensors
|
| 896 |
+
dout = dout.to(input.dtype)
|
| 897 |
+
|
| 898 |
+
dinput, dweight = _flash_proj_backward(dout=dout, input=input, weight=weight)
|
| 899 |
+
|
| 900 |
+
return dinput, dweight, None, None
|
| 901 |
+
|
| 902 |
+
|
| 903 |
+
def flash_dense_attn_func(
|
| 904 |
+
query: torch.Tensor,
|
| 905 |
+
key: torch.Tensor,
|
| 906 |
+
value: torch.Tensor,
|
| 907 |
+
is_causal: bool = False,
|
| 908 |
+
softmax_scale: Optional[float] = None,
|
| 909 |
+
query_scale: Optional[torch.Tensor] = None,
|
| 910 |
+
key_scale: Optional[torch.Tensor] = None,
|
| 911 |
+
value_scale: Optional[torch.Tensor] = None,
|
| 912 |
+
window_sizes: Optional[torch.Tensor] = None,
|
| 913 |
+
is_local: bool = False,
|
| 914 |
+
is_quant: bool = False,
|
| 915 |
+
is_split_kv: bool = False,
|
| 916 |
+
is_split_qo: bool = False,
|
| 917 |
+
pack_gqa: bool = False,
|
| 918 |
+
out: Optional[torch.Tensor] = None,
|
| 919 |
+
lse: Optional[torch.Tensor] = None,
|
| 920 |
+
is_autotune: bool = False,
|
| 921 |
+
skip_checks: bool = False,
|
| 922 |
+
return_lse: bool = False,
|
| 923 |
+
) -> Union[torch.Tensor, Tuple[torch.Tensor, torch.Tensor]]:
|
| 924 |
+
"""
|
| 925 |
+
Flash dense attention function that computes the attention output and optionally the logsumexp.
|
| 926 |
+
|
| 927 |
+
:param query: Query tensor of shape [batch_size, seqlen_q, num_heads, head_dim].
|
| 928 |
+
:param key: Key tensor of shape [batch_size, seqlen_k, num_kv_heads, head_dim].
|
| 929 |
+
:param value: Value tensor of shape [batch_size, seqlen_k, num_kv_heads, head_dim].
|
| 930 |
+
:param is_causal: Whether to apply a causal mask.
|
| 931 |
+
:param softmax_scale: Optional scaling factor for the softmax. If None, defaults to 1/sqrt(head_dim).
|
| 932 |
+
:param query_scale: Optional per-tensor scale for query dequantization.
|
| 933 |
+
:param key_scale: Optional per-tensor scale for key dequantization.
|
| 934 |
+
:param value_scale: Optional per-tensor scale for value dequantization.
|
| 935 |
+
:param window_sizes: Optional window sizes tensor for flexible local attention. Must be shape [num_kv_heads, 4] with dtype int32, with columns [window_sink, window_left, window_right, window_dist]. If provided, is_local is automatically set to True.
|
| 936 |
+
:param is_local: Whether to apply a local mask.
|
| 937 |
+
:param is_quant: Whether the inputs are quantized. If True, query_scale, key_scale, and value_scale must be provided or will be computed from the input tensors.
|
| 938 |
+
:param is_split_kv: Whether to enable split-KV for forward occupancy.
|
| 939 |
+
:param is_split_qo: Whether to enable split-QO for backward occupancy.
|
| 940 |
+
:param pack_gqa: Whether to pack grouped-query attention.
|
| 941 |
+
:param out: Optional preallocated output tensor with shape [batch_size, seqlen_q, num_heads, head_dim].
|
| 942 |
+
:param lse: Optional preallocated logsumexp tensor with shape [batch_size, num_heads, seqlen_q].
|
| 943 |
+
:param is_autotune: Force re-run Triton autotuner and overwrite cache. Default False uses cached config.
|
| 944 |
+
:param skip_checks: Whether to skip input validation checks for faster performance.
|
| 945 |
+
:param return_lse: Whether to return the logsumexp tensor for numerical stability analysis. If True, returns a tuple (out, lse). If False, returns only out.
|
| 946 |
+
|
| 947 |
+
:returns: If return_lse is False, returns out with shape [batch_size, seqlen_q, num_heads, head_dim]. If return_lse is True, returns a tuple (out, lse), where lse has shape [batch_size, num_heads, seqlen_q].
|
| 948 |
+
"""
|
| 949 |
+
return FlashDenseAttnFunc.apply(
|
| 950 |
+
query,
|
| 951 |
+
key,
|
| 952 |
+
value,
|
| 953 |
+
is_causal,
|
| 954 |
+
softmax_scale,
|
| 955 |
+
query_scale,
|
| 956 |
+
key_scale,
|
| 957 |
+
value_scale,
|
| 958 |
+
window_sizes,
|
| 959 |
+
is_local,
|
| 960 |
+
is_quant,
|
| 961 |
+
is_split_kv,
|
| 962 |
+
is_split_qo,
|
| 963 |
+
pack_gqa,
|
| 964 |
+
out,
|
| 965 |
+
lse,
|
| 966 |
+
is_autotune,
|
| 967 |
+
skip_checks,
|
| 968 |
+
return_lse,
|
| 969 |
+
)
|
| 970 |
+
|
| 971 |
+
|
| 972 |
+
def flash_dense_attn_with_kvcache_func(
|
| 973 |
+
query: torch.Tensor,
|
| 974 |
+
key: torch.Tensor,
|
| 975 |
+
value: torch.Tensor,
|
| 976 |
+
softmax_scale: Optional[float] = None,
|
| 977 |
+
query_scale: Optional[torch.Tensor] = None,
|
| 978 |
+
key_scale: Optional[torch.Tensor] = None,
|
| 979 |
+
value_scale: Optional[torch.Tensor] = None,
|
| 980 |
+
window_sizes: Optional[torch.Tensor] = None,
|
| 981 |
+
is_local: bool = False,
|
| 982 |
+
is_quant: bool = False,
|
| 983 |
+
out: Optional[torch.Tensor] = None,
|
| 984 |
+
lse: Optional[torch.Tensor] = None,
|
| 985 |
+
is_autotune: bool = False,
|
| 986 |
+
skip_checks: bool = False,
|
| 987 |
+
return_lse: bool = False,
|
| 988 |
+
) -> Union[torch.Tensor, Tuple[torch.Tensor, torch.Tensor]]:
|
| 989 |
+
"""
|
| 990 |
+
Flash dense attention function for decoding with KV cache that computes the attention output and optionally the logsumexp.
|
| 991 |
+
|
| 992 |
+
:param query: Query tensor of shape [batch_size, num_heads, head_dim].
|
| 993 |
+
:param key: Key tensor of shape [batch_size, seqlen_k, num_kv_heads, head_dim].
|
| 994 |
+
:param value: Value tensor of shape [batch_size, seqlen_k, num_kv_heads, head_dim].
|
| 995 |
+
:param softmax_scale: Optional scaling factor for the softmax. If None, defaults to 1/sqrt(head_dim).
|
| 996 |
+
:param query_scale: Optional per-tensor scale for query dequantization.
|
| 997 |
+
:param key_scale: Optional per-tensor scale for key dequantization.
|
| 998 |
+
:param value_scale: Optional per-tensor scale for value dequantization.
|
| 999 |
+
:param window_sizes: Optional window sizes tensor for flexible local attention. Must be shape [num_kv_heads, 4] with dtype int32, with columns [window_sink, window_left, window_right, window_dist]. If provided, is_local is automatically set to True.
|
| 1000 |
+
:param is_local: Whether to apply a local mask.
|
| 1001 |
+
:param is_quant: Whether the inputs are quantized. If True, query_scale, key_scale, and value_scale must be provided for dequantization.
|
| 1002 |
+
:param out: Optional preallocated output tensor with shape [batch_size, num_heads, head_dim].
|
| 1003 |
+
:param lse: Optional preallocated logsumexp tensor with shape [batch_size, num_heads].
|
| 1004 |
+
:param is_autotune: Force re-run Triton autotuner and overwrite cache. Default False uses cached config.
|
| 1005 |
+
:param skip_checks: Whether to skip input validation checks for faster performance.
|
| 1006 |
+
:param return_lse: Whether to return the logsumexp tensor for numerical stability analysis. If True, returns a tuple (out, lse). If False, returns only out.
|
| 1007 |
+
|
| 1008 |
+
:returns: If return_lse is False, returns out with shape [batch_size, num_heads, head_dim]. If return_lse is True, returns a tuple (out, lse), where lse has shape [batch_size, num_heads].
|
| 1009 |
+
"""
|
| 1010 |
+
|
| 1011 |
+
if is_quant and (query_scale is None or key_scale is None or value_scale is None):
|
| 1012 |
+
query, query_scale = quant.quantize_fp8(query)
|
| 1013 |
+
key, key_scale = quant.quantize_fp8(key)
|
| 1014 |
+
value, value_scale = quant.quantize_fp8(value)
|
| 1015 |
+
|
| 1016 |
+
if window_sizes is not None:
|
| 1017 |
+
is_local = True
|
| 1018 |
+
|
| 1019 |
+
out, lse = _flash_dense_attn_decode(
|
| 1020 |
+
query=query,
|
| 1021 |
+
key=key,
|
| 1022 |
+
value=value,
|
| 1023 |
+
softmax_scale=softmax_scale,
|
| 1024 |
+
query_scale=query_scale,
|
| 1025 |
+
key_scale=key_scale,
|
| 1026 |
+
value_scale=value_scale,
|
| 1027 |
+
window_sizes=window_sizes,
|
| 1028 |
+
is_local=is_local,
|
| 1029 |
+
is_quant=is_quant,
|
| 1030 |
+
out=out,
|
| 1031 |
+
lse=lse,
|
| 1032 |
+
is_autotune=is_autotune,
|
| 1033 |
+
skip_checks=skip_checks,
|
| 1034 |
+
)
|
| 1035 |
+
|
| 1036 |
+
if return_lse:
|
| 1037 |
+
return out, lse
|
| 1038 |
+
return out
|
| 1039 |
+
|
| 1040 |
+
|
| 1041 |
+
def flash_dense_attn_varlen_func(
|
| 1042 |
+
query: torch.Tensor,
|
| 1043 |
+
key: torch.Tensor,
|
| 1044 |
+
value: torch.Tensor,
|
| 1045 |
+
cu_seqlens_q: torch.Tensor,
|
| 1046 |
+
cu_seqlens_k: torch.Tensor,
|
| 1047 |
+
max_seqlen_q: int,
|
| 1048 |
+
max_seqlen_k: int,
|
| 1049 |
+
is_causal: bool = False,
|
| 1050 |
+
softmax_scale: Optional[float] = None,
|
| 1051 |
+
query_scale: Optional[torch.Tensor] = None,
|
| 1052 |
+
key_scale: Optional[torch.Tensor] = None,
|
| 1053 |
+
value_scale: Optional[torch.Tensor] = None,
|
| 1054 |
+
window_sizes: Optional[torch.Tensor] = None,
|
| 1055 |
+
is_local: bool = False,
|
| 1056 |
+
is_quant: bool = False,
|
| 1057 |
+
is_split_kv: bool = False,
|
| 1058 |
+
is_split_qo: bool = False,
|
| 1059 |
+
pack_gqa: bool = False,
|
| 1060 |
+
seqused_q: Optional[torch.Tensor] = None,
|
| 1061 |
+
seqused_k: Optional[torch.Tensor] = None,
|
| 1062 |
+
out: Optional[torch.Tensor] = None,
|
| 1063 |
+
lse: Optional[torch.Tensor] = None,
|
| 1064 |
+
is_autotune: bool = False,
|
| 1065 |
+
skip_checks: bool = False,
|
| 1066 |
+
return_lse: bool = False,
|
| 1067 |
+
) -> Union[torch.Tensor, Tuple[torch.Tensor, torch.Tensor]]:
|
| 1068 |
+
"""
|
| 1069 |
+
Flash dense attention function for variable-length sequences that computes the attention output and optionally the logsumexp.
|
| 1070 |
+
|
| 1071 |
+
:param query: Query tensor of shape [total_seqlen_q, num_heads_q, head_dim].
|
| 1072 |
+
:param key: Key tensor of shape [total_seqlen_k, num_heads_kv, head_dim].
|
| 1073 |
+
:param value: Value tensor of shape [total_seqlen_k, num_heads_kv, head_dim].
|
| 1074 |
+
:param cu_seqlens_q: Cumulative sequence lengths for queries, shape [batch_size + 1].
|
| 1075 |
+
:param cu_seqlens_k: Cumulative sequence lengths for keys/values, shape [batch_size + 1].
|
| 1076 |
+
:param max_seqlen_q: Maximum sequence length for queries.
|
| 1077 |
+
:param max_seqlen_k: Maximum sequence length for keys/values.
|
| 1078 |
+
:param is_causal: Whether to apply a causal mask.
|
| 1079 |
+
:param softmax_scale: Optional scaling factor for the softmax. If None, defaults to 1/sqrt(head_dim).
|
| 1080 |
+
:param query_scale: Optional per-tensor scale for query dequantization.
|
| 1081 |
+
:param key_scale: Optional per-tensor scale for key dequantization.
|
| 1082 |
+
:param value_scale: Optional per-tensor scale for value dequantization.
|
| 1083 |
+
:param window_sizes: Optional window sizes tensor for flexible local attention. Must be shape [num_kv_heads, 4] with dtype int32, with columns [window_sink, window_left, window_right, window_dist]. If provided, is_local is automatically set to True.
|
| 1084 |
+
:param is_local: Whether to apply a local mask.
|
| 1085 |
+
:param is_quant: Whether the inputs are quantized. If True, query_scale, key_scale, and value_scale must be provided or will be computed from the input tensors.
|
| 1086 |
+
:param is_split_kv: Whether to enable split-KV for forward occupancy.
|
| 1087 |
+
:param is_split_qo: Whether to enable split-QO for backward occupancy.
|
| 1088 |
+
:param pack_gqa: Whether to pack grouped-query attention.
|
| 1089 |
+
:param seqused_q: Optional tensor of shape [total_seqlen_q] indicating the actual sequence lengths for queries. If provided, overrides cu_seqlens_q for masking.
|
| 1090 |
+
:param seqused_k: Optional tensor of shape [total_seqlen_k] indicating the actual sequence lengths for keys/values. If provided, overrides cu_seqlens_k for masking.
|
| 1091 |
+
:param out: Optional preallocated output tensor with shape [batch_size, seqlen_q, num_heads, head_dim].
|
| 1092 |
+
:param lse: Optional preallocated logsumexp tensor with shape [batch_size, num_heads, seqlen_q].
|
| 1093 |
+
:param is_autotune: Force re-run Triton autotuner and overwrite cache. Default False uses cached config.
|
| 1094 |
+
:param skip_checks: Whether to skip input validation checks for faster performance.
|
| 1095 |
+
:param return_lse: Whether to return the logsumexp tensor for numerical stability analysis. If True, returns a tuple (out, lse). If False, returns only out.
|
| 1096 |
+
|
| 1097 |
+
:returns: If return_lse is False, returns out with shape [total_seqlen_q, num_heads_q, head_dim]. If return_lse is True, returns a tuple (out, lse), where lse has shape [total_seqlen_q, num_heads_q].
|
| 1098 |
+
"""
|
| 1099 |
+
return FlashDenseAttnVarlenFunc.apply(
|
| 1100 |
+
query,
|
| 1101 |
+
key,
|
| 1102 |
+
value,
|
| 1103 |
+
cu_seqlens_q,
|
| 1104 |
+
cu_seqlens_k,
|
| 1105 |
+
max_seqlen_q,
|
| 1106 |
+
max_seqlen_k,
|
| 1107 |
+
is_causal,
|
| 1108 |
+
softmax_scale,
|
| 1109 |
+
query_scale,
|
| 1110 |
+
key_scale,
|
| 1111 |
+
value_scale,
|
| 1112 |
+
window_sizes,
|
| 1113 |
+
is_local,
|
| 1114 |
+
is_quant,
|
| 1115 |
+
is_split_kv,
|
| 1116 |
+
is_split_qo,
|
| 1117 |
+
pack_gqa,
|
| 1118 |
+
seqused_q,
|
| 1119 |
+
seqused_k,
|
| 1120 |
+
out,
|
| 1121 |
+
lse,
|
| 1122 |
+
is_autotune,
|
| 1123 |
+
skip_checks,
|
| 1124 |
+
return_lse,
|
| 1125 |
+
)
|
| 1126 |
+
|
| 1127 |
+
|
| 1128 |
+
def flash_dense_attn_varlen_with_kvcache_func(
|
| 1129 |
+
query: torch.Tensor,
|
| 1130 |
+
key: torch.Tensor,
|
| 1131 |
+
value: torch.Tensor,
|
| 1132 |
+
cu_seqlens_k: torch.Tensor,
|
| 1133 |
+
max_seqlen_k: int,
|
| 1134 |
+
softmax_scale: Optional[float] = None,
|
| 1135 |
+
query_scale: Optional[torch.Tensor] = None,
|
| 1136 |
+
key_scale: Optional[torch.Tensor] = None,
|
| 1137 |
+
value_scale: Optional[torch.Tensor] = None,
|
| 1138 |
+
window_sizes: Optional[torch.Tensor] = None,
|
| 1139 |
+
is_local: bool = False,
|
| 1140 |
+
is_quant: bool = False,
|
| 1141 |
+
seqused_k: Optional[torch.Tensor] = None,
|
| 1142 |
+
out: Optional[torch.Tensor] = None,
|
| 1143 |
+
lse: Optional[torch.Tensor] = None,
|
| 1144 |
+
is_autotune: bool = False,
|
| 1145 |
+
skip_checks: bool = False,
|
| 1146 |
+
return_lse: bool = False,
|
| 1147 |
+
) -> Union[torch.Tensor, Tuple[torch.Tensor, torch.Tensor]]:
|
| 1148 |
+
"""
|
| 1149 |
+
Flash dense attention function for variable-length decoding with KV cache that computes the attention output and optionally the logsumexp.
|
| 1150 |
+
|
| 1151 |
+
:param query: Query tensor of shape [batch_size, num_heads_q, head_dim].
|
| 1152 |
+
:param key: Key tensor of shape [total_seqlen_k, num_heads_kv, head_dim].
|
| 1153 |
+
:param value: Value tensor of shape [total_seqlen_k, num_heads_kv, head_dim].
|
| 1154 |
+
:param cu_seqlens_k: Cumulative sequence lengths for keys/values, shape [batch_size + 1].
|
| 1155 |
+
:param max_seqlen_k: Maximum sequence length for keys/values.
|
| 1156 |
+
:param softmax_scale: Optional scaling factor for the softmax. If None, defaults to 1/sqrt(head_dim).
|
| 1157 |
+
:param query_scale: Optional per-tensor scale for query dequantization.
|
| 1158 |
+
:param key_scale: Optional per-tensor scale for key dequantization.
|
| 1159 |
+
:param value_scale: Optional per-tensor scale for value dequantization.
|
| 1160 |
+
:param window_sizes: Optional window sizes tensor for flexible local attention. Must be shape [num_kv_heads, 4] with dtype int32, with columns [window_sink, window_left, window_right, window_dist]. If provided, is_local is automatically set to True.
|
| 1161 |
+
:param is_local: Whether to apply a local mask.
|
| 1162 |
+
:param is_quant: Whether the inputs are quantized. If True, query_scale, key_scale, and value_scale must be provided for dequantization.
|
| 1163 |
+
:param seqused_k: Optional tensor indicating the actual sequence lengths for keys/values.
|
| 1164 |
+
:param out: Optional preallocated output tensor with shape [batch_size, num_heads_q, head_dim].
|
| 1165 |
+
:param lse: Optional preallocated logsumexp tensor with shape [batch_size, num_heads_q].
|
| 1166 |
+
:param is_autotune: Force re-run Triton autotuner and overwrite cache. Default False uses cached config.
|
| 1167 |
+
:param skip_checks: Whether to skip input validation checks for faster performance.
|
| 1168 |
+
:param return_lse: Whether to return the logsumexp tensor for numerical stability analysis. If True, returns a tuple (out, lse). If False, returns only out.
|
| 1169 |
+
|
| 1170 |
+
:returns: If return_lse is False, returns out with shape [batch_size, num_heads_q, head_dim]. If return_lse is True, returns a tuple (out, lse), where lse has shape [batch_size, num_heads_q].
|
| 1171 |
+
"""
|
| 1172 |
+
|
| 1173 |
+
if is_quant and (query_scale is None or key_scale is None or value_scale is None):
|
| 1174 |
+
query, query_scale = quant.quantize_fp8(query)
|
| 1175 |
+
key, key_scale = quant.quantize_fp8(key)
|
| 1176 |
+
value, value_scale = quant.quantize_fp8(value)
|
| 1177 |
+
|
| 1178 |
+
if window_sizes is not None:
|
| 1179 |
+
is_local = True
|
| 1180 |
+
|
| 1181 |
+
out, lse = _flash_dense_attn_varlen_decode(
|
| 1182 |
+
query=query,
|
| 1183 |
+
key=key,
|
| 1184 |
+
value=value,
|
| 1185 |
+
cu_seqlens_k=cu_seqlens_k,
|
| 1186 |
+
max_seqlen_k=max_seqlen_k,
|
| 1187 |
+
softmax_scale=softmax_scale,
|
| 1188 |
+
query_scale=query_scale,
|
| 1189 |
+
key_scale=key_scale,
|
| 1190 |
+
value_scale=value_scale,
|
| 1191 |
+
window_sizes=window_sizes,
|
| 1192 |
+
is_local=is_local,
|
| 1193 |
+
is_quant=is_quant,
|
| 1194 |
+
seqused_k=seqused_k,
|
| 1195 |
+
out=out,
|
| 1196 |
+
lse=lse,
|
| 1197 |
+
is_autotune=is_autotune,
|
| 1198 |
+
skip_checks=skip_checks,
|
| 1199 |
+
)
|
| 1200 |
+
|
| 1201 |
+
if return_lse:
|
| 1202 |
+
return out, lse
|
| 1203 |
+
return out
|
| 1204 |
+
|
| 1205 |
+
|
| 1206 |
+
def flash_sparse_attn_func(
|
| 1207 |
+
query: torch.Tensor,
|
| 1208 |
+
key: torch.Tensor,
|
| 1209 |
+
value: torch.Tensor,
|
| 1210 |
+
is_causal: bool = False,
|
| 1211 |
+
softmax_scale: Optional[float] = None,
|
| 1212 |
+
query_scale: Optional[torch.Tensor] = None,
|
| 1213 |
+
key_scale: Optional[torch.Tensor] = None,
|
| 1214 |
+
value_scale: Optional[torch.Tensor] = None,
|
| 1215 |
+
window_sizes: Optional[torch.Tensor] = None,
|
| 1216 |
+
softmax_threshold: Optional[float] = None,
|
| 1217 |
+
is_local: bool = False,
|
| 1218 |
+
is_quant: bool = False,
|
| 1219 |
+
is_split_kv: bool = False,
|
| 1220 |
+
is_split_qo: bool = False,
|
| 1221 |
+
pack_gqa: bool = False,
|
| 1222 |
+
out: Optional[torch.Tensor] = None,
|
| 1223 |
+
lse: Optional[torch.Tensor] = None,
|
| 1224 |
+
is_autotune: bool = False,
|
| 1225 |
+
skip_checks: bool = False,
|
| 1226 |
+
return_lse: bool = False,
|
| 1227 |
+
) -> Union[torch.Tensor, Tuple[torch.Tensor, torch.Tensor]]:
|
| 1228 |
+
"""
|
| 1229 |
+
Flash sparse attention function that computes the attention output and optionally the logsumexp.
|
| 1230 |
+
|
| 1231 |
+
:param query: Query tensor of shape [batch_size, seqlen_q, num_heads, head_dim].
|
| 1232 |
+
:param key: Key tensor of shape [batch_size, seqlen_k, num_kv_heads, head_dim].
|
| 1233 |
+
:param value: Value tensor of shape [batch_size, seqlen_k, num_kv_heads, head_dim].
|
| 1234 |
+
:param is_causal: Whether to apply a causal mask.
|
| 1235 |
+
:param softmax_scale: Optional scaling factor for the softmax. If None, defaults to 1/sqrt(head_dim).
|
| 1236 |
+
:param query_scale: Optional per-tensor scale for query dequantization.
|
| 1237 |
+
:param key_scale: Optional per-tensor scale for key dequantization.
|
| 1238 |
+
:param value_scale: Optional per-tensor scale for value dequantization.
|
| 1239 |
+
:param window_sizes: Optional window sizes tensor for flexible local attention. Must be shape [num_kv_heads, 4] with dtype int32, with columns [window_sink, window_left, window_right, window_dist]. If provided, is_local is automatically set to True.
|
| 1240 |
+
:param softmax_threshold: Optional threshold for the sparse softmax. If None, defaults to head_dim / seqlen_k.
|
| 1241 |
+
:param is_local: Whether to apply a local mask.
|
| 1242 |
+
:param is_quant: Whether the inputs are quantized. If True, query_scale, key_scale, and value_scale must be provided or will be computed from the input tensors.
|
| 1243 |
+
:param is_split_kv: Whether to enable split-KV for forward ccupancy.
|
| 1244 |
+
:param is_split_qo: Whether to enable split-QO for backward occupancy.
|
| 1245 |
+
:param pack_gqa: Whether to pack grouped-query attention.
|
| 1246 |
+
:param out: Optional preallocated output tensor with shape [batch_size, seqlen_q, num_heads, head_dim].
|
| 1247 |
+
:param lse: Optional preallocated logsumexp tensor with shape [batch_size, num_heads, seqlen_q].
|
| 1248 |
+
:param is_autotune: Force re-run Triton autotuner and overwrite cache. Default False uses cached config.
|
| 1249 |
+
:param skip_checks: Whether to skip input validation checks for faster performance.
|
| 1250 |
+
:param return_lse: Whether to return the logsumexp tensor for numerical stability analysis. If True, returns a tuple (out, lse). If False, returns only out.
|
| 1251 |
+
|
| 1252 |
+
:returns: If return_lse is False, returns out with shape [batch_size, seqlen_q, num_heads, head_dim]. If return_lse is True, returns a tuple (out, lse), where lse has shape [batch_size, num_heads, seqlen_q].
|
| 1253 |
+
"""
|
| 1254 |
+
return FlashSparseAttnFunc.apply(
|
| 1255 |
+
query,
|
| 1256 |
+
key,
|
| 1257 |
+
value,
|
| 1258 |
+
is_causal,
|
| 1259 |
+
softmax_scale,
|
| 1260 |
+
query_scale,
|
| 1261 |
+
key_scale,
|
| 1262 |
+
value_scale,
|
| 1263 |
+
window_sizes,
|
| 1264 |
+
softmax_threshold,
|
| 1265 |
+
is_local,
|
| 1266 |
+
is_quant,
|
| 1267 |
+
is_split_kv,
|
| 1268 |
+
is_split_qo,
|
| 1269 |
+
pack_gqa,
|
| 1270 |
+
out,
|
| 1271 |
+
lse,
|
| 1272 |
+
is_autotune,
|
| 1273 |
+
skip_checks,
|
| 1274 |
+
return_lse,
|
| 1275 |
+
)
|
| 1276 |
+
|
| 1277 |
+
|
| 1278 |
+
def flash_sparse_attn_with_kvcache_func(
|
| 1279 |
+
query: torch.Tensor,
|
| 1280 |
+
key: torch.Tensor,
|
| 1281 |
+
value: torch.Tensor,
|
| 1282 |
+
softmax_scale: Optional[float] = None,
|
| 1283 |
+
softmax_threshold: Optional[float] = None,
|
| 1284 |
+
query_scale: Optional[torch.Tensor] = None,
|
| 1285 |
+
key_scale: Optional[torch.Tensor] = None,
|
| 1286 |
+
value_scale: Optional[torch.Tensor] = None,
|
| 1287 |
+
window_sizes: Optional[torch.Tensor] = None,
|
| 1288 |
+
is_local: bool = False,
|
| 1289 |
+
is_quant: bool = False,
|
| 1290 |
+
out: Optional[torch.Tensor] = None,
|
| 1291 |
+
lse: Optional[torch.Tensor] = None,
|
| 1292 |
+
is_autotune: bool = False,
|
| 1293 |
+
skip_checks: bool = False,
|
| 1294 |
+
return_lse: bool = False,
|
| 1295 |
+
) -> Union[torch.Tensor, Tuple[torch.Tensor, torch.Tensor]]:
|
| 1296 |
+
"""
|
| 1297 |
+
Flash sparse attention function for decoding with KV cache that computes the attention output and optionally the logsumexp.
|
| 1298 |
+
|
| 1299 |
+
:param query: Query tensor of shape [batch_size, num_heads, head_dim].
|
| 1300 |
+
:param key: Key tensor of shape [batch_size, seqlen_k, num_kv_heads, head_dim].
|
| 1301 |
+
:param value: Value tensor of shape [batch_size, seqlen_k, num_kv_heads, head_dim].
|
| 1302 |
+
:param softmax_scale: Optional scaling factor for the softmax. If None, defaults to 1/sqrt(head_dim).
|
| 1303 |
+
:param softmax_threshold: Optional threshold for the sparse softmax. If None, defaults to head_dim / seqlen_k.
|
| 1304 |
+
:param query_scale: Optional per-tensor scale for query dequantization.
|
| 1305 |
+
:param key_scale: Optional per-tensor scale for key dequantization.
|
| 1306 |
+
:param value_scale: Optional per-tensor scale for value dequantization.
|
| 1307 |
+
:param window_sizes: Optional window sizes tensor for flexible local attention. Must be shape [num_kv_heads, 4] with dtype int32, with columns [window_sink, window_left, window_right, window_dist]. If provided, is_local is automatically set to True.
|
| 1308 |
+
:param is_local: Whether to apply a local mask.
|
| 1309 |
+
:param is_quant: Whether the inputs are quantized. If True, query_scale, key_scale, and value_scale must be provided for dequantization.
|
| 1310 |
+
:param out: Optional preallocated output tensor with shape [batch_size, num_heads, head_dim].
|
| 1311 |
+
:param lse: Optional preallocated logsumexp tensor with shape [batch_size, num_heads].
|
| 1312 |
+
:param is_autotune: Force re-run Triton autotuner and overwrite cache. Default False uses cached config.
|
| 1313 |
+
:param skip_checks: Whether to skip input validation checks for faster performance.
|
| 1314 |
+
:param return_lse: Whether to return the logsumexp tensor for numerical stability analysis. If True, returns a tuple (out, lse). If False, returns only out.
|
| 1315 |
+
|
| 1316 |
+
:returns: If return_lse is False, returns out with shape [batch_size, num_heads, head_dim]. If return_lse is True, returns a tuple (out, lse), where lse has shape [batch_size, num_heads].
|
| 1317 |
+
"""
|
| 1318 |
+
|
| 1319 |
+
if is_quant and (query_scale is None or key_scale is None or value_scale is None):
|
| 1320 |
+
query, query_scale = quant.quantize_fp8(query)
|
| 1321 |
+
key, key_scale = quant.quantize_fp8(key)
|
| 1322 |
+
value, value_scale = quant.quantize_fp8(value)
|
| 1323 |
+
|
| 1324 |
+
if window_sizes is not None:
|
| 1325 |
+
is_local = True
|
| 1326 |
+
|
| 1327 |
+
out, lse = _flash_sparse_attn_decode(
|
| 1328 |
+
query=query,
|
| 1329 |
+
key=key,
|
| 1330 |
+
value=value,
|
| 1331 |
+
softmax_scale=softmax_scale,
|
| 1332 |
+
softmax_threshold=softmax_threshold,
|
| 1333 |
+
query_scale=query_scale,
|
| 1334 |
+
key_scale=key_scale,
|
| 1335 |
+
value_scale=value_scale,
|
| 1336 |
+
window_sizes=window_sizes,
|
| 1337 |
+
is_local=is_local,
|
| 1338 |
+
is_quant=is_quant,
|
| 1339 |
+
out=out,
|
| 1340 |
+
lse=lse,
|
| 1341 |
+
is_autotune=is_autotune,
|
| 1342 |
+
skip_checks=skip_checks,
|
| 1343 |
+
)
|
| 1344 |
+
|
| 1345 |
+
if return_lse:
|
| 1346 |
+
return out, lse
|
| 1347 |
+
return out
|
| 1348 |
+
|
| 1349 |
+
|
| 1350 |
+
def flash_sparse_attn_varlen_func(
|
| 1351 |
+
query: torch.Tensor,
|
| 1352 |
+
key: torch.Tensor,
|
| 1353 |
+
value: torch.Tensor,
|
| 1354 |
+
cu_seqlens_q: torch.Tensor,
|
| 1355 |
+
cu_seqlens_k: torch.Tensor,
|
| 1356 |
+
max_seqlen_q: int,
|
| 1357 |
+
max_seqlen_k: int,
|
| 1358 |
+
is_causal: bool = False,
|
| 1359 |
+
softmax_scale: Optional[float] = None,
|
| 1360 |
+
query_scale: Optional[torch.Tensor] = None,
|
| 1361 |
+
key_scale: Optional[torch.Tensor] = None,
|
| 1362 |
+
value_scale: Optional[torch.Tensor] = None,
|
| 1363 |
+
window_sizes: Optional[torch.Tensor] = None,
|
| 1364 |
+
softmax_threshold: Optional[float] = None,
|
| 1365 |
+
is_local: bool = False,
|
| 1366 |
+
is_quant: bool = False,
|
| 1367 |
+
is_split_kv: bool = False,
|
| 1368 |
+
is_split_qo: bool = False,
|
| 1369 |
+
pack_gqa: bool = False,
|
| 1370 |
+
seqused_q: Optional[torch.Tensor] = None,
|
| 1371 |
+
seqused_k: Optional[torch.Tensor] = None,
|
| 1372 |
+
out: Optional[torch.Tensor] = None,
|
| 1373 |
+
lse: Optional[torch.Tensor] = None,
|
| 1374 |
+
is_autotune: bool = False,
|
| 1375 |
+
skip_checks: bool = False,
|
| 1376 |
+
return_lse: bool = False,
|
| 1377 |
+
) -> Union[torch.Tensor, Tuple[torch.Tensor, torch.Tensor]]:
|
| 1378 |
+
"""
|
| 1379 |
+
Flash sparse attention function for variable-length sequences that computes the attention output and optionally the logsumexp.
|
| 1380 |
+
|
| 1381 |
+
:param query: Query tensor of shape [total_seqlen_q, num_heads_q, head_dim].
|
| 1382 |
+
:param key: Key tensor of shape [total_seqlen_k, num_heads_kv, head_dim].
|
| 1383 |
+
:param value: Value tensor of shape [total_seqlen_k, num_heads_kv, head_dim].
|
| 1384 |
+
:param cu_seqlens_q: Cumulative sequence lengths for queries, shape [batch_size + 1].
|
| 1385 |
+
:param cu_seqlens_k: Cumulative sequence lengths for keys/values, shape [batch_size + 1].
|
| 1386 |
+
:param max_seqlen_q: Maximum sequence length for queries.
|
| 1387 |
+
:param max_seqlen_k: Maximum sequence length for keys/values.
|
| 1388 |
+
:param is_causal: Whether to apply a causal mask.
|
| 1389 |
+
:param softmax_scale: Optional scaling factor for the softmax. If None, defaults to 1/sqrt(head_dim).
|
| 1390 |
+
:param query_scale: Optional per-tensor scale for query dequantization.
|
| 1391 |
+
:param key_scale: Optional per-tensor scale for key dequantization.
|
| 1392 |
+
:param value_scale: Optional per-tensor scale for value dequantization.
|
| 1393 |
+
:param window_sizes: Optional window sizes tensor for flexible local attention. Must be shape [num_kv_heads, 4] with dtype int32, with columns [window_sink, window_left, window_right, window_dist]. If provided, is_local is automatically set to True.
|
| 1394 |
+
:param softmax_threshold: Optional threshold for the sparse softmax. If None, defaults to head_dim / max_seqlen_k.
|
| 1395 |
+
:param is_local: Whether to apply a local mask.
|
| 1396 |
+
:param is_quant: Whether the inputs are quantized. If True, query_scale, key_scale, and value_scale must be provided or will be computed from the input tensors.
|
| 1397 |
+
:param is_split_kv: Whether to enable split-KV for forward occupancy.
|
| 1398 |
+
:param is_split_qo: Whether to enable split-QO for backward occupancy.
|
| 1399 |
+
:param pack_gqa: Whether to pack grouped-query attention.
|
| 1400 |
+
:param seqused_q: Optional tensor of shape [total_seqlen_q] indicating the actual sequence lengths for queries. If provided, overrides cu_seqlens_q for masking.
|
| 1401 |
+
:param seqused_k: Optional tensor of shape [total_seqlen_k] indicating the actual sequence lengths for keys/values. If provided, overrides cu_seqlens_k for masking.
|
| 1402 |
+
:param out: Optional preallocated output tensor with shape [batch_size, seqlen_q, num_heads, head_dim].
|
| 1403 |
+
:param lse: Optional preallocated logsumexp tensor with shape [batch_size, num_heads, seqlen_q].
|
| 1404 |
+
:param is_autotune: Force re-run Triton autotuner and overwrite cache. Default False uses cached config.
|
| 1405 |
+
:param skip_checks: Whether to skip input validation checks for faster performance.
|
| 1406 |
+
:param return_lse: Whether to return the logsumexp tensor for numerical stability analysis. If True, returns a tuple (out, lse). If False, returns only out.
|
| 1407 |
+
|
| 1408 |
+
:returns: If return_lse is False, returns out with shape [total_seqlen_q, num_heads_q, head_dim]. If return_lse is True, returns a tuple (out, lse), where lse has shape [total_seqlen_q, num_heads_q].
|
| 1409 |
+
"""
|
| 1410 |
+
return FlashSparseAttnVarlenFunc.apply(
|
| 1411 |
+
query,
|
| 1412 |
+
key,
|
| 1413 |
+
value,
|
| 1414 |
+
cu_seqlens_q,
|
| 1415 |
+
cu_seqlens_k,
|
| 1416 |
+
max_seqlen_q,
|
| 1417 |
+
max_seqlen_k,
|
| 1418 |
+
is_causal,
|
| 1419 |
+
softmax_scale,
|
| 1420 |
+
query_scale,
|
| 1421 |
+
key_scale,
|
| 1422 |
+
value_scale,
|
| 1423 |
+
window_sizes,
|
| 1424 |
+
softmax_threshold,
|
| 1425 |
+
is_local,
|
| 1426 |
+
is_quant,
|
| 1427 |
+
is_split_kv,
|
| 1428 |
+
is_split_qo,
|
| 1429 |
+
pack_gqa,
|
| 1430 |
+
seqused_q,
|
| 1431 |
+
seqused_k,
|
| 1432 |
+
out,
|
| 1433 |
+
lse,
|
| 1434 |
+
is_autotune,
|
| 1435 |
+
skip_checks,
|
| 1436 |
+
return_lse,
|
| 1437 |
+
)
|
| 1438 |
+
|
| 1439 |
+
|
| 1440 |
+
def flash_sparse_attn_varlen_with_kvcache_func(
|
| 1441 |
+
query: torch.Tensor,
|
| 1442 |
+
key: torch.Tensor,
|
| 1443 |
+
value: torch.Tensor,
|
| 1444 |
+
cu_seqlens_k: torch.Tensor,
|
| 1445 |
+
max_seqlen_k: int,
|
| 1446 |
+
softmax_scale: Optional[float] = None,
|
| 1447 |
+
softmax_threshold: Optional[float] = None,
|
| 1448 |
+
query_scale: Optional[torch.Tensor] = None,
|
| 1449 |
+
key_scale: Optional[torch.Tensor] = None,
|
| 1450 |
+
value_scale: Optional[torch.Tensor] = None,
|
| 1451 |
+
window_sizes: Optional[torch.Tensor] = None,
|
| 1452 |
+
is_local: bool = False,
|
| 1453 |
+
is_quant: bool = False,
|
| 1454 |
+
seqused_k: Optional[torch.Tensor] = None,
|
| 1455 |
+
out: Optional[torch.Tensor] = None,
|
| 1456 |
+
lse: Optional[torch.Tensor] = None,
|
| 1457 |
+
is_autotune: bool = False,
|
| 1458 |
+
skip_checks: bool = False,
|
| 1459 |
+
return_lse: bool = False,
|
| 1460 |
+
) -> Union[torch.Tensor, Tuple[torch.Tensor, torch.Tensor]]:
|
| 1461 |
+
"""
|
| 1462 |
+
Flash sparse attention function for variable-length decoding with KV cache that computes the attention output and optionally the logsumexp.
|
| 1463 |
+
|
| 1464 |
+
:param query: Query tensor of shape [batch_size, num_heads_q, head_dim].
|
| 1465 |
+
:param key: Key tensor of shape [total_seqlen_k, num_heads_kv, head_dim].
|
| 1466 |
+
:param value: Value tensor of shape [total_seqlen_k, num_heads_kv, head_dim].
|
| 1467 |
+
:param cu_seqlens_k: Cumulative sequence lengths for keys/values, shape [batch_size + 1].
|
| 1468 |
+
:param max_seqlen_k: Maximum sequence length for keys/values.
|
| 1469 |
+
:param softmax_scale: Optional scaling factor for the softmax. If None, defaults to 1/sqrt(head_dim).
|
| 1470 |
+
:param softmax_threshold: Optional threshold for the sparse softmax. If None, defaults to head_dim / max_seqlen_k.
|
| 1471 |
+
:param query_scale: Optional per-tensor scale for query dequantization.
|
| 1472 |
+
:param key_scale: Optional per-tensor scale for key dequantization.
|
| 1473 |
+
:param value_scale: Optional per-tensor scale for value dequantization.
|
| 1474 |
+
:param window_sizes: Optional window sizes tensor for flexible local attention. Must be shape [num_kv_heads, 4] with dtype int32, with columns [window_sink, window_left, window_right, window_dist]. If provided, is_local is automatically set to True.
|
| 1475 |
+
:param is_local: Whether to apply a local mask.
|
| 1476 |
+
:param is_quant: Whether the inputs are quantized. If True, query_scale, key_scale, and value_scale must be provided for dequantization.
|
| 1477 |
+
:param seqused_k: Optional tensor indicating the actual sequence lengths for keys/values.
|
| 1478 |
+
:param out: Optional preallocated output tensor with shape [batch_size, num_heads_q, head_dim].
|
| 1479 |
+
:param lse: Optional preallocated logsumexp tensor with shape [batch_size, num_heads_q].
|
| 1480 |
+
:param is_autotune: Force re-run Triton autotuner and overwrite cache. Default False uses cached config.
|
| 1481 |
+
:param skip_checks: Whether to skip input validation checks for faster performance.
|
| 1482 |
+
:param return_lse: Whether to return the logsumexp tensor for numerical stability analysis. If True, returns a tuple (out, lse). If False, returns only out.
|
| 1483 |
+
|
| 1484 |
+
:returns: If return_lse is False, returns out with shape [batch_size, num_heads_q, head_dim]. If return_lse is True, returns a tuple (out, lse), where lse has shape [batch_size, num_heads_q].
|
| 1485 |
+
"""
|
| 1486 |
+
|
| 1487 |
+
if is_quant and (query_scale is None or key_scale is None or value_scale is None):
|
| 1488 |
+
query, query_scale = quant.quantize_fp8(query)
|
| 1489 |
+
key, key_scale = quant.quantize_fp8(key)
|
| 1490 |
+
value, value_scale = quant.quantize_fp8(value)
|
| 1491 |
+
|
| 1492 |
+
if window_sizes is not None:
|
| 1493 |
+
is_local = True
|
| 1494 |
+
|
| 1495 |
+
out, lse = _flash_sparse_attn_varlen_decode(
|
| 1496 |
+
query=query,
|
| 1497 |
+
key=key,
|
| 1498 |
+
value=value,
|
| 1499 |
+
cu_seqlens_k=cu_seqlens_k,
|
| 1500 |
+
max_seqlen_k=max_seqlen_k,
|
| 1501 |
+
softmax_scale=softmax_scale,
|
| 1502 |
+
softmax_threshold=softmax_threshold,
|
| 1503 |
+
query_scale=query_scale,
|
| 1504 |
+
key_scale=key_scale,
|
| 1505 |
+
value_scale=value_scale,
|
| 1506 |
+
window_sizes=window_sizes,
|
| 1507 |
+
is_local=is_local,
|
| 1508 |
+
is_quant=is_quant,
|
| 1509 |
+
seqused_k=seqused_k,
|
| 1510 |
+
out=out,
|
| 1511 |
+
lse=lse,
|
| 1512 |
+
is_autotune=is_autotune,
|
| 1513 |
+
skip_checks=skip_checks,
|
| 1514 |
+
)
|
| 1515 |
+
|
| 1516 |
+
if return_lse:
|
| 1517 |
+
return out, lse
|
| 1518 |
+
return out
|
| 1519 |
+
|
| 1520 |
+
|
| 1521 |
+
def flash_gated_attn_func(
|
| 1522 |
+
query: torch.Tensor,
|
| 1523 |
+
key: torch.Tensor,
|
| 1524 |
+
value: torch.Tensor,
|
| 1525 |
+
alpha: torch.Tensor,
|
| 1526 |
+
delta: torch.Tensor,
|
| 1527 |
+
is_causal: bool = False,
|
| 1528 |
+
softmax_scale: Optional[float] = None,
|
| 1529 |
+
query_scale: Optional[torch.Tensor] = None,
|
| 1530 |
+
key_scale: Optional[torch.Tensor] = None,
|
| 1531 |
+
value_scale: Optional[torch.Tensor] = None,
|
| 1532 |
+
window_sizes: Optional[torch.Tensor] = None,
|
| 1533 |
+
softmax_threshold: Optional[float] = None,
|
| 1534 |
+
gate_threshold: Optional[float] = None,
|
| 1535 |
+
is_logsigmoid_gate: bool = True,
|
| 1536 |
+
is_adapt_gate: bool = True,
|
| 1537 |
+
is_local: bool = False,
|
| 1538 |
+
is_quant: bool = False,
|
| 1539 |
+
is_split_kv: bool = False,
|
| 1540 |
+
is_split_qo: bool = False,
|
| 1541 |
+
pack_gqa: bool = False,
|
| 1542 |
+
out: Optional[torch.Tensor] = None,
|
| 1543 |
+
lse: Optional[torch.Tensor] = None,
|
| 1544 |
+
is_autotune: bool = False,
|
| 1545 |
+
skip_checks: bool = False,
|
| 1546 |
+
return_lse: bool = False,
|
| 1547 |
+
) -> Union[torch.Tensor, Tuple[torch.Tensor, torch.Tensor]]:
|
| 1548 |
+
"""
|
| 1549 |
+
Flash gated attention function that computes the attention output and optionally the logsumexp.
|
| 1550 |
+
|
| 1551 |
+
:param query: Query tensor of shape [batch_size, seqlen_q, num_heads, head_dim].
|
| 1552 |
+
:param key: Key tensor of shape [batch_size, seqlen_k, num_kv_heads, head_dim].
|
| 1553 |
+
:param value: Value tensor of shape [batch_size, seqlen_k, num_kv_heads, head_dim].
|
| 1554 |
+
:param alpha: Tensor of shape [batch_size, seqlen_q, num_heads] representing the sparsity pattern for queries.
|
| 1555 |
+
:param delta: Tensor of shape [batch_size, seqlen_k, num_kv_heads] representing the sparsity pattern for keys/values.
|
| 1556 |
+
:param is_causal: Whether to apply a causal mask.
|
| 1557 |
+
:param softmax_scale: Optional scaling factor for the softmax. If None, defaults to 1/sqrt(head_dim).
|
| 1558 |
+
:param query_scale: Optional per-tensor scale for query dequantization.
|
| 1559 |
+
:param key_scale: Optional per-tensor scale for key dequantization.
|
| 1560 |
+
:param value_scale: Optional per-tensor scale for value dequantization.
|
| 1561 |
+
:param window_sizes: Optional window sizes tensor for flexible local attention. Must be shape [num_kv_heads, 4] with dtype int32, with columns [window_sink, window_left, window_right, window_dist]. If provided, is_local is automatically set to True.
|
| 1562 |
+
:param softmax_threshold: Optional threshold for the sparse softmax.
|
| 1563 |
+
:param gate_threshold: Optional threshold for the sparsity gate.
|
| 1564 |
+
:param is_logsigmoid_gate: Whether to use a log-sigmoid function for the sparsity gate. If False, uses a linear function.
|
| 1565 |
+
:param is_adapt_gate: Whether to adapt the gate threshold based on sequence length.
|
| 1566 |
+
:param is_local: Whether to apply a local mask.
|
| 1567 |
+
:param is_quant: Whether the inputs are quantized. If True, query_scale, key_scale, and value_scale must be provided or will be computed from the input tensors.
|
| 1568 |
+
:param is_split_kv: Whether to enable split-KV for forward occupancy.
|
| 1569 |
+
:param is_split_qo: Whether to enable split-QO for backward occupancy.
|
| 1570 |
+
:param pack_gqa: Whether to pack grouped-query attention.
|
| 1571 |
+
:param out: Optional preallocated output tensor with shape [batch_size, seqlen_q, num_heads, head_dim].
|
| 1572 |
+
:param lse: Optional preallocated logsumexp tensor with shape [batch_size, num_heads, seqlen_q].
|
| 1573 |
+
:param is_autotune: Force re-run Triton autotuner and overwrite cache. Default False uses cached config.
|
| 1574 |
+
:param skip_checks: Whether to skip input validation checks for faster performance.
|
| 1575 |
+
:param return_lse: Whether to return the logsumexp tensor for numerical stability analysis. If True, returns a tuple (out, lse). If False, returns only out.
|
| 1576 |
+
|
| 1577 |
+
:returns: If return_lse is False, returns out with shape [batch_size, seqlen_q, num_heads, head_dim]. If return_lse is True, returns a tuple (out, lse), where lse has shape [batch_size, num_heads, seqlen_q].
|
| 1578 |
+
"""
|
| 1579 |
+
return FlashGatedAttnFunc.apply(
|
| 1580 |
+
query,
|
| 1581 |
+
key,
|
| 1582 |
+
value,
|
| 1583 |
+
alpha,
|
| 1584 |
+
delta,
|
| 1585 |
+
is_causal,
|
| 1586 |
+
softmax_scale,
|
| 1587 |
+
query_scale,
|
| 1588 |
+
key_scale,
|
| 1589 |
+
value_scale,
|
| 1590 |
+
window_sizes,
|
| 1591 |
+
softmax_threshold,
|
| 1592 |
+
gate_threshold,
|
| 1593 |
+
is_logsigmoid_gate,
|
| 1594 |
+
is_adapt_gate,
|
| 1595 |
+
is_local,
|
| 1596 |
+
is_quant,
|
| 1597 |
+
is_split_kv,
|
| 1598 |
+
is_split_qo,
|
| 1599 |
+
pack_gqa,
|
| 1600 |
+
out,
|
| 1601 |
+
lse,
|
| 1602 |
+
is_autotune,
|
| 1603 |
+
skip_checks,
|
| 1604 |
+
return_lse,
|
| 1605 |
+
)
|
| 1606 |
+
|
| 1607 |
+
|
| 1608 |
+
def flash_gated_attn_with_kvcache_func(
|
| 1609 |
+
query: torch.Tensor,
|
| 1610 |
+
key: torch.Tensor,
|
| 1611 |
+
value: torch.Tensor,
|
| 1612 |
+
alpha: torch.Tensor,
|
| 1613 |
+
delta: torch.Tensor,
|
| 1614 |
+
softmax_scale: Optional[float] = None,
|
| 1615 |
+
softmax_threshold: Optional[float] = None,
|
| 1616 |
+
gate_threshold: Optional[float] = None,
|
| 1617 |
+
is_logsigmoid_gate: bool = True,
|
| 1618 |
+
query_scale: Optional[torch.Tensor] = None,
|
| 1619 |
+
key_scale: Optional[torch.Tensor] = None,
|
| 1620 |
+
value_scale: Optional[torch.Tensor] = None,
|
| 1621 |
+
window_sizes: Optional[torch.Tensor] = None,
|
| 1622 |
+
is_local: bool = False,
|
| 1623 |
+
is_quant: bool = False,
|
| 1624 |
+
out: Optional[torch.Tensor] = None,
|
| 1625 |
+
lse: Optional[torch.Tensor] = None,
|
| 1626 |
+
is_autotune: bool = False,
|
| 1627 |
+
skip_checks: bool = False,
|
| 1628 |
+
return_lse: bool = False,
|
| 1629 |
+
) -> Union[torch.Tensor, Tuple[torch.Tensor, torch.Tensor]]:
|
| 1630 |
+
"""
|
| 1631 |
+
Flash gated attention function for decoding with KV cache that computes the attention output and optionally the logsumexp.
|
| 1632 |
+
|
| 1633 |
+
:param query: Query tensor of shape [batch_size, num_heads, head_dim].
|
| 1634 |
+
:param key: Key tensor of shape [batch_size, seqlen_k, num_kv_heads, head_dim].
|
| 1635 |
+
:param value: Value tensor of shape [batch_size, seqlen_k, num_kv_heads, head_dim].
|
| 1636 |
+
:param alpha: Tensor of shape [batch_size, num_heads] representing the sparsity pattern for queries.
|
| 1637 |
+
:param delta: Tensor of shape [batch_size, seqlen_k, num_kv_heads] representing the sparsity pattern for keys/values.
|
| 1638 |
+
:param softmax_scale: Optional scaling factor for the softmax. If None, defaults to 1/sqrt(head_dim).
|
| 1639 |
+
:param softmax_threshold: Optional threshold for the sparse softmax.
|
| 1640 |
+
:param gate_threshold: Optional threshold for the sparsity gate.
|
| 1641 |
+
:param is_logsigmoid_gate: Whether to use a log-sigmoid function for the sparsity gate. If False, uses a linear function.
|
| 1642 |
+
:param query_scale: Optional per-tensor scale for query dequantization.
|
| 1643 |
+
:param key_scale: Optional per-tensor scale for key dequantization.
|
| 1644 |
+
:param value_scale: Optional per-tensor scale for value dequantization.
|
| 1645 |
+
:param window_sizes: Optional window sizes tensor for flexible local attention. Must be shape [num_kv_heads, 4] with dtype int32, with columns [window_sink, window_left, window_right, window_dist]. If provided, is_local is automatically set to True.
|
| 1646 |
+
:param is_local: Whether to apply a local mask.
|
| 1647 |
+
:param is_quant: Whether the inputs are quantized. If True, query_scale, key_scale, and value_scale must be provided for dequantization.
|
| 1648 |
+
:param out: Optional preallocated output tensor with shape [batch_size, num_heads, head_dim].
|
| 1649 |
+
:param lse: Optional preallocated logsumexp tensor with shape [batch_size, num_heads].
|
| 1650 |
+
:param is_autotune: Force re-run Triton autotuner and overwrite cache. Default False uses cached config.
|
| 1651 |
+
:param skip_checks: Whether to skip input validation checks for faster performance.
|
| 1652 |
+
:param return_lse: Whether to return the logsumexp tensor for numerical stability analysis. If True, returns a tuple (out, lse). If False, returns only out.
|
| 1653 |
+
|
| 1654 |
+
:returns: If return_lse is False, returns out with shape [batch_size, num_heads, head_dim]. If return_lse is True, returns a tuple (out, lse), where lse has shape [batch_size, num_heads].
|
| 1655 |
+
"""
|
| 1656 |
+
|
| 1657 |
+
if is_quant and (query_scale is None or key_scale is None or value_scale is None):
|
| 1658 |
+
query, query_scale = quant.quantize_fp8(query)
|
| 1659 |
+
key, key_scale = quant.quantize_fp8(key)
|
| 1660 |
+
value, value_scale = quant.quantize_fp8(value)
|
| 1661 |
+
|
| 1662 |
+
if window_sizes is not None:
|
| 1663 |
+
is_local = True
|
| 1664 |
+
|
| 1665 |
+
# Transpose for make_tensor_descriptor
|
| 1666 |
+
delta = delta.transpose(-2, -1).contiguous()
|
| 1667 |
+
|
| 1668 |
+
out, lse = _flash_gated_attn_decode(
|
| 1669 |
+
query=query,
|
| 1670 |
+
key=key,
|
| 1671 |
+
value=value,
|
| 1672 |
+
alpha=alpha,
|
| 1673 |
+
delta=delta,
|
| 1674 |
+
softmax_scale=softmax_scale,
|
| 1675 |
+
softmax_threshold=softmax_threshold,
|
| 1676 |
+
gate_threshold=gate_threshold,
|
| 1677 |
+
is_logsigmoid_gate=is_logsigmoid_gate,
|
| 1678 |
+
query_scale=query_scale,
|
| 1679 |
+
key_scale=key_scale,
|
| 1680 |
+
value_scale=value_scale,
|
| 1681 |
+
window_sizes=window_sizes,
|
| 1682 |
+
is_local=is_local,
|
| 1683 |
+
is_quant=is_quant,
|
| 1684 |
+
out=out,
|
| 1685 |
+
lse=lse,
|
| 1686 |
+
is_autotune=is_autotune,
|
| 1687 |
+
skip_checks=skip_checks,
|
| 1688 |
+
)
|
| 1689 |
+
|
| 1690 |
+
if return_lse:
|
| 1691 |
+
return out, lse
|
| 1692 |
+
return out
|
| 1693 |
+
|
| 1694 |
+
|
| 1695 |
+
def flash_gated_attn_varlen_func(
|
| 1696 |
+
query: torch.Tensor,
|
| 1697 |
+
key: torch.Tensor,
|
| 1698 |
+
value: torch.Tensor,
|
| 1699 |
+
alpha: torch.Tensor,
|
| 1700 |
+
delta: torch.Tensor,
|
| 1701 |
+
cu_seqlens_q: torch.Tensor,
|
| 1702 |
+
cu_seqlens_k: torch.Tensor,
|
| 1703 |
+
max_seqlen_q: int,
|
| 1704 |
+
max_seqlen_k: int,
|
| 1705 |
+
is_causal: bool = False,
|
| 1706 |
+
softmax_scale: Optional[float] = None,
|
| 1707 |
+
query_scale: Optional[torch.Tensor] = None,
|
| 1708 |
+
key_scale: Optional[torch.Tensor] = None,
|
| 1709 |
+
value_scale: Optional[torch.Tensor] = None,
|
| 1710 |
+
window_sizes: Optional[torch.Tensor] = None,
|
| 1711 |
+
softmax_threshold: Optional[float] = None,
|
| 1712 |
+
gate_threshold: Optional[float] = None,
|
| 1713 |
+
is_logsigmoid_gate: bool = True,
|
| 1714 |
+
is_adapt_gate: bool = True,
|
| 1715 |
+
is_local: bool = False,
|
| 1716 |
+
is_quant: bool = False,
|
| 1717 |
+
is_split_kv: bool = False,
|
| 1718 |
+
is_split_qo: bool = False,
|
| 1719 |
+
pack_gqa: bool = False,
|
| 1720 |
+
seqused_q: Optional[torch.Tensor] = None,
|
| 1721 |
+
seqused_k: Optional[torch.Tensor] = None,
|
| 1722 |
+
out: Optional[torch.Tensor] = None,
|
| 1723 |
+
lse: Optional[torch.Tensor] = None,
|
| 1724 |
+
is_autotune: bool = False,
|
| 1725 |
+
skip_checks: bool = False,
|
| 1726 |
+
return_lse: bool = False,
|
| 1727 |
+
) -> Union[torch.Tensor, Tuple[torch.Tensor, torch.Tensor]]:
|
| 1728 |
+
"""
|
| 1729 |
+
Flash gated attention function for variable-length sequences that computes the attention output and optionally the logsumexp.
|
| 1730 |
+
|
| 1731 |
+
:param query: Query tensor of shape [total_seqlen_q, num_heads_q, head_dim].
|
| 1732 |
+
:param key: Key tensor of shape [total_seqlen_k, num_heads_kv, head_dim].
|
| 1733 |
+
:param value: Value tensor of shape [total_seqlen_k, num_heads_kv, head_dim].
|
| 1734 |
+
:param alpha: Tensor of shape [total_seqlen_q, num_heads_q] representing the sparsity pattern for queries.
|
| 1735 |
+
:param delta: Tensor of shape [total_seqlen_k, num_heads_kv] representing the sparsity pattern for keys/values.
|
| 1736 |
+
:param cu_seqlens_q: Cumulative sequence lengths for queries, shape [batch_size + 1].
|
| 1737 |
+
:param cu_seqlens_k: Cumulative sequence lengths for keys/values, shape [batch_size + 1].
|
| 1738 |
+
:param max_seqlen_q: Maximum sequence length for queries.
|
| 1739 |
+
:param max_seqlen_k: Maximum sequence length for keys/values.
|
| 1740 |
+
:param is_causal: Whether to apply a causal mask.
|
| 1741 |
+
:param softmax_scale: Optional scaling factor for the softmax. If None, defaults to 1/sqrt(head_dim).
|
| 1742 |
+
:param query_scale: Optional per-tensor scale for query dequantization.
|
| 1743 |
+
:param key_scale: Optional per-tensor scale for key dequantization.
|
| 1744 |
+
:param value_scale: Optional per-tensor scale for value dequantization.
|
| 1745 |
+
:param window_sizes: Optional window sizes tensor for flexible local attention. Must be shape [num_kv_heads, 4] with dtype int32, with columns [window_sink, window_left, window_right, window_dist]. If provided, is_local is automatically set to True.
|
| 1746 |
+
:param softmax_threshold: Optional threshold for the sparse softmax.
|
| 1747 |
+
:param gate_threshold: Optional threshold for the sparsity gate.
|
| 1748 |
+
:param is_logsigmoid_gate: Whether to use a log-sigmoid function for the sparsity gate. If False, uses a linear function.
|
| 1749 |
+
:param is_adapt_gate: Whether to adapt the gate threshold based on sequence length.
|
| 1750 |
+
:param is_local: Whether to apply a local mask.
|
| 1751 |
+
:param is_quant: Whether the inputs are quantized. If True, query_scale, key_scale, and value_scale must be provided or will be computed from the input tensors.
|
| 1752 |
+
:param is_split_kv: Whether to enable split-KV for forward occupancy.
|
| 1753 |
+
:param is_split_qo: Whether to enable split-QO for backward occupancy.
|
| 1754 |
+
:param pack_gqa: Whether to pack grouped-query attention.
|
| 1755 |
+
:param seqused_q: Optional tensor of shape [total_seqlen_q] indicating the actual sequence lengths for queries. If provided, overrides cu_seqlens_q for masking.
|
| 1756 |
+
:param seqused_k: Optional tensor of shape [total_seqlen_k] indicating the actual sequence lengths for keys/values. If provided, overrides cu_seqlens_k for masking.
|
| 1757 |
+
:param out: Optional preallocated output tensor with shape [batch_size, seqlen_q, num_heads, head_dim].
|
| 1758 |
+
:param lse: Optional preallocated logsumexp tensor with shape [batch_size, num_heads, seqlen_q].
|
| 1759 |
+
:param is_autotune: Force re-run Triton autotuner and overwrite cache. Default False uses cached config.
|
| 1760 |
+
:param skip_checks: Whether to skip input validation checks for faster performance.
|
| 1761 |
+
:param return_lse: Whether to return the logsumexp tensor for numerical stability analysis. If True, returns a tuple (out, lse). If False, returns only out.
|
| 1762 |
+
|
| 1763 |
+
:returns: If return_lse is False, returns out with shape [total_seqlen_q, num_heads_q, head_dim]. If return_lse is True, returns a tuple (out, lse), where lse has shape [total_seqlen_q, num_heads_q].
|
| 1764 |
+
"""
|
| 1765 |
+
return FlashGatedAttnVarlenFunc.apply(
|
| 1766 |
+
query,
|
| 1767 |
+
key,
|
| 1768 |
+
value,
|
| 1769 |
+
alpha,
|
| 1770 |
+
delta,
|
| 1771 |
+
cu_seqlens_q,
|
| 1772 |
+
cu_seqlens_k,
|
| 1773 |
+
max_seqlen_q,
|
| 1774 |
+
max_seqlen_k,
|
| 1775 |
+
is_causal,
|
| 1776 |
+
softmax_scale,
|
| 1777 |
+
query_scale,
|
| 1778 |
+
key_scale,
|
| 1779 |
+
value_scale,
|
| 1780 |
+
window_sizes,
|
| 1781 |
+
softmax_threshold,
|
| 1782 |
+
gate_threshold,
|
| 1783 |
+
is_logsigmoid_gate,
|
| 1784 |
+
is_adapt_gate,
|
| 1785 |
+
is_local,
|
| 1786 |
+
is_quant,
|
| 1787 |
+
is_split_kv,
|
| 1788 |
+
is_split_qo,
|
| 1789 |
+
pack_gqa,
|
| 1790 |
+
seqused_q,
|
| 1791 |
+
seqused_k,
|
| 1792 |
+
out,
|
| 1793 |
+
lse,
|
| 1794 |
+
is_autotune,
|
| 1795 |
+
skip_checks,
|
| 1796 |
+
return_lse,
|
| 1797 |
+
)
|
| 1798 |
+
|
| 1799 |
+
|
| 1800 |
+
def flash_gated_attn_varlen_with_kvcache_func(
|
| 1801 |
+
query: torch.Tensor,
|
| 1802 |
+
key: torch.Tensor,
|
| 1803 |
+
value: torch.Tensor,
|
| 1804 |
+
alpha: torch.Tensor,
|
| 1805 |
+
delta: torch.Tensor,
|
| 1806 |
+
cu_seqlens_k: torch.Tensor,
|
| 1807 |
+
max_seqlen_k: int,
|
| 1808 |
+
softmax_scale: Optional[float] = None,
|
| 1809 |
+
softmax_threshold: Optional[float] = None,
|
| 1810 |
+
gate_threshold: Optional[float] = None,
|
| 1811 |
+
is_logsigmoid_gate: bool = True,
|
| 1812 |
+
query_scale: Optional[torch.Tensor] = None,
|
| 1813 |
+
key_scale: Optional[torch.Tensor] = None,
|
| 1814 |
+
value_scale: Optional[torch.Tensor] = None,
|
| 1815 |
+
window_sizes: Optional[torch.Tensor] = None,
|
| 1816 |
+
is_local: bool = False,
|
| 1817 |
+
is_quant: bool = False,
|
| 1818 |
+
seqused_k: Optional[torch.Tensor] = None,
|
| 1819 |
+
out: Optional[torch.Tensor] = None,
|
| 1820 |
+
lse: Optional[torch.Tensor] = None,
|
| 1821 |
+
is_autotune: bool = False,
|
| 1822 |
+
skip_checks: bool = False,
|
| 1823 |
+
return_lse: bool = False,
|
| 1824 |
+
) -> Union[torch.Tensor, Tuple[torch.Tensor, torch.Tensor]]:
|
| 1825 |
+
"""
|
| 1826 |
+
Flash gated attention function for variable-length decoding with KV cache that computes the attention output and optionally the logsumexp.
|
| 1827 |
+
|
| 1828 |
+
:param query: Query tensor of shape [batch_size, num_heads_q, head_dim].
|
| 1829 |
+
:param key: Key tensor of shape [total_seqlen_k, num_heads_kv, head_dim].
|
| 1830 |
+
:param value: Value tensor of shape [total_seqlen_k, num_heads_kv, head_dim].
|
| 1831 |
+
:param alpha: Tensor of shape [batch_size, num_heads_q] representing the sparsity pattern for queries.
|
| 1832 |
+
:param delta: Tensor of shape [total_seqlen_k, num_heads_kv] representing the sparsity pattern for keys/values.
|
| 1833 |
+
:param cu_seqlens_k: Cumulative sequence lengths for keys/values, shape [batch_size + 1].
|
| 1834 |
+
:param max_seqlen_k: Maximum sequence length for keys/values.
|
| 1835 |
+
:param softmax_scale: Optional scaling factor for the softmax. If None, defaults to 1/sqrt(head_dim).
|
| 1836 |
+
:param softmax_threshold: Optional threshold for the sparse softmax.
|
| 1837 |
+
:param gate_threshold: Optional threshold for the sparsity gate.
|
| 1838 |
+
:param is_logsigmoid_gate: Whether to use a log-sigmoid function for the sparsity gate. If False, uses a linear function.
|
| 1839 |
+
:param query_scale: Optional per-tensor scale for query dequantization.
|
| 1840 |
+
:param key_scale: Optional per-tensor scale for key dequantization.
|
| 1841 |
+
:param value_scale: Optional per-tensor scale for value dequantization.
|
| 1842 |
+
:param window_sizes: Optional window sizes tensor for flexible local attention. Must be shape [num_kv_heads, 4] with dtype int32, with columns [window_sink, window_left, window_right, window_dist]. If provided, is_local is automatically set to True.
|
| 1843 |
+
:param is_local: Whether to apply a local mask.
|
| 1844 |
+
:param is_quant: Whether the inputs are quantized. If True, query_scale, key_scale, and value_scale must be provided for dequantization.
|
| 1845 |
+
:param seqused_k: Optional tensor indicating the actual sequence lengths for keys/values.
|
| 1846 |
+
:param out: Optional preallocated output tensor with shape [batch_size, num_heads_q, head_dim].
|
| 1847 |
+
:param lse: Optional preallocated logsumexp tensor with shape [batch_size, num_heads_q].
|
| 1848 |
+
:param is_autotune: Force re-run Triton autotuner and overwrite cache. Default False uses cached config.
|
| 1849 |
+
:param skip_checks: Whether to skip input validation checks for faster performance.
|
| 1850 |
+
:param return_lse: Whether to return the logsumexp tensor for numerical stability analysis. If True, returns a tuple (out, lse). If False, returns only out.
|
| 1851 |
+
|
| 1852 |
+
:returns: If return_lse is False, returns out with shape [batch_size, num_heads_q, head_dim]. If return_lse is True, returns a tuple (out, lse), where lse has shape [batch_size, num_heads_q].
|
| 1853 |
+
"""
|
| 1854 |
+
|
| 1855 |
+
if is_quant and (query_scale is None or key_scale is None or value_scale is None):
|
| 1856 |
+
query, query_scale = quant.quantize_fp8(query)
|
| 1857 |
+
key, key_scale = quant.quantize_fp8(key)
|
| 1858 |
+
value, value_scale = quant.quantize_fp8(value)
|
| 1859 |
+
|
| 1860 |
+
if window_sizes is not None:
|
| 1861 |
+
is_local = True
|
| 1862 |
+
|
| 1863 |
+
# Transpose for make_tensor_descriptor
|
| 1864 |
+
delta = delta.transpose(-2, -1).contiguous()
|
| 1865 |
+
|
| 1866 |
+
out, lse = _flash_gated_attn_varlen_decode(
|
| 1867 |
+
query=query,
|
| 1868 |
+
key=key,
|
| 1869 |
+
value=value,
|
| 1870 |
+
alpha=alpha,
|
| 1871 |
+
delta=delta,
|
| 1872 |
+
cu_seqlens_k=cu_seqlens_k,
|
| 1873 |
+
max_seqlen_k=max_seqlen_k,
|
| 1874 |
+
softmax_scale=softmax_scale,
|
| 1875 |
+
softmax_threshold=softmax_threshold,
|
| 1876 |
+
gate_threshold=gate_threshold,
|
| 1877 |
+
is_logsigmoid_gate=is_logsigmoid_gate,
|
| 1878 |
+
query_scale=query_scale,
|
| 1879 |
+
key_scale=key_scale,
|
| 1880 |
+
value_scale=value_scale,
|
| 1881 |
+
window_sizes=window_sizes,
|
| 1882 |
+
is_local=is_local,
|
| 1883 |
+
is_quant=is_quant,
|
| 1884 |
+
seqused_k=seqused_k,
|
| 1885 |
+
out=out,
|
| 1886 |
+
lse=lse,
|
| 1887 |
+
is_autotune=is_autotune,
|
| 1888 |
+
skip_checks=skip_checks,
|
| 1889 |
+
)
|
| 1890 |
+
|
| 1891 |
+
if return_lse:
|
| 1892 |
+
return out, lse
|
| 1893 |
+
return out
|
| 1894 |
+
|
| 1895 |
+
|
| 1896 |
+
def flash_proj_func(
|
| 1897 |
+
input: torch.Tensor,
|
| 1898 |
+
weight: torch.Tensor,
|
| 1899 |
+
out_dtype: torch.dtype = torch.float8_e5m2,
|
| 1900 |
+
prev_scale: Optional[torch.Tensor] = None,
|
| 1901 |
+
) -> Tuple[torch.Tensor, torch.Tensor]:
|
| 1902 |
+
"""
|
| 1903 |
+
Fused linear projection with quantized output and per-tensor scale.
|
| 1904 |
+
|
| 1905 |
+
:param input: Input tensor of shape [batch_size, seqlen, in_features].
|
| 1906 |
+
:param weight: Weights tensor of shape [out_features, in_features].
|
| 1907 |
+
:param out_dtype: Target output dtype.
|
| 1908 |
+
:param prev_scale: Per-tensor scale from the previous forward.
|
| 1909 |
+
|
| 1910 |
+
:returns: Tuple of (out, actual_scale), where out has shape [batch_size, seqlen, out_features].
|
| 1911 |
+
"""
|
| 1912 |
+
return FlashProjFunc.apply(input, weight, out_dtype, prev_scale)
|
build/torch-cuda/kernel_repr.py
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
def _get(c, key, default=None):
|
| 2 |
+
try:
|
| 3 |
+
return c[key]
|
| 4 |
+
except (KeyError, TypeError):
|
| 5 |
+
return default
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
def fwd_dense_repr(specialization):
|
| 9 |
+
c = specialization.constants
|
| 10 |
+
mask = (
|
| 11 |
+
"causal"
|
| 12 |
+
if _get(c, "IS_CAUSAL")
|
| 13 |
+
else ("local" if _get(c, "IS_LOCAL") else "full")
|
| 14 |
+
)
|
| 15 |
+
varlen = "_varlen" if _get(c, "HAS_CU_SEQLENS_Q") else ""
|
| 16 |
+
split = "_splitkv" if _get(c, "IS_SPLIT_KV") else ""
|
| 17 |
+
return f"flash_dense_fwd_{mask}_{c['TILE_M']}x{c['TILE_N']}{split}{varlen}"
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
def fwd_sparse_repr(specialization):
|
| 21 |
+
c = specialization.constants
|
| 22 |
+
mask = (
|
| 23 |
+
"causal"
|
| 24 |
+
if _get(c, "IS_CAUSAL")
|
| 25 |
+
else ("local" if _get(c, "IS_LOCAL") else "full")
|
| 26 |
+
)
|
| 27 |
+
varlen = "_varlen" if _get(c, "HAS_CU_SEQLENS_Q") else ""
|
| 28 |
+
split = "_splitkv" if _get(c, "IS_SPLIT_KV") else ""
|
| 29 |
+
return f"flash_sparse_fwd_{mask}_{c['TILE_M']}x{c['TILE_N']}{split}{varlen}"
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
def fwd_gated_repr(specialization):
|
| 33 |
+
c = specialization.constants
|
| 34 |
+
mask = (
|
| 35 |
+
"causal"
|
| 36 |
+
if _get(c, "IS_CAUSAL")
|
| 37 |
+
else ("local" if _get(c, "IS_LOCAL") else "full")
|
| 38 |
+
)
|
| 39 |
+
varlen = "_varlen" if _get(c, "HAS_CU_SEQLENS_Q") else ""
|
| 40 |
+
split = "_splitkv" if _get(c, "IS_SPLIT_KV") else ""
|
| 41 |
+
return f"flash_gated_fwd_{mask}_{c['TILE_M']}x{c['TILE_N']}{split}{varlen}"
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
def bwd_dense_repr(specialization):
|
| 45 |
+
c = specialization.constants
|
| 46 |
+
mask = (
|
| 47 |
+
"causal"
|
| 48 |
+
if _get(c, "IS_CAUSAL")
|
| 49 |
+
else ("local" if _get(c, "IS_LOCAL") else "full")
|
| 50 |
+
)
|
| 51 |
+
varlen = "_varlen" if _get(c, "HAS_CU_SEQLENS_Q") else ""
|
| 52 |
+
return f"flash_dense_bwd_{mask}_{c['TILE_M']}x{c['TILE_N']}{varlen}"
|
| 53 |
+
|
| 54 |
+
|
| 55 |
+
def bwd_sparse_repr(specialization):
|
| 56 |
+
c = specialization.constants
|
| 57 |
+
mask = (
|
| 58 |
+
"causal"
|
| 59 |
+
if _get(c, "IS_CAUSAL")
|
| 60 |
+
else ("local" if _get(c, "IS_LOCAL") else "full")
|
| 61 |
+
)
|
| 62 |
+
varlen = "_varlen" if _get(c, "HAS_CU_SEQLENS_Q") else ""
|
| 63 |
+
return f"flash_sparse_bwd_{mask}_{c['TILE_M']}x{c['TILE_N']}{varlen}"
|
| 64 |
+
|
| 65 |
+
|
| 66 |
+
def bwd_gated_repr(specialization):
|
| 67 |
+
c = specialization.constants
|
| 68 |
+
mask = (
|
| 69 |
+
"causal"
|
| 70 |
+
if _get(c, "IS_CAUSAL")
|
| 71 |
+
else ("local" if _get(c, "IS_LOCAL") else "full")
|
| 72 |
+
)
|
| 73 |
+
varlen = "_varlen" if _get(c, "HAS_CU_SEQLENS_Q") else ""
|
| 74 |
+
return f"flash_gated_bwd_{mask}_{c['TILE_M']}x{c['TILE_N']}{varlen}"
|
| 75 |
+
|
| 76 |
+
|
| 77 |
+
def dec_dense_repr(specialization):
|
| 78 |
+
c = specialization.constants
|
| 79 |
+
mask = "local" if _get(c, "IS_LOCAL") else "full"
|
| 80 |
+
varlen = "_varlen" if _get(c, "HAS_CU_SEQLENS_Q") else ""
|
| 81 |
+
return f"flash_dense_dec_{mask}_{c['TILE_M']}x{c['TILE_N']}{varlen}"
|
| 82 |
+
|
| 83 |
+
|
| 84 |
+
def dec_sparse_repr(specialization):
|
| 85 |
+
c = specialization.constants
|
| 86 |
+
mask = "local" if _get(c, "IS_LOCAL") else "full"
|
| 87 |
+
varlen = "_varlen" if _get(c, "HAS_CU_SEQLENS_Q") else ""
|
| 88 |
+
return f"flash_sparse_dec_{mask}_{c['TILE_M']}x{c['TILE_N']}{varlen}"
|
| 89 |
+
|
| 90 |
+
|
| 91 |
+
def dec_gated_repr(specialization):
|
| 92 |
+
c = specialization.constants
|
| 93 |
+
mask = "local" if _get(c, "IS_LOCAL") else "full"
|
| 94 |
+
varlen = "_varlen" if _get(c, "HAS_CU_SEQLENS_Q") else ""
|
| 95 |
+
return f"flash_gated_dec_{mask}_{c['TILE_M']}x{c['TILE_N']}{varlen}"
|
| 96 |
+
|
| 97 |
+
|
| 98 |
+
def fwd_combine_repr(specialization):
|
| 99 |
+
c = specialization.constants
|
| 100 |
+
return f"flash_fwd_combine_{c['TILE_M']}"
|
| 101 |
+
|
| 102 |
+
|
| 103 |
+
def dec_combine_repr(specialization):
|
| 104 |
+
c = specialization.constants
|
| 105 |
+
return f"flash_dec_combine_{c['TILE_K']}"
|
| 106 |
+
|
| 107 |
+
|
| 108 |
+
def bwd_preprocess_repr(specialization):
|
| 109 |
+
c = specialization.constants
|
| 110 |
+
return f"flash_bwd_preprocess_{c['TILE_M']}"
|
| 111 |
+
|
| 112 |
+
|
| 113 |
+
def bwd_postprocess_repr(specialization):
|
| 114 |
+
c = specialization.constants
|
| 115 |
+
return f"flash_bwd_postprocess_{c['TILE_M']}"
|
build/torch-cuda/launch_grid.py
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import triton
|
| 2 |
+
|
| 3 |
+
from . import cache_utils
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
def get_fwd_grid(
|
| 7 |
+
batch_size: int,
|
| 8 |
+
seqlen_q: int,
|
| 9 |
+
num_heads_q: int,
|
| 10 |
+
num_heads_kv: int,
|
| 11 |
+
pack_gqa: bool,
|
| 12 |
+
num_splits: int,
|
| 13 |
+
):
|
| 14 |
+
"""
|
| 15 |
+
Get the grid function for the forward kernel.
|
| 16 |
+
|
| 17 |
+
:param batch_size: Batch size
|
| 18 |
+
:param seqlen_q: Sequence length of queries
|
| 19 |
+
:param num_heads_q: Number of query heads
|
| 20 |
+
:param num_heads_kv: Number of key/value heads
|
| 21 |
+
:param pack_gqa: Whether GQA packing is used
|
| 22 |
+
:param num_splits: Number of KV splits
|
| 23 |
+
|
| 24 |
+
:return grid: Grid function
|
| 25 |
+
"""
|
| 26 |
+
|
| 27 |
+
def grid(META):
|
| 28 |
+
return (
|
| 29 |
+
triton.cdiv(
|
| 30 |
+
seqlen_q * (num_heads_q // num_heads_kv) if pack_gqa else seqlen_q,
|
| 31 |
+
META["TILE_M"],
|
| 32 |
+
),
|
| 33 |
+
num_heads_kv if pack_gqa else num_heads_q,
|
| 34 |
+
batch_size * num_splits,
|
| 35 |
+
)
|
| 36 |
+
|
| 37 |
+
return grid
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
get_fwd_grid = cache_utils.cache_launch_grid(get_fwd_grid)
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
def get_bwd_grid(
|
| 44 |
+
batch_size: int,
|
| 45 |
+
seqlen_k: int,
|
| 46 |
+
num_heads_q: int,
|
| 47 |
+
num_splits: int = 1,
|
| 48 |
+
):
|
| 49 |
+
"""
|
| 50 |
+
Get the grid function for the backward kernel.
|
| 51 |
+
|
| 52 |
+
:param batch_size: Batch size
|
| 53 |
+
:param seqlen_k: Sequence length of keys
|
| 54 |
+
:param num_heads_q: Number of query heads
|
| 55 |
+
:param num_splits: Number of QO splits
|
| 56 |
+
|
| 57 |
+
:return grid: Grid function
|
| 58 |
+
"""
|
| 59 |
+
|
| 60 |
+
def grid(META):
|
| 61 |
+
return (
|
| 62 |
+
triton.cdiv(seqlen_k, META["TILE_N"]),
|
| 63 |
+
num_heads_q,
|
| 64 |
+
batch_size * num_splits,
|
| 65 |
+
)
|
| 66 |
+
|
| 67 |
+
return grid
|
| 68 |
+
|
| 69 |
+
|
| 70 |
+
get_bwd_grid = cache_utils.cache_launch_grid(get_bwd_grid)
|
| 71 |
+
|
| 72 |
+
|
| 73 |
+
def get_dec_grid(
|
| 74 |
+
batch_size: int,
|
| 75 |
+
num_heads_kv: int,
|
| 76 |
+
num_splits: int,
|
| 77 |
+
):
|
| 78 |
+
"""
|
| 79 |
+
Get the grid function for the decode kernel.
|
| 80 |
+
|
| 81 |
+
:param batch_size: Batch size
|
| 82 |
+
:param num_heads_kv: Number of key/value heads
|
| 83 |
+
:param num_splits: Number of KV splits
|
| 84 |
+
|
| 85 |
+
:return grid: Grid function
|
| 86 |
+
"""
|
| 87 |
+
|
| 88 |
+
def grid(META):
|
| 89 |
+
return (
|
| 90 |
+
num_heads_kv,
|
| 91 |
+
batch_size * num_splits,
|
| 92 |
+
)
|
| 93 |
+
|
| 94 |
+
return grid
|
| 95 |
+
|
| 96 |
+
|
| 97 |
+
get_dec_grid = cache_utils.cache_launch_grid(get_dec_grid)
|
| 98 |
+
|
| 99 |
+
|
| 100 |
+
def get_fwd_combine_grid(
|
| 101 |
+
batch_size: int,
|
| 102 |
+
seqlen_q: int,
|
| 103 |
+
num_heads_q: int,
|
| 104 |
+
):
|
| 105 |
+
"""
|
| 106 |
+
Get the grid function for the forward combine kernel.
|
| 107 |
+
|
| 108 |
+
:param batch_size: Batch size
|
| 109 |
+
:param seqlen_q: Sequence length of queries
|
| 110 |
+
:param num_heads_q: Number of query heads
|
| 111 |
+
|
| 112 |
+
:return grid: Grid function
|
| 113 |
+
"""
|
| 114 |
+
|
| 115 |
+
def grid(META):
|
| 116 |
+
return (
|
| 117 |
+
triton.cdiv(seqlen_q, META["TILE_M"]),
|
| 118 |
+
batch_size * num_heads_q,
|
| 119 |
+
)
|
| 120 |
+
|
| 121 |
+
return grid
|
| 122 |
+
|
| 123 |
+
|
| 124 |
+
get_fwd_combine_grid = cache_utils.cache_launch_grid(get_fwd_combine_grid)
|
| 125 |
+
|
| 126 |
+
|
| 127 |
+
def get_bwd_preprocess_grid(
|
| 128 |
+
batch_size: int,
|
| 129 |
+
seqlen_q: int,
|
| 130 |
+
num_heads_q: int,
|
| 131 |
+
):
|
| 132 |
+
"""
|
| 133 |
+
Get the grid function for the backward preprocess kernel.
|
| 134 |
+
|
| 135 |
+
:param batch_size: Batch size
|
| 136 |
+
:param seqlen_q: Sequence length of queries
|
| 137 |
+
:param num_heads_q: Number of query heads
|
| 138 |
+
|
| 139 |
+
:return grid: Grid function
|
| 140 |
+
"""
|
| 141 |
+
|
| 142 |
+
def grid(META):
|
| 143 |
+
return (
|
| 144 |
+
triton.cdiv(seqlen_q, META["TILE_M"]),
|
| 145 |
+
num_heads_q,
|
| 146 |
+
batch_size,
|
| 147 |
+
)
|
| 148 |
+
|
| 149 |
+
return grid
|
| 150 |
+
|
| 151 |
+
|
| 152 |
+
get_bwd_preprocess_grid = cache_utils.cache_launch_grid(get_bwd_preprocess_grid)
|
| 153 |
+
|
| 154 |
+
|
| 155 |
+
def get_bwd_postprocess_grid(
|
| 156 |
+
batch_size: int,
|
| 157 |
+
seqlen_q: int,
|
| 158 |
+
num_heads_q: int,
|
| 159 |
+
):
|
| 160 |
+
"""
|
| 161 |
+
Get the grid function for the backward postprocess kernel.
|
| 162 |
+
|
| 163 |
+
:param batch_size: Batch size
|
| 164 |
+
:param seqlen_q: Sequence length of queries
|
| 165 |
+
:param num_heads_q: Number of query heads
|
| 166 |
+
|
| 167 |
+
:return grid: Grid function
|
| 168 |
+
"""
|
| 169 |
+
|
| 170 |
+
def grid(META):
|
| 171 |
+
return (
|
| 172 |
+
triton.cdiv(seqlen_q, META["TILE_M"]),
|
| 173 |
+
num_heads_q,
|
| 174 |
+
batch_size,
|
| 175 |
+
)
|
| 176 |
+
|
| 177 |
+
return grid
|
| 178 |
+
|
| 179 |
+
|
| 180 |
+
get_bwd_postprocess_grid = cache_utils.cache_launch_grid(get_bwd_postprocess_grid)
|
| 181 |
+
|
| 182 |
+
|
| 183 |
+
def get_dec_combine_grid(
|
| 184 |
+
batch_size: int,
|
| 185 |
+
num_heads_q: int,
|
| 186 |
+
):
|
| 187 |
+
"""
|
| 188 |
+
Get the grid function for the decode combine kernel.
|
| 189 |
+
|
| 190 |
+
:param batch_size: Batch size
|
| 191 |
+
:param num_heads_q: Number of query heads
|
| 192 |
+
|
| 193 |
+
:return grid: Grid function
|
| 194 |
+
"""
|
| 195 |
+
|
| 196 |
+
def grid(META):
|
| 197 |
+
return (batch_size * num_heads_q,)
|
| 198 |
+
|
| 199 |
+
return grid
|
| 200 |
+
|
| 201 |
+
|
| 202 |
+
get_dec_combine_grid = cache_utils.cache_launch_grid(get_dec_combine_grid)
|
build/torch-cuda/launch_template.py
ADDED
|
@@ -0,0 +1,457 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
NOTE: Setting num_ctas=2 for the kernel can trigger Triton's PlanCTA assertion
|
| 3 |
+
Setting num_ctas=1 for now to avoid this issue, but we may want to revisit this in the future
|
| 4 |
+
"""
|
| 5 |
+
|
| 6 |
+
import functools
|
| 7 |
+
import hashlib
|
| 8 |
+
import json
|
| 9 |
+
import os
|
| 10 |
+
import sys
|
| 11 |
+
import tempfile
|
| 12 |
+
from pathlib import Path
|
| 13 |
+
from filelock import ReadWriteLock
|
| 14 |
+
|
| 15 |
+
import torch
|
| 16 |
+
import triton
|
| 17 |
+
|
| 18 |
+
from . import cache_utils
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
TRITON_CACHE_DIR: str = os.getenv(
|
| 22 |
+
"FLASH_SPARSE_ATTENTION_LAUNCH_CONFIG_DIR",
|
| 23 |
+
str(Path(__file__).resolve().parent / "launch_config"),
|
| 24 |
+
)
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
@functools.lru_cache(maxsize=1)
|
| 28 |
+
def _compute_source_fingerprint() -> str:
|
| 29 |
+
"""
|
| 30 |
+
Hash all Triton Python sources plus runtime ABI stamps into a short fingerprint.
|
| 31 |
+
The fingerprint changes whenever any .py file under flash_sparse_attn/ops/triton is
|
| 32 |
+
modified, the Python minor version changes, or the triton package version changes.
|
| 33 |
+
|
| 34 |
+
:return: SHA-256 hex digest string.
|
| 35 |
+
"""
|
| 36 |
+
triton_root = Path(__file__).resolve().parent
|
| 37 |
+
h = hashlib.sha256()
|
| 38 |
+
h.update(f"py{sys.version_info.major}.{sys.version_info.minor}".encode())
|
| 39 |
+
h.update(f"triton={getattr(triton, '__version__', 'unknown')}".encode())
|
| 40 |
+
for src in sorted(triton_root.rglob("*.py")):
|
| 41 |
+
if not src.is_file():
|
| 42 |
+
continue
|
| 43 |
+
h.update(src.relative_to(triton_root).as_posix().encode())
|
| 44 |
+
content = src.read_bytes()
|
| 45 |
+
h.update(len(content).to_bytes(8, "little"))
|
| 46 |
+
h.update(content)
|
| 47 |
+
return h.hexdigest()
|
| 48 |
+
|
| 49 |
+
|
| 50 |
+
def _seqlen_bucket(seqlen: int) -> int:
|
| 51 |
+
if seqlen <= 0:
|
| 52 |
+
return 1
|
| 53 |
+
return triton.next_power_of_2(seqlen)
|
| 54 |
+
|
| 55 |
+
|
| 56 |
+
def _sanitize_device_name(name: str) -> str:
|
| 57 |
+
return name.replace(" ", "_").replace("/", "_")
|
| 58 |
+
|
| 59 |
+
|
| 60 |
+
class LaunchConfigCache:
|
| 61 |
+
"""
|
| 62 |
+
Persistent per-device cache for Triton kernel launch configurations.
|
| 63 |
+
Stores tuned (TILE_M, TILE_N, num_warps, num_stages, num_ctas) keyed by
|
| 64 |
+
kernel name, sequence length buckets, and dispatch flags. Cache is namespaced
|
| 65 |
+
under a source fingerprint directory for automatic invalidation on code changes.
|
| 66 |
+
"""
|
| 67 |
+
|
| 68 |
+
__slots__ = ("_memory", "_fingerprint")
|
| 69 |
+
|
| 70 |
+
def __init__(self):
|
| 71 |
+
self._memory: dict[str, dict[tuple, tuple]] = {}
|
| 72 |
+
self._fingerprint: str = _compute_source_fingerprint()
|
| 73 |
+
|
| 74 |
+
def _get_cache_dir(self) -> Path:
|
| 75 |
+
cache_dir = Path(TRITON_CACHE_DIR) / self._fingerprint
|
| 76 |
+
cache_dir.mkdir(parents=True, exist_ok=True)
|
| 77 |
+
return cache_dir
|
| 78 |
+
|
| 79 |
+
def _json_path(self, device_name: str) -> Path:
|
| 80 |
+
return self._get_cache_dir() / f"{_sanitize_device_name(device_name)}.json"
|
| 81 |
+
|
| 82 |
+
def _lock(self, device_name: str) -> ReadWriteLock:
|
| 83 |
+
lock_path = self._get_cache_dir() / f"{_sanitize_device_name(device_name)}"
|
| 84 |
+
return ReadWriteLock(str(lock_path), timeout=15)
|
| 85 |
+
|
| 86 |
+
def _read_unlocked(self, device_name: str) -> dict[tuple, tuple]:
|
| 87 |
+
json_path = self._json_path(device_name)
|
| 88 |
+
result: dict[tuple, tuple] = {}
|
| 89 |
+
if not json_path.exists():
|
| 90 |
+
return result
|
| 91 |
+
try:
|
| 92 |
+
data = json.loads(json_path.read_text())
|
| 93 |
+
for entry in data:
|
| 94 |
+
key = (
|
| 95 |
+
entry["kernel"],
|
| 96 |
+
entry["seqlen_q_bucket"],
|
| 97 |
+
entry["seqlen_k_bucket"],
|
| 98 |
+
entry["tile_k"],
|
| 99 |
+
entry["is_local"],
|
| 100 |
+
entry["qhead_per_kvhead"],
|
| 101 |
+
entry.get("is_causal", False),
|
| 102 |
+
entry.get("pack_gqa", False),
|
| 103 |
+
entry.get("is_quant", False),
|
| 104 |
+
)
|
| 105 |
+
result[key] = tuple(entry["config"])
|
| 106 |
+
except (OSError, json.JSONDecodeError, KeyError, TypeError):
|
| 107 |
+
result = {}
|
| 108 |
+
return result
|
| 109 |
+
|
| 110 |
+
def _write_unlocked(self, device_name: str, cache: dict[tuple, tuple]) -> None:
|
| 111 |
+
json_path = self._json_path(device_name)
|
| 112 |
+
data = []
|
| 113 |
+
for (k_name, sq, sk, tk, local, qpk, causal, pgqa, quant), config in sorted(
|
| 114 |
+
cache.items(), key=lambda x: x[0]
|
| 115 |
+
):
|
| 116 |
+
data.append(
|
| 117 |
+
{
|
| 118 |
+
"kernel": k_name,
|
| 119 |
+
"seqlen_q_bucket": sq,
|
| 120 |
+
"seqlen_k_bucket": sk,
|
| 121 |
+
"tile_k": tk,
|
| 122 |
+
"is_local": local,
|
| 123 |
+
"qhead_per_kvhead": qpk,
|
| 124 |
+
"is_causal": causal,
|
| 125 |
+
"pack_gqa": pgqa,
|
| 126 |
+
"is_quant": quant,
|
| 127 |
+
"config": list(config),
|
| 128 |
+
}
|
| 129 |
+
)
|
| 130 |
+
|
| 131 |
+
fd, tmp_path = tempfile.mkstemp(
|
| 132 |
+
dir=str(json_path.parent), suffix=".json.tmp", prefix=json_path.stem
|
| 133 |
+
)
|
| 134 |
+
try:
|
| 135 |
+
with os.fdopen(fd, "w") as f:
|
| 136 |
+
json.dump(data, f)
|
| 137 |
+
os.replace(tmp_path, str(json_path))
|
| 138 |
+
except BaseException:
|
| 139 |
+
try:
|
| 140 |
+
os.unlink(tmp_path)
|
| 141 |
+
except OSError:
|
| 142 |
+
pass
|
| 143 |
+
raise
|
| 144 |
+
|
| 145 |
+
def _load_device(self, device_name: str) -> dict[tuple, tuple]:
|
| 146 |
+
if device_name in self._memory:
|
| 147 |
+
return self._memory[device_name]
|
| 148 |
+
with self._lock(device_name).read_lock():
|
| 149 |
+
result = self._read_unlocked(device_name)
|
| 150 |
+
self._memory[device_name] = result
|
| 151 |
+
return result
|
| 152 |
+
|
| 153 |
+
def get(
|
| 154 |
+
self,
|
| 155 |
+
device: torch.device,
|
| 156 |
+
kernel_name: str,
|
| 157 |
+
seqlen_q: int,
|
| 158 |
+
seqlen_k: int,
|
| 159 |
+
tile_k: int,
|
| 160 |
+
is_local: bool = False,
|
| 161 |
+
qhead_per_kvhead: int = 1,
|
| 162 |
+
is_causal: bool = False,
|
| 163 |
+
pack_gqa: bool = False,
|
| 164 |
+
is_quant: bool = False,
|
| 165 |
+
) -> tuple[int, int, int, int, int] | None:
|
| 166 |
+
"""
|
| 167 |
+
Load a cached launch config for the given kernel specialization.
|
| 168 |
+
|
| 169 |
+
:param device: The device to run the kernel on
|
| 170 |
+
:param kernel_name: Name of the Triton kernel
|
| 171 |
+
:param seqlen_q: Sequence length of queries
|
| 172 |
+
:param seqlen_k: Sequence length of keys
|
| 173 |
+
:param tile_k: Tile size along the K dimension
|
| 174 |
+
:param is_local: Whether local mask is applied
|
| 175 |
+
:param qhead_per_kvhead: Ratio of query heads to key/value heads
|
| 176 |
+
:param is_causal: Whether causal mask is applied
|
| 177 |
+
:param pack_gqa: Whether GQA packing is enabled
|
| 178 |
+
:param is_quant: Whether quantization is used
|
| 179 |
+
|
| 180 |
+
:return launch_config: Tuple of (TILE_M, TILE_N, num_warps, num_stages, num_ctas) or None on cache miss
|
| 181 |
+
"""
|
| 182 |
+
device_name = torch.cuda.get_device_name(device)
|
| 183 |
+
cache = self._load_device(device_name)
|
| 184 |
+
key = (
|
| 185 |
+
kernel_name,
|
| 186 |
+
_seqlen_bucket(seqlen_q),
|
| 187 |
+
_seqlen_bucket(seqlen_k),
|
| 188 |
+
tile_k,
|
| 189 |
+
is_local,
|
| 190 |
+
qhead_per_kvhead,
|
| 191 |
+
is_causal,
|
| 192 |
+
pack_gqa,
|
| 193 |
+
is_quant,
|
| 194 |
+
)
|
| 195 |
+
return cache.get(key)
|
| 196 |
+
|
| 197 |
+
def put(
|
| 198 |
+
self,
|
| 199 |
+
device: torch.device,
|
| 200 |
+
kernel_name: str,
|
| 201 |
+
seqlen_q: int,
|
| 202 |
+
seqlen_k: int,
|
| 203 |
+
tile_k: int,
|
| 204 |
+
is_local: bool = False,
|
| 205 |
+
qhead_per_kvhead: int = 1,
|
| 206 |
+
is_causal: bool = False,
|
| 207 |
+
pack_gqa: bool = False,
|
| 208 |
+
is_quant: bool = False,
|
| 209 |
+
*,
|
| 210 |
+
config: tuple[int, int, int, int, int],
|
| 211 |
+
) -> None:
|
| 212 |
+
"""
|
| 213 |
+
Store a launch config to the per-device cache.
|
| 214 |
+
|
| 215 |
+
:param device: The device to run the kernel on
|
| 216 |
+
:param kernel_name: Name of the Triton kernel
|
| 217 |
+
:param seqlen_q: Sequence length of queries
|
| 218 |
+
:param seqlen_k: Sequence length of keys
|
| 219 |
+
:param tile_k: Tile size along the K dimension
|
| 220 |
+
:param is_local: Whether local mask is applied
|
| 221 |
+
:param qhead_per_kvhead: Ratio of query heads to key/value heads
|
| 222 |
+
:param is_causal: Whether causal mask is applied
|
| 223 |
+
:param pack_gqa: Whether GQA packing is enabled
|
| 224 |
+
:param is_quant: Whether quantization is used
|
| 225 |
+
:param config: Tuple of (TILE_M, TILE_N, num_warps, num_stages, num_ctas)
|
| 226 |
+
"""
|
| 227 |
+
device_name = torch.cuda.get_device_name(device)
|
| 228 |
+
key = (
|
| 229 |
+
kernel_name,
|
| 230 |
+
_seqlen_bucket(seqlen_q),
|
| 231 |
+
_seqlen_bucket(seqlen_k),
|
| 232 |
+
tile_k,
|
| 233 |
+
is_local,
|
| 234 |
+
qhead_per_kvhead,
|
| 235 |
+
is_causal,
|
| 236 |
+
pack_gqa,
|
| 237 |
+
is_quant,
|
| 238 |
+
)
|
| 239 |
+
# Skip disk write if memory cache already has the same config
|
| 240 |
+
mem = self._memory.get(device_name)
|
| 241 |
+
if mem is not None and mem.get(key) == config:
|
| 242 |
+
return
|
| 243 |
+
with self._lock(device_name).write_lock():
|
| 244 |
+
disk_cache = self._read_unlocked(device_name)
|
| 245 |
+
disk_cache[key] = config
|
| 246 |
+
self._write_unlocked(device_name, disk_cache)
|
| 247 |
+
self._memory[device_name] = disk_cache
|
| 248 |
+
|
| 249 |
+
def clear(self) -> None:
|
| 250 |
+
self._memory.clear()
|
| 251 |
+
|
| 252 |
+
def stats(self) -> dict:
|
| 253 |
+
total_entries = sum(len(v) for v in self._memory.values())
|
| 254 |
+
return {
|
| 255 |
+
"devices": len(self._memory),
|
| 256 |
+
"entries": total_entries,
|
| 257 |
+
"fingerprint": self._fingerprint,
|
| 258 |
+
}
|
| 259 |
+
|
| 260 |
+
|
| 261 |
+
_cache_instance: LaunchConfigCache | None = None
|
| 262 |
+
|
| 263 |
+
|
| 264 |
+
def get_launch_config_cache() -> LaunchConfigCache:
|
| 265 |
+
global _cache_instance
|
| 266 |
+
if _cache_instance is None:
|
| 267 |
+
_cache_instance = LaunchConfigCache()
|
| 268 |
+
return _cache_instance
|
| 269 |
+
|
| 270 |
+
|
| 271 |
+
def load_launch_config(
|
| 272 |
+
device: torch.device,
|
| 273 |
+
kernel_name: str,
|
| 274 |
+
seqlen_q: int,
|
| 275 |
+
seqlen_k: int,
|
| 276 |
+
tile_k: int,
|
| 277 |
+
is_local: bool = False,
|
| 278 |
+
qhead_per_kvhead: int = 1,
|
| 279 |
+
is_causal: bool = False,
|
| 280 |
+
pack_gqa: bool = False,
|
| 281 |
+
is_quant: bool = False,
|
| 282 |
+
) -> tuple[int, int, int, int, int] | None:
|
| 283 |
+
"""
|
| 284 |
+
Load cached launch config for a kernel specialization.
|
| 285 |
+
|
| 286 |
+
:param device: The device to run the kernel on
|
| 287 |
+
:param kernel_name: Name of the Triton kernel
|
| 288 |
+
:param seqlen_q: Sequence length of queries
|
| 289 |
+
:param seqlen_k: Sequence length of keys
|
| 290 |
+
:param tile_k: Tile size along the K dimension
|
| 291 |
+
:param is_local: Whether local mask is applied
|
| 292 |
+
:param qhead_per_kvhead: Ratio of query heads to key/value heads
|
| 293 |
+
:param is_causal: Whether causal mask is applied
|
| 294 |
+
:param pack_gqa: Whether GQA packing is enabled
|
| 295 |
+
:param is_quant: Whether quantization is used
|
| 296 |
+
|
| 297 |
+
:return launch_config: Tuple of (TILE_M, TILE_N, num_warps, num_stages, num_ctas) or None on cache miss
|
| 298 |
+
"""
|
| 299 |
+
return get_launch_config_cache().get(
|
| 300 |
+
device,
|
| 301 |
+
kernel_name,
|
| 302 |
+
seqlen_q,
|
| 303 |
+
seqlen_k,
|
| 304 |
+
tile_k,
|
| 305 |
+
is_local,
|
| 306 |
+
qhead_per_kvhead,
|
| 307 |
+
is_causal,
|
| 308 |
+
pack_gqa,
|
| 309 |
+
is_quant,
|
| 310 |
+
)
|
| 311 |
+
|
| 312 |
+
|
| 313 |
+
def store_launch_config(
|
| 314 |
+
device: torch.device,
|
| 315 |
+
kernel_name: str,
|
| 316 |
+
seqlen_q: int,
|
| 317 |
+
seqlen_k: int,
|
| 318 |
+
tile_k: int,
|
| 319 |
+
*,
|
| 320 |
+
config: tuple[int, int, int, int, int],
|
| 321 |
+
is_local: bool = False,
|
| 322 |
+
qhead_per_kvhead: int = 1,
|
| 323 |
+
is_causal: bool = False,
|
| 324 |
+
pack_gqa: bool = False,
|
| 325 |
+
is_quant: bool = False,
|
| 326 |
+
) -> None:
|
| 327 |
+
"""
|
| 328 |
+
Store a launch config to the per-device JSON cache.
|
| 329 |
+
|
| 330 |
+
:param device: The device to run the kernel on
|
| 331 |
+
:param kernel_name: Name of the Triton kernel
|
| 332 |
+
:param seqlen_q: Sequence length of queries
|
| 333 |
+
:param seqlen_k: Sequence length of keys
|
| 334 |
+
:param tile_k: Tile size along the K dimension
|
| 335 |
+
:param config: Tuple of (TILE_M, TILE_N, num_warps, num_stages, num_ctas)
|
| 336 |
+
:param is_local: Whether local mask is applied
|
| 337 |
+
:param qhead_per_kvhead: Ratio of query heads to key/value heads
|
| 338 |
+
:param is_causal: Whether causal mask is applied
|
| 339 |
+
:param pack_gqa: Whether GQA packing is enabled
|
| 340 |
+
:param is_quant: Whether quantization is used
|
| 341 |
+
"""
|
| 342 |
+
get_launch_config_cache().put(
|
| 343 |
+
device,
|
| 344 |
+
kernel_name,
|
| 345 |
+
seqlen_q,
|
| 346 |
+
seqlen_k,
|
| 347 |
+
tile_k,
|
| 348 |
+
is_local,
|
| 349 |
+
qhead_per_kvhead,
|
| 350 |
+
is_causal,
|
| 351 |
+
pack_gqa,
|
| 352 |
+
is_quant,
|
| 353 |
+
config=config,
|
| 354 |
+
)
|
| 355 |
+
|
| 356 |
+
|
| 357 |
+
def extract_best_config(autotuned_kernel) -> tuple[int, int, int, int, int] | None:
|
| 358 |
+
"""
|
| 359 |
+
Extract best config from a Triton autotuned kernel after it has run.
|
| 360 |
+
|
| 361 |
+
:param autotuned_kernel: A Triton autotuned kernel instance
|
| 362 |
+
|
| 363 |
+
:return launch_config: Tuple of (TILE_M, TILE_N, num_warps, num_stages, num_ctas) or None if no best config found
|
| 364 |
+
"""
|
| 365 |
+
raw = getattr(autotuned_kernel, "_autotuned", autotuned_kernel)
|
| 366 |
+
best = getattr(raw, "best_config", None)
|
| 367 |
+
if best is None:
|
| 368 |
+
return None
|
| 369 |
+
return (
|
| 370 |
+
best.kwargs.get("TILE_M", 64),
|
| 371 |
+
best.kwargs.get("TILE_N", 64),
|
| 372 |
+
best.num_warps,
|
| 373 |
+
best.num_stages,
|
| 374 |
+
getattr(best, "num_ctas", 1),
|
| 375 |
+
)
|
| 376 |
+
|
| 377 |
+
|
| 378 |
+
def get_fwd_combine_launch_config(
|
| 379 |
+
tile_k: int,
|
| 380 |
+
device: torch.device,
|
| 381 |
+
arch: int,
|
| 382 |
+
) -> tuple[int, int, int, int]:
|
| 383 |
+
"""
|
| 384 |
+
Get launch configuration for forward combine kernel based on input parameters and device architecture.
|
| 385 |
+
|
| 386 |
+
:param tile_k: Tile size along the K dimension
|
| 387 |
+
:param device: The device to run the kernel on
|
| 388 |
+
:param arch: The architecture of the device
|
| 389 |
+
|
| 390 |
+
:return launch_config: Tuple of (tile_m, num_warps, num_stages, num_ctas) for launching the kernel
|
| 391 |
+
"""
|
| 392 |
+
if arch == -1:
|
| 393 |
+
raise NotImplementedError(f"Unsupported device: {device} with arch {arch}")
|
| 394 |
+
|
| 395 |
+
if device.type == "cuda":
|
| 396 |
+
if arch // 10 == 8:
|
| 397 |
+
tile_m = 4 if tile_k % 128 == 0 else (8 if tile_k % 64 == 0 else 16)
|
| 398 |
+
return (tile_m, 4, 1, 1)
|
| 399 |
+
elif arch // 10 == 9:
|
| 400 |
+
tile_m = 8 if tile_k % 128 == 0 else (16 if tile_k % 64 == 0 else 32)
|
| 401 |
+
return (tile_m, 4, 1, 1)
|
| 402 |
+
elif arch // 10 == 10:
|
| 403 |
+
tile_m = 16 if tile_k % 128 == 0 else (32 if tile_k % 64 == 0 else 64)
|
| 404 |
+
return (tile_m, 4, 1, 1)
|
| 405 |
+
elif arch // 10 == 12:
|
| 406 |
+
tile_m = 4 if tile_k % 128 == 0 else (8 if tile_k % 64 == 0 else 16)
|
| 407 |
+
return (tile_m, 4, 1, 1)
|
| 408 |
+
else:
|
| 409 |
+
raise NotImplementedError(f"Unsupported CUDA architecture: {arch}")
|
| 410 |
+
else:
|
| 411 |
+
raise NotImplementedError(f"Unsupported device type: {device.type}")
|
| 412 |
+
|
| 413 |
+
|
| 414 |
+
get_fwd_combine_launch_config = cache_utils.cache_launch_config(
|
| 415 |
+
get_fwd_combine_launch_config
|
| 416 |
+
)
|
| 417 |
+
|
| 418 |
+
|
| 419 |
+
def get_dec_combine_launch_config(
|
| 420 |
+
tile_k: int,
|
| 421 |
+
device: torch.device,
|
| 422 |
+
arch: int,
|
| 423 |
+
) -> tuple[int, int, int]:
|
| 424 |
+
"""
|
| 425 |
+
Get launch configuration for decode combine kernel based on input parameters and device architecture.
|
| 426 |
+
|
| 427 |
+
:param tile_k: Tile size along the K dimension
|
| 428 |
+
:param device: The device to run the kernel on
|
| 429 |
+
:param arch: The architecture of the device
|
| 430 |
+
|
| 431 |
+
:return launch_config: Tuple of (num_warps, num_stages, num_ctas) for launching the kernel
|
| 432 |
+
"""
|
| 433 |
+
if arch == -1:
|
| 434 |
+
raise NotImplementedError(f"Unsupported device: {device} with arch {arch}")
|
| 435 |
+
|
| 436 |
+
if device.type == "cuda":
|
| 437 |
+
if arch // 10 == 8:
|
| 438 |
+
num_stages = max(min(8, 512 // tile_k), 1)
|
| 439 |
+
return (4, num_stages, 1)
|
| 440 |
+
elif arch // 10 == 9:
|
| 441 |
+
num_stages = max(min(8, 512 // tile_k), 1)
|
| 442 |
+
return (4, num_stages, 1)
|
| 443 |
+
elif arch // 10 == 10:
|
| 444 |
+
num_stages = max(min(16, 512 // tile_k), 1)
|
| 445 |
+
return (4, num_stages, 1)
|
| 446 |
+
elif arch // 10 == 12:
|
| 447 |
+
num_stages = max(min(4, 512 // tile_k), 1)
|
| 448 |
+
return (4, num_stages, 1)
|
| 449 |
+
else:
|
| 450 |
+
raise NotImplementedError(f"Unsupported CUDA architecture: {arch}")
|
| 451 |
+
else:
|
| 452 |
+
raise NotImplementedError(f"Unsupported device type: {device.type}")
|
| 453 |
+
|
| 454 |
+
|
| 455 |
+
get_dec_combine_launch_config = cache_utils.cache_launch_config(
|
| 456 |
+
get_dec_combine_launch_config
|
| 457 |
+
)
|
build/torch-cuda/mask.py
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import triton
|
| 2 |
+
import triton.language as tl
|
| 3 |
+
|
| 4 |
+
|
| 5 |
+
@triton.jit
|
| 6 |
+
def apply_mask(
|
| 7 |
+
acc_s,
|
| 8 |
+
m_block,
|
| 9 |
+
n_block,
|
| 10 |
+
seqlen_q,
|
| 11 |
+
seqlen_k,
|
| 12 |
+
window_size_sink,
|
| 13 |
+
window_size_left,
|
| 14 |
+
window_size_right,
|
| 15 |
+
window_size_dist,
|
| 16 |
+
MASK_SEQLEN: tl.constexpr,
|
| 17 |
+
MASK_CAUSAL: tl.constexpr,
|
| 18 |
+
MASK_LOCAL: tl.constexpr,
|
| 19 |
+
MASK_SINK: tl.constexpr,
|
| 20 |
+
TILE_M: tl.constexpr,
|
| 21 |
+
TILE_N: tl.constexpr,
|
| 22 |
+
QHEAD_PER_KVHEAD_PACKGQA: tl.constexpr,
|
| 23 |
+
SWAP_AB: tl.constexpr,
|
| 24 |
+
):
|
| 25 |
+
"""
|
| 26 |
+
Apply seqlen, causal, and local masks to the attention scores.
|
| 27 |
+
|
| 28 |
+
:param acc_s: Attention scores tensor of shape [BLOCK_M, BLOCK_N].
|
| 29 |
+
:param m_block: Current block index along the M dimension.
|
| 30 |
+
:param n_block: Current block index along the N dimension.
|
| 31 |
+
:param seqlen_q: The sequence length of the query.
|
| 32 |
+
:param seqlen_k: The sequence length of the key.
|
| 33 |
+
:param window_size_sink: Prefix sink token count.
|
| 34 |
+
:param window_size_left: Distant local band token count.
|
| 35 |
+
:param window_size_right: Gap token count after the near-diagonal window before the distant band.
|
| 36 |
+
:param window_size_dist: Near-diagonal local token count.
|
| 37 |
+
:param MASK_SEQLEN: Boolean flag indicating if seqlen masking should be applied.
|
| 38 |
+
:param MASK_CAUSAL: Boolean flag indicating if causal masking should be applied.
|
| 39 |
+
:param MASK_LOCAL: Boolean flag indicating if local masking should be applied.
|
| 40 |
+
:param MASK_SINK: Boolean flag indicating if sink masking should be applied.
|
| 41 |
+
:param TILE_M: Tile size along the M dimension.
|
| 42 |
+
:param TILE_N: Tile size along the N dimension.
|
| 43 |
+
:param QHEAD_PER_KVHEAD_PACKGQA: Ratio of query heads to key/value heads for packed GQA.
|
| 44 |
+
:param SWAP_AB: Boolean flag indicating if query and key dimensions are swapped.
|
| 45 |
+
|
| 46 |
+
:return acc_s: Masked attention scores tensor of shape [BLOCK_M, BLOCK_N].
|
| 47 |
+
"""
|
| 48 |
+
offs_m = m_block * TILE_M + tl.arange(0, TILE_M)
|
| 49 |
+
offs_n = n_block * TILE_N + tl.arange(0, TILE_N)
|
| 50 |
+
|
| 51 |
+
if SWAP_AB:
|
| 52 |
+
tl.static_assert(
|
| 53 |
+
QHEAD_PER_KVHEAD_PACKGQA == 1, "SWAP_AB with PACKGQA > 1 not supported"
|
| 54 |
+
)
|
| 55 |
+
q_idx = offs_m[None, :]
|
| 56 |
+
k_idx = offs_n[:, None]
|
| 57 |
+
else:
|
| 58 |
+
q_idx = offs_m[:, None]
|
| 59 |
+
k_idx = offs_n[None, :]
|
| 60 |
+
if QHEAD_PER_KVHEAD_PACKGQA > 1:
|
| 61 |
+
q_idx = q_idx // QHEAD_PER_KVHEAD_PACKGQA
|
| 62 |
+
|
| 63 |
+
if MASK_SEQLEN:
|
| 64 |
+
acc_s = tl.where(
|
| 65 |
+
(k_idx < seqlen_k) & (q_idx < seqlen_q),
|
| 66 |
+
acc_s,
|
| 67 |
+
float("-inf"),
|
| 68 |
+
)
|
| 69 |
+
|
| 70 |
+
if MASK_CAUSAL or MASK_LOCAL or MASK_SINK:
|
| 71 |
+
causal_offset = seqlen_k - seqlen_q
|
| 72 |
+
dist = q_idx + causal_offset - k_idx
|
| 73 |
+
|
| 74 |
+
if MASK_LOCAL or MASK_SINK:
|
| 75 |
+
allowed = (dist >= 0) & (dist < 0)
|
| 76 |
+
if MASK_LOCAL:
|
| 77 |
+
allowed = allowed | ((dist >= 0) & (dist < window_size_dist))
|
| 78 |
+
allowed = allowed | (
|
| 79 |
+
(dist >= window_size_dist + window_size_right)
|
| 80 |
+
& (dist < window_size_dist + window_size_right + window_size_left)
|
| 81 |
+
)
|
| 82 |
+
if MASK_SINK:
|
| 83 |
+
allowed = allowed | ((k_idx < window_size_sink) & (dist >= 0))
|
| 84 |
+
acc_s = tl.where(
|
| 85 |
+
allowed,
|
| 86 |
+
acc_s,
|
| 87 |
+
float("-inf"),
|
| 88 |
+
)
|
| 89 |
+
elif MASK_CAUSAL:
|
| 90 |
+
acc_s = tl.where(
|
| 91 |
+
dist >= 0,
|
| 92 |
+
acc_s,
|
| 93 |
+
float("-inf"),
|
| 94 |
+
)
|
| 95 |
+
else:
|
| 96 |
+
acc_s = tl.where(
|
| 97 |
+
((dist >= 0) & (dist < window_size_dist))
|
| 98 |
+
| (
|
| 99 |
+
(dist >= window_size_dist + window_size_right)
|
| 100 |
+
& (dist < window_size_dist + window_size_right + window_size_left)
|
| 101 |
+
),
|
| 102 |
+
acc_s,
|
| 103 |
+
float("-inf"),
|
| 104 |
+
)
|
| 105 |
+
|
| 106 |
+
return acc_s
|
build/torch-cuda/metadata.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"name": "flash-sparse-attention",
|
| 3 |
+
"id": "_flash_sparse_attention_cuda_7db628a_dirty",
|
| 4 |
+
"version": 1,
|
| 5 |
+
"license": "BSD-3-Clause",
|
| 6 |
+
"python-depends": [],
|
| 7 |
+
"backend": {
|
| 8 |
+
"type": "cuda"
|
| 9 |
+
},
|
| 10 |
+
"digest": {
|
| 11 |
+
"algorithm": "sha256",
|
| 12 |
+
"files": {
|
| 13 |
+
"__init__.py": "OXYzVpCpaU4x34L+qzPOSlycSDR+2i/CsF04RT/tBL4=",
|
| 14 |
+
"_ops.py": "Ds3MXWORK2pW+eBqjHYPr5gUiUF/1eJs3P2ryUEnuH4=",
|
| 15 |
+
"activations.py": "bAa6rHqI0noI/8zSGNrY2uzM5VHQmnsS4d2b+V0e5eU=",
|
| 16 |
+
"assert_inputs.py": "eLNN5h2Qhq3hdg2nnSSOAqLUqMZcYW2QqFvPo1FGmLg=",
|
| 17 |
+
"autotuner.py": "rpAY5s+/E6gSau/zZrNJjVOmvteEHsOjDNX/qAsc1Z0=",
|
| 18 |
+
"block_info.py": "JCm1Pp0jCHJOFGpwK6eXdiNhr2ZospuXx8bAX7Bez1E=",
|
| 19 |
+
"cache_utils.py": "+PzgjtFoef+lFNqrVvst06iOLmkTOXeW/y95kFOEi64=",
|
| 20 |
+
"flash_bwd_postprocess.py": "HqYiH4lrwEph+EifxSJqG3jACZLASSBUcuHGq/2ZgyM=",
|
| 21 |
+
"flash_bwd_preprocess.py": "A5/CJhk0Zg6mMb0PU4Lk9mwjDhr3/WaxlFaBtNOhBac=",
|
| 22 |
+
"flash_dec_combine.py": "7ZHMacWY+QPPjgSezKmQzhogibUZfb7abIwQW+2U6ak=",
|
| 23 |
+
"flash_dense_bwd.py": "sSRrZs5y0VB5Ga/9KQCAmuYaKwxeoVJfhwrumzby7xw=",
|
| 24 |
+
"flash_dense_dec.py": "9/JfVxX+4hz3Bq/4liJih6pDwjEZiOyHAZeUYYlMyjM=",
|
| 25 |
+
"flash_dense_fwd.py": "ydJnTHmzMX52WOyGilqNf7pj+6qimeCjc6NmSDJs9BY=",
|
| 26 |
+
"flash_fwd_combine.py": "8iYebjXMgL3fWzR1hiA6/PNLVLjCnze6PmgrI+D/cOA=",
|
| 27 |
+
"flash_gated_bwd.py": "i9+fAUdp0xxpk7A/V6KnLwMVThdrOlFpaWwurQDXyDU=",
|
| 28 |
+
"flash_gated_dec.py": "fLJHd8ii4lV5SCcLYvuDwZVT3Q+x7ANgsQ3xojZgHrw=",
|
| 29 |
+
"flash_gated_fwd.py": "W/9zB7NdC8Ap/dNuMYSNGMMaTxpyQTeyUk2AAKL8vvU=",
|
| 30 |
+
"flash_proj_bwd.py": "a+CpQe6DsiNeVZlq+AJDTjdSeqquUAhege37vrvckDo=",
|
| 31 |
+
"flash_proj_fwd.py": "TVuL7nkXcTi+R5j1tTd5gwFPWl1ddbvnupER5QhUQtY=",
|
| 32 |
+
"flash_sparse_attention/__init__.py": "DFYPlrhXwYjEqCl/8n0SmWGZV8NFml5DPhMjKfv98GY=",
|
| 33 |
+
"flash_sparse_bwd.py": "sPh3T1XiomcbSHb/+fUvYZBYgWZNCuwMJW5UoabQUPE=",
|
| 34 |
+
"flash_sparse_dec.py": "yqOVdw3aVkSoD1aDtqng1bmNn3eWQkTLphVRm+3vBOA=",
|
| 35 |
+
"flash_sparse_fwd.py": "7cUeJrayY/yu/0+FpTqG2foJVfdweaq7KuZsge7/rno=",
|
| 36 |
+
"interface.py": "0b2Ya0TXU/6QS5plzsK6FOStUJNGl49fH7EsspTMkoo=",
|
| 37 |
+
"kernel_repr.py": "GdLRDHO7bMvfWt52vCDqO9HLxQeaK7/sr3iMeeSTJnA=",
|
| 38 |
+
"launch_grid.py": "NKGkqIro3SDRikGl1C7Om0/2DVH0eN8ecepkd6h8A98=",
|
| 39 |
+
"launch_template.py": "CbrryDz3BPY8zpWcTo6hNpI0DKfW16BkY66f0oNNAYs=",
|
| 40 |
+
"mask.py": "aF/KiRajGsdsLdQllFO99NBrOQeTjCPK9aCIunxen/0=",
|
| 41 |
+
"quant.py": "B47A0bWsIb5E5dynB0NrcJjg1l1F/rj/7y6yVXIWrNI=",
|
| 42 |
+
"scheduler.py": "4sgkUQyAuijIyfIk88/kyD4a+dI2LWeptB28Pfe2nko=",
|
| 43 |
+
"seqlen_info.py": "BCd/B9qu9DfS2+VxX9uBdh8qxrdDYgjJzwfp7Q7i6Bc=",
|
| 44 |
+
"utils.py": "g8U91hmiF+yFwiGEVpYzq1k1CMH+3sIrHq0nrOaNrMI="
|
| 45 |
+
}
|
| 46 |
+
}
|
| 47 |
+
}
|
build/torch-cuda/quant.py
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
import triton
|
| 3 |
+
import triton.language as tl
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
@triton.jit
|
| 7 |
+
def _amax_kernel(
|
| 8 |
+
X,
|
| 9 |
+
Amax,
|
| 10 |
+
n_elements,
|
| 11 |
+
BLOCK: tl.constexpr,
|
| 12 |
+
):
|
| 13 |
+
pid = tl.program_id(0)
|
| 14 |
+
offsets = pid * BLOCK + tl.arange(0, BLOCK)
|
| 15 |
+
mask = offsets < n_elements
|
| 16 |
+
|
| 17 |
+
x = tl.load(X + offsets, mask=mask, other=0.0, cache_modifier=".cg")
|
| 18 |
+
local_amax = tl.max(tl.abs(x))
|
| 19 |
+
tl.atomic_max(Amax, local_amax, sem="relaxed")
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
@triton.jit
|
| 23 |
+
def _quantize_fp8_kernel(
|
| 24 |
+
X,
|
| 25 |
+
Out,
|
| 26 |
+
Scale,
|
| 27 |
+
n_elements,
|
| 28 |
+
BLOCK: tl.constexpr,
|
| 29 |
+
FP8_DTYPE: tl.constexpr,
|
| 30 |
+
):
|
| 31 |
+
pid = tl.program_id(0)
|
| 32 |
+
offsets = pid * BLOCK + tl.arange(0, BLOCK)
|
| 33 |
+
mask = offsets < n_elements
|
| 34 |
+
|
| 35 |
+
x = tl.load(X + offsets, mask=mask, other=0.0, cache_modifier=".cg")
|
| 36 |
+
scale = tl.load(Scale)
|
| 37 |
+
|
| 38 |
+
x_fp8 = (x * scale).to(FP8_DTYPE)
|
| 39 |
+
tl.store(Out + offsets, x_fp8, mask=mask)
|
| 40 |
+
|
| 41 |
+
|
| 42 |
+
def quantize_fp8(x: torch.Tensor, dtype: torch.dtype = torch.float8_e5m2):
|
| 43 |
+
assert x.is_cuda and x.dtype in (torch.float16, torch.bfloat16, torch.float32)
|
| 44 |
+
assert dtype in (torch.float8_e5m2, torch.float8_e4m3fn)
|
| 45 |
+
|
| 46 |
+
tl_dtype = tl.float8e5 if dtype == torch.float8_e5m2 else tl.float8e4nv
|
| 47 |
+
FP8_MAX = 57344.0 if dtype == torch.float8_e5m2 else 448.0
|
| 48 |
+
|
| 49 |
+
n_elements = x.numel()
|
| 50 |
+
amax = torch.zeros(1, device=x.device, dtype=torch.float32)
|
| 51 |
+
x_fp8 = torch.empty_like(x, dtype=dtype)
|
| 52 |
+
|
| 53 |
+
BLOCK = 512
|
| 54 |
+
|
| 55 |
+
def grid(META):
|
| 56 |
+
return (triton.cdiv(n_elements, META["BLOCK"]),)
|
| 57 |
+
|
| 58 |
+
_amax_kernel[grid](x, amax, n_elements, BLOCK=BLOCK)
|
| 59 |
+
|
| 60 |
+
scale = FP8_MAX / amax
|
| 61 |
+
|
| 62 |
+
_quantize_fp8_kernel[grid](
|
| 63 |
+
x, x_fp8, scale, n_elements, BLOCK=BLOCK, FP8_DTYPE=tl_dtype
|
| 64 |
+
)
|
| 65 |
+
|
| 66 |
+
return x_fp8, (amax / FP8_MAX).to(x.dtype)
|
build/torch-cuda/scheduler.py
ADDED
|
@@ -0,0 +1,2609 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import triton
|
| 2 |
+
import triton.language as tl
|
| 3 |
+
from triton.language.core import _aggregate as aggregate
|
| 4 |
+
from triton.runtime.jit import constexpr_function
|
| 5 |
+
|
| 6 |
+
from . import mask
|
| 7 |
+
from . import activations
|
| 8 |
+
from . import seqlen_info, block_info
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
@aggregate
|
| 12 |
+
class AttnFwdGridIndex:
|
| 13 |
+
m_block: tl.tensor
|
| 14 |
+
batch_idx: tl.tensor
|
| 15 |
+
head_idx: tl.tensor
|
| 16 |
+
head_kv_idx: tl.tensor
|
| 17 |
+
split_idx: tl.tensor
|
| 18 |
+
|
| 19 |
+
@constexpr_function
|
| 20 |
+
def __init__(self, m_block, batch_idx, head_idx, head_kv_idx, split_idx):
|
| 21 |
+
self.m_block = m_block
|
| 22 |
+
self.batch_idx = batch_idx
|
| 23 |
+
self.head_idx = head_idx
|
| 24 |
+
self.head_kv_idx = head_kv_idx
|
| 25 |
+
self.split_idx = split_idx
|
| 26 |
+
|
| 27 |
+
@staticmethod
|
| 28 |
+
@triton.jit
|
| 29 |
+
def create(
|
| 30 |
+
num_splits,
|
| 31 |
+
QHEAD_PER_KVHEAD: tl.constexpr,
|
| 32 |
+
IS_SPLIT_KV: tl.constexpr,
|
| 33 |
+
PACK_GQA: tl.constexpr,
|
| 34 |
+
):
|
| 35 |
+
m_block = tl.program_id(0)
|
| 36 |
+
head_idx = tl.program_id(1)
|
| 37 |
+
batch_split_idx = tl.program_id(2)
|
| 38 |
+
if IS_SPLIT_KV:
|
| 39 |
+
batch_idx = batch_split_idx // num_splits
|
| 40 |
+
split_idx = batch_split_idx - batch_idx * num_splits
|
| 41 |
+
else:
|
| 42 |
+
batch_idx = batch_split_idx
|
| 43 |
+
split_idx = 0
|
| 44 |
+
if PACK_GQA:
|
| 45 |
+
head_kv_idx = head_idx
|
| 46 |
+
else:
|
| 47 |
+
head_kv_idx = head_idx // QHEAD_PER_KVHEAD
|
| 48 |
+
return AttnFwdGridIndex(m_block, batch_idx, head_idx, head_kv_idx, split_idx)
|
| 49 |
+
|
| 50 |
+
@triton.jit
|
| 51 |
+
def load_window_sizes(self, window_sizes, stride_wh, IS_LOCAL: tl.constexpr):
|
| 52 |
+
if IS_LOCAL:
|
| 53 |
+
window_size_sink = tl.load(window_sizes + self.head_kv_idx * stride_wh)
|
| 54 |
+
window_size_left = tl.load(window_sizes + self.head_kv_idx * stride_wh + 1)
|
| 55 |
+
window_size_right = tl.load(window_sizes + self.head_kv_idx * stride_wh + 2)
|
| 56 |
+
window_size_dist = tl.load(window_sizes + self.head_kv_idx * stride_wh + 3)
|
| 57 |
+
else:
|
| 58 |
+
window_size_sink = 0
|
| 59 |
+
window_size_left = 0
|
| 60 |
+
window_size_right = 0
|
| 61 |
+
window_size_dist = 0
|
| 62 |
+
return window_size_sink, window_size_left, window_size_right, window_size_dist
|
| 63 |
+
|
| 64 |
+
|
| 65 |
+
@aggregate
|
| 66 |
+
class AttnBwdGridIndex:
|
| 67 |
+
n_block: tl.tensor
|
| 68 |
+
batch_idx: tl.tensor
|
| 69 |
+
head_idx: tl.tensor
|
| 70 |
+
head_kv_idx: tl.tensor
|
| 71 |
+
split_idx: tl.tensor
|
| 72 |
+
|
| 73 |
+
@constexpr_function
|
| 74 |
+
def __init__(self, n_block, batch_idx, head_idx, head_kv_idx, split_idx):
|
| 75 |
+
self.n_block = n_block
|
| 76 |
+
self.batch_idx = batch_idx
|
| 77 |
+
self.head_idx = head_idx
|
| 78 |
+
self.head_kv_idx = head_kv_idx
|
| 79 |
+
self.split_idx = split_idx
|
| 80 |
+
|
| 81 |
+
@staticmethod
|
| 82 |
+
@triton.jit
|
| 83 |
+
def create(
|
| 84 |
+
num_splits,
|
| 85 |
+
QHEAD_PER_KVHEAD: tl.constexpr,
|
| 86 |
+
IS_SPLIT_QO: tl.constexpr,
|
| 87 |
+
):
|
| 88 |
+
n_block = tl.program_id(0)
|
| 89 |
+
head_idx = tl.program_id(1)
|
| 90 |
+
batch_split_idx = tl.program_id(2)
|
| 91 |
+
if IS_SPLIT_QO:
|
| 92 |
+
batch_idx = batch_split_idx // num_splits
|
| 93 |
+
split_idx = batch_split_idx - batch_idx * num_splits
|
| 94 |
+
else:
|
| 95 |
+
batch_idx = batch_split_idx
|
| 96 |
+
split_idx = 0
|
| 97 |
+
head_kv_idx = head_idx // QHEAD_PER_KVHEAD
|
| 98 |
+
return AttnBwdGridIndex(n_block, batch_idx, head_idx, head_kv_idx, split_idx)
|
| 99 |
+
|
| 100 |
+
@triton.jit
|
| 101 |
+
def load_window_sizes(self, window_sizes, stride_wh, IS_LOCAL: tl.constexpr):
|
| 102 |
+
if IS_LOCAL:
|
| 103 |
+
window_size_sink = tl.load(window_sizes + self.head_kv_idx * stride_wh)
|
| 104 |
+
window_size_left = tl.load(window_sizes + self.head_kv_idx * stride_wh + 1)
|
| 105 |
+
window_size_right = tl.load(window_sizes + self.head_kv_idx * stride_wh + 2)
|
| 106 |
+
window_size_dist = tl.load(window_sizes + self.head_kv_idx * stride_wh + 3)
|
| 107 |
+
else:
|
| 108 |
+
window_size_sink = 0
|
| 109 |
+
window_size_left = 0
|
| 110 |
+
window_size_right = 0
|
| 111 |
+
window_size_dist = 0
|
| 112 |
+
return window_size_sink, window_size_left, window_size_right, window_size_dist
|
| 113 |
+
|
| 114 |
+
|
| 115 |
+
@aggregate
|
| 116 |
+
class AttnDecGridIndex:
|
| 117 |
+
batch_idx: tl.tensor
|
| 118 |
+
head_idx: tl.tensor
|
| 119 |
+
head_kv_idx: tl.tensor
|
| 120 |
+
split_idx: tl.tensor
|
| 121 |
+
|
| 122 |
+
@constexpr_function
|
| 123 |
+
def __init__(self, batch_idx, head_idx, head_kv_idx, split_idx):
|
| 124 |
+
self.batch_idx = batch_idx
|
| 125 |
+
self.head_idx = head_idx
|
| 126 |
+
self.head_kv_idx = head_kv_idx
|
| 127 |
+
self.split_idx = split_idx
|
| 128 |
+
|
| 129 |
+
@staticmethod
|
| 130 |
+
@triton.jit
|
| 131 |
+
def create(
|
| 132 |
+
num_splits,
|
| 133 |
+
):
|
| 134 |
+
head_idx = tl.program_id(0)
|
| 135 |
+
batch_split_idx = tl.program_id(1)
|
| 136 |
+
batch_idx = batch_split_idx // num_splits
|
| 137 |
+
split_idx = batch_split_idx - batch_idx * num_splits
|
| 138 |
+
head_kv_idx = head_idx
|
| 139 |
+
return AttnDecGridIndex(batch_idx, head_idx, head_kv_idx, split_idx)
|
| 140 |
+
|
| 141 |
+
@triton.jit
|
| 142 |
+
def load_window_sizes(self, window_sizes, stride_wh, IS_LOCAL: tl.constexpr):
|
| 143 |
+
if IS_LOCAL:
|
| 144 |
+
window_size_sink = tl.load(window_sizes + self.head_kv_idx * stride_wh)
|
| 145 |
+
window_size_left = tl.load(window_sizes + self.head_kv_idx * stride_wh + 1)
|
| 146 |
+
window_size_right = tl.load(window_sizes + self.head_kv_idx * stride_wh + 2)
|
| 147 |
+
window_size_dist = tl.load(window_sizes + self.head_kv_idx * stride_wh + 3)
|
| 148 |
+
else:
|
| 149 |
+
window_size_sink = 0
|
| 150 |
+
window_size_left = 0
|
| 151 |
+
window_size_right = 0
|
| 152 |
+
window_size_dist = 0
|
| 153 |
+
return window_size_sink, window_size_left, window_size_right, window_size_dist
|
| 154 |
+
|
| 155 |
+
|
| 156 |
+
@aggregate
|
| 157 |
+
class AttnFwdConfig:
|
| 158 |
+
softmax_scale_log2: tl.tensor
|
| 159 |
+
softmax_threshold_log2: tl.tensor
|
| 160 |
+
gate_threshold_log2: tl.tensor
|
| 161 |
+
value_scale: tl.tensor
|
| 162 |
+
m_block: tl.tensor
|
| 163 |
+
actual_seqlen_q: tl.tensor
|
| 164 |
+
actual_seqlen_k: tl.tensor
|
| 165 |
+
offset_q: tl.tensor
|
| 166 |
+
offset_k: tl.tensor
|
| 167 |
+
padded_offset_q: tl.tensor
|
| 168 |
+
padded_offset_k: tl.tensor
|
| 169 |
+
window_size_sink: tl.tensor
|
| 170 |
+
window_size_left: tl.tensor
|
| 171 |
+
window_size_right: tl.tensor
|
| 172 |
+
window_size_dist: tl.tensor
|
| 173 |
+
head_dim: tl.tensor
|
| 174 |
+
PACK_GQA: tl.constexpr
|
| 175 |
+
QHEAD_PER_KVHEAD_PACKGQA: tl.constexpr
|
| 176 |
+
TILE_M: tl.constexpr
|
| 177 |
+
TILE_N: tl.constexpr
|
| 178 |
+
TILE_K: tl.constexpr
|
| 179 |
+
IS_LOGSIGMOID_GATE: tl.constexpr
|
| 180 |
+
|
| 181 |
+
@constexpr_function
|
| 182 |
+
def __init__(
|
| 183 |
+
self,
|
| 184 |
+
softmax_scale_log2,
|
| 185 |
+
softmax_threshold_log2,
|
| 186 |
+
gate_threshold_log2,
|
| 187 |
+
value_scale,
|
| 188 |
+
m_block,
|
| 189 |
+
actual_seqlen_q,
|
| 190 |
+
actual_seqlen_k,
|
| 191 |
+
offset_q,
|
| 192 |
+
offset_k,
|
| 193 |
+
padded_offset_q,
|
| 194 |
+
padded_offset_k,
|
| 195 |
+
window_size_sink,
|
| 196 |
+
window_size_left,
|
| 197 |
+
window_size_right,
|
| 198 |
+
window_size_dist,
|
| 199 |
+
head_dim,
|
| 200 |
+
PACK_GQA,
|
| 201 |
+
QHEAD_PER_KVHEAD_PACKGQA,
|
| 202 |
+
TILE_M,
|
| 203 |
+
TILE_N,
|
| 204 |
+
TILE_K,
|
| 205 |
+
IS_LOGSIGMOID_GATE,
|
| 206 |
+
):
|
| 207 |
+
self.softmax_scale_log2 = softmax_scale_log2
|
| 208 |
+
self.softmax_threshold_log2 = softmax_threshold_log2
|
| 209 |
+
self.gate_threshold_log2 = gate_threshold_log2
|
| 210 |
+
self.value_scale = value_scale
|
| 211 |
+
self.m_block = m_block
|
| 212 |
+
self.actual_seqlen_q = actual_seqlen_q
|
| 213 |
+
self.actual_seqlen_k = actual_seqlen_k
|
| 214 |
+
self.offset_q = offset_q
|
| 215 |
+
self.offset_k = offset_k
|
| 216 |
+
self.padded_offset_q = padded_offset_q
|
| 217 |
+
self.padded_offset_k = padded_offset_k
|
| 218 |
+
self.window_size_sink = window_size_sink
|
| 219 |
+
self.window_size_left = window_size_left
|
| 220 |
+
self.window_size_right = window_size_right
|
| 221 |
+
self.window_size_dist = window_size_dist
|
| 222 |
+
self.head_dim = head_dim
|
| 223 |
+
self.PACK_GQA = tl.constexpr(PACK_GQA)
|
| 224 |
+
self.QHEAD_PER_KVHEAD_PACKGQA = tl.constexpr(QHEAD_PER_KVHEAD_PACKGQA)
|
| 225 |
+
self.TILE_M = tl.constexpr(TILE_M)
|
| 226 |
+
self.TILE_N = tl.constexpr(TILE_N)
|
| 227 |
+
self.TILE_K = tl.constexpr(TILE_K)
|
| 228 |
+
self.IS_LOGSIGMOID_GATE = tl.constexpr(IS_LOGSIGMOID_GATE)
|
| 229 |
+
|
| 230 |
+
@staticmethod
|
| 231 |
+
@triton.jit
|
| 232 |
+
def create(
|
| 233 |
+
softmax_scale=0.0,
|
| 234 |
+
softmax_threshold=0.0,
|
| 235 |
+
gate_threshold=0.0,
|
| 236 |
+
query_scale=None,
|
| 237 |
+
key_scale=None,
|
| 238 |
+
value_scale=None,
|
| 239 |
+
m_block=0,
|
| 240 |
+
batch_idx=0,
|
| 241 |
+
window_size_sink=0,
|
| 242 |
+
window_size_left=0,
|
| 243 |
+
window_size_right=0,
|
| 244 |
+
window_size_dist=0,
|
| 245 |
+
head_dim=0,
|
| 246 |
+
cu_seqlens_q=None,
|
| 247 |
+
cu_seqlens_k=None,
|
| 248 |
+
seqused_q=None,
|
| 249 |
+
seqused_k=None,
|
| 250 |
+
seqlen_q=0,
|
| 251 |
+
seqlen_k=0,
|
| 252 |
+
PACK_GQA: tl.constexpr = False,
|
| 253 |
+
QHEAD_PER_KVHEAD_PACKGQA: tl.constexpr = 1,
|
| 254 |
+
TILE_M: tl.constexpr = 64,
|
| 255 |
+
TILE_N: tl.constexpr = 64,
|
| 256 |
+
TILE_K: tl.constexpr = 64,
|
| 257 |
+
IS_CAUSAL: tl.constexpr = False,
|
| 258 |
+
IS_LOGSIGMOID_GATE: tl.constexpr = False,
|
| 259 |
+
IS_ADAPT_GATE: tl.constexpr = False,
|
| 260 |
+
HAS_CU_SEQLENS_Q: tl.constexpr = False,
|
| 261 |
+
HAS_CU_SEQLENS_K: tl.constexpr = False,
|
| 262 |
+
HAS_SEQUSED_Q: tl.constexpr = False,
|
| 263 |
+
HAS_SEQUSED_K: tl.constexpr = False,
|
| 264 |
+
):
|
| 265 |
+
# Get seqlen info for this batch
|
| 266 |
+
(
|
| 267 |
+
offset_q,
|
| 268 |
+
offset_k,
|
| 269 |
+
padded_offset_q,
|
| 270 |
+
padded_offset_k,
|
| 271 |
+
actual_seqlen_q,
|
| 272 |
+
actual_seqlen_k,
|
| 273 |
+
) = seqlen_info.get_seqlen_info_qk(
|
| 274 |
+
batch_idx=batch_idx,
|
| 275 |
+
seqlen_q_static=seqlen_q,
|
| 276 |
+
seqlen_k_static=seqlen_k,
|
| 277 |
+
cu_seqlens_q=cu_seqlens_q,
|
| 278 |
+
cu_seqlens_k=cu_seqlens_k,
|
| 279 |
+
seqused_q=seqused_q,
|
| 280 |
+
seqused_k=seqused_k,
|
| 281 |
+
TILE_M=TILE_M,
|
| 282 |
+
TILE_N=TILE_N,
|
| 283 |
+
HAS_CU_SEQLENS_Q=HAS_CU_SEQLENS_Q,
|
| 284 |
+
HAS_CU_SEQLENS_K=HAS_CU_SEQLENS_K,
|
| 285 |
+
HAS_SEQUSED_Q=HAS_SEQUSED_Q,
|
| 286 |
+
HAS_SEQUSED_K=HAS_SEQUSED_K,
|
| 287 |
+
)
|
| 288 |
+
# Load query scale
|
| 289 |
+
q_scale = tl.load(query_scale)
|
| 290 |
+
# Load key scale
|
| 291 |
+
k_scale = tl.load(key_scale)
|
| 292 |
+
# Load value scale
|
| 293 |
+
v_scale = tl.load(value_scale)
|
| 294 |
+
LOG2E: tl.constexpr = 1.44269504089
|
| 295 |
+
softmax_scale_log2 = softmax_scale * LOG2E * q_scale * k_scale
|
| 296 |
+
softmax_threshold_log2 = seqlen_info.get_softmax_threshold(
|
| 297 |
+
softmax_threshold,
|
| 298 |
+
m_block,
|
| 299 |
+
actual_seqlen_q,
|
| 300 |
+
actual_seqlen_k,
|
| 301 |
+
IS_CAUSAL,
|
| 302 |
+
TILE_M,
|
| 303 |
+
QHEAD_PER_KVHEAD_PACKGQA,
|
| 304 |
+
)
|
| 305 |
+
gate_threshold_log2 = seqlen_info.get_gate_threshold(
|
| 306 |
+
gate_threshold,
|
| 307 |
+
m_block,
|
| 308 |
+
actual_seqlen_q,
|
| 309 |
+
actual_seqlen_k,
|
| 310 |
+
IS_CAUSAL,
|
| 311 |
+
TILE_M,
|
| 312 |
+
QHEAD_PER_KVHEAD_PACKGQA,
|
| 313 |
+
IS_ADAPT_GATE,
|
| 314 |
+
)
|
| 315 |
+
return AttnFwdConfig(
|
| 316 |
+
softmax_scale_log2,
|
| 317 |
+
softmax_threshold_log2,
|
| 318 |
+
gate_threshold_log2,
|
| 319 |
+
v_scale,
|
| 320 |
+
m_block,
|
| 321 |
+
actual_seqlen_q,
|
| 322 |
+
actual_seqlen_k,
|
| 323 |
+
offset_q,
|
| 324 |
+
offset_k,
|
| 325 |
+
padded_offset_q,
|
| 326 |
+
padded_offset_k,
|
| 327 |
+
window_size_sink,
|
| 328 |
+
window_size_left,
|
| 329 |
+
window_size_right,
|
| 330 |
+
window_size_dist,
|
| 331 |
+
head_dim,
|
| 332 |
+
PACK_GQA,
|
| 333 |
+
QHEAD_PER_KVHEAD_PACKGQA,
|
| 334 |
+
TILE_M,
|
| 335 |
+
TILE_N,
|
| 336 |
+
TILE_K,
|
| 337 |
+
IS_LOGSIGMOID_GATE,
|
| 338 |
+
)
|
| 339 |
+
|
| 340 |
+
@triton.jit
|
| 341 |
+
def get_offs_m(self):
|
| 342 |
+
return self.m_block * self.TILE_M + tl.arange(0, self.TILE_M)
|
| 343 |
+
|
| 344 |
+
@triton.jit
|
| 345 |
+
def get_offs_k(self):
|
| 346 |
+
return tl.arange(0, self.TILE_K)
|
| 347 |
+
|
| 348 |
+
|
| 349 |
+
@aggregate
|
| 350 |
+
class AttnBwdConfig:
|
| 351 |
+
softmax_scale_log2: tl.tensor
|
| 352 |
+
softmax_threshold: tl.tensor
|
| 353 |
+
gate_threshold: tl.tensor
|
| 354 |
+
query_scale: tl.tensor
|
| 355 |
+
key_scale: tl.tensor
|
| 356 |
+
value_scale: tl.tensor
|
| 357 |
+
n_block: tl.tensor
|
| 358 |
+
actual_seqlen_q: tl.tensor
|
| 359 |
+
actual_seqlen_k: tl.tensor
|
| 360 |
+
offset_q: tl.tensor
|
| 361 |
+
offset_k: tl.tensor
|
| 362 |
+
padded_offset_q: tl.tensor
|
| 363 |
+
padded_offset_k: tl.tensor
|
| 364 |
+
window_size_sink: tl.tensor
|
| 365 |
+
window_size_left: tl.tensor
|
| 366 |
+
window_size_right: tl.tensor
|
| 367 |
+
window_size_dist: tl.tensor
|
| 368 |
+
head_dim: tl.tensor
|
| 369 |
+
QHEAD_PER_KVHEAD: tl.constexpr
|
| 370 |
+
TILE_M: tl.constexpr
|
| 371 |
+
TILE_N: tl.constexpr
|
| 372 |
+
TILE_K: tl.constexpr
|
| 373 |
+
IS_CAUSAL: tl.constexpr
|
| 374 |
+
IS_LOGSIGMOID_GATE: tl.constexpr
|
| 375 |
+
IS_ADAPT_GATE: tl.constexpr
|
| 376 |
+
|
| 377 |
+
@constexpr_function
|
| 378 |
+
def __init__(
|
| 379 |
+
self,
|
| 380 |
+
softmax_scale_log2,
|
| 381 |
+
softmax_threshold,
|
| 382 |
+
gate_threshold,
|
| 383 |
+
query_scale,
|
| 384 |
+
key_scale,
|
| 385 |
+
value_scale,
|
| 386 |
+
n_block,
|
| 387 |
+
actual_seqlen_q,
|
| 388 |
+
actual_seqlen_k,
|
| 389 |
+
offset_q,
|
| 390 |
+
offset_k,
|
| 391 |
+
padded_offset_q,
|
| 392 |
+
padded_offset_k,
|
| 393 |
+
window_size_sink,
|
| 394 |
+
window_size_left,
|
| 395 |
+
window_size_right,
|
| 396 |
+
window_size_dist,
|
| 397 |
+
head_dim,
|
| 398 |
+
QHEAD_PER_KVHEAD,
|
| 399 |
+
TILE_M,
|
| 400 |
+
TILE_N,
|
| 401 |
+
TILE_K,
|
| 402 |
+
IS_CAUSAL,
|
| 403 |
+
IS_LOGSIGMOID_GATE,
|
| 404 |
+
IS_ADAPT_GATE,
|
| 405 |
+
):
|
| 406 |
+
self.softmax_scale_log2 = softmax_scale_log2
|
| 407 |
+
self.softmax_threshold = softmax_threshold
|
| 408 |
+
self.gate_threshold = gate_threshold
|
| 409 |
+
self.query_scale = query_scale
|
| 410 |
+
self.key_scale = key_scale
|
| 411 |
+
self.value_scale = value_scale
|
| 412 |
+
self.n_block = n_block
|
| 413 |
+
self.actual_seqlen_q = actual_seqlen_q
|
| 414 |
+
self.actual_seqlen_k = actual_seqlen_k
|
| 415 |
+
self.offset_q = offset_q
|
| 416 |
+
self.offset_k = offset_k
|
| 417 |
+
self.padded_offset_q = padded_offset_q
|
| 418 |
+
self.padded_offset_k = padded_offset_k
|
| 419 |
+
self.window_size_sink = window_size_sink
|
| 420 |
+
self.window_size_left = window_size_left
|
| 421 |
+
self.window_size_right = window_size_right
|
| 422 |
+
self.window_size_dist = window_size_dist
|
| 423 |
+
self.head_dim = head_dim
|
| 424 |
+
self.QHEAD_PER_KVHEAD = tl.constexpr(QHEAD_PER_KVHEAD)
|
| 425 |
+
self.TILE_M = tl.constexpr(TILE_M)
|
| 426 |
+
self.TILE_N = tl.constexpr(TILE_N)
|
| 427 |
+
self.TILE_K = tl.constexpr(TILE_K)
|
| 428 |
+
self.IS_CAUSAL = tl.constexpr(IS_CAUSAL)
|
| 429 |
+
self.IS_LOGSIGMOID_GATE = tl.constexpr(IS_LOGSIGMOID_GATE)
|
| 430 |
+
self.IS_ADAPT_GATE = tl.constexpr(IS_ADAPT_GATE)
|
| 431 |
+
|
| 432 |
+
@staticmethod
|
| 433 |
+
@triton.jit
|
| 434 |
+
def create(
|
| 435 |
+
softmax_scale=0.0,
|
| 436 |
+
softmax_threshold=0.0,
|
| 437 |
+
gate_threshold=0.0,
|
| 438 |
+
query_scale=None,
|
| 439 |
+
key_scale=None,
|
| 440 |
+
value_scale=None,
|
| 441 |
+
n_block=0,
|
| 442 |
+
batch_idx=0,
|
| 443 |
+
window_size_sink=0,
|
| 444 |
+
window_size_left=0,
|
| 445 |
+
window_size_right=0,
|
| 446 |
+
window_size_dist=0,
|
| 447 |
+
head_dim=0,
|
| 448 |
+
cu_seqlens_q=None,
|
| 449 |
+
cu_seqlens_k=None,
|
| 450 |
+
seqused_q=None,
|
| 451 |
+
seqused_k=None,
|
| 452 |
+
seqlen_q=0,
|
| 453 |
+
seqlen_k=0,
|
| 454 |
+
QHEAD_PER_KVHEAD: tl.constexpr = 1,
|
| 455 |
+
TILE_M: tl.constexpr = 64,
|
| 456 |
+
TILE_N: tl.constexpr = 64,
|
| 457 |
+
TILE_K: tl.constexpr = 64,
|
| 458 |
+
IS_CAUSAL: tl.constexpr = False,
|
| 459 |
+
IS_LOGSIGMOID_GATE: tl.constexpr = False,
|
| 460 |
+
IS_ADAPT_GATE: tl.constexpr = False,
|
| 461 |
+
HAS_CU_SEQLENS_Q: tl.constexpr = False,
|
| 462 |
+
HAS_CU_SEQLENS_K: tl.constexpr = False,
|
| 463 |
+
HAS_SEQUSED_Q: tl.constexpr = False,
|
| 464 |
+
HAS_SEQUSED_K: tl.constexpr = False,
|
| 465 |
+
):
|
| 466 |
+
# Get seqlen info for this batch
|
| 467 |
+
(
|
| 468 |
+
offset_q,
|
| 469 |
+
offset_k,
|
| 470 |
+
padded_offset_q,
|
| 471 |
+
padded_offset_k,
|
| 472 |
+
actual_seqlen_q,
|
| 473 |
+
actual_seqlen_k,
|
| 474 |
+
) = seqlen_info.get_seqlen_info_qk(
|
| 475 |
+
batch_idx=batch_idx,
|
| 476 |
+
seqlen_q_static=seqlen_q,
|
| 477 |
+
seqlen_k_static=seqlen_k,
|
| 478 |
+
cu_seqlens_q=cu_seqlens_q,
|
| 479 |
+
cu_seqlens_k=cu_seqlens_k,
|
| 480 |
+
seqused_q=seqused_q,
|
| 481 |
+
seqused_k=seqused_k,
|
| 482 |
+
TILE_M=TILE_M,
|
| 483 |
+
TILE_N=TILE_N,
|
| 484 |
+
HAS_CU_SEQLENS_Q=HAS_CU_SEQLENS_Q,
|
| 485 |
+
HAS_CU_SEQLENS_K=HAS_CU_SEQLENS_K,
|
| 486 |
+
HAS_SEQUSED_Q=HAS_SEQUSED_Q,
|
| 487 |
+
HAS_SEQUSED_K=HAS_SEQUSED_K,
|
| 488 |
+
)
|
| 489 |
+
# Load query scale
|
| 490 |
+
q_scale = tl.load(query_scale)
|
| 491 |
+
# Load key scale
|
| 492 |
+
k_scale = tl.load(key_scale)
|
| 493 |
+
# Load value scale
|
| 494 |
+
v_scale = tl.load(value_scale)
|
| 495 |
+
LOG2E: tl.constexpr = 1.44269504089
|
| 496 |
+
softmax_scale_log2 = softmax_scale * LOG2E
|
| 497 |
+
softmax_threshold = tl.full((), softmax_threshold, tl.float32)
|
| 498 |
+
gate_threshold = tl.full((), gate_threshold, tl.float32)
|
| 499 |
+
return AttnBwdConfig(
|
| 500 |
+
softmax_scale_log2,
|
| 501 |
+
softmax_threshold,
|
| 502 |
+
gate_threshold,
|
| 503 |
+
q_scale,
|
| 504 |
+
k_scale,
|
| 505 |
+
v_scale,
|
| 506 |
+
n_block,
|
| 507 |
+
actual_seqlen_q,
|
| 508 |
+
actual_seqlen_k,
|
| 509 |
+
offset_q,
|
| 510 |
+
offset_k,
|
| 511 |
+
padded_offset_q,
|
| 512 |
+
padded_offset_k,
|
| 513 |
+
window_size_sink,
|
| 514 |
+
window_size_left,
|
| 515 |
+
window_size_right,
|
| 516 |
+
window_size_dist,
|
| 517 |
+
head_dim,
|
| 518 |
+
QHEAD_PER_KVHEAD,
|
| 519 |
+
TILE_M,
|
| 520 |
+
TILE_N,
|
| 521 |
+
TILE_K,
|
| 522 |
+
IS_CAUSAL,
|
| 523 |
+
IS_LOGSIGMOID_GATE,
|
| 524 |
+
IS_ADAPT_GATE,
|
| 525 |
+
)
|
| 526 |
+
|
| 527 |
+
@triton.jit
|
| 528 |
+
def get_softmax_threshold_log2(
|
| 529 |
+
self,
|
| 530 |
+
m_block,
|
| 531 |
+
):
|
| 532 |
+
return seqlen_info.get_softmax_threshold(
|
| 533 |
+
self.softmax_threshold,
|
| 534 |
+
m_block,
|
| 535 |
+
self.actual_seqlen_q,
|
| 536 |
+
self.actual_seqlen_k,
|
| 537 |
+
self.IS_CAUSAL,
|
| 538 |
+
self.TILE_M,
|
| 539 |
+
QHEAD_PER_KVHEAD_PACKGQA=1,
|
| 540 |
+
)
|
| 541 |
+
|
| 542 |
+
@triton.jit
|
| 543 |
+
def get_gate_threshold_log2(
|
| 544 |
+
self,
|
| 545 |
+
m_block,
|
| 546 |
+
):
|
| 547 |
+
return seqlen_info.get_gate_threshold(
|
| 548 |
+
self.gate_threshold,
|
| 549 |
+
m_block,
|
| 550 |
+
self.actual_seqlen_q,
|
| 551 |
+
self.actual_seqlen_k,
|
| 552 |
+
self.IS_CAUSAL,
|
| 553 |
+
self.TILE_M,
|
| 554 |
+
QHEAD_PER_KVHEAD_PACKGQA=1,
|
| 555 |
+
IS_ADAPT_GATE=self.IS_ADAPT_GATE,
|
| 556 |
+
)
|
| 557 |
+
|
| 558 |
+
|
| 559 |
+
@aggregate
|
| 560 |
+
class AttnDecConfig:
|
| 561 |
+
softmax_scale_log2: tl.tensor
|
| 562 |
+
softmax_threshold_log2: tl.tensor
|
| 563 |
+
gate_threshold_log2: tl.tensor
|
| 564 |
+
value_scale: tl.tensor
|
| 565 |
+
m_block: tl.tensor
|
| 566 |
+
actual_seqlen_q: tl.tensor
|
| 567 |
+
actual_seqlen_k: tl.tensor
|
| 568 |
+
offset_q: tl.tensor
|
| 569 |
+
offset_k: tl.tensor
|
| 570 |
+
padded_offset_q: tl.tensor
|
| 571 |
+
padded_offset_k: tl.tensor
|
| 572 |
+
window_size_sink: tl.tensor
|
| 573 |
+
window_size_left: tl.tensor
|
| 574 |
+
window_size_right: tl.tensor
|
| 575 |
+
window_size_dist: tl.tensor
|
| 576 |
+
head_dim: tl.tensor
|
| 577 |
+
QHEAD_PER_KVHEAD_PACKGQA: tl.constexpr
|
| 578 |
+
TILE_M: tl.constexpr
|
| 579 |
+
TILE_N: tl.constexpr
|
| 580 |
+
TILE_K: tl.constexpr
|
| 581 |
+
IS_LOGSIGMOID_GATE: tl.constexpr
|
| 582 |
+
|
| 583 |
+
@constexpr_function
|
| 584 |
+
def __init__(
|
| 585 |
+
self,
|
| 586 |
+
softmax_scale_log2,
|
| 587 |
+
softmax_threshold_log2,
|
| 588 |
+
gate_threshold_log2,
|
| 589 |
+
value_scale,
|
| 590 |
+
m_block,
|
| 591 |
+
actual_seqlen_q,
|
| 592 |
+
actual_seqlen_k,
|
| 593 |
+
offset_q,
|
| 594 |
+
offset_k,
|
| 595 |
+
padded_offset_q,
|
| 596 |
+
padded_offset_k,
|
| 597 |
+
window_size_sink,
|
| 598 |
+
window_size_left,
|
| 599 |
+
window_size_right,
|
| 600 |
+
window_size_dist,
|
| 601 |
+
head_dim,
|
| 602 |
+
QHEAD_PER_KVHEAD_PACKGQA,
|
| 603 |
+
TILE_M,
|
| 604 |
+
TILE_N,
|
| 605 |
+
TILE_K,
|
| 606 |
+
IS_LOGSIGMOID_GATE,
|
| 607 |
+
):
|
| 608 |
+
self.softmax_scale_log2 = softmax_scale_log2
|
| 609 |
+
self.softmax_threshold_log2 = softmax_threshold_log2
|
| 610 |
+
self.gate_threshold_log2 = gate_threshold_log2
|
| 611 |
+
self.value_scale = value_scale
|
| 612 |
+
self.m_block = m_block
|
| 613 |
+
self.actual_seqlen_q = actual_seqlen_q
|
| 614 |
+
self.actual_seqlen_k = actual_seqlen_k
|
| 615 |
+
self.offset_q = offset_q
|
| 616 |
+
self.offset_k = offset_k
|
| 617 |
+
self.padded_offset_q = padded_offset_q
|
| 618 |
+
self.padded_offset_k = padded_offset_k
|
| 619 |
+
self.window_size_sink = window_size_sink
|
| 620 |
+
self.window_size_left = window_size_left
|
| 621 |
+
self.window_size_right = window_size_right
|
| 622 |
+
self.window_size_dist = window_size_dist
|
| 623 |
+
self.head_dim = head_dim
|
| 624 |
+
self.QHEAD_PER_KVHEAD_PACKGQA = tl.constexpr(QHEAD_PER_KVHEAD_PACKGQA)
|
| 625 |
+
self.TILE_M = tl.constexpr(TILE_M)
|
| 626 |
+
self.TILE_N = tl.constexpr(TILE_N)
|
| 627 |
+
self.TILE_K = tl.constexpr(TILE_K)
|
| 628 |
+
self.IS_LOGSIGMOID_GATE = tl.constexpr(IS_LOGSIGMOID_GATE)
|
| 629 |
+
|
| 630 |
+
@staticmethod
|
| 631 |
+
@triton.jit
|
| 632 |
+
def create(
|
| 633 |
+
softmax_scale=0.0,
|
| 634 |
+
softmax_threshold=0.0,
|
| 635 |
+
gate_threshold=0.0,
|
| 636 |
+
query_scale=None,
|
| 637 |
+
key_scale=None,
|
| 638 |
+
value_scale=None,
|
| 639 |
+
batch_idx=0,
|
| 640 |
+
window_size_sink=0,
|
| 641 |
+
window_size_left=0,
|
| 642 |
+
window_size_right=0,
|
| 643 |
+
window_size_dist=0,
|
| 644 |
+
head_dim=0,
|
| 645 |
+
cu_seqlens_q=None,
|
| 646 |
+
cu_seqlens_k=None,
|
| 647 |
+
seqused_q=None,
|
| 648 |
+
seqused_k=None,
|
| 649 |
+
seqlen_q=0,
|
| 650 |
+
seqlen_k=0,
|
| 651 |
+
QHEAD_PER_KVHEAD_PACKGQA: tl.constexpr = 1,
|
| 652 |
+
TILE_M: tl.constexpr = 16,
|
| 653 |
+
TILE_N: tl.constexpr = 64,
|
| 654 |
+
TILE_K: tl.constexpr = 64,
|
| 655 |
+
IS_LOGSIGMOID_GATE: tl.constexpr = False,
|
| 656 |
+
HAS_CU_SEQLENS_Q: tl.constexpr = False,
|
| 657 |
+
HAS_CU_SEQLENS_K: tl.constexpr = False,
|
| 658 |
+
HAS_SEQUSED_Q: tl.constexpr = False,
|
| 659 |
+
HAS_SEQUSED_K: tl.constexpr = False,
|
| 660 |
+
):
|
| 661 |
+
# Get seqlen info for this batch
|
| 662 |
+
(
|
| 663 |
+
offset_q,
|
| 664 |
+
offset_k,
|
| 665 |
+
padded_offset_q,
|
| 666 |
+
padded_offset_k,
|
| 667 |
+
actual_seqlen_q,
|
| 668 |
+
actual_seqlen_k,
|
| 669 |
+
) = seqlen_info.get_seqlen_info_qk(
|
| 670 |
+
batch_idx=batch_idx,
|
| 671 |
+
seqlen_q_static=seqlen_q,
|
| 672 |
+
seqlen_k_static=seqlen_k,
|
| 673 |
+
cu_seqlens_q=cu_seqlens_q,
|
| 674 |
+
cu_seqlens_k=cu_seqlens_k,
|
| 675 |
+
seqused_q=seqused_q,
|
| 676 |
+
seqused_k=seqused_k,
|
| 677 |
+
TILE_M=TILE_M,
|
| 678 |
+
TILE_N=TILE_N,
|
| 679 |
+
HAS_CU_SEQLENS_Q=HAS_CU_SEQLENS_Q,
|
| 680 |
+
HAS_CU_SEQLENS_K=HAS_CU_SEQLENS_K,
|
| 681 |
+
HAS_SEQUSED_Q=HAS_SEQUSED_Q,
|
| 682 |
+
HAS_SEQUSED_K=HAS_SEQUSED_K,
|
| 683 |
+
)
|
| 684 |
+
# Load query scale
|
| 685 |
+
q_scale = tl.load(query_scale)
|
| 686 |
+
# Load key scale
|
| 687 |
+
k_scale = tl.load(key_scale)
|
| 688 |
+
# Load value scale
|
| 689 |
+
v_scale = tl.load(value_scale)
|
| 690 |
+
LOG2E: tl.constexpr = 1.44269504089
|
| 691 |
+
softmax_scale_log2 = softmax_scale * LOG2E * q_scale * k_scale
|
| 692 |
+
softmax_threshold_log2 = seqlen_info.get_softmax_threshold(
|
| 693 |
+
softmax_threshold,
|
| 694 |
+
0,
|
| 695 |
+
actual_seqlen_q,
|
| 696 |
+
actual_seqlen_k,
|
| 697 |
+
False,
|
| 698 |
+
TILE_M,
|
| 699 |
+
QHEAD_PER_KVHEAD_PACKGQA,
|
| 700 |
+
)
|
| 701 |
+
gate_threshold_log2 = seqlen_info.get_gate_threshold(
|
| 702 |
+
gate_threshold,
|
| 703 |
+
0,
|
| 704 |
+
actual_seqlen_q,
|
| 705 |
+
actual_seqlen_k,
|
| 706 |
+
False,
|
| 707 |
+
TILE_M,
|
| 708 |
+
QHEAD_PER_KVHEAD_PACKGQA,
|
| 709 |
+
False,
|
| 710 |
+
)
|
| 711 |
+
return AttnDecConfig(
|
| 712 |
+
softmax_scale_log2,
|
| 713 |
+
softmax_threshold_log2,
|
| 714 |
+
gate_threshold_log2,
|
| 715 |
+
v_scale,
|
| 716 |
+
tl.full((), 0, tl.int32),
|
| 717 |
+
actual_seqlen_q,
|
| 718 |
+
actual_seqlen_k,
|
| 719 |
+
offset_q,
|
| 720 |
+
offset_k,
|
| 721 |
+
padded_offset_q,
|
| 722 |
+
padded_offset_k,
|
| 723 |
+
window_size_sink,
|
| 724 |
+
window_size_left,
|
| 725 |
+
window_size_right,
|
| 726 |
+
window_size_dist,
|
| 727 |
+
head_dim,
|
| 728 |
+
QHEAD_PER_KVHEAD_PACKGQA,
|
| 729 |
+
TILE_M,
|
| 730 |
+
TILE_N,
|
| 731 |
+
TILE_K,
|
| 732 |
+
IS_LOGSIGMOID_GATE,
|
| 733 |
+
)
|
| 734 |
+
|
| 735 |
+
|
| 736 |
+
@aggregate
|
| 737 |
+
class AttnFwdBlockScheduler:
|
| 738 |
+
n_block_min: tl.tensor
|
| 739 |
+
n_block_max: tl.tensor
|
| 740 |
+
n_block_max_no_mask: tl.tensor
|
| 741 |
+
n_block_window_min: tl.tensor
|
| 742 |
+
n_block_window_max: tl.tensor
|
| 743 |
+
n_block_window_min_no_mask: tl.tensor
|
| 744 |
+
n_block_window_max_no_mask: tl.tensor
|
| 745 |
+
n_block_sink_min: tl.tensor
|
| 746 |
+
n_block_sink_max: tl.tensor
|
| 747 |
+
|
| 748 |
+
@constexpr_function
|
| 749 |
+
def __init__(
|
| 750 |
+
self,
|
| 751 |
+
n_block_min,
|
| 752 |
+
n_block_max,
|
| 753 |
+
n_block_max_no_mask,
|
| 754 |
+
n_block_window_min,
|
| 755 |
+
n_block_window_max,
|
| 756 |
+
n_block_window_min_no_mask,
|
| 757 |
+
n_block_window_max_no_mask,
|
| 758 |
+
n_block_sink_min,
|
| 759 |
+
n_block_sink_max,
|
| 760 |
+
):
|
| 761 |
+
self.n_block_min = n_block_min
|
| 762 |
+
self.n_block_max = n_block_max
|
| 763 |
+
self.n_block_max_no_mask = n_block_max_no_mask
|
| 764 |
+
self.n_block_window_min = n_block_window_min
|
| 765 |
+
self.n_block_window_max = n_block_window_max
|
| 766 |
+
self.n_block_window_min_no_mask = n_block_window_min_no_mask
|
| 767 |
+
self.n_block_window_max_no_mask = n_block_window_max_no_mask
|
| 768 |
+
self.n_block_sink_min = n_block_sink_min
|
| 769 |
+
self.n_block_sink_max = n_block_sink_max
|
| 770 |
+
|
| 771 |
+
@triton.jit
|
| 772 |
+
def is_empty(self):
|
| 773 |
+
return (
|
| 774 |
+
(self.n_block_max <= self.n_block_min)
|
| 775 |
+
& (self.n_block_window_max <= self.n_block_window_min)
|
| 776 |
+
& (self.n_block_sink_max <= self.n_block_sink_min)
|
| 777 |
+
)
|
| 778 |
+
|
| 779 |
+
@staticmethod
|
| 780 |
+
@triton.jit
|
| 781 |
+
def create(
|
| 782 |
+
config: AttnFwdConfig,
|
| 783 |
+
split_idx,
|
| 784 |
+
num_splits,
|
| 785 |
+
IS_CAUSAL: tl.constexpr,
|
| 786 |
+
IS_LOCAL: tl.constexpr,
|
| 787 |
+
IS_SPLIT_KV: tl.constexpr,
|
| 788 |
+
):
|
| 789 |
+
# Compute causal n_block range for this m_block
|
| 790 |
+
(
|
| 791 |
+
n_block_min,
|
| 792 |
+
n_block_max,
|
| 793 |
+
n_block_window_min,
|
| 794 |
+
n_block_window_max,
|
| 795 |
+
n_block_sink_min,
|
| 796 |
+
n_block_sink_max,
|
| 797 |
+
) = block_info.get_n_block_min_max(
|
| 798 |
+
seqlen_q=config.actual_seqlen_q,
|
| 799 |
+
seqlen_k=config.actual_seqlen_k,
|
| 800 |
+
m_block=config.m_block,
|
| 801 |
+
split_idx=split_idx,
|
| 802 |
+
num_splits=num_splits,
|
| 803 |
+
window_size_sink=config.window_size_sink,
|
| 804 |
+
window_size_left=config.window_size_left,
|
| 805 |
+
window_size_right=config.window_size_right,
|
| 806 |
+
window_size_dist=config.window_size_dist,
|
| 807 |
+
TILE_N=config.TILE_N,
|
| 808 |
+
TILE_M=config.TILE_M,
|
| 809 |
+
IS_CAUSAL=IS_CAUSAL,
|
| 810 |
+
IS_LOCAL=IS_LOCAL,
|
| 811 |
+
IS_SPLIT_KV=IS_SPLIT_KV,
|
| 812 |
+
QHEAD_PER_KVHEAD_PACKGQA=config.QHEAD_PER_KVHEAD_PACKGQA,
|
| 813 |
+
)
|
| 814 |
+
n_block_max_no_mask = block_info.get_n_block_min_causal_local_mask(
|
| 815 |
+
seqlen_q=config.actual_seqlen_q,
|
| 816 |
+
seqlen_k=config.actual_seqlen_k,
|
| 817 |
+
m_block=config.m_block,
|
| 818 |
+
n_block_min=n_block_min,
|
| 819 |
+
window_size_right=0,
|
| 820 |
+
window_size_dist=0,
|
| 821 |
+
TILE_N=config.TILE_N,
|
| 822 |
+
TILE_M=config.TILE_M,
|
| 823 |
+
IS_LOCAL=False,
|
| 824 |
+
QHEAD_PER_KVHEAD_PACKGQA=config.QHEAD_PER_KVHEAD_PACKGQA,
|
| 825 |
+
)
|
| 826 |
+
|
| 827 |
+
# Clamp to split's range so the no-mask loop stays within bounds
|
| 828 |
+
if IS_SPLIT_KV:
|
| 829 |
+
n_block_max_no_mask = tl.minimum(n_block_max_no_mask, n_block_max)
|
| 830 |
+
|
| 831 |
+
if IS_LOCAL:
|
| 832 |
+
# Compute local n_block range for this m_block
|
| 833 |
+
if IS_SPLIT_KV:
|
| 834 |
+
n_block_max_no_mask = tl.where(
|
| 835 |
+
split_idx >= num_splits - 1,
|
| 836 |
+
n_block_min,
|
| 837 |
+
n_block_max_no_mask,
|
| 838 |
+
)
|
| 839 |
+
else:
|
| 840 |
+
n_block_max_no_mask = n_block_min
|
| 841 |
+
if IS_SPLIT_KV:
|
| 842 |
+
n_block_window_min = tl.maximum(n_block_window_min, n_block_min)
|
| 843 |
+
n_block_window_max = tl.minimum(n_block_window_max, n_block_max_no_mask)
|
| 844 |
+
n_block_window_max_no_mask = block_info.get_n_block_min_causal_local_mask(
|
| 845 |
+
seqlen_q=config.actual_seqlen_q,
|
| 846 |
+
seqlen_k=config.actual_seqlen_k,
|
| 847 |
+
m_block=config.m_block,
|
| 848 |
+
n_block_min=n_block_window_min,
|
| 849 |
+
window_size_right=config.window_size_right,
|
| 850 |
+
window_size_dist=config.window_size_dist,
|
| 851 |
+
TILE_N=config.TILE_N,
|
| 852 |
+
TILE_M=config.TILE_M,
|
| 853 |
+
IS_LOCAL=True,
|
| 854 |
+
QHEAD_PER_KVHEAD_PACKGQA=config.QHEAD_PER_KVHEAD_PACKGQA,
|
| 855 |
+
)
|
| 856 |
+
n_block_window_min_no_mask = block_info.get_n_block_min_before_local_mask(
|
| 857 |
+
seqlen_q=config.actual_seqlen_q,
|
| 858 |
+
seqlen_k=config.actual_seqlen_k,
|
| 859 |
+
m_block=config.m_block,
|
| 860 |
+
n_block_min=n_block_window_min,
|
| 861 |
+
window_size_left=config.window_size_left,
|
| 862 |
+
window_size_right=config.window_size_right,
|
| 863 |
+
window_size_dist=config.window_size_dist,
|
| 864 |
+
TILE_N=config.TILE_N,
|
| 865 |
+
TILE_M=config.TILE_M,
|
| 866 |
+
IS_LOCAL=True,
|
| 867 |
+
QHEAD_PER_KVHEAD_PACKGQA=config.QHEAD_PER_KVHEAD_PACKGQA,
|
| 868 |
+
)
|
| 869 |
+
n_block_window_min_no_mask = tl.minimum(
|
| 870 |
+
n_block_window_min_no_mask, n_block_window_max_no_mask
|
| 871 |
+
)
|
| 872 |
+
|
| 873 |
+
# Clamp window no-mask boundaries to the window's range
|
| 874 |
+
if IS_SPLIT_KV:
|
| 875 |
+
n_block_window_max_no_mask = tl.maximum(
|
| 876 |
+
tl.minimum(n_block_window_max_no_mask, n_block_window_max),
|
| 877 |
+
n_block_window_min,
|
| 878 |
+
)
|
| 879 |
+
n_block_window_min_no_mask = tl.maximum(
|
| 880 |
+
tl.minimum(n_block_window_min_no_mask, n_block_window_max),
|
| 881 |
+
n_block_window_min,
|
| 882 |
+
)
|
| 883 |
+
else:
|
| 884 |
+
n_block_window_min = 0
|
| 885 |
+
n_block_window_max = 0
|
| 886 |
+
n_block_window_min_no_mask = 0
|
| 887 |
+
n_block_window_max_no_mask = 0
|
| 888 |
+
|
| 889 |
+
return AttnFwdBlockScheduler(
|
| 890 |
+
n_block_min,
|
| 891 |
+
n_block_max,
|
| 892 |
+
n_block_max_no_mask,
|
| 893 |
+
n_block_window_min,
|
| 894 |
+
n_block_window_max,
|
| 895 |
+
n_block_window_min_no_mask,
|
| 896 |
+
n_block_window_max_no_mask,
|
| 897 |
+
n_block_sink_min,
|
| 898 |
+
n_block_sink_max,
|
| 899 |
+
)
|
| 900 |
+
|
| 901 |
+
|
| 902 |
+
@aggregate
|
| 903 |
+
class AttnBwdBlockScheduler:
|
| 904 |
+
m_block_min: tl.tensor
|
| 905 |
+
m_block_max: tl.tensor
|
| 906 |
+
m_block_min_no_mask: tl.tensor
|
| 907 |
+
m_block_window_min: tl.tensor
|
| 908 |
+
m_block_window_max: tl.tensor
|
| 909 |
+
m_block_window_min_no_mask: tl.tensor
|
| 910 |
+
m_block_window_max_no_mask: tl.tensor
|
| 911 |
+
m_block_sink_min: tl.tensor
|
| 912 |
+
m_block_sink_max: tl.tensor
|
| 913 |
+
|
| 914 |
+
@constexpr_function
|
| 915 |
+
def __init__(
|
| 916 |
+
self,
|
| 917 |
+
m_block_min,
|
| 918 |
+
m_block_max,
|
| 919 |
+
m_block_min_no_mask,
|
| 920 |
+
m_block_window_min,
|
| 921 |
+
m_block_window_max,
|
| 922 |
+
m_block_window_min_no_mask,
|
| 923 |
+
m_block_window_max_no_mask,
|
| 924 |
+
m_block_sink_min,
|
| 925 |
+
m_block_sink_max,
|
| 926 |
+
):
|
| 927 |
+
self.m_block_min = m_block_min
|
| 928 |
+
self.m_block_max = m_block_max
|
| 929 |
+
self.m_block_min_no_mask = m_block_min_no_mask
|
| 930 |
+
self.m_block_window_min = m_block_window_min
|
| 931 |
+
self.m_block_window_max = m_block_window_max
|
| 932 |
+
self.m_block_window_min_no_mask = m_block_window_min_no_mask
|
| 933 |
+
self.m_block_window_max_no_mask = m_block_window_max_no_mask
|
| 934 |
+
self.m_block_sink_min = m_block_sink_min
|
| 935 |
+
self.m_block_sink_max = m_block_sink_max
|
| 936 |
+
|
| 937 |
+
@triton.jit
|
| 938 |
+
def is_empty(self):
|
| 939 |
+
return (
|
| 940 |
+
(self.m_block_max <= self.m_block_min)
|
| 941 |
+
& (self.m_block_window_max <= self.m_block_window_min)
|
| 942 |
+
& (self.m_block_sink_max <= self.m_block_sink_min)
|
| 943 |
+
)
|
| 944 |
+
|
| 945 |
+
@staticmethod
|
| 946 |
+
@triton.jit
|
| 947 |
+
def create(
|
| 948 |
+
config: AttnBwdConfig,
|
| 949 |
+
split_idx,
|
| 950 |
+
num_splits,
|
| 951 |
+
IS_CAUSAL: tl.constexpr,
|
| 952 |
+
IS_LOCAL: tl.constexpr,
|
| 953 |
+
IS_SPLIT_QO: tl.constexpr,
|
| 954 |
+
):
|
| 955 |
+
# Compute causal m_block range for this n_block
|
| 956 |
+
(
|
| 957 |
+
m_block_min,
|
| 958 |
+
m_block_max,
|
| 959 |
+
m_block_window_min,
|
| 960 |
+
m_block_window_max,
|
| 961 |
+
m_block_sink_min,
|
| 962 |
+
m_block_sink_max,
|
| 963 |
+
) = block_info.get_m_block_min_max(
|
| 964 |
+
seqlen_q=config.actual_seqlen_q,
|
| 965 |
+
seqlen_k=config.actual_seqlen_k,
|
| 966 |
+
n_block=config.n_block,
|
| 967 |
+
split_idx=split_idx,
|
| 968 |
+
num_splits=num_splits,
|
| 969 |
+
window_size_sink=config.window_size_sink,
|
| 970 |
+
window_size_left=config.window_size_left,
|
| 971 |
+
window_size_right=config.window_size_right,
|
| 972 |
+
window_size_dist=config.window_size_dist,
|
| 973 |
+
TILE_N=config.TILE_N,
|
| 974 |
+
TILE_M=config.TILE_M,
|
| 975 |
+
IS_CAUSAL=IS_CAUSAL,
|
| 976 |
+
IS_LOCAL=IS_LOCAL,
|
| 977 |
+
IS_SPLIT_QO=IS_SPLIT_QO,
|
| 978 |
+
)
|
| 979 |
+
m_block_min_no_mask = block_info.get_m_block_min_causal_local_mask(
|
| 980 |
+
seqlen_q=config.actual_seqlen_q,
|
| 981 |
+
seqlen_k=config.actual_seqlen_k,
|
| 982 |
+
n_block=config.n_block,
|
| 983 |
+
m_block_min=m_block_min,
|
| 984 |
+
window_size_right=0,
|
| 985 |
+
window_size_dist=0,
|
| 986 |
+
TILE_N=config.TILE_N,
|
| 987 |
+
TILE_M=config.TILE_M,
|
| 988 |
+
IS_CAUSAL=IS_CAUSAL or IS_LOCAL,
|
| 989 |
+
IS_LOCAL=False,
|
| 990 |
+
)
|
| 991 |
+
|
| 992 |
+
# Clamp to split's range so the no-mask loop stays within bounds
|
| 993 |
+
if IS_SPLIT_QO:
|
| 994 |
+
m_block_min_no_mask = tl.minimum(m_block_min_no_mask, m_block_max)
|
| 995 |
+
|
| 996 |
+
if IS_LOCAL:
|
| 997 |
+
# Compute local m_block range for this n_block
|
| 998 |
+
m_block_min_no_mask = m_block_max
|
| 999 |
+
m_block_window_min = tl.maximum(m_block_window_min, m_block_min_no_mask)
|
| 1000 |
+
m_block_window_min_no_mask = block_info.get_m_block_min_causal_local_mask(
|
| 1001 |
+
seqlen_q=config.actual_seqlen_q,
|
| 1002 |
+
seqlen_k=config.actual_seqlen_k,
|
| 1003 |
+
n_block=config.n_block,
|
| 1004 |
+
m_block_min=m_block_window_min,
|
| 1005 |
+
window_size_right=config.window_size_right,
|
| 1006 |
+
window_size_dist=config.window_size_dist,
|
| 1007 |
+
TILE_N=config.TILE_N,
|
| 1008 |
+
TILE_M=config.TILE_M,
|
| 1009 |
+
IS_CAUSAL=False,
|
| 1010 |
+
IS_LOCAL=True,
|
| 1011 |
+
)
|
| 1012 |
+
m_block_window_min_no_mask = tl.maximum(
|
| 1013 |
+
m_block_window_min_no_mask, m_block_window_min
|
| 1014 |
+
)
|
| 1015 |
+
m_block_window_max_no_mask = block_info.get_m_block_max_before_local_mask(
|
| 1016 |
+
seqlen_q=config.actual_seqlen_q,
|
| 1017 |
+
seqlen_k=config.actual_seqlen_k,
|
| 1018 |
+
n_block=config.n_block,
|
| 1019 |
+
m_block_max=m_block_window_max,
|
| 1020 |
+
window_size_left=config.window_size_left,
|
| 1021 |
+
window_size_right=config.window_size_right,
|
| 1022 |
+
window_size_dist=config.window_size_dist,
|
| 1023 |
+
TILE_N=config.TILE_N,
|
| 1024 |
+
TILE_M=config.TILE_M,
|
| 1025 |
+
IS_LOCAL=True,
|
| 1026 |
+
)
|
| 1027 |
+
m_block_window_max_no_mask = tl.maximum(
|
| 1028 |
+
m_block_window_max_no_mask, m_block_window_min_no_mask
|
| 1029 |
+
)
|
| 1030 |
+
m_block_window_min_no_mask = tl.maximum(
|
| 1031 |
+
tl.minimum(m_block_window_min_no_mask, m_block_window_max),
|
| 1032 |
+
m_block_window_min,
|
| 1033 |
+
)
|
| 1034 |
+
m_block_window_max_no_mask = tl.maximum(
|
| 1035 |
+
tl.minimum(m_block_window_max_no_mask, m_block_window_max),
|
| 1036 |
+
m_block_window_min_no_mask,
|
| 1037 |
+
)
|
| 1038 |
+
else:
|
| 1039 |
+
m_block_window_min_no_mask = 0
|
| 1040 |
+
m_block_window_max_no_mask = 0
|
| 1041 |
+
m_block_sink_min = 0
|
| 1042 |
+
m_block_sink_max = 0
|
| 1043 |
+
|
| 1044 |
+
return AttnBwdBlockScheduler(
|
| 1045 |
+
m_block_min,
|
| 1046 |
+
m_block_max,
|
| 1047 |
+
m_block_min_no_mask,
|
| 1048 |
+
m_block_window_min,
|
| 1049 |
+
m_block_window_max,
|
| 1050 |
+
m_block_window_min_no_mask,
|
| 1051 |
+
m_block_window_max_no_mask,
|
| 1052 |
+
m_block_sink_min,
|
| 1053 |
+
m_block_sink_max,
|
| 1054 |
+
)
|
| 1055 |
+
|
| 1056 |
+
|
| 1057 |
+
@aggregate
|
| 1058 |
+
class AttnDecBlockScheduler:
|
| 1059 |
+
n_block_min: tl.tensor
|
| 1060 |
+
n_block_max: tl.tensor
|
| 1061 |
+
n_block_max_no_mask: tl.tensor
|
| 1062 |
+
n_block_window_min: tl.tensor
|
| 1063 |
+
n_block_window_max: tl.tensor
|
| 1064 |
+
n_block_window_min_no_mask: tl.tensor
|
| 1065 |
+
n_block_window_max_no_mask: tl.tensor
|
| 1066 |
+
n_block_sink_min: tl.tensor
|
| 1067 |
+
n_block_sink_max: tl.tensor
|
| 1068 |
+
|
| 1069 |
+
@constexpr_function
|
| 1070 |
+
def __init__(
|
| 1071 |
+
self,
|
| 1072 |
+
n_block_min,
|
| 1073 |
+
n_block_max,
|
| 1074 |
+
n_block_max_no_mask,
|
| 1075 |
+
n_block_window_min,
|
| 1076 |
+
n_block_window_max,
|
| 1077 |
+
n_block_window_min_no_mask,
|
| 1078 |
+
n_block_window_max_no_mask,
|
| 1079 |
+
n_block_sink_min,
|
| 1080 |
+
n_block_sink_max,
|
| 1081 |
+
):
|
| 1082 |
+
self.n_block_min = n_block_min
|
| 1083 |
+
self.n_block_max = n_block_max
|
| 1084 |
+
self.n_block_max_no_mask = n_block_max_no_mask
|
| 1085 |
+
self.n_block_window_min = n_block_window_min
|
| 1086 |
+
self.n_block_window_max = n_block_window_max
|
| 1087 |
+
self.n_block_window_min_no_mask = n_block_window_min_no_mask
|
| 1088 |
+
self.n_block_window_max_no_mask = n_block_window_max_no_mask
|
| 1089 |
+
self.n_block_sink_min = n_block_sink_min
|
| 1090 |
+
self.n_block_sink_max = n_block_sink_max
|
| 1091 |
+
|
| 1092 |
+
@triton.jit
|
| 1093 |
+
def is_empty(self):
|
| 1094 |
+
return (
|
| 1095 |
+
(self.n_block_max <= self.n_block_min)
|
| 1096 |
+
& (self.n_block_window_max <= self.n_block_window_min)
|
| 1097 |
+
& (self.n_block_sink_max <= self.n_block_sink_min)
|
| 1098 |
+
)
|
| 1099 |
+
|
| 1100 |
+
@staticmethod
|
| 1101 |
+
@triton.jit
|
| 1102 |
+
def create(
|
| 1103 |
+
config: AttnDecConfig,
|
| 1104 |
+
split_idx,
|
| 1105 |
+
num_splits,
|
| 1106 |
+
IS_LOCAL: tl.constexpr,
|
| 1107 |
+
):
|
| 1108 |
+
# Compute non-causal n_block range for this m_block
|
| 1109 |
+
(
|
| 1110 |
+
n_block_min,
|
| 1111 |
+
n_block_max,
|
| 1112 |
+
n_block_window_min,
|
| 1113 |
+
n_block_window_max,
|
| 1114 |
+
n_block_sink_min,
|
| 1115 |
+
n_block_sink_max,
|
| 1116 |
+
) = block_info.get_n_block_min_max(
|
| 1117 |
+
seqlen_q=1,
|
| 1118 |
+
seqlen_k=config.actual_seqlen_k,
|
| 1119 |
+
m_block=0,
|
| 1120 |
+
split_idx=split_idx,
|
| 1121 |
+
num_splits=num_splits,
|
| 1122 |
+
window_size_sink=config.window_size_sink,
|
| 1123 |
+
window_size_left=config.window_size_left,
|
| 1124 |
+
window_size_right=config.window_size_right,
|
| 1125 |
+
window_size_dist=config.window_size_dist,
|
| 1126 |
+
TILE_N=config.TILE_N,
|
| 1127 |
+
TILE_M=config.TILE_M,
|
| 1128 |
+
IS_CAUSAL=False,
|
| 1129 |
+
IS_LOCAL=IS_LOCAL,
|
| 1130 |
+
IS_SPLIT_KV=True,
|
| 1131 |
+
QHEAD_PER_KVHEAD_PACKGQA=config.QHEAD_PER_KVHEAD_PACKGQA,
|
| 1132 |
+
)
|
| 1133 |
+
n_block_max_no_mask = block_info.get_n_block_min_causal_local_mask(
|
| 1134 |
+
seqlen_q=1,
|
| 1135 |
+
seqlen_k=config.actual_seqlen_k,
|
| 1136 |
+
m_block=0,
|
| 1137 |
+
n_block_min=n_block_min,
|
| 1138 |
+
window_size_right=0,
|
| 1139 |
+
window_size_dist=0,
|
| 1140 |
+
TILE_N=config.TILE_N,
|
| 1141 |
+
TILE_M=config.TILE_M,
|
| 1142 |
+
IS_LOCAL=False,
|
| 1143 |
+
QHEAD_PER_KVHEAD_PACKGQA=config.QHEAD_PER_KVHEAD_PACKGQA,
|
| 1144 |
+
)
|
| 1145 |
+
|
| 1146 |
+
# Clamp to split's range so the no-mask loop stays within bounds
|
| 1147 |
+
n_block_max_no_mask = tl.minimum(n_block_max_no_mask, n_block_max)
|
| 1148 |
+
|
| 1149 |
+
if IS_LOCAL:
|
| 1150 |
+
# Compute local n_block range for this m_block
|
| 1151 |
+
n_block_max_no_mask = tl.where(
|
| 1152 |
+
split_idx >= num_splits - 1,
|
| 1153 |
+
n_block_min,
|
| 1154 |
+
n_block_max_no_mask,
|
| 1155 |
+
)
|
| 1156 |
+
n_block_window_min = tl.maximum(n_block_window_min, n_block_min)
|
| 1157 |
+
n_block_window_max = tl.minimum(n_block_window_max, n_block_max_no_mask)
|
| 1158 |
+
n_block_window_max_no_mask = block_info.get_n_block_min_causal_local_mask(
|
| 1159 |
+
seqlen_q=1,
|
| 1160 |
+
seqlen_k=config.actual_seqlen_k,
|
| 1161 |
+
m_block=0,
|
| 1162 |
+
n_block_min=n_block_window_min,
|
| 1163 |
+
window_size_right=config.window_size_right,
|
| 1164 |
+
window_size_dist=config.window_size_dist,
|
| 1165 |
+
TILE_N=config.TILE_N,
|
| 1166 |
+
TILE_M=config.TILE_M,
|
| 1167 |
+
IS_LOCAL=True,
|
| 1168 |
+
QHEAD_PER_KVHEAD_PACKGQA=config.QHEAD_PER_KVHEAD_PACKGQA,
|
| 1169 |
+
)
|
| 1170 |
+
n_block_window_min_no_mask = block_info.get_n_block_min_before_local_mask(
|
| 1171 |
+
seqlen_q=1,
|
| 1172 |
+
seqlen_k=config.actual_seqlen_k,
|
| 1173 |
+
m_block=0,
|
| 1174 |
+
n_block_min=n_block_window_min,
|
| 1175 |
+
window_size_left=config.window_size_left,
|
| 1176 |
+
window_size_right=config.window_size_right,
|
| 1177 |
+
window_size_dist=config.window_size_dist,
|
| 1178 |
+
TILE_N=config.TILE_N,
|
| 1179 |
+
TILE_M=config.TILE_M,
|
| 1180 |
+
IS_LOCAL=True,
|
| 1181 |
+
QHEAD_PER_KVHEAD_PACKGQA=config.QHEAD_PER_KVHEAD_PACKGQA,
|
| 1182 |
+
)
|
| 1183 |
+
n_block_window_min_no_mask = tl.minimum(
|
| 1184 |
+
n_block_window_min_no_mask, n_block_window_max_no_mask
|
| 1185 |
+
)
|
| 1186 |
+
|
| 1187 |
+
# Clamp window no-mask boundaries to the window's range
|
| 1188 |
+
n_block_window_max_no_mask = tl.maximum(
|
| 1189 |
+
tl.minimum(n_block_window_max_no_mask, n_block_window_max),
|
| 1190 |
+
n_block_window_min,
|
| 1191 |
+
)
|
| 1192 |
+
n_block_window_min_no_mask = tl.maximum(
|
| 1193 |
+
tl.minimum(n_block_window_min_no_mask, n_block_window_max),
|
| 1194 |
+
n_block_window_min,
|
| 1195 |
+
)
|
| 1196 |
+
else:
|
| 1197 |
+
n_block_window_min = 0
|
| 1198 |
+
n_block_window_max = 0
|
| 1199 |
+
n_block_window_min_no_mask = 0
|
| 1200 |
+
n_block_window_max_no_mask = 0
|
| 1201 |
+
|
| 1202 |
+
return AttnDecBlockScheduler(
|
| 1203 |
+
n_block_min,
|
| 1204 |
+
n_block_max,
|
| 1205 |
+
n_block_max_no_mask,
|
| 1206 |
+
n_block_window_min,
|
| 1207 |
+
n_block_window_max,
|
| 1208 |
+
n_block_window_min_no_mask,
|
| 1209 |
+
n_block_window_max_no_mask,
|
| 1210 |
+
n_block_sink_min,
|
| 1211 |
+
n_block_sink_max,
|
| 1212 |
+
)
|
| 1213 |
+
|
| 1214 |
+
|
| 1215 |
+
@aggregate
|
| 1216 |
+
class AttnFwdPointerScheduler:
|
| 1217 |
+
q_base: tl.tensor
|
| 1218 |
+
k_base: tl.tensor
|
| 1219 |
+
v_base: tl.tensor
|
| 1220 |
+
a_base: tl.tensor
|
| 1221 |
+
d_base: tl.tensor
|
| 1222 |
+
out_base: tl.tensor
|
| 1223 |
+
lse_base: tl.tensor
|
| 1224 |
+
head_idx: tl.tensor
|
| 1225 |
+
stride_qh: tl.tensor
|
| 1226 |
+
stride_qm: tl.tensor
|
| 1227 |
+
stride_kn: tl.tensor
|
| 1228 |
+
stride_vn: tl.tensor
|
| 1229 |
+
stride_ah: tl.tensor
|
| 1230 |
+
stride_oh: tl.tensor
|
| 1231 |
+
stride_om: tl.tensor
|
| 1232 |
+
stride_lh: tl.tensor
|
| 1233 |
+
|
| 1234 |
+
@constexpr_function
|
| 1235 |
+
def __init__(
|
| 1236 |
+
self,
|
| 1237 |
+
q_base,
|
| 1238 |
+
k_base,
|
| 1239 |
+
v_base,
|
| 1240 |
+
a_base,
|
| 1241 |
+
d_base,
|
| 1242 |
+
out_base,
|
| 1243 |
+
lse_base,
|
| 1244 |
+
head_idx,
|
| 1245 |
+
stride_qh,
|
| 1246 |
+
stride_qm,
|
| 1247 |
+
stride_kn,
|
| 1248 |
+
stride_vn,
|
| 1249 |
+
stride_ah,
|
| 1250 |
+
stride_oh,
|
| 1251 |
+
stride_om,
|
| 1252 |
+
stride_lh,
|
| 1253 |
+
):
|
| 1254 |
+
self.q_base = q_base
|
| 1255 |
+
self.k_base = k_base
|
| 1256 |
+
self.v_base = v_base
|
| 1257 |
+
self.a_base = a_base
|
| 1258 |
+
self.d_base = d_base
|
| 1259 |
+
self.out_base = out_base
|
| 1260 |
+
self.lse_base = lse_base
|
| 1261 |
+
self.head_idx = head_idx
|
| 1262 |
+
self.stride_qh = stride_qh
|
| 1263 |
+
self.stride_qm = stride_qm
|
| 1264 |
+
self.stride_kn = stride_kn
|
| 1265 |
+
self.stride_vn = stride_vn
|
| 1266 |
+
self.stride_ah = stride_ah
|
| 1267 |
+
self.stride_oh = stride_oh
|
| 1268 |
+
self.stride_om = stride_om
|
| 1269 |
+
self.stride_lh = stride_lh
|
| 1270 |
+
|
| 1271 |
+
@staticmethod
|
| 1272 |
+
@triton.jit
|
| 1273 |
+
def create(
|
| 1274 |
+
config: AttnFwdConfig,
|
| 1275 |
+
Q=None,
|
| 1276 |
+
K=None,
|
| 1277 |
+
V=None,
|
| 1278 |
+
A=None,
|
| 1279 |
+
D=None,
|
| 1280 |
+
Out=None,
|
| 1281 |
+
Lse=None,
|
| 1282 |
+
batch_idx=0,
|
| 1283 |
+
head_idx=0,
|
| 1284 |
+
head_kv_idx=0,
|
| 1285 |
+
split_idx=0,
|
| 1286 |
+
stride_qb=0,
|
| 1287 |
+
stride_qh=0,
|
| 1288 |
+
stride_qm=0,
|
| 1289 |
+
stride_kb=0,
|
| 1290 |
+
stride_kh=0,
|
| 1291 |
+
stride_kn=0,
|
| 1292 |
+
stride_vb=0,
|
| 1293 |
+
stride_vh=0,
|
| 1294 |
+
stride_vn=0,
|
| 1295 |
+
stride_ab=0,
|
| 1296 |
+
stride_ah=0,
|
| 1297 |
+
stride_am=0,
|
| 1298 |
+
stride_db=0,
|
| 1299 |
+
stride_dh=0,
|
| 1300 |
+
stride_dn=0,
|
| 1301 |
+
stride_ob=0,
|
| 1302 |
+
stride_oh=0,
|
| 1303 |
+
stride_om=0,
|
| 1304 |
+
stride_os=0,
|
| 1305 |
+
stride_lb=0,
|
| 1306 |
+
stride_lh=0,
|
| 1307 |
+
stride_ls=0,
|
| 1308 |
+
IS_SPLIT_KV: tl.constexpr = False,
|
| 1309 |
+
IS_GATED: tl.constexpr = False,
|
| 1310 |
+
HAS_CU_SEQLENS_Q: tl.constexpr = False,
|
| 1311 |
+
HAS_CU_SEQLENS_K: tl.constexpr = False,
|
| 1312 |
+
):
|
| 1313 |
+
# Initialize base pointers
|
| 1314 |
+
q_base = seqlen_info.offset_batch_Q(
|
| 1315 |
+
Q + head_idx * stride_qh if not config.PACK_GQA else Q,
|
| 1316 |
+
batch_idx,
|
| 1317 |
+
config.offset_q,
|
| 1318 |
+
config.padded_offset_q,
|
| 1319 |
+
stride_qb,
|
| 1320 |
+
stride_qm,
|
| 1321 |
+
HAS_CU_SEQLENS_Q,
|
| 1322 |
+
USE_PADDED=False,
|
| 1323 |
+
)
|
| 1324 |
+
k_base = seqlen_info.offset_batch_K(
|
| 1325 |
+
K + head_kv_idx * stride_kh,
|
| 1326 |
+
batch_idx,
|
| 1327 |
+
config.offset_k,
|
| 1328 |
+
config.padded_offset_k,
|
| 1329 |
+
stride_kb,
|
| 1330 |
+
stride_kn,
|
| 1331 |
+
HAS_CU_SEQLENS_K,
|
| 1332 |
+
USE_PADDED=False,
|
| 1333 |
+
)
|
| 1334 |
+
v_base = seqlen_info.offset_batch_K(
|
| 1335 |
+
V + head_kv_idx * stride_vh,
|
| 1336 |
+
batch_idx,
|
| 1337 |
+
config.offset_k,
|
| 1338 |
+
config.padded_offset_k,
|
| 1339 |
+
stride_vb,
|
| 1340 |
+
stride_vn,
|
| 1341 |
+
HAS_CU_SEQLENS_K,
|
| 1342 |
+
USE_PADDED=False,
|
| 1343 |
+
)
|
| 1344 |
+
out_base = seqlen_info.offset_batch_Q(
|
| 1345 |
+
Out + head_idx * stride_oh if not config.PACK_GQA else Out,
|
| 1346 |
+
batch_idx,
|
| 1347 |
+
config.offset_q,
|
| 1348 |
+
config.padded_offset_q,
|
| 1349 |
+
stride_ob,
|
| 1350 |
+
stride_om,
|
| 1351 |
+
HAS_CU_SEQLENS_Q,
|
| 1352 |
+
USE_PADDED=False,
|
| 1353 |
+
)
|
| 1354 |
+
lse_base = seqlen_info.offset_batch_Q(
|
| 1355 |
+
Lse + head_idx * stride_lh if not config.PACK_GQA else Lse,
|
| 1356 |
+
batch_idx,
|
| 1357 |
+
config.offset_q,
|
| 1358 |
+
config.padded_offset_q,
|
| 1359 |
+
stride_lb,
|
| 1360 |
+
1,
|
| 1361 |
+
HAS_CU_SEQLENS_Q,
|
| 1362 |
+
USE_PADDED=False,
|
| 1363 |
+
)
|
| 1364 |
+
|
| 1365 |
+
if IS_GATED:
|
| 1366 |
+
a_base = seqlen_info.offset_batch_Q(
|
| 1367 |
+
A + head_idx * stride_ah if not config.PACK_GQA else A,
|
| 1368 |
+
batch_idx,
|
| 1369 |
+
config.offset_q,
|
| 1370 |
+
config.padded_offset_q,
|
| 1371 |
+
stride_ab,
|
| 1372 |
+
stride_am,
|
| 1373 |
+
HAS_CU_SEQLENS_Q,
|
| 1374 |
+
USE_PADDED=False,
|
| 1375 |
+
)
|
| 1376 |
+
d_base = seqlen_info.offset_batch_K(
|
| 1377 |
+
D + head_kv_idx * stride_dh,
|
| 1378 |
+
batch_idx,
|
| 1379 |
+
config.offset_k,
|
| 1380 |
+
config.padded_offset_k,
|
| 1381 |
+
stride_db,
|
| 1382 |
+
stride_dn,
|
| 1383 |
+
HAS_CU_SEQLENS_K,
|
| 1384 |
+
USE_PADDED=False,
|
| 1385 |
+
)
|
| 1386 |
+
else:
|
| 1387 |
+
a_base = tl.full((), 0, tl.int64)
|
| 1388 |
+
d_base = tl.full((), 0, tl.int64)
|
| 1389 |
+
stride_ah = tl.full((), 0, tl.int64)
|
| 1390 |
+
|
| 1391 |
+
# For split KV, offset output and LSE base pointers by split_idx
|
| 1392 |
+
if IS_SPLIT_KV:
|
| 1393 |
+
out_base += split_idx * stride_os
|
| 1394 |
+
lse_base += split_idx * stride_ls
|
| 1395 |
+
|
| 1396 |
+
# TODO: remove once Triton aggregate accepts host scalar strides or callers materialize strides
|
| 1397 |
+
return AttnFwdPointerScheduler(
|
| 1398 |
+
q_base,
|
| 1399 |
+
k_base,
|
| 1400 |
+
v_base,
|
| 1401 |
+
a_base,
|
| 1402 |
+
d_base,
|
| 1403 |
+
out_base,
|
| 1404 |
+
lse_base,
|
| 1405 |
+
head_idx,
|
| 1406 |
+
stride_qh + tl.full((), 0, tl.int64),
|
| 1407 |
+
stride_qm + tl.full((), 0, tl.int64),
|
| 1408 |
+
stride_kn + tl.full((), 0, tl.int64),
|
| 1409 |
+
stride_vn + tl.full((), 0, tl.int64),
|
| 1410 |
+
stride_ah + tl.full((), 0, tl.int64),
|
| 1411 |
+
stride_oh + tl.full((), 0, tl.int64),
|
| 1412 |
+
stride_om + tl.full((), 0, tl.int64),
|
| 1413 |
+
stride_lh + tl.full((), 0, tl.int64),
|
| 1414 |
+
)
|
| 1415 |
+
|
| 1416 |
+
@triton.jit
|
| 1417 |
+
def make_q_ptrs(self, config: AttnFwdConfig):
|
| 1418 |
+
if config.PACK_GQA:
|
| 1419 |
+
return seqlen_info.make_pack_gqa_ptrs(
|
| 1420 |
+
self.q_base,
|
| 1421 |
+
config.m_block,
|
| 1422 |
+
self.head_idx,
|
| 1423 |
+
self.stride_qh,
|
| 1424 |
+
self.stride_qm,
|
| 1425 |
+
TILE_M=config.TILE_M,
|
| 1426 |
+
TILE_K=config.TILE_K,
|
| 1427 |
+
QHEAD_PER_KVHEAD_PACKGQA=config.QHEAD_PER_KVHEAD_PACKGQA,
|
| 1428 |
+
)
|
| 1429 |
+
else:
|
| 1430 |
+
return tl.make_tensor_descriptor(
|
| 1431 |
+
self.q_base,
|
| 1432 |
+
shape=[config.actual_seqlen_q, config.head_dim],
|
| 1433 |
+
strides=[self.stride_qm, 1],
|
| 1434 |
+
block_shape=[config.TILE_M, config.TILE_K],
|
| 1435 |
+
)
|
| 1436 |
+
|
| 1437 |
+
@triton.jit
|
| 1438 |
+
def make_k_ptrs(self, config: AttnFwdConfig):
|
| 1439 |
+
return tl.make_tensor_descriptor(
|
| 1440 |
+
self.k_base,
|
| 1441 |
+
shape=[config.actual_seqlen_k, config.head_dim],
|
| 1442 |
+
strides=[self.stride_kn, 1],
|
| 1443 |
+
block_shape=[config.TILE_N, config.TILE_K],
|
| 1444 |
+
)
|
| 1445 |
+
|
| 1446 |
+
@triton.jit
|
| 1447 |
+
def make_v_ptrs(self, config: AttnFwdConfig):
|
| 1448 |
+
return tl.make_tensor_descriptor(
|
| 1449 |
+
self.v_base,
|
| 1450 |
+
shape=[config.actual_seqlen_k, config.head_dim],
|
| 1451 |
+
strides=[self.stride_vn, 1],
|
| 1452 |
+
block_shape=[config.TILE_N, config.TILE_K],
|
| 1453 |
+
)
|
| 1454 |
+
|
| 1455 |
+
@triton.jit
|
| 1456 |
+
def make_a_ptrs(self, config: AttnFwdConfig):
|
| 1457 |
+
if config.PACK_GQA:
|
| 1458 |
+
return seqlen_info.make_pack_gqa_ptrs(
|
| 1459 |
+
self.a_base,
|
| 1460 |
+
config.m_block,
|
| 1461 |
+
self.head_idx,
|
| 1462 |
+
self.stride_ah,
|
| 1463 |
+
1,
|
| 1464 |
+
TILE_M=config.TILE_M,
|
| 1465 |
+
TILE_K=1,
|
| 1466 |
+
QHEAD_PER_KVHEAD_PACKGQA=config.QHEAD_PER_KVHEAD_PACKGQA,
|
| 1467 |
+
)
|
| 1468 |
+
else:
|
| 1469 |
+
return tl.make_tensor_descriptor(
|
| 1470 |
+
self.a_base,
|
| 1471 |
+
shape=[config.actual_seqlen_q],
|
| 1472 |
+
strides=[1],
|
| 1473 |
+
block_shape=[config.TILE_M],
|
| 1474 |
+
)
|
| 1475 |
+
|
| 1476 |
+
@triton.jit
|
| 1477 |
+
def make_d_ptrs(self, config: AttnFwdConfig):
|
| 1478 |
+
return tl.make_tensor_descriptor(
|
| 1479 |
+
self.d_base,
|
| 1480 |
+
shape=[config.actual_seqlen_k],
|
| 1481 |
+
strides=[1],
|
| 1482 |
+
block_shape=[config.TILE_N],
|
| 1483 |
+
)
|
| 1484 |
+
|
| 1485 |
+
@triton.jit
|
| 1486 |
+
def make_out_ptrs(self, config: AttnFwdConfig):
|
| 1487 |
+
if config.PACK_GQA:
|
| 1488 |
+
return seqlen_info.make_pack_gqa_ptrs(
|
| 1489 |
+
self.out_base,
|
| 1490 |
+
config.m_block,
|
| 1491 |
+
self.head_idx,
|
| 1492 |
+
self.stride_oh,
|
| 1493 |
+
self.stride_om,
|
| 1494 |
+
TILE_M=config.TILE_M,
|
| 1495 |
+
TILE_K=config.TILE_K,
|
| 1496 |
+
QHEAD_PER_KVHEAD_PACKGQA=config.QHEAD_PER_KVHEAD_PACKGQA,
|
| 1497 |
+
)
|
| 1498 |
+
else:
|
| 1499 |
+
return tl.make_tensor_descriptor(
|
| 1500 |
+
self.out_base,
|
| 1501 |
+
shape=[config.actual_seqlen_q, config.head_dim],
|
| 1502 |
+
strides=[self.stride_om, 1],
|
| 1503 |
+
block_shape=[config.TILE_M, config.TILE_K],
|
| 1504 |
+
)
|
| 1505 |
+
|
| 1506 |
+
@triton.jit
|
| 1507 |
+
def make_lse_ptrs(self, config: AttnFwdConfig):
|
| 1508 |
+
if config.PACK_GQA:
|
| 1509 |
+
return seqlen_info.make_pack_gqa_ptrs(
|
| 1510 |
+
self.lse_base,
|
| 1511 |
+
config.m_block,
|
| 1512 |
+
self.head_idx,
|
| 1513 |
+
self.stride_lh,
|
| 1514 |
+
1,
|
| 1515 |
+
TILE_M=config.TILE_M,
|
| 1516 |
+
TILE_K=1,
|
| 1517 |
+
QHEAD_PER_KVHEAD_PACKGQA=config.QHEAD_PER_KVHEAD_PACKGQA,
|
| 1518 |
+
)
|
| 1519 |
+
else:
|
| 1520 |
+
return tl.make_tensor_descriptor(
|
| 1521 |
+
self.lse_base,
|
| 1522 |
+
shape=[config.actual_seqlen_q],
|
| 1523 |
+
strides=[1],
|
| 1524 |
+
block_shape=[config.TILE_M],
|
| 1525 |
+
)
|
| 1526 |
+
|
| 1527 |
+
@triton.jit
|
| 1528 |
+
def load_q(self, config: AttnFwdConfig, q_ptrs):
|
| 1529 |
+
if config.PACK_GQA:
|
| 1530 |
+
offs_m = config.get_offs_m()
|
| 1531 |
+
offs_k = config.get_offs_k()
|
| 1532 |
+
return tl.load(
|
| 1533 |
+
q_ptrs,
|
| 1534 |
+
mask=(
|
| 1535 |
+
(offs_m // config.QHEAD_PER_KVHEAD_PACKGQA) < config.actual_seqlen_q
|
| 1536 |
+
)[:, None]
|
| 1537 |
+
& (offs_k < config.head_dim)[None, :],
|
| 1538 |
+
other=0.0,
|
| 1539 |
+
cache_modifier=".ca",
|
| 1540 |
+
)
|
| 1541 |
+
else:
|
| 1542 |
+
return q_ptrs.load([config.m_block * config.TILE_M, 0])
|
| 1543 |
+
|
| 1544 |
+
@triton.jit
|
| 1545 |
+
def load_k(self, config: AttnFwdConfig, k_ptrs, n_block):
|
| 1546 |
+
return k_ptrs.load([n_block * config.TILE_N, 0])
|
| 1547 |
+
|
| 1548 |
+
@triton.jit
|
| 1549 |
+
def load_v(self, config: AttnFwdConfig, v_ptrs, n_block):
|
| 1550 |
+
return v_ptrs.load([n_block * config.TILE_N, 0])
|
| 1551 |
+
|
| 1552 |
+
@triton.jit
|
| 1553 |
+
def load_a(self, config: AttnFwdConfig, a_ptrs):
|
| 1554 |
+
if config.PACK_GQA:
|
| 1555 |
+
offs_m = config.get_offs_m()
|
| 1556 |
+
return tl.load(
|
| 1557 |
+
a_ptrs,
|
| 1558 |
+
mask=(offs_m // config.QHEAD_PER_KVHEAD_PACKGQA)
|
| 1559 |
+
< config.actual_seqlen_q,
|
| 1560 |
+
other=0.0,
|
| 1561 |
+
).to(tl.float32)
|
| 1562 |
+
else:
|
| 1563 |
+
return a_ptrs.load([config.m_block * config.TILE_M]).to(tl.float32)
|
| 1564 |
+
|
| 1565 |
+
@triton.jit
|
| 1566 |
+
def load_d(self, config: AttnFwdConfig, d_ptrs, n_block):
|
| 1567 |
+
return d_ptrs.load([n_block * config.TILE_N]).to(tl.float32)
|
| 1568 |
+
|
| 1569 |
+
@triton.jit
|
| 1570 |
+
def store_out(
|
| 1571 |
+
self, config: AttnFwdConfig, out_ptrs, o_tile, IS_SPLIT_KV: tl.constexpr = False
|
| 1572 |
+
):
|
| 1573 |
+
if not IS_SPLIT_KV:
|
| 1574 |
+
if config.PACK_GQA:
|
| 1575 |
+
o_tile = o_tile.to(out_ptrs.dtype.element_ty)
|
| 1576 |
+
else:
|
| 1577 |
+
o_tile = o_tile.to(out_ptrs.dtype)
|
| 1578 |
+
if config.PACK_GQA:
|
| 1579 |
+
offs_m = config.get_offs_m()
|
| 1580 |
+
offs_k = config.get_offs_k()
|
| 1581 |
+
tl.store(
|
| 1582 |
+
out_ptrs,
|
| 1583 |
+
o_tile,
|
| 1584 |
+
mask=(
|
| 1585 |
+
(offs_m // config.QHEAD_PER_KVHEAD_PACKGQA) < config.actual_seqlen_q
|
| 1586 |
+
)[:, None]
|
| 1587 |
+
& (offs_k < config.head_dim)[None, :],
|
| 1588 |
+
cache_modifier=".wb",
|
| 1589 |
+
)
|
| 1590 |
+
else:
|
| 1591 |
+
out_ptrs.store([config.m_block * config.TILE_M, 0], o_tile)
|
| 1592 |
+
|
| 1593 |
+
@triton.jit
|
| 1594 |
+
def store_lse(self, config: AttnFwdConfig, lse_ptrs, lse_tile):
|
| 1595 |
+
if config.PACK_GQA:
|
| 1596 |
+
offs_m = config.get_offs_m()
|
| 1597 |
+
tl.store(
|
| 1598 |
+
lse_ptrs,
|
| 1599 |
+
lse_tile,
|
| 1600 |
+
mask=(
|
| 1601 |
+
(offs_m // config.QHEAD_PER_KVHEAD_PACKGQA) < config.actual_seqlen_q
|
| 1602 |
+
),
|
| 1603 |
+
cache_modifier=".wb",
|
| 1604 |
+
)
|
| 1605 |
+
else:
|
| 1606 |
+
lse_ptrs.store([config.m_block * config.TILE_M], lse_tile)
|
| 1607 |
+
|
| 1608 |
+
@triton.jit
|
| 1609 |
+
def store_empty(
|
| 1610 |
+
self,
|
| 1611 |
+
config: AttnFwdConfig,
|
| 1612 |
+
out_ptrs,
|
| 1613 |
+
lse_ptrs,
|
| 1614 |
+
IS_SPLIT_KV: tl.constexpr = False,
|
| 1615 |
+
):
|
| 1616 |
+
lse_tile = tl.full((config.TILE_M,), float("-inf"), dtype=tl.float32)
|
| 1617 |
+
self.store_lse(config, lse_ptrs, lse_tile)
|
| 1618 |
+
o_tile = tl.zeros((config.TILE_M, config.TILE_K), dtype=tl.float32)
|
| 1619 |
+
self.store_out(config, out_ptrs, o_tile, IS_SPLIT_KV=IS_SPLIT_KV)
|
| 1620 |
+
|
| 1621 |
+
|
| 1622 |
+
@aggregate
|
| 1623 |
+
class AttnBwdPointerScheduler:
|
| 1624 |
+
q_base: tl.tensor
|
| 1625 |
+
k_base: tl.tensor
|
| 1626 |
+
v_base: tl.tensor
|
| 1627 |
+
a_base: tl.tensor
|
| 1628 |
+
d_base: tl.tensor
|
| 1629 |
+
do_base: tl.tensor
|
| 1630 |
+
lse_base: tl.tensor
|
| 1631 |
+
dpsum_base: tl.tensor
|
| 1632 |
+
dq_accum_base: tl.tensor
|
| 1633 |
+
dk_base: tl.tensor
|
| 1634 |
+
dv_base: tl.tensor
|
| 1635 |
+
da_base: tl.tensor
|
| 1636 |
+
dd_base: tl.tensor
|
| 1637 |
+
stride_qm: tl.tensor
|
| 1638 |
+
stride_kn: tl.tensor
|
| 1639 |
+
stride_vn: tl.tensor
|
| 1640 |
+
stride_dom: tl.tensor
|
| 1641 |
+
stride_dqam: tl.tensor
|
| 1642 |
+
stride_dkn: tl.tensor
|
| 1643 |
+
stride_dvn: tl.tensor
|
| 1644 |
+
|
| 1645 |
+
@constexpr_function
|
| 1646 |
+
def __init__(
|
| 1647 |
+
self,
|
| 1648 |
+
q_base,
|
| 1649 |
+
k_base,
|
| 1650 |
+
v_base,
|
| 1651 |
+
a_base,
|
| 1652 |
+
d_base,
|
| 1653 |
+
do_base,
|
| 1654 |
+
lse_base,
|
| 1655 |
+
dpsum_base,
|
| 1656 |
+
dq_accum_base,
|
| 1657 |
+
dk_base,
|
| 1658 |
+
dv_base,
|
| 1659 |
+
da_base,
|
| 1660 |
+
dd_base,
|
| 1661 |
+
stride_qm,
|
| 1662 |
+
stride_kn,
|
| 1663 |
+
stride_vn,
|
| 1664 |
+
stride_dom,
|
| 1665 |
+
stride_dqam,
|
| 1666 |
+
stride_dkn,
|
| 1667 |
+
stride_dvn,
|
| 1668 |
+
):
|
| 1669 |
+
self.q_base = q_base
|
| 1670 |
+
self.k_base = k_base
|
| 1671 |
+
self.v_base = v_base
|
| 1672 |
+
self.a_base = a_base
|
| 1673 |
+
self.d_base = d_base
|
| 1674 |
+
self.do_base = do_base
|
| 1675 |
+
self.lse_base = lse_base
|
| 1676 |
+
self.dpsum_base = dpsum_base
|
| 1677 |
+
self.dq_accum_base = dq_accum_base
|
| 1678 |
+
self.dk_base = dk_base
|
| 1679 |
+
self.dv_base = dv_base
|
| 1680 |
+
self.da_base = da_base
|
| 1681 |
+
self.dd_base = dd_base
|
| 1682 |
+
self.stride_qm = stride_qm
|
| 1683 |
+
self.stride_kn = stride_kn
|
| 1684 |
+
self.stride_vn = stride_vn
|
| 1685 |
+
self.stride_dom = stride_dom
|
| 1686 |
+
self.stride_dqam = stride_dqam
|
| 1687 |
+
self.stride_dkn = stride_dkn
|
| 1688 |
+
self.stride_dvn = stride_dvn
|
| 1689 |
+
|
| 1690 |
+
@staticmethod
|
| 1691 |
+
@triton.jit
|
| 1692 |
+
def create(
|
| 1693 |
+
config: AttnBwdConfig,
|
| 1694 |
+
Q=None,
|
| 1695 |
+
K=None,
|
| 1696 |
+
V=None,
|
| 1697 |
+
A=None,
|
| 1698 |
+
D=None,
|
| 1699 |
+
dO=None,
|
| 1700 |
+
LSELog2=None,
|
| 1701 |
+
dPsum=None,
|
| 1702 |
+
dQaccum=None,
|
| 1703 |
+
dK=None,
|
| 1704 |
+
dV=None,
|
| 1705 |
+
dA=None,
|
| 1706 |
+
dD=None,
|
| 1707 |
+
batch_idx=0,
|
| 1708 |
+
head_idx=0,
|
| 1709 |
+
head_kv_idx=0,
|
| 1710 |
+
split_idx=0,
|
| 1711 |
+
stride_qb=0,
|
| 1712 |
+
stride_qh=0,
|
| 1713 |
+
stride_qm=0,
|
| 1714 |
+
stride_kb=0,
|
| 1715 |
+
stride_kh=0,
|
| 1716 |
+
stride_kn=0,
|
| 1717 |
+
stride_vb=0,
|
| 1718 |
+
stride_vh=0,
|
| 1719 |
+
stride_vn=0,
|
| 1720 |
+
stride_ab=0,
|
| 1721 |
+
stride_ah=0,
|
| 1722 |
+
stride_db=0,
|
| 1723 |
+
stride_dh=0,
|
| 1724 |
+
stride_dob=0,
|
| 1725 |
+
stride_doh=0,
|
| 1726 |
+
stride_dom=0,
|
| 1727 |
+
stride_lb=0,
|
| 1728 |
+
stride_lh=0,
|
| 1729 |
+
stride_pb=0,
|
| 1730 |
+
stride_ph=0,
|
| 1731 |
+
stride_dqab=0,
|
| 1732 |
+
stride_dqah=0,
|
| 1733 |
+
stride_dqam=0,
|
| 1734 |
+
stride_dkb=0,
|
| 1735 |
+
stride_dkh=0,
|
| 1736 |
+
stride_dkn=0,
|
| 1737 |
+
stride_dks=0,
|
| 1738 |
+
stride_dvb=0,
|
| 1739 |
+
stride_dvh=0,
|
| 1740 |
+
stride_dvn=0,
|
| 1741 |
+
stride_dvs=0,
|
| 1742 |
+
stride_dab=0,
|
| 1743 |
+
stride_dah=0,
|
| 1744 |
+
stride_ddb=0,
|
| 1745 |
+
stride_ddh=0,
|
| 1746 |
+
stride_dds=0,
|
| 1747 |
+
IS_SPLIT_QO: tl.constexpr = False,
|
| 1748 |
+
IS_GATED: tl.constexpr = False,
|
| 1749 |
+
HAS_CU_SEQLENS_Q: tl.constexpr = False,
|
| 1750 |
+
HAS_CU_SEQLENS_K: tl.constexpr = False,
|
| 1751 |
+
):
|
| 1752 |
+
# Initialize base pointers
|
| 1753 |
+
q_base = seqlen_info.offset_batch_Q(
|
| 1754 |
+
Q + head_idx * stride_qh,
|
| 1755 |
+
batch_idx,
|
| 1756 |
+
config.offset_q,
|
| 1757 |
+
config.padded_offset_q,
|
| 1758 |
+
stride_qb,
|
| 1759 |
+
stride_qm,
|
| 1760 |
+
HAS_CU_SEQLENS_Q,
|
| 1761 |
+
USE_PADDED=False,
|
| 1762 |
+
)
|
| 1763 |
+
k_base = seqlen_info.offset_batch_K(
|
| 1764 |
+
K + head_kv_idx * stride_kh,
|
| 1765 |
+
batch_idx,
|
| 1766 |
+
config.offset_k,
|
| 1767 |
+
config.padded_offset_k,
|
| 1768 |
+
stride_kb,
|
| 1769 |
+
stride_kn,
|
| 1770 |
+
HAS_CU_SEQLENS_K,
|
| 1771 |
+
USE_PADDED=False,
|
| 1772 |
+
)
|
| 1773 |
+
v_base = seqlen_info.offset_batch_K(
|
| 1774 |
+
V + head_kv_idx * stride_vh,
|
| 1775 |
+
batch_idx,
|
| 1776 |
+
config.offset_k,
|
| 1777 |
+
config.padded_offset_k,
|
| 1778 |
+
stride_vb,
|
| 1779 |
+
stride_vn,
|
| 1780 |
+
HAS_CU_SEQLENS_K,
|
| 1781 |
+
USE_PADDED=False,
|
| 1782 |
+
)
|
| 1783 |
+
do_base = seqlen_info.offset_batch_Q(
|
| 1784 |
+
dO + head_idx * stride_doh,
|
| 1785 |
+
batch_idx,
|
| 1786 |
+
config.offset_q,
|
| 1787 |
+
config.padded_offset_q,
|
| 1788 |
+
stride_dob,
|
| 1789 |
+
stride_dom,
|
| 1790 |
+
HAS_CU_SEQLENS_Q,
|
| 1791 |
+
USE_PADDED=False,
|
| 1792 |
+
)
|
| 1793 |
+
lse_base = seqlen_info.offset_batch_Q(
|
| 1794 |
+
LSELog2 + head_idx * stride_lh,
|
| 1795 |
+
batch_idx,
|
| 1796 |
+
config.offset_q,
|
| 1797 |
+
config.padded_offset_q,
|
| 1798 |
+
stride_lb,
|
| 1799 |
+
1,
|
| 1800 |
+
HAS_CU_SEQLENS_Q,
|
| 1801 |
+
USE_PADDED=True,
|
| 1802 |
+
)
|
| 1803 |
+
dpsum_base = seqlen_info.offset_batch_Q(
|
| 1804 |
+
dPsum + head_idx * stride_ph,
|
| 1805 |
+
batch_idx,
|
| 1806 |
+
config.offset_q,
|
| 1807 |
+
config.padded_offset_q,
|
| 1808 |
+
stride_pb,
|
| 1809 |
+
1,
|
| 1810 |
+
HAS_CU_SEQLENS_Q,
|
| 1811 |
+
USE_PADDED=True,
|
| 1812 |
+
)
|
| 1813 |
+
dq_accum_base = seqlen_info.offset_batch_Q(
|
| 1814 |
+
dQaccum + head_idx * stride_dqah,
|
| 1815 |
+
batch_idx,
|
| 1816 |
+
config.offset_q,
|
| 1817 |
+
config.padded_offset_q,
|
| 1818 |
+
stride_dqab,
|
| 1819 |
+
stride_dqam,
|
| 1820 |
+
HAS_CU_SEQLENS_Q,
|
| 1821 |
+
USE_PADDED=True,
|
| 1822 |
+
)
|
| 1823 |
+
dk_base = seqlen_info.offset_batch_K(
|
| 1824 |
+
dK + head_kv_idx * stride_dkh,
|
| 1825 |
+
batch_idx,
|
| 1826 |
+
config.offset_k,
|
| 1827 |
+
config.padded_offset_k,
|
| 1828 |
+
stride_dkb,
|
| 1829 |
+
stride_dkn,
|
| 1830 |
+
HAS_CU_SEQLENS_K,
|
| 1831 |
+
USE_PADDED=False,
|
| 1832 |
+
)
|
| 1833 |
+
dv_base = seqlen_info.offset_batch_K(
|
| 1834 |
+
dV + head_kv_idx * stride_dvh,
|
| 1835 |
+
batch_idx,
|
| 1836 |
+
config.offset_k,
|
| 1837 |
+
config.padded_offset_k,
|
| 1838 |
+
stride_dvb,
|
| 1839 |
+
stride_dvn,
|
| 1840 |
+
HAS_CU_SEQLENS_K,
|
| 1841 |
+
USE_PADDED=False,
|
| 1842 |
+
)
|
| 1843 |
+
|
| 1844 |
+
if IS_GATED:
|
| 1845 |
+
a_base = seqlen_info.offset_batch_Q(
|
| 1846 |
+
A + head_idx * stride_ah,
|
| 1847 |
+
batch_idx,
|
| 1848 |
+
config.offset_q,
|
| 1849 |
+
config.padded_offset_q,
|
| 1850 |
+
stride_ab,
|
| 1851 |
+
1,
|
| 1852 |
+
HAS_CU_SEQLENS_Q,
|
| 1853 |
+
USE_PADDED=False,
|
| 1854 |
+
)
|
| 1855 |
+
d_base = seqlen_info.offset_batch_K(
|
| 1856 |
+
D + head_kv_idx * stride_dh,
|
| 1857 |
+
batch_idx,
|
| 1858 |
+
config.offset_k,
|
| 1859 |
+
config.padded_offset_k,
|
| 1860 |
+
stride_db,
|
| 1861 |
+
1,
|
| 1862 |
+
HAS_CU_SEQLENS_K,
|
| 1863 |
+
USE_PADDED=False,
|
| 1864 |
+
)
|
| 1865 |
+
da_base = seqlen_info.offset_batch_Q(
|
| 1866 |
+
dA + head_idx * stride_dah,
|
| 1867 |
+
batch_idx,
|
| 1868 |
+
config.offset_q,
|
| 1869 |
+
config.padded_offset_q,
|
| 1870 |
+
stride_dab,
|
| 1871 |
+
1,
|
| 1872 |
+
HAS_CU_SEQLENS_Q,
|
| 1873 |
+
USE_PADDED=True,
|
| 1874 |
+
)
|
| 1875 |
+
dd_base = seqlen_info.offset_batch_K(
|
| 1876 |
+
dD + head_kv_idx * stride_ddh,
|
| 1877 |
+
batch_idx,
|
| 1878 |
+
config.offset_k,
|
| 1879 |
+
config.padded_offset_k,
|
| 1880 |
+
stride_ddb,
|
| 1881 |
+
1,
|
| 1882 |
+
HAS_CU_SEQLENS_K,
|
| 1883 |
+
USE_PADDED=False,
|
| 1884 |
+
)
|
| 1885 |
+
else:
|
| 1886 |
+
a_base = tl.full((), 0, tl.int64)
|
| 1887 |
+
d_base = tl.full((), 0, tl.int64)
|
| 1888 |
+
da_base = tl.full((), 0, tl.int64)
|
| 1889 |
+
dd_base = tl.full((), 0, tl.int64)
|
| 1890 |
+
|
| 1891 |
+
# For split QO, offset key and value gradients base pointers by split_idx
|
| 1892 |
+
if IS_SPLIT_QO:
|
| 1893 |
+
dk_base += split_idx * stride_dks
|
| 1894 |
+
dv_base += split_idx * stride_dvs
|
| 1895 |
+
if IS_GATED:
|
| 1896 |
+
dd_base += split_idx * stride_dds
|
| 1897 |
+
|
| 1898 |
+
# TODO: remove once Triton aggregate accepts host scalar strides or callers materialize strides
|
| 1899 |
+
return AttnBwdPointerScheduler(
|
| 1900 |
+
q_base,
|
| 1901 |
+
k_base,
|
| 1902 |
+
v_base,
|
| 1903 |
+
a_base,
|
| 1904 |
+
d_base,
|
| 1905 |
+
do_base,
|
| 1906 |
+
lse_base,
|
| 1907 |
+
dpsum_base,
|
| 1908 |
+
dq_accum_base,
|
| 1909 |
+
dk_base,
|
| 1910 |
+
dv_base,
|
| 1911 |
+
da_base,
|
| 1912 |
+
dd_base,
|
| 1913 |
+
stride_qm + tl.full((), 0, tl.int64),
|
| 1914 |
+
stride_kn + tl.full((), 0, tl.int64),
|
| 1915 |
+
stride_vn + tl.full((), 0, tl.int64),
|
| 1916 |
+
stride_dom + tl.full((), 0, tl.int64),
|
| 1917 |
+
stride_dqam + tl.full((), 0, tl.int64),
|
| 1918 |
+
stride_dkn + tl.full((), 0, tl.int64),
|
| 1919 |
+
stride_dvn + tl.full((), 0, tl.int64),
|
| 1920 |
+
)
|
| 1921 |
+
|
| 1922 |
+
@triton.jit
|
| 1923 |
+
def make_q_ptrs(self, config: AttnBwdConfig):
|
| 1924 |
+
return tl.make_tensor_descriptor(
|
| 1925 |
+
self.q_base,
|
| 1926 |
+
shape=[config.actual_seqlen_q, config.head_dim],
|
| 1927 |
+
strides=[self.stride_qm, 1],
|
| 1928 |
+
block_shape=[config.TILE_M, config.TILE_K],
|
| 1929 |
+
)
|
| 1930 |
+
|
| 1931 |
+
@triton.jit
|
| 1932 |
+
def make_k_ptrs(self, config: AttnBwdConfig):
|
| 1933 |
+
return tl.make_tensor_descriptor(
|
| 1934 |
+
self.k_base,
|
| 1935 |
+
shape=[config.actual_seqlen_k, config.head_dim],
|
| 1936 |
+
strides=[self.stride_kn, 1],
|
| 1937 |
+
block_shape=[config.TILE_N, config.TILE_K],
|
| 1938 |
+
)
|
| 1939 |
+
|
| 1940 |
+
@triton.jit
|
| 1941 |
+
def make_v_ptrs(self, config: AttnBwdConfig):
|
| 1942 |
+
return tl.make_tensor_descriptor(
|
| 1943 |
+
self.v_base,
|
| 1944 |
+
shape=[config.actual_seqlen_k, config.head_dim],
|
| 1945 |
+
strides=[self.stride_vn, 1],
|
| 1946 |
+
block_shape=[config.TILE_N, config.TILE_K],
|
| 1947 |
+
)
|
| 1948 |
+
|
| 1949 |
+
@triton.jit
|
| 1950 |
+
def make_a_ptrs(self, config: AttnBwdConfig):
|
| 1951 |
+
return tl.make_tensor_descriptor(
|
| 1952 |
+
self.a_base,
|
| 1953 |
+
shape=[config.actual_seqlen_q],
|
| 1954 |
+
strides=[1],
|
| 1955 |
+
block_shape=[config.TILE_M],
|
| 1956 |
+
)
|
| 1957 |
+
|
| 1958 |
+
@triton.jit
|
| 1959 |
+
def make_d_ptrs(self, config: AttnBwdConfig):
|
| 1960 |
+
return tl.make_tensor_descriptor(
|
| 1961 |
+
self.d_base,
|
| 1962 |
+
shape=[config.actual_seqlen_k],
|
| 1963 |
+
strides=[1],
|
| 1964 |
+
block_shape=[config.TILE_N],
|
| 1965 |
+
)
|
| 1966 |
+
|
| 1967 |
+
@triton.jit
|
| 1968 |
+
def make_do_ptrs(self, config: AttnBwdConfig):
|
| 1969 |
+
return tl.make_tensor_descriptor(
|
| 1970 |
+
self.do_base,
|
| 1971 |
+
shape=[config.actual_seqlen_q, config.head_dim],
|
| 1972 |
+
strides=[self.stride_dom, 1],
|
| 1973 |
+
block_shape=[config.TILE_M, config.TILE_K],
|
| 1974 |
+
)
|
| 1975 |
+
|
| 1976 |
+
@triton.jit
|
| 1977 |
+
def make_lse_ptrs(self, config: AttnBwdConfig):
|
| 1978 |
+
return tl.make_tensor_descriptor(
|
| 1979 |
+
self.lse_base,
|
| 1980 |
+
shape=[config.actual_seqlen_q],
|
| 1981 |
+
strides=[1],
|
| 1982 |
+
block_shape=[config.TILE_M],
|
| 1983 |
+
)
|
| 1984 |
+
|
| 1985 |
+
@triton.jit
|
| 1986 |
+
def make_dpsum_ptrs(self, config: AttnBwdConfig):
|
| 1987 |
+
return tl.make_tensor_descriptor(
|
| 1988 |
+
self.dpsum_base,
|
| 1989 |
+
shape=[config.actual_seqlen_q],
|
| 1990 |
+
strides=[1],
|
| 1991 |
+
block_shape=[config.TILE_M],
|
| 1992 |
+
)
|
| 1993 |
+
|
| 1994 |
+
@triton.jit
|
| 1995 |
+
def make_dq_accum_ptrs(self, config: AttnBwdConfig):
|
| 1996 |
+
return tl.make_tensor_descriptor(
|
| 1997 |
+
self.dq_accum_base,
|
| 1998 |
+
shape=[config.actual_seqlen_q, config.head_dim],
|
| 1999 |
+
strides=[self.stride_dqam, 1],
|
| 2000 |
+
block_shape=[config.TILE_M, config.TILE_K],
|
| 2001 |
+
)
|
| 2002 |
+
|
| 2003 |
+
@triton.jit
|
| 2004 |
+
def make_dk_ptrs(self, config: AttnBwdConfig):
|
| 2005 |
+
return tl.make_tensor_descriptor(
|
| 2006 |
+
self.dk_base,
|
| 2007 |
+
shape=[config.actual_seqlen_k, config.head_dim],
|
| 2008 |
+
strides=[self.stride_dkn, 1],
|
| 2009 |
+
block_shape=[config.TILE_N, config.TILE_K],
|
| 2010 |
+
)
|
| 2011 |
+
|
| 2012 |
+
@triton.jit
|
| 2013 |
+
def make_dv_ptrs(self, config: AttnBwdConfig):
|
| 2014 |
+
return tl.make_tensor_descriptor(
|
| 2015 |
+
self.dv_base,
|
| 2016 |
+
shape=[config.actual_seqlen_k, config.head_dim],
|
| 2017 |
+
strides=[self.stride_dvn, 1],
|
| 2018 |
+
block_shape=[config.TILE_N, config.TILE_K],
|
| 2019 |
+
)
|
| 2020 |
+
|
| 2021 |
+
@triton.jit
|
| 2022 |
+
def make_da_ptrs(self, config: AttnBwdConfig):
|
| 2023 |
+
return tl.make_tensor_descriptor(
|
| 2024 |
+
self.da_base,
|
| 2025 |
+
shape=[config.actual_seqlen_q],
|
| 2026 |
+
strides=[1],
|
| 2027 |
+
block_shape=[config.TILE_M],
|
| 2028 |
+
)
|
| 2029 |
+
|
| 2030 |
+
@triton.jit
|
| 2031 |
+
def make_dd_ptrs(self, config: AttnBwdConfig):
|
| 2032 |
+
return tl.make_tensor_descriptor(
|
| 2033 |
+
self.dd_base,
|
| 2034 |
+
shape=[config.actual_seqlen_k],
|
| 2035 |
+
strides=[1],
|
| 2036 |
+
block_shape=[config.TILE_N],
|
| 2037 |
+
)
|
| 2038 |
+
|
| 2039 |
+
@triton.jit
|
| 2040 |
+
def load_q(self, config: AttnBwdConfig, q_ptrs, m_block):
|
| 2041 |
+
return (q_ptrs.load([m_block * config.TILE_M, 0]) * config.query_scale).to(
|
| 2042 |
+
self.do_base.dtype.element_ty
|
| 2043 |
+
)
|
| 2044 |
+
|
| 2045 |
+
@triton.jit
|
| 2046 |
+
def load_k(self, config: AttnBwdConfig, k_ptrs):
|
| 2047 |
+
return (k_ptrs.load([config.n_block * config.TILE_N, 0]) * config.key_scale).to(
|
| 2048 |
+
self.do_base.dtype.element_ty
|
| 2049 |
+
)
|
| 2050 |
+
|
| 2051 |
+
@triton.jit
|
| 2052 |
+
def load_v(self, config: AttnBwdConfig, v_ptrs):
|
| 2053 |
+
return (
|
| 2054 |
+
v_ptrs.load([config.n_block * config.TILE_N, 0]) * config.value_scale
|
| 2055 |
+
).to(self.do_base.dtype.element_ty)
|
| 2056 |
+
|
| 2057 |
+
@triton.jit
|
| 2058 |
+
def load_a(self, config: AttnBwdConfig, a_ptrs, m_block):
|
| 2059 |
+
return a_ptrs.load([m_block * config.TILE_M]).to(tl.float32)
|
| 2060 |
+
|
| 2061 |
+
@triton.jit
|
| 2062 |
+
def load_d(self, config: AttnBwdConfig, d_ptrs):
|
| 2063 |
+
return d_ptrs.load([config.n_block * config.TILE_N]).to(tl.float32)
|
| 2064 |
+
|
| 2065 |
+
@triton.jit
|
| 2066 |
+
def load_do(self, config: AttnBwdConfig, do_ptrs, m_block):
|
| 2067 |
+
return do_ptrs.load([m_block * config.TILE_M, 0])
|
| 2068 |
+
|
| 2069 |
+
@triton.jit
|
| 2070 |
+
def load_lse(self, config: AttnBwdConfig, lse_ptrs, m_block):
|
| 2071 |
+
return lse_ptrs.load([m_block * config.TILE_M])
|
| 2072 |
+
|
| 2073 |
+
@triton.jit
|
| 2074 |
+
def load_dpsum(self, config: AttnBwdConfig, dpsum_ptrs, m_block):
|
| 2075 |
+
return dpsum_ptrs.load([m_block * config.TILE_M])
|
| 2076 |
+
|
| 2077 |
+
@triton.jit
|
| 2078 |
+
def store_dq(self, config: AttnBwdConfig, dq_ptrs, m_block, dq_tile):
|
| 2079 |
+
dq_ptrs.atomic_add([m_block * config.TILE_M, 0], dq_tile)
|
| 2080 |
+
|
| 2081 |
+
@triton.jit
|
| 2082 |
+
def store_dk(self, config: AttnBwdConfig, dk_ptrs, dk_tile):
|
| 2083 |
+
if config.QHEAD_PER_KVHEAD > 1:
|
| 2084 |
+
dk_ptrs.atomic_add([config.n_block * config.TILE_N, 0], dk_tile)
|
| 2085 |
+
else:
|
| 2086 |
+
dk_ptrs.store([config.n_block * config.TILE_N, 0], dk_tile)
|
| 2087 |
+
|
| 2088 |
+
@triton.jit
|
| 2089 |
+
def store_dv(self, config: AttnBwdConfig, dv_ptrs, dv_tile):
|
| 2090 |
+
if config.QHEAD_PER_KVHEAD > 1:
|
| 2091 |
+
dv_ptrs.atomic_add([config.n_block * config.TILE_N, 0], dv_tile)
|
| 2092 |
+
else:
|
| 2093 |
+
dv_ptrs.store([config.n_block * config.TILE_N, 0], dv_tile)
|
| 2094 |
+
|
| 2095 |
+
@triton.jit
|
| 2096 |
+
def store_da(self, config: AttnBwdConfig, da_ptrs, m_block, da_tile):
|
| 2097 |
+
da_ptrs.atomic_add([m_block * config.TILE_M], da_tile)
|
| 2098 |
+
|
| 2099 |
+
@triton.jit
|
| 2100 |
+
def store_dd(self, config: AttnBwdConfig, dd_ptrs, dd_tile):
|
| 2101 |
+
if config.QHEAD_PER_KVHEAD > 1:
|
| 2102 |
+
dd_ptrs.atomic_add([config.n_block * config.TILE_N], dd_tile)
|
| 2103 |
+
else:
|
| 2104 |
+
dd_ptrs.store([config.n_block * config.TILE_N], dd_tile)
|
| 2105 |
+
|
| 2106 |
+
|
| 2107 |
+
@aggregate
|
| 2108 |
+
class AttnDecPointerScheduler:
|
| 2109 |
+
q_base: tl.tensor
|
| 2110 |
+
k_base: tl.tensor
|
| 2111 |
+
v_base: tl.tensor
|
| 2112 |
+
a_base: tl.tensor
|
| 2113 |
+
d_base: tl.tensor
|
| 2114 |
+
out_base: tl.tensor
|
| 2115 |
+
lse_base: tl.tensor
|
| 2116 |
+
stride_qh: tl.tensor
|
| 2117 |
+
stride_kn: tl.tensor
|
| 2118 |
+
stride_vn: tl.tensor
|
| 2119 |
+
stride_oh: tl.tensor
|
| 2120 |
+
|
| 2121 |
+
@constexpr_function
|
| 2122 |
+
def __init__(
|
| 2123 |
+
self,
|
| 2124 |
+
q_base,
|
| 2125 |
+
k_base,
|
| 2126 |
+
v_base,
|
| 2127 |
+
a_base,
|
| 2128 |
+
d_base,
|
| 2129 |
+
out_base,
|
| 2130 |
+
lse_base,
|
| 2131 |
+
stride_qh,
|
| 2132 |
+
stride_kn,
|
| 2133 |
+
stride_vn,
|
| 2134 |
+
stride_oh,
|
| 2135 |
+
):
|
| 2136 |
+
self.q_base = q_base
|
| 2137 |
+
self.k_base = k_base
|
| 2138 |
+
self.v_base = v_base
|
| 2139 |
+
self.a_base = a_base
|
| 2140 |
+
self.d_base = d_base
|
| 2141 |
+
self.out_base = out_base
|
| 2142 |
+
self.lse_base = lse_base
|
| 2143 |
+
self.stride_qh = stride_qh
|
| 2144 |
+
self.stride_kn = stride_kn
|
| 2145 |
+
self.stride_vn = stride_vn
|
| 2146 |
+
self.stride_oh = stride_oh
|
| 2147 |
+
|
| 2148 |
+
@staticmethod
|
| 2149 |
+
@triton.jit
|
| 2150 |
+
def create(
|
| 2151 |
+
config: AttnDecConfig,
|
| 2152 |
+
Q=None,
|
| 2153 |
+
K=None,
|
| 2154 |
+
V=None,
|
| 2155 |
+
A=None,
|
| 2156 |
+
D=None,
|
| 2157 |
+
Out=None,
|
| 2158 |
+
Lse=None,
|
| 2159 |
+
batch_idx=0,
|
| 2160 |
+
head_idx=0,
|
| 2161 |
+
head_kv_idx=0,
|
| 2162 |
+
split_idx=0,
|
| 2163 |
+
stride_qb=0,
|
| 2164 |
+
stride_qh=0,
|
| 2165 |
+
stride_qm=0,
|
| 2166 |
+
stride_kb=0,
|
| 2167 |
+
stride_kh=0,
|
| 2168 |
+
stride_kn=0,
|
| 2169 |
+
stride_vb=0,
|
| 2170 |
+
stride_vh=0,
|
| 2171 |
+
stride_vn=0,
|
| 2172 |
+
stride_ab=0,
|
| 2173 |
+
stride_ah=0,
|
| 2174 |
+
stride_db=0,
|
| 2175 |
+
stride_dh=0,
|
| 2176 |
+
stride_ob=0,
|
| 2177 |
+
stride_oh=0,
|
| 2178 |
+
stride_om=0,
|
| 2179 |
+
stride_os=0,
|
| 2180 |
+
stride_lb=0,
|
| 2181 |
+
stride_lh=0,
|
| 2182 |
+
stride_lm=0,
|
| 2183 |
+
stride_ls=0,
|
| 2184 |
+
IS_GATED: tl.constexpr = False,
|
| 2185 |
+
HAS_CU_SEQLENS_Q: tl.constexpr = False,
|
| 2186 |
+
HAS_CU_SEQLENS_K: tl.constexpr = False,
|
| 2187 |
+
):
|
| 2188 |
+
# Initialize base pointers
|
| 2189 |
+
q_base = seqlen_info.offset_batch_Q(
|
| 2190 |
+
Q + head_idx * config.QHEAD_PER_KVHEAD_PACKGQA * stride_qh,
|
| 2191 |
+
batch_idx,
|
| 2192 |
+
config.offset_q,
|
| 2193 |
+
config.padded_offset_q,
|
| 2194 |
+
stride_qb,
|
| 2195 |
+
stride_qm,
|
| 2196 |
+
HAS_CU_SEQLENS_Q,
|
| 2197 |
+
USE_PADDED=False,
|
| 2198 |
+
)
|
| 2199 |
+
k_base = seqlen_info.offset_batch_K(
|
| 2200 |
+
K + head_kv_idx * stride_kh,
|
| 2201 |
+
batch_idx,
|
| 2202 |
+
config.offset_k,
|
| 2203 |
+
config.padded_offset_k,
|
| 2204 |
+
stride_kb,
|
| 2205 |
+
stride_kn,
|
| 2206 |
+
HAS_CU_SEQLENS_K,
|
| 2207 |
+
USE_PADDED=False,
|
| 2208 |
+
)
|
| 2209 |
+
v_base = seqlen_info.offset_batch_K(
|
| 2210 |
+
V + head_kv_idx * stride_vh,
|
| 2211 |
+
batch_idx,
|
| 2212 |
+
config.offset_k,
|
| 2213 |
+
config.padded_offset_k,
|
| 2214 |
+
stride_vb,
|
| 2215 |
+
stride_vn,
|
| 2216 |
+
HAS_CU_SEQLENS_K,
|
| 2217 |
+
USE_PADDED=False,
|
| 2218 |
+
)
|
| 2219 |
+
out_base = seqlen_info.offset_batch_Q(
|
| 2220 |
+
Out + head_idx * config.QHEAD_PER_KVHEAD_PACKGQA * stride_oh,
|
| 2221 |
+
batch_idx,
|
| 2222 |
+
config.offset_q,
|
| 2223 |
+
config.padded_offset_q,
|
| 2224 |
+
stride_ob,
|
| 2225 |
+
stride_om,
|
| 2226 |
+
HAS_CU_SEQLENS_Q,
|
| 2227 |
+
USE_PADDED=False,
|
| 2228 |
+
)
|
| 2229 |
+
lse_base = seqlen_info.offset_batch_Q(
|
| 2230 |
+
Lse + head_idx * config.QHEAD_PER_KVHEAD_PACKGQA * stride_lh,
|
| 2231 |
+
batch_idx,
|
| 2232 |
+
config.offset_q,
|
| 2233 |
+
config.padded_offset_q,
|
| 2234 |
+
stride_lb,
|
| 2235 |
+
stride_lm,
|
| 2236 |
+
HAS_CU_SEQLENS_Q,
|
| 2237 |
+
USE_PADDED=False,
|
| 2238 |
+
)
|
| 2239 |
+
|
| 2240 |
+
if IS_GATED:
|
| 2241 |
+
a_base = seqlen_info.offset_batch_Q(
|
| 2242 |
+
A + head_idx * config.QHEAD_PER_KVHEAD_PACKGQA * stride_ah,
|
| 2243 |
+
batch_idx,
|
| 2244 |
+
config.offset_q,
|
| 2245 |
+
config.padded_offset_q,
|
| 2246 |
+
stride_ab,
|
| 2247 |
+
1,
|
| 2248 |
+
HAS_CU_SEQLENS_Q,
|
| 2249 |
+
USE_PADDED=False,
|
| 2250 |
+
)
|
| 2251 |
+
d_base = seqlen_info.offset_batch_K(
|
| 2252 |
+
D + head_kv_idx * stride_dh,
|
| 2253 |
+
batch_idx,
|
| 2254 |
+
config.offset_k,
|
| 2255 |
+
config.padded_offset_k,
|
| 2256 |
+
stride_db,
|
| 2257 |
+
1,
|
| 2258 |
+
HAS_CU_SEQLENS_K,
|
| 2259 |
+
USE_PADDED=False,
|
| 2260 |
+
)
|
| 2261 |
+
else:
|
| 2262 |
+
a_base = tl.full((), 0, tl.int64)
|
| 2263 |
+
d_base = tl.full((), 0, tl.int64)
|
| 2264 |
+
|
| 2265 |
+
# For split KV, offset output and LSE base pointers by split_idx
|
| 2266 |
+
out_base += split_idx * stride_os
|
| 2267 |
+
lse_base += split_idx * stride_ls
|
| 2268 |
+
|
| 2269 |
+
# TODO: remove once Triton aggregate accepts host scalar strides or callers materialize strides
|
| 2270 |
+
return AttnDecPointerScheduler(
|
| 2271 |
+
q_base,
|
| 2272 |
+
k_base,
|
| 2273 |
+
v_base,
|
| 2274 |
+
a_base,
|
| 2275 |
+
d_base,
|
| 2276 |
+
out_base,
|
| 2277 |
+
lse_base,
|
| 2278 |
+
stride_qh + tl.full((), 0, tl.int64),
|
| 2279 |
+
stride_kn + tl.full((), 0, tl.int64),
|
| 2280 |
+
stride_vn + tl.full((), 0, tl.int64),
|
| 2281 |
+
stride_oh + tl.full((), 0, tl.int64),
|
| 2282 |
+
)
|
| 2283 |
+
|
| 2284 |
+
@triton.jit
|
| 2285 |
+
def make_q_ptrs(self, config: AttnDecConfig):
|
| 2286 |
+
return tl.make_tensor_descriptor(
|
| 2287 |
+
self.q_base,
|
| 2288 |
+
shape=[config.QHEAD_PER_KVHEAD_PACKGQA, config.head_dim],
|
| 2289 |
+
strides=[self.stride_qh, 1],
|
| 2290 |
+
block_shape=[config.TILE_M, config.TILE_K],
|
| 2291 |
+
)
|
| 2292 |
+
|
| 2293 |
+
@triton.jit
|
| 2294 |
+
def make_k_ptrs(self, config: AttnDecConfig):
|
| 2295 |
+
return tl.make_tensor_descriptor(
|
| 2296 |
+
self.k_base,
|
| 2297 |
+
shape=[config.actual_seqlen_k, config.head_dim],
|
| 2298 |
+
strides=[self.stride_kn, 1],
|
| 2299 |
+
block_shape=[config.TILE_N, config.TILE_K],
|
| 2300 |
+
)
|
| 2301 |
+
|
| 2302 |
+
@triton.jit
|
| 2303 |
+
def make_v_ptrs(self, config: AttnDecConfig):
|
| 2304 |
+
return tl.make_tensor_descriptor(
|
| 2305 |
+
self.v_base,
|
| 2306 |
+
shape=[config.actual_seqlen_k, config.head_dim],
|
| 2307 |
+
strides=[self.stride_vn, 1],
|
| 2308 |
+
block_shape=[config.TILE_N, config.TILE_K],
|
| 2309 |
+
)
|
| 2310 |
+
|
| 2311 |
+
@triton.jit
|
| 2312 |
+
def make_a_ptrs(self, config: AttnDecConfig):
|
| 2313 |
+
return tl.make_tensor_descriptor(
|
| 2314 |
+
self.a_base,
|
| 2315 |
+
shape=[config.QHEAD_PER_KVHEAD_PACKGQA],
|
| 2316 |
+
strides=[1],
|
| 2317 |
+
block_shape=[config.TILE_M],
|
| 2318 |
+
)
|
| 2319 |
+
|
| 2320 |
+
@triton.jit
|
| 2321 |
+
def make_d_ptrs(self, config: AttnDecConfig):
|
| 2322 |
+
return tl.make_tensor_descriptor(
|
| 2323 |
+
self.d_base,
|
| 2324 |
+
shape=[config.actual_seqlen_k],
|
| 2325 |
+
strides=[1],
|
| 2326 |
+
block_shape=[config.TILE_N],
|
| 2327 |
+
)
|
| 2328 |
+
|
| 2329 |
+
@triton.jit
|
| 2330 |
+
def make_out_ptrs(self, config: AttnDecConfig):
|
| 2331 |
+
return tl.make_tensor_descriptor(
|
| 2332 |
+
self.out_base,
|
| 2333 |
+
shape=[config.QHEAD_PER_KVHEAD_PACKGQA, config.head_dim],
|
| 2334 |
+
strides=[self.stride_oh, 1],
|
| 2335 |
+
block_shape=[config.TILE_M, config.TILE_K],
|
| 2336 |
+
)
|
| 2337 |
+
|
| 2338 |
+
@triton.jit
|
| 2339 |
+
def make_lse_ptrs(self, config: AttnDecConfig):
|
| 2340 |
+
return tl.make_tensor_descriptor(
|
| 2341 |
+
self.lse_base,
|
| 2342 |
+
shape=[config.QHEAD_PER_KVHEAD_PACKGQA],
|
| 2343 |
+
strides=[1],
|
| 2344 |
+
block_shape=[config.TILE_M],
|
| 2345 |
+
)
|
| 2346 |
+
|
| 2347 |
+
@triton.jit
|
| 2348 |
+
def load_q(self, config: AttnDecConfig, q_ptrs):
|
| 2349 |
+
return q_ptrs.load([0, 0])
|
| 2350 |
+
|
| 2351 |
+
@triton.jit
|
| 2352 |
+
def load_k(self, config: AttnDecConfig, k_ptrs, n_block):
|
| 2353 |
+
return k_ptrs.load([n_block * config.TILE_N, 0])
|
| 2354 |
+
|
| 2355 |
+
@triton.jit
|
| 2356 |
+
def load_v(self, config: AttnDecConfig, v_ptrs, n_block):
|
| 2357 |
+
return v_ptrs.load([n_block * config.TILE_N, 0])
|
| 2358 |
+
|
| 2359 |
+
@triton.jit
|
| 2360 |
+
def load_a(self, config: AttnDecConfig, a_ptrs):
|
| 2361 |
+
return a_ptrs.load([0]).to(tl.float32)
|
| 2362 |
+
|
| 2363 |
+
@triton.jit
|
| 2364 |
+
def load_d(self, config: AttnDecConfig, d_ptrs, n_block):
|
| 2365 |
+
return d_ptrs.load([n_block * config.TILE_N]).to(tl.float32)
|
| 2366 |
+
|
| 2367 |
+
@triton.jit
|
| 2368 |
+
def store_out(self, config: AttnDecConfig, out_ptrs, o_tile):
|
| 2369 |
+
out_ptrs.store([0, 0], o_tile)
|
| 2370 |
+
|
| 2371 |
+
@triton.jit
|
| 2372 |
+
def store_lse(self, config: AttnDecConfig, lse_ptrs, lse_tile):
|
| 2373 |
+
lse_ptrs.store([0], lse_tile)
|
| 2374 |
+
|
| 2375 |
+
@triton.jit
|
| 2376 |
+
def store_empty(self, config: AttnDecConfig, out_ptrs, lse_ptrs, Out):
|
| 2377 |
+
lse_tile = tl.full((config.TILE_M,), float("-inf"), dtype=tl.float32)
|
| 2378 |
+
self.store_lse(config, lse_ptrs, lse_tile)
|
| 2379 |
+
o_tile = tl.zeros((config.TILE_M, config.TILE_K), dtype=Out.dtype.element_ty)
|
| 2380 |
+
self.store_out(config, out_ptrs, o_tile)
|
| 2381 |
+
|
| 2382 |
+
|
| 2383 |
+
@aggregate
|
| 2384 |
+
class AttnMaskScheduler:
|
| 2385 |
+
fixed_block: tl.tensor
|
| 2386 |
+
actual_seqlen_q: tl.tensor
|
| 2387 |
+
actual_seqlen_k: tl.tensor
|
| 2388 |
+
window_size_sink: tl.tensor
|
| 2389 |
+
window_size_left: tl.tensor
|
| 2390 |
+
window_size_right: tl.tensor
|
| 2391 |
+
window_size_dist: tl.tensor
|
| 2392 |
+
TILE_M: tl.constexpr
|
| 2393 |
+
TILE_N: tl.constexpr
|
| 2394 |
+
QHEAD_PER_KVHEAD_PACKGQA: tl.constexpr
|
| 2395 |
+
SWAP_AB: tl.constexpr
|
| 2396 |
+
|
| 2397 |
+
@constexpr_function
|
| 2398 |
+
def __init__(
|
| 2399 |
+
self,
|
| 2400 |
+
fixed_block,
|
| 2401 |
+
actual_seqlen_q,
|
| 2402 |
+
actual_seqlen_k,
|
| 2403 |
+
window_size_sink,
|
| 2404 |
+
window_size_left,
|
| 2405 |
+
window_size_right,
|
| 2406 |
+
window_size_dist,
|
| 2407 |
+
TILE_M,
|
| 2408 |
+
TILE_N,
|
| 2409 |
+
QHEAD_PER_KVHEAD_PACKGQA,
|
| 2410 |
+
SWAP_AB,
|
| 2411 |
+
):
|
| 2412 |
+
self.fixed_block = fixed_block
|
| 2413 |
+
self.actual_seqlen_q = actual_seqlen_q
|
| 2414 |
+
self.actual_seqlen_k = actual_seqlen_k
|
| 2415 |
+
self.window_size_sink = window_size_sink
|
| 2416 |
+
self.window_size_left = window_size_left
|
| 2417 |
+
self.window_size_right = window_size_right
|
| 2418 |
+
self.window_size_dist = window_size_dist
|
| 2419 |
+
self.TILE_M = tl.constexpr(TILE_M)
|
| 2420 |
+
self.TILE_N = tl.constexpr(TILE_N)
|
| 2421 |
+
self.QHEAD_PER_KVHEAD_PACKGQA = tl.constexpr(QHEAD_PER_KVHEAD_PACKGQA)
|
| 2422 |
+
self.SWAP_AB = tl.constexpr(SWAP_AB)
|
| 2423 |
+
|
| 2424 |
+
@staticmethod
|
| 2425 |
+
@triton.jit
|
| 2426 |
+
def create(
|
| 2427 |
+
config,
|
| 2428 |
+
SWAP_AB: tl.constexpr = False,
|
| 2429 |
+
):
|
| 2430 |
+
if not SWAP_AB:
|
| 2431 |
+
return AttnMaskScheduler(
|
| 2432 |
+
config.m_block,
|
| 2433 |
+
config.actual_seqlen_q,
|
| 2434 |
+
config.actual_seqlen_k,
|
| 2435 |
+
config.window_size_sink,
|
| 2436 |
+
config.window_size_left,
|
| 2437 |
+
config.window_size_right,
|
| 2438 |
+
config.window_size_dist,
|
| 2439 |
+
config.TILE_M,
|
| 2440 |
+
config.TILE_N,
|
| 2441 |
+
config.QHEAD_PER_KVHEAD_PACKGQA,
|
| 2442 |
+
False,
|
| 2443 |
+
)
|
| 2444 |
+
else:
|
| 2445 |
+
return AttnMaskScheduler(
|
| 2446 |
+
config.n_block,
|
| 2447 |
+
config.actual_seqlen_q,
|
| 2448 |
+
config.actual_seqlen_k,
|
| 2449 |
+
config.window_size_sink,
|
| 2450 |
+
config.window_size_left,
|
| 2451 |
+
config.window_size_right,
|
| 2452 |
+
config.window_size_dist,
|
| 2453 |
+
config.TILE_M,
|
| 2454 |
+
config.TILE_N,
|
| 2455 |
+
1,
|
| 2456 |
+
True,
|
| 2457 |
+
)
|
| 2458 |
+
|
| 2459 |
+
@triton.jit
|
| 2460 |
+
def apply_mask(
|
| 2461 |
+
self,
|
| 2462 |
+
acc_s,
|
| 2463 |
+
iter_block,
|
| 2464 |
+
MASK_CAUSAL: tl.constexpr = False,
|
| 2465 |
+
MASK_LOCAL: tl.constexpr = False,
|
| 2466 |
+
MASK_SINK: tl.constexpr = False,
|
| 2467 |
+
):
|
| 2468 |
+
if not self.SWAP_AB:
|
| 2469 |
+
m_block = self.fixed_block
|
| 2470 |
+
n_block = iter_block
|
| 2471 |
+
else:
|
| 2472 |
+
m_block = iter_block
|
| 2473 |
+
n_block = self.fixed_block
|
| 2474 |
+
return mask.apply_mask(
|
| 2475 |
+
acc_s=acc_s,
|
| 2476 |
+
m_block=m_block,
|
| 2477 |
+
n_block=n_block,
|
| 2478 |
+
seqlen_q=self.actual_seqlen_q,
|
| 2479 |
+
seqlen_k=self.actual_seqlen_k,
|
| 2480 |
+
window_size_sink=self.window_size_sink,
|
| 2481 |
+
window_size_left=self.window_size_left,
|
| 2482 |
+
window_size_right=self.window_size_right,
|
| 2483 |
+
window_size_dist=self.window_size_dist,
|
| 2484 |
+
MASK_SEQLEN=True,
|
| 2485 |
+
MASK_CAUSAL=MASK_CAUSAL,
|
| 2486 |
+
MASK_LOCAL=MASK_LOCAL,
|
| 2487 |
+
MASK_SINK=MASK_SINK,
|
| 2488 |
+
TILE_M=self.TILE_M,
|
| 2489 |
+
TILE_N=self.TILE_N,
|
| 2490 |
+
QHEAD_PER_KVHEAD_PACKGQA=self.QHEAD_PER_KVHEAD_PACKGQA,
|
| 2491 |
+
SWAP_AB=self.SWAP_AB,
|
| 2492 |
+
)
|
| 2493 |
+
|
| 2494 |
+
|
| 2495 |
+
@aggregate
|
| 2496 |
+
class SoftmaxScheduler:
|
| 2497 |
+
softmax_scale_log2: tl.tensor
|
| 2498 |
+
value_scale: tl.tensor
|
| 2499 |
+
|
| 2500 |
+
@constexpr_function
|
| 2501 |
+
def __init__(
|
| 2502 |
+
self,
|
| 2503 |
+
softmax_scale_log2,
|
| 2504 |
+
value_scale,
|
| 2505 |
+
):
|
| 2506 |
+
self.softmax_scale_log2 = softmax_scale_log2
|
| 2507 |
+
self.value_scale = value_scale
|
| 2508 |
+
|
| 2509 |
+
@staticmethod
|
| 2510 |
+
@triton.jit
|
| 2511 |
+
def create(config):
|
| 2512 |
+
return SoftmaxScheduler(
|
| 2513 |
+
config.softmax_scale_log2,
|
| 2514 |
+
config.value_scale,
|
| 2515 |
+
)
|
| 2516 |
+
|
| 2517 |
+
@triton.jit
|
| 2518 |
+
def online_softmax(
|
| 2519 |
+
self,
|
| 2520 |
+
acc_s,
|
| 2521 |
+
row_max,
|
| 2522 |
+
row_sum,
|
| 2523 |
+
CHECK_INF: tl.constexpr = False,
|
| 2524 |
+
RESCALE_THRESHOLD: tl.constexpr = 0.0,
|
| 2525 |
+
):
|
| 2526 |
+
return activations.online_softmax(
|
| 2527 |
+
acc_s=acc_s,
|
| 2528 |
+
row_max=row_max,
|
| 2529 |
+
row_sum=row_sum,
|
| 2530 |
+
scale_log2=self.softmax_scale_log2,
|
| 2531 |
+
CHECK_INF=CHECK_INF,
|
| 2532 |
+
RESCALE_THRESHOLD=RESCALE_THRESHOLD,
|
| 2533 |
+
)
|
| 2534 |
+
|
| 2535 |
+
@triton.jit
|
| 2536 |
+
def online_sparse_softmax(
|
| 2537 |
+
self,
|
| 2538 |
+
acc_s,
|
| 2539 |
+
row_max,
|
| 2540 |
+
row_sum,
|
| 2541 |
+
softmax_threshold_log2,
|
| 2542 |
+
CHECK_INF: tl.constexpr = False,
|
| 2543 |
+
):
|
| 2544 |
+
return activations.online_sparse_softmax(
|
| 2545 |
+
acc_s=acc_s,
|
| 2546 |
+
row_max=row_max,
|
| 2547 |
+
row_sum=row_sum,
|
| 2548 |
+
scale_log2=self.softmax_scale_log2,
|
| 2549 |
+
softmax_threshold_log2=softmax_threshold_log2,
|
| 2550 |
+
CHECK_INF=CHECK_INF,
|
| 2551 |
+
)
|
| 2552 |
+
|
| 2553 |
+
@triton.jit
|
| 2554 |
+
def rescale_o(
|
| 2555 |
+
self,
|
| 2556 |
+
acc_o,
|
| 2557 |
+
row_scale,
|
| 2558 |
+
LAZY_RESCALE: tl.constexpr = False,
|
| 2559 |
+
):
|
| 2560 |
+
return activations.rescale_o(
|
| 2561 |
+
acc_o=acc_o,
|
| 2562 |
+
row_scale=row_scale,
|
| 2563 |
+
LAZY_RESCALE=LAZY_RESCALE,
|
| 2564 |
+
)
|
| 2565 |
+
|
| 2566 |
+
@triton.jit
|
| 2567 |
+
def finalize(
|
| 2568 |
+
self,
|
| 2569 |
+
row_max,
|
| 2570 |
+
row_sum,
|
| 2571 |
+
IS_LOG2: tl.constexpr = False,
|
| 2572 |
+
CHECK_NAN: tl.constexpr = True,
|
| 2573 |
+
):
|
| 2574 |
+
return activations.finalize(
|
| 2575 |
+
row_max=row_max,
|
| 2576 |
+
row_sum=row_sum,
|
| 2577 |
+
scale_log2=self.softmax_scale_log2,
|
| 2578 |
+
final_scale=self.value_scale,
|
| 2579 |
+
IS_LOG2=IS_LOG2,
|
| 2580 |
+
CHECK_NAN=CHECK_NAN,
|
| 2581 |
+
)
|
| 2582 |
+
|
| 2583 |
+
@triton.jit
|
| 2584 |
+
def log_sigmoid(
|
| 2585 |
+
self,
|
| 2586 |
+
acc_s,
|
| 2587 |
+
FASTMATH: tl.constexpr = False,
|
| 2588 |
+
):
|
| 2589 |
+
return activations.log_sigmoid(x=acc_s, FASTMATH=FASTMATH)
|
| 2590 |
+
|
| 2591 |
+
@triton.jit
|
| 2592 |
+
def online_gate(
|
| 2593 |
+
self,
|
| 2594 |
+
a_max,
|
| 2595 |
+
a_min,
|
| 2596 |
+
d_max,
|
| 2597 |
+
d_min,
|
| 2598 |
+
gate_max,
|
| 2599 |
+
gate_threshold_log2,
|
| 2600 |
+
):
|
| 2601 |
+
return activations.online_gate(
|
| 2602 |
+
a_max=a_max,
|
| 2603 |
+
a_min=a_min,
|
| 2604 |
+
d_max=d_max,
|
| 2605 |
+
d_min=d_min,
|
| 2606 |
+
gate_max=gate_max,
|
| 2607 |
+
scale_log2=self.softmax_scale_log2,
|
| 2608 |
+
gate_threshold_log2=gate_threshold_log2,
|
| 2609 |
+
)
|
build/torch-cuda/seqlen_info.py
ADDED
|
@@ -0,0 +1,273 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import triton
|
| 2 |
+
import triton.language as tl
|
| 3 |
+
|
| 4 |
+
|
| 5 |
+
@triton.jit
|
| 6 |
+
def get_seqlen_info(
|
| 7 |
+
batch_idx,
|
| 8 |
+
seqlen_static,
|
| 9 |
+
cu_seqlens,
|
| 10 |
+
seqused,
|
| 11 |
+
HAS_CU_SEQLENS: tl.constexpr,
|
| 12 |
+
HAS_SEQUSED: tl.constexpr,
|
| 13 |
+
):
|
| 14 |
+
"""
|
| 15 |
+
Get offset and seqlen for a given batch index.
|
| 16 |
+
|
| 17 |
+
:param batch_idx: Index of the batch.
|
| 18 |
+
:param seqlen_static: Static sequence length if cu_seqlens is not provided.
|
| 19 |
+
:param cu_seqlens: Cumulative sequence lengths tensor.
|
| 20 |
+
:param seqused: Actual sequence lengths tensor.
|
| 21 |
+
:param HAS_CU_SEQLENS: Boolean flag indicating if cu_seqlens is provided.
|
| 22 |
+
:param HAS_SEQUSED: Boolean flag indicating if seqused is provided.
|
| 23 |
+
|
| 24 |
+
:return offset: Offset for the given batch index.
|
| 25 |
+
:return seqlen: Sequence length for the given batch index.
|
| 26 |
+
"""
|
| 27 |
+
if HAS_CU_SEQLENS:
|
| 28 |
+
offset = tl.load(cu_seqlens + batch_idx)
|
| 29 |
+
if HAS_SEQUSED:
|
| 30 |
+
seqlen = tl.load(seqused + batch_idx)
|
| 31 |
+
else:
|
| 32 |
+
seqlen = tl.load(cu_seqlens + batch_idx + 1) - offset
|
| 33 |
+
else:
|
| 34 |
+
offset = 0
|
| 35 |
+
if HAS_SEQUSED:
|
| 36 |
+
seqlen = tl.load(seqused + batch_idx)
|
| 37 |
+
else:
|
| 38 |
+
seqlen = seqlen_static
|
| 39 |
+
return offset, seqlen
|
| 40 |
+
|
| 41 |
+
|
| 42 |
+
@triton.jit
|
| 43 |
+
def get_seqlen_info_qk(
|
| 44 |
+
batch_idx,
|
| 45 |
+
seqlen_q_static,
|
| 46 |
+
seqlen_k_static,
|
| 47 |
+
cu_seqlens_q,
|
| 48 |
+
cu_seqlens_k,
|
| 49 |
+
seqused_q,
|
| 50 |
+
seqused_k,
|
| 51 |
+
TILE_M: tl.constexpr,
|
| 52 |
+
TILE_N: tl.constexpr,
|
| 53 |
+
HAS_CU_SEQLENS_Q: tl.constexpr,
|
| 54 |
+
HAS_CU_SEQLENS_K: tl.constexpr,
|
| 55 |
+
HAS_SEQUSED_Q: tl.constexpr,
|
| 56 |
+
HAS_SEQUSED_K: tl.constexpr,
|
| 57 |
+
):
|
| 58 |
+
"""
|
| 59 |
+
Get offset, padded_offset, and seqlen for both Q and K.
|
| 60 |
+
|
| 61 |
+
:param batch_idx: Index of the batch.
|
| 62 |
+
:param seqlen_q_static: Static sequence length for Q if cu_seqlens_q is not provided.
|
| 63 |
+
:param seqlen_k_static: Static sequence length for K if cu_seqlens_k is not provided.
|
| 64 |
+
:param cu_seqlens_q: Cumulative sequence lengths tensor for Q.
|
| 65 |
+
:param cu_seqlens_k: Cumulative sequence lengths tensor for K.
|
| 66 |
+
:param seqused_q: Actual sequence lengths tensor for Q.
|
| 67 |
+
:param seqused_k: Actual sequence lengths tensor for K.
|
| 68 |
+
:param TILE_M: Tile size for Q.
|
| 69 |
+
:param TILE_N: Tile size for K.
|
| 70 |
+
:param HAS_CU_SEQLENS_Q: Boolean flag indicating if cu_seqlens_q is provided.
|
| 71 |
+
:param HAS_CU_SEQLENS_K: Boolean flag indicating if cu_seqlens_k is provided.
|
| 72 |
+
:param HAS_SEQUSED_Q: Boolean flag indicating if seqused_q is provided.
|
| 73 |
+
:param HAS_SEQUSED_K: Boolean flag indicating if seqused_k is provided.
|
| 74 |
+
|
| 75 |
+
:return offset_q: Offset for Q for the given batch index.
|
| 76 |
+
:return offset_k: Offset for K for the given batch index.
|
| 77 |
+
:return padded_offset_q: Padded offset for Q aligned to TILE_M.
|
| 78 |
+
:return padded_offset_k: Padded offset for K aligned to TILE_N.
|
| 79 |
+
:return seqlen_q: Sequence length for Q for the given batch index.
|
| 80 |
+
:return seqlen_k: Sequence length for K for the given batch index.
|
| 81 |
+
"""
|
| 82 |
+
|
| 83 |
+
# Q offset and seqlen
|
| 84 |
+
if HAS_CU_SEQLENS_Q:
|
| 85 |
+
offset_q = tl.load(cu_seqlens_q + batch_idx)
|
| 86 |
+
padded_offset_q = (offset_q + batch_idx * TILE_M) // TILE_M * TILE_M
|
| 87 |
+
if HAS_SEQUSED_Q:
|
| 88 |
+
seqlen_q = tl.load(seqused_q + batch_idx)
|
| 89 |
+
else:
|
| 90 |
+
seqlen_q = tl.load(cu_seqlens_q + batch_idx + 1) - offset_q
|
| 91 |
+
else:
|
| 92 |
+
offset_q = 0
|
| 93 |
+
padded_offset_q = 0
|
| 94 |
+
if HAS_SEQUSED_Q:
|
| 95 |
+
seqlen_q = tl.load(seqused_q + batch_idx)
|
| 96 |
+
else:
|
| 97 |
+
seqlen_q = seqlen_q_static
|
| 98 |
+
|
| 99 |
+
# K offset and seqlen
|
| 100 |
+
if HAS_CU_SEQLENS_K:
|
| 101 |
+
offset_k = tl.load(cu_seqlens_k + batch_idx)
|
| 102 |
+
padded_offset_k = (offset_k + batch_idx * TILE_N) // TILE_N * TILE_N
|
| 103 |
+
if HAS_SEQUSED_K:
|
| 104 |
+
seqlen_k = tl.load(seqused_k + batch_idx)
|
| 105 |
+
else:
|
| 106 |
+
seqlen_k = tl.load(cu_seqlens_k + batch_idx + 1) - offset_k
|
| 107 |
+
else:
|
| 108 |
+
offset_k = 0
|
| 109 |
+
padded_offset_k = 0
|
| 110 |
+
if HAS_SEQUSED_K:
|
| 111 |
+
seqlen_k = tl.load(seqused_k + batch_idx)
|
| 112 |
+
else:
|
| 113 |
+
seqlen_k = seqlen_k_static
|
| 114 |
+
|
| 115 |
+
return offset_q, offset_k, padded_offset_q, padded_offset_k, seqlen_q, seqlen_k
|
| 116 |
+
|
| 117 |
+
|
| 118 |
+
@triton.jit
|
| 119 |
+
def get_softmax_threshold(
|
| 120 |
+
softmax_threshold,
|
| 121 |
+
m_block,
|
| 122 |
+
seqlen_q,
|
| 123 |
+
seqlen_k,
|
| 124 |
+
IS_CAUSAL: tl.constexpr,
|
| 125 |
+
TILE_M: tl.constexpr,
|
| 126 |
+
QHEAD_PER_KVHEAD_PACKGQA: tl.constexpr,
|
| 127 |
+
):
|
| 128 |
+
"""
|
| 129 |
+
Compute the softmax threshold for a given block.
|
| 130 |
+
|
| 131 |
+
:param softmax_threshold: Threshold value normalized by full key length.
|
| 132 |
+
:param m_block: Current block index along the M dimension.
|
| 133 |
+
:param seqlen_q: Sequence length of the query.
|
| 134 |
+
:param seqlen_k: Sequence length of the key.
|
| 135 |
+
:param IS_CAUSAL: Boolean flag indicating if the attention is causal.
|
| 136 |
+
:param TILE_M: Tile size along the M dimension.
|
| 137 |
+
:param QHEAD_PER_KVHEAD_PACKGQA: Ratio of query heads to key/value heads for packed GQA.
|
| 138 |
+
|
| 139 |
+
:return softmax_threshold_log2: softmax threshold of shape [TILE_M] in log2-domain for the given block.
|
| 140 |
+
"""
|
| 141 |
+
if IS_CAUSAL:
|
| 142 |
+
offs_m = m_block * TILE_M + tl.arange(0, TILE_M)
|
| 143 |
+
q_idx = offs_m
|
| 144 |
+
if QHEAD_PER_KVHEAD_PACKGQA > 1:
|
| 145 |
+
q_idx = q_idx // QHEAD_PER_KVHEAD_PACKGQA
|
| 146 |
+
causal_offset = seqlen_k - seqlen_q
|
| 147 |
+
s_thr = softmax_threshold * (q_idx + causal_offset + 1.0) / seqlen_k
|
| 148 |
+
else:
|
| 149 |
+
s_thr = tl.full((TILE_M,), softmax_threshold, dtype=tl.float32)
|
| 150 |
+
softmax_threshold_log2 = tl.log2(s_thr)
|
| 151 |
+
return softmax_threshold_log2
|
| 152 |
+
|
| 153 |
+
|
| 154 |
+
@triton.jit
|
| 155 |
+
def get_gate_threshold(
|
| 156 |
+
gate_threshold,
|
| 157 |
+
m_block,
|
| 158 |
+
seqlen_q,
|
| 159 |
+
seqlen_k,
|
| 160 |
+
IS_CAUSAL: tl.constexpr,
|
| 161 |
+
TILE_M: tl.constexpr,
|
| 162 |
+
QHEAD_PER_KVHEAD_PACKGQA: tl.constexpr,
|
| 163 |
+
IS_ADAPT_GATE: tl.constexpr,
|
| 164 |
+
):
|
| 165 |
+
"""
|
| 166 |
+
Compute the gate threshold for a given block.
|
| 167 |
+
|
| 168 |
+
:param gate_threshold: Threshold value for the gate.
|
| 169 |
+
:param m_block: Current block index along the M dimension.
|
| 170 |
+
:param seqlen_q: Sequence length of the query.
|
| 171 |
+
:param seqlen_k: Sequence length of the key.
|
| 172 |
+
:param IS_CAUSAL: Boolean flag indicating if the attention is causal.
|
| 173 |
+
:param TILE_M: Tile size along the M dimension.
|
| 174 |
+
:param QHEAD_PER_KVHEAD_PACKGQA: Ratio of query heads to key/value heads for packed GQA.
|
| 175 |
+
:param IS_ADAPT_GATE: Boolean flag indicating if self-adaptive gate threshold is enabled.
|
| 176 |
+
:return gate_threshold_log2: Lower-bound scalar gate threshold in log2-domain for the given block.
|
| 177 |
+
"""
|
| 178 |
+
if IS_CAUSAL and IS_ADAPT_GATE:
|
| 179 |
+
offs_m = m_block * TILE_M + tl.arange(0, TILE_M)
|
| 180 |
+
q_idx = offs_m
|
| 181 |
+
if QHEAD_PER_KVHEAD_PACKGQA > 1:
|
| 182 |
+
q_idx = q_idx // QHEAD_PER_KVHEAD_PACKGQA
|
| 183 |
+
causal_offset = seqlen_k - seqlen_q
|
| 184 |
+
g_thr = tl.min(gate_threshold * (q_idx + causal_offset + 1.0) / seqlen_k)
|
| 185 |
+
else:
|
| 186 |
+
g_thr = tl.full((), gate_threshold, dtype=tl.float32)
|
| 187 |
+
gate_threshold_log2 = tl.log2(g_thr)
|
| 188 |
+
return gate_threshold_log2
|
| 189 |
+
|
| 190 |
+
|
| 191 |
+
@triton.jit
|
| 192 |
+
def offset_batch_Q(
|
| 193 |
+
base_ptr,
|
| 194 |
+
batch_idx,
|
| 195 |
+
offset,
|
| 196 |
+
padded_offset,
|
| 197 |
+
stride_batch,
|
| 198 |
+
stride_seq,
|
| 199 |
+
HAS_CU_SEQLENS: tl.constexpr,
|
| 200 |
+
USE_PADDED: tl.constexpr,
|
| 201 |
+
):
|
| 202 |
+
if HAS_CU_SEQLENS:
|
| 203 |
+
actual_offset = padded_offset if USE_PADDED else offset
|
| 204 |
+
return base_ptr + actual_offset * stride_seq
|
| 205 |
+
else:
|
| 206 |
+
return base_ptr + batch_idx * stride_batch
|
| 207 |
+
|
| 208 |
+
|
| 209 |
+
@triton.jit
|
| 210 |
+
def offset_batch_K(
|
| 211 |
+
base_ptr,
|
| 212 |
+
batch_idx,
|
| 213 |
+
offset,
|
| 214 |
+
padded_offset,
|
| 215 |
+
stride_batch,
|
| 216 |
+
stride_seq,
|
| 217 |
+
HAS_CU_SEQLENS: tl.constexpr,
|
| 218 |
+
USE_PADDED: tl.constexpr,
|
| 219 |
+
):
|
| 220 |
+
if HAS_CU_SEQLENS:
|
| 221 |
+
actual_offset = padded_offset if USE_PADDED else offset
|
| 222 |
+
return base_ptr + actual_offset * stride_seq
|
| 223 |
+
else:
|
| 224 |
+
return base_ptr + batch_idx * stride_batch
|
| 225 |
+
|
| 226 |
+
|
| 227 |
+
@triton.jit
|
| 228 |
+
def make_ptrs(
|
| 229 |
+
base_ptrs,
|
| 230 |
+
mn_block,
|
| 231 |
+
stride_seq,
|
| 232 |
+
TILE_MN: tl.constexpr,
|
| 233 |
+
TILE_K: tl.constexpr,
|
| 234 |
+
SWAP_AB: tl.constexpr,
|
| 235 |
+
):
|
| 236 |
+
offs_mn = mn_block * TILE_MN + tl.arange(0, TILE_MN)
|
| 237 |
+
if TILE_K > 1:
|
| 238 |
+
offs_k = tl.arange(0, TILE_K)
|
| 239 |
+
if SWAP_AB:
|
| 240 |
+
ptrs = base_ptrs + offs_mn[None, :] * stride_seq + offs_k[:, None]
|
| 241 |
+
else:
|
| 242 |
+
ptrs = base_ptrs + offs_mn[:, None] * stride_seq + offs_k[None, :]
|
| 243 |
+
else:
|
| 244 |
+
ptrs = base_ptrs + offs_mn * stride_seq
|
| 245 |
+
return ptrs
|
| 246 |
+
|
| 247 |
+
|
| 248 |
+
@triton.jit
|
| 249 |
+
def make_pack_gqa_ptrs(
|
| 250 |
+
base_ptrs,
|
| 251 |
+
m_block,
|
| 252 |
+
head_idx,
|
| 253 |
+
stride_head,
|
| 254 |
+
stride_seq,
|
| 255 |
+
TILE_M: tl.constexpr,
|
| 256 |
+
TILE_K: tl.constexpr,
|
| 257 |
+
QHEAD_PER_KVHEAD_PACKGQA: tl.constexpr,
|
| 258 |
+
):
|
| 259 |
+
offs_m = m_block * TILE_M + tl.arange(0, TILE_M)
|
| 260 |
+
m_idx = offs_m // QHEAD_PER_KVHEAD_PACKGQA
|
| 261 |
+
q_head_offset = offs_m - m_idx * QHEAD_PER_KVHEAD_PACKGQA
|
| 262 |
+
q_head = head_idx * QHEAD_PER_KVHEAD_PACKGQA + q_head_offset
|
| 263 |
+
if TILE_K > 1:
|
| 264 |
+
offs_k = tl.arange(0, TILE_K)
|
| 265 |
+
ptrs = (
|
| 266 |
+
base_ptrs
|
| 267 |
+
+ m_idx[:, None] * stride_seq
|
| 268 |
+
+ q_head[:, None] * stride_head
|
| 269 |
+
+ offs_k[None, :]
|
| 270 |
+
)
|
| 271 |
+
else:
|
| 272 |
+
ptrs = base_ptrs + m_idx * stride_seq + q_head * stride_head
|
| 273 |
+
return ptrs
|
build/torch-cuda/utils.py
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import functools
|
| 2 |
+
import torch
|
| 3 |
+
import triton
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
def get_device():
|
| 7 |
+
"""
|
| 8 |
+
Get the appropriate device for computation.
|
| 9 |
+
|
| 10 |
+
:return device: torch.device object
|
| 11 |
+
"""
|
| 12 |
+
# TODO: add NPU
|
| 13 |
+
# Works for both NVIDIA and AMD
|
| 14 |
+
if torch.cuda.is_available():
|
| 15 |
+
return torch.device("cuda")
|
| 16 |
+
# Intel XPU if available
|
| 17 |
+
elif torch.xpu.is_available():
|
| 18 |
+
return torch.device("xpu")
|
| 19 |
+
elif torch.mps.is_available():
|
| 20 |
+
return torch.device("mps")
|
| 21 |
+
else:
|
| 22 |
+
return torch.device("cpu")
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
def ensure_contiguous(fn):
|
| 26 |
+
"""
|
| 27 |
+
Decorator to ensure that all tensor inputs to the decorated function are contiguous.
|
| 28 |
+
|
| 29 |
+
:param fn: Function to be decorated
|
| 30 |
+
|
| 31 |
+
:return wrapper: Wrapped function
|
| 32 |
+
"""
|
| 33 |
+
|
| 34 |
+
@functools.wraps(fn)
|
| 35 |
+
def wrapper(ctx, *args, **kwargs):
|
| 36 |
+
def maybe_to_contiguous(x):
|
| 37 |
+
return x.contiguous() if isinstance(x, torch.Tensor) else x
|
| 38 |
+
|
| 39 |
+
args = [maybe_to_contiguous(arg) for arg in args]
|
| 40 |
+
kwargs = {k: maybe_to_contiguous(v) for k, v in kwargs.items()}
|
| 41 |
+
return fn(ctx, *args, **kwargs)
|
| 42 |
+
|
| 43 |
+
return wrapper
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
@functools.lru_cache(maxsize=4096)
|
| 47 |
+
def window_sizes_heuristic(
|
| 48 |
+
seqlen_k: int,
|
| 49 |
+
num_heads_kv: int,
|
| 50 |
+
device: torch.device,
|
| 51 |
+
equal_bandwidth: bool = True,
|
| 52 |
+
window_dist: int = 1024,
|
| 53 |
+
window_sink: int = 64,
|
| 54 |
+
) -> torch.Tensor:
|
| 55 |
+
"""
|
| 56 |
+
Compute token count window sizes that partition non-diagonal causal distances into bands.
|
| 57 |
+
|
| 58 |
+
:param seqlen_k: Sequence length of keys.
|
| 59 |
+
:param num_heads_kv: Number of KV heads.
|
| 60 |
+
:param device: Target device.
|
| 61 |
+
:param equal_bandwidth: If True, use equal-bandwidth partitioning for balanced decode load. If False, use equal-area partitioning for balanced forward and backward load.
|
| 62 |
+
:param window_dist: Near-diagonal token count shared by all KV heads.
|
| 63 |
+
:param window_sink: Sink token count shared by all KV heads.
|
| 64 |
+
|
| 65 |
+
:return: int32 tensor with shape [num_heads_kv, 4], columns are [window_sink, window_left, window_right, window_dist]. window_sink is the prefix sink token count, window_left is the distant local band token count, window_right is the token count gap after the near-diagonal window before the distant band, and window_dist is the near-diagonal token count.
|
| 66 |
+
"""
|
| 67 |
+
window_dist = min(max(window_dist, 0), seqlen_k)
|
| 68 |
+
window_sink = min(max(window_sink, 0), max(seqlen_k - window_dist, 0))
|
| 69 |
+
head_kv_idx = torch.arange(num_heads_kv + 1, dtype=torch.float32)
|
| 70 |
+
distance_span = max(seqlen_k - window_sink - window_dist, 0)
|
| 71 |
+
if equal_bandwidth:
|
| 72 |
+
breakpoints = (distance_span * head_kv_idx / num_heads_kv).to(torch.int32)
|
| 73 |
+
else:
|
| 74 |
+
breakpoints = (
|
| 75 |
+
distance_span * (1.0 - torch.sqrt(1.0 - head_kv_idx / num_heads_kv))
|
| 76 |
+
).to(torch.int32)
|
| 77 |
+
window_size_left = breakpoints[1:] - breakpoints[:-1]
|
| 78 |
+
window_size_right = breakpoints[:-1]
|
| 79 |
+
window_size_dist = torch.full_like(window_size_left, window_dist)
|
| 80 |
+
window_size_sink = torch.full_like(window_size_left, window_sink)
|
| 81 |
+
return torch.stack(
|
| 82 |
+
[window_size_sink, window_size_left, window_size_right, window_size_dist], dim=1
|
| 83 |
+
).to(device)
|
| 84 |
+
|
| 85 |
+
|
| 86 |
+
def max_split_blocks_from_window_sizes(
|
| 87 |
+
window_sizes: torch.Tensor, TILE_N: int
|
| 88 |
+
) -> int | None:
|
| 89 |
+
"""
|
| 90 |
+
Estimate useful split-axis blocks for local attention.
|
| 91 |
+
|
| 92 |
+
The exact useful split count depends on the max values stored in window_sizes.
|
| 93 |
+
|
| 94 |
+
:param window_sizes: Int32 tensor with shape [num_heads_kv, 4], columns are [sink, left, right, dist]
|
| 95 |
+
:param TILE_N: Tile size for N dimension.
|
| 96 |
+
|
| 97 |
+
:return: Useful split-axis blocks, or None when CUDA Graph capture needs a static launch-grid bound.
|
| 98 |
+
"""
|
| 99 |
+
if window_sizes is None or window_sizes.numel() == 0:
|
| 100 |
+
return 1
|
| 101 |
+
if (
|
| 102 |
+
window_sizes.is_cuda
|
| 103 |
+
and torch.cuda.is_available()
|
| 104 |
+
and torch.cuda.is_current_stream_capturing()
|
| 105 |
+
):
|
| 106 |
+
return None
|
| 107 |
+
window_sizes = torch.clamp(window_sizes, min=0)
|
| 108 |
+
window_sink = torch.max(window_sizes[:, 0])
|
| 109 |
+
window_left = torch.max(window_sizes[:, 1])
|
| 110 |
+
window_dist = torch.max(window_sizes[:, 3])
|
| 111 |
+
distant_blocks = torch.div(window_left + TILE_N - 1, TILE_N, rounding_mode="floor")
|
| 112 |
+
near_blocks = (window_dist > 0).to(window_left.dtype)
|
| 113 |
+
sink_blocks = torch.div(window_sink + TILE_N - 1, TILE_N, rounding_mode="floor")
|
| 114 |
+
split_blocks = torch.maximum(
|
| 115 |
+
distant_blocks + near_blocks + sink_blocks,
|
| 116 |
+
torch.ones((), dtype=window_left.dtype, device=window_sizes.device),
|
| 117 |
+
)
|
| 118 |
+
return int(split_blocks)
|
| 119 |
+
|
| 120 |
+
|
| 121 |
+
@functools.lru_cache(maxsize=4096)
|
| 122 |
+
def num_splits_heuristic(
|
| 123 |
+
seqlen_q: int,
|
| 124 |
+
seqlen_k: int,
|
| 125 |
+
num_SMs: int,
|
| 126 |
+
TILE_M: int,
|
| 127 |
+
TILE_N: int,
|
| 128 |
+
max_split_blocks: int | None = None,
|
| 129 |
+
) -> int:
|
| 130 |
+
"""
|
| 131 |
+
Determine the number of KV splits for FlashDecoding.
|
| 132 |
+
|
| 133 |
+
Splits only when there are enough KV blocks to benefit from parallelism,
|
| 134 |
+
and targets full SM occupancy by over-subscribing the M-block count.
|
| 135 |
+
|
| 136 |
+
:param seqlen_q: Sequence length of queries.
|
| 137 |
+
:param seqlen_k: Sequence length of keys.
|
| 138 |
+
:param num_SMs: Number of streaming multiprocessors on the device.
|
| 139 |
+
:param TILE_M: Tile size for M dimension.
|
| 140 |
+
:param TILE_N: Tile size for N dimension.
|
| 141 |
+
:param max_split_blocks: Optional cap on useful split-axis blocks. Local two-band attention should pass this to avoid assigning splits to empty gap regions.
|
| 142 |
+
|
| 143 |
+
:return: Number of splits.
|
| 144 |
+
"""
|
| 145 |
+
total_mblocks = triton.cdiv(seqlen_q, TILE_M)
|
| 146 |
+
num_n_blocks = triton.cdiv(seqlen_k, TILE_N)
|
| 147 |
+
effective_n_blocks = num_n_blocks
|
| 148 |
+
if max_split_blocks is not None:
|
| 149 |
+
effective_n_blocks = min(num_n_blocks, max(max_split_blocks, 1))
|
| 150 |
+
max_splits = 1 << (max(num_SMs, 1).bit_length() - 1)
|
| 151 |
+
if effective_n_blocks <= 4:
|
| 152 |
+
# 1 means no splitting
|
| 153 |
+
return 1
|
| 154 |
+
return max(1, min(num_SMs // max(total_mblocks, 1), max_splits, effective_n_blocks))
|
| 155 |
+
|
| 156 |
+
|
| 157 |
+
def alloc_fn(size: int, alignment: int, stream):
|
| 158 |
+
"""
|
| 159 |
+
TMA descriptors require a global memory allocation
|
| 160 |
+
|
| 161 |
+
:param size: Size of the allocation in bytes.
|
| 162 |
+
:param alignment: Alignment requirement in bytes.
|
| 163 |
+
:param stream: CUDA stream for the allocation.
|
| 164 |
+
|
| 165 |
+
:return: A torch.Tensor representing the allocated memory.
|
| 166 |
+
"""
|
| 167 |
+
return torch.empty(size, device="cuda", dtype=torch.int8)
|