Instructions to use kernels-community/aiter-flash-attn with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Kernels
How to use kernels-community/aiter-flash-attn with Kernels:
# !pip install kernels from kernels import get_kernel # a version (or an explicit revision) is required; see the "Files and versions" tab for the available ones kernel = get_kernel("kernels-community/aiter-flash-attn", version=1) - Notebooks
- Google Colab
- Kaggle
Uploaded using `kernel-builder`.
Browse files- build/torch-rocm/__init__.py +32 -0
- build/torch-rocm/_kernels/__init__.py +0 -0
- build/torch-rocm/_kernels/flash_attn_triton_amd/__init__.py +4 -0
- build/torch-rocm/_kernels/flash_attn_triton_amd/bwd.py +0 -0
- build/torch-rocm/_kernels/flash_attn_triton_amd/common.py +573 -0
- build/torch-rocm/_kernels/flash_attn_triton_amd/fwd_decode.py +1475 -0
- build/torch-rocm/_kernels/flash_attn_triton_amd/fwd_prefill.py +1875 -0
- build/torch-rocm/_kernels/flash_attn_triton_amd/interface_v2.py +828 -0
- build/torch-rocm/_kernels/flash_attn_triton_amd/interface_v3.py +660 -0
- build/torch-rocm/_kernels/flash_attn_triton_amd/utils.py +338 -0
- build/torch-rocm/_kernels/mha.py +942 -0
- build/torch-rocm/_kernels/mha_fused_bwd.py +1073 -0
- build/torch-rocm/_kernels/mha_onekernel_bwd.py +1783 -0
- build/torch-rocm/_ops.py +38 -0
- build/torch-rocm/aiter_flash_attn/__init__.py +26 -0
- build/torch-rocm/metadata.json +10 -0
- build/torch-rocm/mha.py +1000 -0
- build/torch-rocm/mha_fused_bwd.py +317 -0
- build/torch-rocm/mha_onekernel_bwd.py +380 -0
- build/torch-rocm/utils/__init__.py +0 -0
- build/torch-rocm/utils/_triton/__init__.py +0 -0
- build/torch-rocm/utils/_triton/arch_info.py +42 -0
- build/torch-rocm/utils/_triton/kernel_repr.py +57 -0
- build/torch-rocm/utils/_triton/mha_kernel_utils.py +15 -0
- build/torch-rocm/utils/_triton/pid_preprocessing.py +100 -0
- build/torch-rocm/utils/core.py +5 -0
- build/torch-rocm/utils/device_info.py +14 -0
- build/torch-rocm/utils/logger.py +47 -0
- build/torch-rocm/utils/types.py +85 -0
build/torch-rocm/__init__.py
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""AITER Flash Attention kernels for AMD ROCm (Triton MHA).
|
| 2 |
+
|
| 3 |
+
Repackaged from the `aiter.ops.triton.attention.mha` module of the
|
| 4 |
+
`ROCm/aiter <https://github.com/ROCm/aiter>`_ project as a self-contained
|
| 5 |
+
Hugging Face Hub kernel. Exposes the two FlashAttention entry points used by
|
| 6 |
+
the `transformers` ROCm fallback.
|
| 7 |
+
"""
|
| 8 |
+
|
| 9 |
+
from .mha import (
|
| 10 |
+
flash_attn_func,
|
| 11 |
+
flash_attn_varlen_func,
|
| 12 |
+
flash_attn_with_kvcache,
|
| 13 |
+
mha_set_impl,
|
| 14 |
+
mha_set_use_fused_bwd_kernel,
|
| 15 |
+
mha_set_use_int64_strides,
|
| 16 |
+
)
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
__kernel_metadata__ = {
|
| 20 |
+
"license": "mit",
|
| 21 |
+
}
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
__all__ = [
|
| 25 |
+
"__kernel_metadata__",
|
| 26 |
+
"flash_attn_func",
|
| 27 |
+
"flash_attn_varlen_func",
|
| 28 |
+
"flash_attn_with_kvcache",
|
| 29 |
+
"mha_set_impl",
|
| 30 |
+
"mha_set_use_fused_bwd_kernel",
|
| 31 |
+
"mha_set_use_int64_strides",
|
| 32 |
+
]
|
build/torch-rocm/_kernels/__init__.py
ADDED
|
File without changes
|
build/torch-rocm/_kernels/flash_attn_triton_amd/__init__.py
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from . import interface_v2 as flash_attn_2
|
| 2 |
+
from . import interface_v3 as flash_attn_3
|
| 3 |
+
|
| 4 |
+
__all__ = ["flash_attn_2", "flash_attn_3"]
|
build/torch-rocm/_kernels/flash_attn_triton_amd/bwd.py
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
build/torch-rocm/_kernels/flash_attn_triton_amd/common.py
ADDED
|
@@ -0,0 +1,573 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Triton kernel helper functions shared across flash attention modules.
|
| 3 |
+
|
| 4 |
+
This module contains Triton JIT-compiled helper functions that are used within
|
| 5 |
+
the main attention kernels (fwd_prefill, fwd_decode, bwd). These are kept
|
| 6 |
+
separate from utils.py to allow stricter type checking on pure Python utilities.
|
| 7 |
+
"""
|
| 8 |
+
|
| 9 |
+
from typing import Literal, Optional, Tuple, Union
|
| 10 |
+
|
| 11 |
+
import torch
|
| 12 |
+
import triton
|
| 13 |
+
import triton.language as tl
|
| 14 |
+
|
| 15 |
+
from .utils import DEBUG, get_shape_from_layout, get_stride_from_layout, is_fp8
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
@triton.jit
|
| 19 |
+
def compute_alibi_block(
|
| 20 |
+
alibi_slope, seqlen_q, seqlen_k, offs_m, offs_n, transpose=False
|
| 21 |
+
):
|
| 22 |
+
"""
|
| 23 |
+
Compute ALiBi (Attention with Linear Biases) block.
|
| 24 |
+
|
| 25 |
+
When seqlen_k and seqlen_q are different, the diagonal sticks to the
|
| 26 |
+
bottom right of the attention matrix.
|
| 27 |
+
"""
|
| 28 |
+
# e.g. alibi_slope = 1, seqlen_q = 2, seqlen_k = 5
|
| 29 |
+
# offs_m = [0, 1], offs_n = [0, 1, 2, 3, 4]
|
| 30 |
+
# Result: [[-3, -2, -1, 0, -1], [-4, -3, -2, -1, 0]]
|
| 31 |
+
relative_pos_block = offs_m[:, None] + seqlen_k - seqlen_q - offs_n[None, :]
|
| 32 |
+
alibi_block = -1 * alibi_slope * tl.abs(relative_pos_block)
|
| 33 |
+
if transpose:
|
| 34 |
+
return alibi_block.T
|
| 35 |
+
else:
|
| 36 |
+
return alibi_block
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
@triton.jit
|
| 40 |
+
def compute_fp8_scaling_factors(x, fp8_max: tl.constexpr):
|
| 41 |
+
"""Compute FP8 scaling and descaling factors for a block."""
|
| 42 |
+
x_amax = tl.max(tl.abs(x))
|
| 43 |
+
x_amax = tl.where(x_amax <= 1e-9, 1e-9, x_amax)
|
| 44 |
+
scale_x = fp8_max / x_amax
|
| 45 |
+
descale_x = x_amax / fp8_max
|
| 46 |
+
return scale_x, descale_x
|
| 47 |
+
|
| 48 |
+
|
| 49 |
+
@triton.jit
|
| 50 |
+
def _cast_varlen_to_fp8_kernel_2d(
|
| 51 |
+
X,
|
| 52 |
+
X_fp8,
|
| 53 |
+
Descale,
|
| 54 |
+
cu_seqlens,
|
| 55 |
+
H,
|
| 56 |
+
MAX_SEQLEN,
|
| 57 |
+
stride_batch,
|
| 58 |
+
stride_seq,
|
| 59 |
+
stride_head,
|
| 60 |
+
stride_dim,
|
| 61 |
+
stride_out_batch,
|
| 62 |
+
stride_out_seq,
|
| 63 |
+
stride_out_head,
|
| 64 |
+
stride_out_dim,
|
| 65 |
+
stride_desc_batch,
|
| 66 |
+
stride_desc_head,
|
| 67 |
+
FP8_CLAMP_VAL,
|
| 68 |
+
FP8_MAX,
|
| 69 |
+
BLOCK_SIZE: tl.constexpr,
|
| 70 |
+
HEAD_DIM: tl.constexpr,
|
| 71 |
+
ACTUAL_HEAD_DIM: tl.constexpr,
|
| 72 |
+
IS_VARLEN: tl.constexpr,
|
| 73 |
+
):
|
| 74 |
+
"""Cast tensor to FP8 with per-(batch, head) scaling."""
|
| 75 |
+
b_id = tl.program_id(0)
|
| 76 |
+
h_id = tl.program_id(1)
|
| 77 |
+
|
| 78 |
+
# Get sequence bounds for this batch
|
| 79 |
+
if IS_VARLEN:
|
| 80 |
+
seq_start = tl.load(cu_seqlens + b_id)
|
| 81 |
+
seq_end = tl.load(cu_seqlens + b_id + 1)
|
| 82 |
+
seqlen = seq_end - seq_start
|
| 83 |
+
else:
|
| 84 |
+
seq_start = 0
|
| 85 |
+
seqlen = MAX_SEQLEN
|
| 86 |
+
|
| 87 |
+
# initialize max value tracker
|
| 88 |
+
x_max_val = 0.0
|
| 89 |
+
|
| 90 |
+
# STEP 1: Find max absolute value across the entire sequence
|
| 91 |
+
num_of_blocks = tl.cdiv(seqlen, BLOCK_SIZE)
|
| 92 |
+
for blk_idx in range(0, num_of_blocks):
|
| 93 |
+
offs_seq = blk_idx * BLOCK_SIZE + tl.arange(0, BLOCK_SIZE)
|
| 94 |
+
offs_dim = tl.arange(0, HEAD_DIM)
|
| 95 |
+
|
| 96 |
+
mask_seq = offs_seq[:, None] < seqlen
|
| 97 |
+
if ACTUAL_HEAD_DIM != HEAD_DIM:
|
| 98 |
+
mask_dim = offs_dim[None, :] < ACTUAL_HEAD_DIM
|
| 99 |
+
mask_seq = mask_seq & mask_dim
|
| 100 |
+
|
| 101 |
+
adj_x = (
|
| 102 |
+
b_id * stride_batch
|
| 103 |
+
+ h_id * stride_head
|
| 104 |
+
+ seq_start * stride_seq
|
| 105 |
+
+ offs_seq[:, None] * stride_seq
|
| 106 |
+
+ offs_dim[None, :] * stride_dim
|
| 107 |
+
)
|
| 108 |
+
x_block = tl.load(X + adj_x, mask=mask_seq, other=0.0)
|
| 109 |
+
block_max = tl.max(tl.abs(x_block))
|
| 110 |
+
x_max_val = tl.maximum(x_max_val, block_max)
|
| 111 |
+
|
| 112 |
+
# clamp to avoid division by zero
|
| 113 |
+
x_max_val = tl.maximum(x_max_val, FP8_CLAMP_VAL)
|
| 114 |
+
|
| 115 |
+
# compute scale and descale factors
|
| 116 |
+
scale = FP8_MAX / x_max_val
|
| 117 |
+
descale = x_max_val / FP8_MAX
|
| 118 |
+
|
| 119 |
+
# store descale factor
|
| 120 |
+
desc_ptr = Descale + b_id * stride_desc_batch + h_id
|
| 121 |
+
tl.store(desc_ptr, descale)
|
| 122 |
+
|
| 123 |
+
# STEP 2: Apply scaling and convert to FP8
|
| 124 |
+
for blk_idx in range(0, num_of_blocks):
|
| 125 |
+
offs_seq = blk_idx * BLOCK_SIZE + tl.arange(0, BLOCK_SIZE)
|
| 126 |
+
offs_dim = tl.arange(0, HEAD_DIM)
|
| 127 |
+
|
| 128 |
+
mask_seq = offs_seq[:, None] < seqlen
|
| 129 |
+
if ACTUAL_HEAD_DIM != HEAD_DIM:
|
| 130 |
+
mask_dim = offs_dim[None, :] < ACTUAL_HEAD_DIM
|
| 131 |
+
mask_seq = mask_seq & mask_dim
|
| 132 |
+
|
| 133 |
+
addr = (
|
| 134 |
+
b_id * stride_batch
|
| 135 |
+
+ h_id * stride_head
|
| 136 |
+
+ seq_start * stride_seq
|
| 137 |
+
+ offs_seq[:, None] * stride_seq
|
| 138 |
+
+ offs_dim[None, :] * stride_dim
|
| 139 |
+
)
|
| 140 |
+
x_block = tl.load(X + addr, mask=mask_seq, other=0.0)
|
| 141 |
+
x_fp8_block = (x_block * scale).to(X_fp8.type.element_ty)
|
| 142 |
+
|
| 143 |
+
addr_out = (
|
| 144 |
+
b_id * stride_out_batch
|
| 145 |
+
+ h_id * stride_out_head
|
| 146 |
+
+ seq_start * stride_out_seq
|
| 147 |
+
+ offs_seq[:, None] * stride_out_seq
|
| 148 |
+
+ offs_dim[None, :] * stride_out_dim
|
| 149 |
+
)
|
| 150 |
+
tl.store(X_fp8 + addr_out, x_fp8_block, mask=mask_seq)
|
| 151 |
+
|
| 152 |
+
|
| 153 |
+
@triton.jit
|
| 154 |
+
def _rotary_kernel(
|
| 155 |
+
OUT,
|
| 156 |
+
X,
|
| 157 |
+
COS,
|
| 158 |
+
SIN,
|
| 159 |
+
CU_SEQLENS,
|
| 160 |
+
SEQLEN_OFFSETS,
|
| 161 |
+
seqlen,
|
| 162 |
+
nheads,
|
| 163 |
+
seqlen_ro,
|
| 164 |
+
stride_out_batch,
|
| 165 |
+
stride_out_seqlen,
|
| 166 |
+
stride_out_nheads,
|
| 167 |
+
stride_out_headdim,
|
| 168 |
+
stride_x_batch,
|
| 169 |
+
stride_x_seqlen,
|
| 170 |
+
stride_x_nheads,
|
| 171 |
+
stride_x_headdim,
|
| 172 |
+
ROTARY_DIM: tl.constexpr,
|
| 173 |
+
IS_SEQLEN_OFFSETS_TENSOR: tl.constexpr,
|
| 174 |
+
IS_VARLEN: tl.constexpr,
|
| 175 |
+
INTERLEAVED: tl.constexpr,
|
| 176 |
+
CONJUGATE: tl.constexpr,
|
| 177 |
+
BLOCK_H: tl.constexpr,
|
| 178 |
+
BLOCK_M: tl.constexpr,
|
| 179 |
+
):
|
| 180 |
+
"""Apply rotary positional embeddings."""
|
| 181 |
+
BLOCK_K: tl.constexpr = triton.next_power_of_2(ROTARY_DIM)
|
| 182 |
+
ROTARY_DIM_HALF = ROTARY_DIM // 2
|
| 183 |
+
pid_head = tl.program_id(axis=0)
|
| 184 |
+
pid_m = tl.program_id(axis=1)
|
| 185 |
+
pid_batch = tl.program_id(axis=2)
|
| 186 |
+
|
| 187 |
+
if not IS_VARLEN:
|
| 188 |
+
X = X + pid_batch * stride_x_batch
|
| 189 |
+
OUT = OUT + pid_batch * stride_out_batch
|
| 190 |
+
else:
|
| 191 |
+
start_idx = tl.load(CU_SEQLENS + pid_batch)
|
| 192 |
+
seqlen = tl.load(CU_SEQLENS + pid_batch + 1) - start_idx
|
| 193 |
+
X = X + start_idx * stride_x_seqlen
|
| 194 |
+
OUT = OUT + start_idx * stride_out_seqlen
|
| 195 |
+
|
| 196 |
+
if pid_m * BLOCK_M >= seqlen:
|
| 197 |
+
return
|
| 198 |
+
|
| 199 |
+
rh = pid_head * BLOCK_H + tl.arange(0, BLOCK_H)
|
| 200 |
+
rm = pid_m * BLOCK_M + tl.arange(0, BLOCK_M)
|
| 201 |
+
if not IS_SEQLEN_OFFSETS_TENSOR:
|
| 202 |
+
rm_cs = rm + SEQLEN_OFFSETS
|
| 203 |
+
else:
|
| 204 |
+
rm_cs = rm + tl.load(SEQLEN_OFFSETS + pid_batch)
|
| 205 |
+
|
| 206 |
+
rk_half = tl.arange(0, BLOCK_K // 2)
|
| 207 |
+
COS = COS + (rm_cs[:, None] * ROTARY_DIM_HALF + rk_half[None, :])
|
| 208 |
+
SIN = SIN + (rm_cs[:, None] * ROTARY_DIM_HALF + rk_half[None, :])
|
| 209 |
+
mask_cs = (rm_cs[:, None] < seqlen_ro) & (rk_half[None, :] < ROTARY_DIM_HALF)
|
| 210 |
+
cos = tl.load(COS, mask=mask_cs, other=1.0).to(tl.float32)
|
| 211 |
+
sin = tl.load(SIN, mask=mask_cs, other=0.0).to(tl.float32)
|
| 212 |
+
if CONJUGATE:
|
| 213 |
+
sin = -sin
|
| 214 |
+
|
| 215 |
+
if not INTERLEAVED:
|
| 216 |
+
X = X + (
|
| 217 |
+
rh[:, None, None] * stride_x_nheads
|
| 218 |
+
+ rm[None, :, None] * stride_x_seqlen
|
| 219 |
+
+ rk_half[None, None, :] * stride_x_headdim
|
| 220 |
+
)
|
| 221 |
+
OUT = OUT + (
|
| 222 |
+
rh[:, None, None] * stride_out_nheads
|
| 223 |
+
+ rm[None, :, None] * stride_out_seqlen
|
| 224 |
+
+ rk_half[None, None, :] * stride_out_headdim
|
| 225 |
+
)
|
| 226 |
+
mask = (
|
| 227 |
+
(rh[:, None, None] < nheads)
|
| 228 |
+
& (rm[None, :, None] < seqlen)
|
| 229 |
+
& (rk_half[None, None, :] < ROTARY_DIM_HALF)
|
| 230 |
+
)
|
| 231 |
+
x0 = tl.load(X, mask=mask, other=0.0).to(tl.float32)
|
| 232 |
+
x1 = tl.load(X + ROTARY_DIM_HALF * stride_x_headdim, mask=mask, other=0.0).to(
|
| 233 |
+
tl.float32
|
| 234 |
+
)
|
| 235 |
+
o0 = x0 * cos - x1 * sin
|
| 236 |
+
o1 = x0 * sin + x1 * cos
|
| 237 |
+
tl.store(OUT, o0, mask=mask)
|
| 238 |
+
tl.store(OUT + ROTARY_DIM_HALF * stride_out_headdim, o1, mask=mask)
|
| 239 |
+
else:
|
| 240 |
+
rk = tl.arange(0, BLOCK_K)
|
| 241 |
+
X = X + (
|
| 242 |
+
rh[:, None, None] * stride_x_nheads
|
| 243 |
+
+ rm[None, :, None] * stride_x_seqlen
|
| 244 |
+
+ rk[None, None, :] * stride_x_headdim
|
| 245 |
+
)
|
| 246 |
+
OUT = OUT + (
|
| 247 |
+
rh[:, None, None] * stride_out_nheads
|
| 248 |
+
+ rm[None, :, None] * stride_out_seqlen
|
| 249 |
+
+ rk[None, None, :] * stride_out_headdim
|
| 250 |
+
)
|
| 251 |
+
mask = (
|
| 252 |
+
(rh[:, None, None] < nheads)
|
| 253 |
+
& (rm[None, :, None] < seqlen)
|
| 254 |
+
& (rk[None, None, :] < ROTARY_DIM)
|
| 255 |
+
)
|
| 256 |
+
x = tl.load(X, mask=mask, other=0.0).to(tl.float32)
|
| 257 |
+
x0, x1 = tl.split(tl.reshape(x, [BLOCK_H, BLOCK_M, BLOCK_K // 2, 2]))
|
| 258 |
+
o0 = x0 * cos - x1 * sin
|
| 259 |
+
o1 = x0 * sin + x1 * cos
|
| 260 |
+
o = tl.reshape(tl.join(o0, o1), [BLOCK_H, BLOCK_M, BLOCK_K])
|
| 261 |
+
tl.store(OUT, o, mask=mask)
|
| 262 |
+
|
| 263 |
+
|
| 264 |
+
# -------------------------------
|
| 265 |
+
# Python wrappers for Triton kernels
|
| 266 |
+
# -------------------------------
|
| 267 |
+
|
| 268 |
+
|
| 269 |
+
def cast_to_fp8(
|
| 270 |
+
x: torch.Tensor,
|
| 271 |
+
fp8_dtype: torch.dtype,
|
| 272 |
+
layout: Literal["bshd", "thd"],
|
| 273 |
+
clamp_val: float = 1e-9,
|
| 274 |
+
cu_seqlens: Optional[torch.Tensor] = None,
|
| 275 |
+
max_seqlen: Optional[int] = None,
|
| 276 |
+
) -> tuple[torch.Tensor, torch.Tensor]:
|
| 277 |
+
"""Cast tensor to FP8 with per-(batch, head) scaling factors."""
|
| 278 |
+
if DEBUG > 0:
|
| 279 |
+
print()
|
| 280 |
+
print("cast_to_fp8")
|
| 281 |
+
print("x:", x, x.shape)
|
| 282 |
+
print("fp8_dtype:", fp8_dtype)
|
| 283 |
+
print("cu_seqlens:", cu_seqlens)
|
| 284 |
+
print("max_seqlen:", max_seqlen)
|
| 285 |
+
print("clamp_val:", clamp_val)
|
| 286 |
+
|
| 287 |
+
assert x.dtype in {
|
| 288 |
+
torch.float16,
|
| 289 |
+
torch.float32,
|
| 290 |
+
torch.float64,
|
| 291 |
+
torch.bfloat16,
|
| 292 |
+
} and is_fp8(fp8_dtype), f"Cannot cast {x.dtype} to {fp8_dtype}"
|
| 293 |
+
|
| 294 |
+
batch, max_seqlen_final, num_heads, head_dim = get_shape_from_layout(
|
| 295 |
+
x, layout, cu_seqlens, max_seqlen
|
| 296 |
+
)
|
| 297 |
+
is_varlen = layout == "thd"
|
| 298 |
+
fp8_max = torch.finfo(fp8_dtype).max
|
| 299 |
+
|
| 300 |
+
padded_head_dim = 1 << (head_dim - 1).bit_length()
|
| 301 |
+
padded_head_dim = max(padded_head_dim, 32)
|
| 302 |
+
|
| 303 |
+
x_fp8 = torch.zeros_like(x, dtype=fp8_dtype)
|
| 304 |
+
descale_factors = torch.zeros(
|
| 305 |
+
(batch, num_heads), device=x.device, dtype=torch.float32
|
| 306 |
+
)
|
| 307 |
+
BLOCK_SIZE = 128
|
| 308 |
+
|
| 309 |
+
stride_batch, stride_head, stride_seq, stride_dim = get_stride_from_layout(
|
| 310 |
+
x, layout
|
| 311 |
+
)
|
| 312 |
+
stride_out_batch, stride_out_head, stride_out_seq, stride_out_dim = (
|
| 313 |
+
get_stride_from_layout(x_fp8, layout)
|
| 314 |
+
)
|
| 315 |
+
stride_desc_batch, stride_desc_head = descale_factors.stride()
|
| 316 |
+
|
| 317 |
+
grid = (batch, num_heads)
|
| 318 |
+
_cast_varlen_to_fp8_kernel_2d[grid](
|
| 319 |
+
x,
|
| 320 |
+
x_fp8,
|
| 321 |
+
descale_factors,
|
| 322 |
+
cu_seqlens,
|
| 323 |
+
num_heads,
|
| 324 |
+
max_seqlen_final,
|
| 325 |
+
stride_batch,
|
| 326 |
+
stride_seq,
|
| 327 |
+
stride_head,
|
| 328 |
+
stride_dim,
|
| 329 |
+
stride_out_batch,
|
| 330 |
+
stride_out_seq,
|
| 331 |
+
stride_out_head,
|
| 332 |
+
stride_out_dim,
|
| 333 |
+
stride_desc_batch,
|
| 334 |
+
stride_desc_head,
|
| 335 |
+
clamp_val,
|
| 336 |
+
fp8_max,
|
| 337 |
+
BLOCK_SIZE=BLOCK_SIZE,
|
| 338 |
+
HEAD_DIM=padded_head_dim,
|
| 339 |
+
ACTUAL_HEAD_DIM=head_dim,
|
| 340 |
+
IS_VARLEN=is_varlen,
|
| 341 |
+
)
|
| 342 |
+
|
| 343 |
+
return x_fp8, descale_factors
|
| 344 |
+
|
| 345 |
+
|
| 346 |
+
def _apply_rotary_kernel(
|
| 347 |
+
x: torch.Tensor,
|
| 348 |
+
cos: torch.Tensor,
|
| 349 |
+
sin: torch.Tensor,
|
| 350 |
+
seqlen_offsets: Union[int, torch.Tensor] = 0,
|
| 351 |
+
cu_seqlens: Optional[torch.Tensor] = None,
|
| 352 |
+
max_seqlen: Optional[int] = None,
|
| 353 |
+
interleaved: bool = False,
|
| 354 |
+
inplace: bool = False,
|
| 355 |
+
conjugate: bool = False,
|
| 356 |
+
) -> torch.Tensor:
|
| 357 |
+
"""Apply rotary positional embeddings using Triton kernel."""
|
| 358 |
+
is_varlen = cu_seqlens is not None
|
| 359 |
+
if not is_varlen:
|
| 360 |
+
batch, seqlen, nheads, headdim = x.shape
|
| 361 |
+
else:
|
| 362 |
+
assert (
|
| 363 |
+
max_seqlen is not None
|
| 364 |
+
), "If cu_seqlens is passed, max_seqlen must also be provided"
|
| 365 |
+
total_seqlen, nheads, headdim = x.shape
|
| 366 |
+
assert cu_seqlens is not None
|
| 367 |
+
batch_p_1 = cu_seqlens.shape[0]
|
| 368 |
+
batch = batch_p_1 - 1
|
| 369 |
+
seqlen = max_seqlen
|
| 370 |
+
seqlen_ro, rotary_dim_half = cos.shape
|
| 371 |
+
assert sin.shape == cos.shape
|
| 372 |
+
rotary_dim = 2 * rotary_dim_half
|
| 373 |
+
assert rotary_dim <= headdim
|
| 374 |
+
assert headdim <= 256
|
| 375 |
+
assert seqlen_ro >= seqlen
|
| 376 |
+
|
| 377 |
+
cos, sin = cos.contiguous(), sin.contiguous()
|
| 378 |
+
if isinstance(seqlen_offsets, torch.Tensor):
|
| 379 |
+
assert seqlen_offsets.shape == (batch,)
|
| 380 |
+
assert seqlen_offsets.dtype in (torch.int32, torch.int64)
|
| 381 |
+
seqlen_offsets = seqlen_offsets.contiguous()
|
| 382 |
+
else:
|
| 383 |
+
assert seqlen_offsets + seqlen <= seqlen_ro
|
| 384 |
+
|
| 385 |
+
out = torch.empty_like(x) if not inplace else x
|
| 386 |
+
if rotary_dim < headdim and not inplace:
|
| 387 |
+
out[..., rotary_dim:].copy_(x[..., rotary_dim:])
|
| 388 |
+
|
| 389 |
+
BLOCK_M = 8 if rotary_dim <= 128 else 4
|
| 390 |
+
grid = (
|
| 391 |
+
triton.cdiv(nheads, 2),
|
| 392 |
+
triton.cdiv(seqlen, BLOCK_M),
|
| 393 |
+
batch,
|
| 394 |
+
)
|
| 395 |
+
|
| 396 |
+
with torch.cuda.device(x.device.index):
|
| 397 |
+
torch.library.wrap_triton(_rotary_kernel)[grid](
|
| 398 |
+
out,
|
| 399 |
+
x,
|
| 400 |
+
cos,
|
| 401 |
+
sin,
|
| 402 |
+
cu_seqlens,
|
| 403 |
+
seqlen_offsets,
|
| 404 |
+
seqlen,
|
| 405 |
+
nheads,
|
| 406 |
+
seqlen_ro,
|
| 407 |
+
out.stride(0) if not is_varlen else 0,
|
| 408 |
+
out.stride(-3),
|
| 409 |
+
out.stride(-2),
|
| 410 |
+
out.stride(-1),
|
| 411 |
+
x.stride(0) if not is_varlen else 0,
|
| 412 |
+
x.stride(-3),
|
| 413 |
+
x.stride(-2),
|
| 414 |
+
x.stride(-1),
|
| 415 |
+
rotary_dim,
|
| 416 |
+
isinstance(seqlen_offsets, torch.Tensor),
|
| 417 |
+
is_varlen,
|
| 418 |
+
interleaved,
|
| 419 |
+
conjugate,
|
| 420 |
+
BLOCK_M=BLOCK_M,
|
| 421 |
+
BLOCK_H=2,
|
| 422 |
+
)
|
| 423 |
+
return out
|
| 424 |
+
|
| 425 |
+
|
| 426 |
+
class _ApplyRotary(torch.autograd.Function):
|
| 427 |
+
@staticmethod
|
| 428 |
+
def forward(
|
| 429 |
+
ctx,
|
| 430 |
+
x: torch.Tensor,
|
| 431 |
+
cos: torch.Tensor,
|
| 432 |
+
sin: torch.Tensor,
|
| 433 |
+
interleaved: bool,
|
| 434 |
+
inplace: bool,
|
| 435 |
+
seqlen_offsets: Union[int, torch.Tensor],
|
| 436 |
+
cu_seqlens: Optional[torch.Tensor],
|
| 437 |
+
max_seqlen: Optional[int],
|
| 438 |
+
) -> torch.Tensor:
|
| 439 |
+
out = _apply_rotary_kernel(
|
| 440 |
+
x,
|
| 441 |
+
cos,
|
| 442 |
+
sin,
|
| 443 |
+
seqlen_offsets=seqlen_offsets,
|
| 444 |
+
cu_seqlens=cu_seqlens,
|
| 445 |
+
max_seqlen=max_seqlen,
|
| 446 |
+
interleaved=interleaved,
|
| 447 |
+
inplace=inplace,
|
| 448 |
+
conjugate=False,
|
| 449 |
+
)
|
| 450 |
+
if isinstance(seqlen_offsets, int):
|
| 451 |
+
ctx.save_for_backward(cos, sin, cu_seqlens)
|
| 452 |
+
ctx.seqlen_offsets = seqlen_offsets
|
| 453 |
+
else:
|
| 454 |
+
ctx.save_for_backward(cos, sin, cu_seqlens, seqlen_offsets)
|
| 455 |
+
ctx.seqlen_offsets = None
|
| 456 |
+
ctx.interleaved = interleaved
|
| 457 |
+
ctx.inplace = inplace
|
| 458 |
+
ctx.max_seqlen = max_seqlen
|
| 459 |
+
return out if not inplace else x
|
| 460 |
+
|
| 461 |
+
@staticmethod
|
| 462 |
+
def backward(
|
| 463 |
+
ctx, do: torch.Tensor
|
| 464 |
+
) -> tuple[torch.Tensor, None, None, None, None, None, None, None]:
|
| 465 |
+
seqlen_offsets = ctx.seqlen_offsets
|
| 466 |
+
if seqlen_offsets is None:
|
| 467 |
+
cos, sin, cu_seqlens, seqlen_offsets = ctx.saved_tensors
|
| 468 |
+
else:
|
| 469 |
+
cos, sin, cu_seqlens = ctx.saved_tensors
|
| 470 |
+
dx = _apply_rotary_kernel(
|
| 471 |
+
do,
|
| 472 |
+
cos,
|
| 473 |
+
sin,
|
| 474 |
+
seqlen_offsets=seqlen_offsets,
|
| 475 |
+
cu_seqlens=cu_seqlens,
|
| 476 |
+
max_seqlen=ctx.max_seqlen,
|
| 477 |
+
interleaved=ctx.interleaved,
|
| 478 |
+
inplace=ctx.inplace,
|
| 479 |
+
conjugate=True,
|
| 480 |
+
)
|
| 481 |
+
return dx, None, None, None, None, None, None, None
|
| 482 |
+
|
| 483 |
+
|
| 484 |
+
def apply_rotary_emb(
|
| 485 |
+
x: torch.Tensor,
|
| 486 |
+
cos: torch.Tensor,
|
| 487 |
+
sin: torch.Tensor,
|
| 488 |
+
interleaved: bool = False,
|
| 489 |
+
inplace: bool = False,
|
| 490 |
+
seqlen_offsets: Union[int, torch.Tensor] = 0,
|
| 491 |
+
cu_seqlens: Optional[torch.Tensor] = None,
|
| 492 |
+
max_seqlen: Optional[int] = None,
|
| 493 |
+
) -> torch.Tensor:
|
| 494 |
+
"""Apply rotary embeddings to tensor x.
|
| 495 |
+
|
| 496 |
+
Args:
|
| 497 |
+
x: (B, S, H, D) if `cu_seqlens` is None else (total_S, H, D).
|
| 498 |
+
cos, sin: (S_rotary, rotary_dim/2)
|
| 499 |
+
interleaved: GPT-J style if True.
|
| 500 |
+
inplace: modify x in place.
|
| 501 |
+
seqlen_offsets: int or (B,) tensor of starting offsets per sequence.
|
| 502 |
+
cu_seqlens: (B+1,) tensor enabling varlen mode.
|
| 503 |
+
max_seqlen: required when `cu_seqlens` is provided.
|
| 504 |
+
"""
|
| 505 |
+
original_dtype = x.dtype
|
| 506 |
+
is_fp8_input = is_fp8(original_dtype)
|
| 507 |
+
if is_fp8_input:
|
| 508 |
+
target_dtype = (
|
| 509 |
+
torch.bfloat16
|
| 510 |
+
if cos.dtype == torch.bfloat16 or torch.cuda.is_bf16_supported()
|
| 511 |
+
else torch.float16
|
| 512 |
+
)
|
| 513 |
+
x_up = x.to(target_dtype)
|
| 514 |
+
cos_up = cos.to(target_dtype) if cos.dtype != target_dtype else cos
|
| 515 |
+
sin_up = sin.to(target_dtype) if sin.dtype != target_dtype else sin
|
| 516 |
+
out_up = _ApplyRotary.apply(
|
| 517 |
+
x_up,
|
| 518 |
+
cos_up,
|
| 519 |
+
sin_up,
|
| 520 |
+
interleaved,
|
| 521 |
+
False,
|
| 522 |
+
seqlen_offsets,
|
| 523 |
+
cu_seqlens,
|
| 524 |
+
max_seqlen,
|
| 525 |
+
)
|
| 526 |
+
if inplace:
|
| 527 |
+
x.copy_(out_up.to(original_dtype))
|
| 528 |
+
return x
|
| 529 |
+
return out_up.to(original_dtype)
|
| 530 |
+
else:
|
| 531 |
+
return _ApplyRotary.apply(
|
| 532 |
+
x, cos, sin, interleaved, inplace, seqlen_offsets, cu_seqlens, max_seqlen
|
| 533 |
+
)
|
| 534 |
+
|
| 535 |
+
|
| 536 |
+
def apply_rotary(
|
| 537 |
+
q: torch.Tensor,
|
| 538 |
+
k_new: Optional[torch.Tensor],
|
| 539 |
+
cos: torch.Tensor,
|
| 540 |
+
sin: torch.Tensor,
|
| 541 |
+
*,
|
| 542 |
+
causal: bool,
|
| 543 |
+
local: bool,
|
| 544 |
+
interleaved: bool = False,
|
| 545 |
+
seqlen_offsets: Union[int, torch.Tensor] = 0,
|
| 546 |
+
) -> Tuple[torch.Tensor, Optional[torch.Tensor]]:
|
| 547 |
+
"""Apply rotary embeddings to q and optionally k_new.
|
| 548 |
+
|
| 549 |
+
Policy:
|
| 550 |
+
- If causal OR local attention: apply rotary directly on (B, S, H, D).
|
| 551 |
+
- Else (non-causal global): flatten heads into sequence, apply, unflatten.
|
| 552 |
+
- k_new is always rotated directly when provided.
|
| 553 |
+
"""
|
| 554 |
+
assert q.ndim == 4, f"Expected q shape (B,S,H,D), got {q.shape}"
|
| 555 |
+
B, S, H, D = q.shape
|
| 556 |
+
use_flatten = (not causal) and (not local)
|
| 557 |
+
|
| 558 |
+
if use_flatten:
|
| 559 |
+
q_flat = q.reshape(B, S * H, D).unsqueeze(1)
|
| 560 |
+
q_flat = apply_rotary_emb(
|
| 561 |
+
q_flat, cos, sin, interleaved=interleaved, seqlen_offsets=seqlen_offsets
|
| 562 |
+
)
|
| 563 |
+
q = q_flat.view(B, 1, S * H, D).reshape(B, S, H, D)
|
| 564 |
+
else:
|
| 565 |
+
q = apply_rotary_emb(
|
| 566 |
+
q, cos, sin, interleaved=interleaved, seqlen_offsets=seqlen_offsets
|
| 567 |
+
)
|
| 568 |
+
|
| 569 |
+
if k_new is not None:
|
| 570 |
+
k_new = apply_rotary_emb(
|
| 571 |
+
k_new, cos, sin, interleaved=interleaved, seqlen_offsets=seqlen_offsets
|
| 572 |
+
)
|
| 573 |
+
return q, k_new
|
build/torch-rocm/_kernels/flash_attn_triton_amd/fwd_decode.py
ADDED
|
@@ -0,0 +1,1475 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import warnings
|
| 2 |
+
import torch
|
| 3 |
+
import triton
|
| 4 |
+
import triton.language as tl
|
| 5 |
+
from typing import Literal, Optional
|
| 6 |
+
from .common import apply_rotary
|
| 7 |
+
from .utils import (
|
| 8 |
+
DEBUG,
|
| 9 |
+
AUTOTUNE,
|
| 10 |
+
AutotuneMode,
|
| 11 |
+
get_arch,
|
| 12 |
+
get_padded_headsize,
|
| 13 |
+
get_shape_from_layout,
|
| 14 |
+
get_stride_from_layout,
|
| 15 |
+
is_fp8,
|
| 16 |
+
)
|
| 17 |
+
|
| 18 |
+
FWD_DECODE_AUTOTUNE_KEYS = [
|
| 19 |
+
"N_CTX_Q",
|
| 20 |
+
"N_CTX_K",
|
| 21 |
+
"ACTUAL_BLOCK_DMODEL",
|
| 22 |
+
"H_q",
|
| 23 |
+
"H_kv",
|
| 24 |
+
"IS_CAUSAL",
|
| 25 |
+
"IS_GQA",
|
| 26 |
+
]
|
| 27 |
+
|
| 28 |
+
# Maximum BLOCK_M across all configs (for intermediate tensor allocation)
|
| 29 |
+
MAX_BLOCK_M = 64
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
def get_fwd_decode_configs(mode: AutotuneMode):
|
| 33 |
+
"""
|
| 34 |
+
Returns configs for both the splitK kernel and reduce kernel.
|
| 35 |
+
|
| 36 |
+
Returns:
|
| 37 |
+
(splitk_configs, reduce_config): Tuple of config lists for each kernel
|
| 38 |
+
"""
|
| 39 |
+
|
| 40 |
+
if mode == "off":
|
| 41 |
+
arch = get_arch()
|
| 42 |
+
if arch.is_rdna:
|
| 43 |
+
return (
|
| 44 |
+
[
|
| 45 |
+
triton.Config(
|
| 46 |
+
{"BLOCK_M": 32, "BLOCK_N": 32},
|
| 47 |
+
num_stages=1,
|
| 48 |
+
num_warps=4,
|
| 49 |
+
),
|
| 50 |
+
],
|
| 51 |
+
[triton.Config({}, num_stages=1, num_warps=4)],
|
| 52 |
+
)
|
| 53 |
+
elif arch.is_cdna:
|
| 54 |
+
return (
|
| 55 |
+
[
|
| 56 |
+
triton.Config(
|
| 57 |
+
{"BLOCK_M": 64, "BLOCK_N": 64, "waves_per_eu": 1},
|
| 58 |
+
num_stages=1,
|
| 59 |
+
num_warps=4,
|
| 60 |
+
),
|
| 61 |
+
],
|
| 62 |
+
[triton.Config({}, num_stages=1, num_warps=4)],
|
| 63 |
+
)
|
| 64 |
+
else:
|
| 65 |
+
return (
|
| 66 |
+
[
|
| 67 |
+
triton.Config(
|
| 68 |
+
{"BLOCK_M": 64, "BLOCK_N": 64, "waves_per_eu": 1},
|
| 69 |
+
num_stages=1,
|
| 70 |
+
num_warps=4,
|
| 71 |
+
),
|
| 72 |
+
],
|
| 73 |
+
[triton.Config({}, num_stages=1, num_warps=4)],
|
| 74 |
+
)
|
| 75 |
+
|
| 76 |
+
elif mode == "on":
|
| 77 |
+
arch = get_arch()
|
| 78 |
+
if arch.is_rdna:
|
| 79 |
+
return (
|
| 80 |
+
[
|
| 81 |
+
triton.Config(
|
| 82 |
+
{"BLOCK_M": 32, "BLOCK_N": 32},
|
| 83 |
+
num_stages=1,
|
| 84 |
+
num_warps=4,
|
| 85 |
+
),
|
| 86 |
+
],
|
| 87 |
+
[triton.Config({}, num_stages=1, num_warps=4)],
|
| 88 |
+
)
|
| 89 |
+
elif arch.is_cdna:
|
| 90 |
+
return (
|
| 91 |
+
[
|
| 92 |
+
triton.Config(
|
| 93 |
+
{"BLOCK_M": 64, "BLOCK_N": 64, "waves_per_eu": 1},
|
| 94 |
+
num_stages=1,
|
| 95 |
+
num_warps=4,
|
| 96 |
+
),
|
| 97 |
+
],
|
| 98 |
+
[triton.Config({}, num_stages=1, num_warps=4)],
|
| 99 |
+
)
|
| 100 |
+
else:
|
| 101 |
+
return (
|
| 102 |
+
[
|
| 103 |
+
triton.Config(
|
| 104 |
+
{"BLOCK_M": 64, "BLOCK_N": 64, "waves_per_eu": 1},
|
| 105 |
+
num_stages=1,
|
| 106 |
+
num_warps=4,
|
| 107 |
+
),
|
| 108 |
+
],
|
| 109 |
+
[triton.Config({}, num_stages=1, num_warps=4)],
|
| 110 |
+
)
|
| 111 |
+
|
| 112 |
+
else: # sweep
|
| 113 |
+
arch = get_arch()
|
| 114 |
+
splitk_configs = []
|
| 115 |
+
|
| 116 |
+
BLOCK_M_OPTIONS = [64, 32, 16]
|
| 117 |
+
BLOCK_N_OPTIONS = [128, 64, 32, 16]
|
| 118 |
+
NUM_WARPS_OPTIONS = [2, 4]
|
| 119 |
+
NUM_STAGES_OPTIONS = [1]
|
| 120 |
+
WAVES_PER_EU_OPTIONS = [4, 2, 1]
|
| 121 |
+
|
| 122 |
+
# Ensure BLOCK_M options don't exceed MAX_BLOCK_M
|
| 123 |
+
assert all(
|
| 124 |
+
bm <= MAX_BLOCK_M for bm in BLOCK_M_OPTIONS
|
| 125 |
+
), f"BLOCK_M_OPTIONS {BLOCK_M_OPTIONS} exceeds MAX_BLOCK_M {MAX_BLOCK_M}"
|
| 126 |
+
|
| 127 |
+
for bm in BLOCK_M_OPTIONS:
|
| 128 |
+
for bn in BLOCK_N_OPTIONS:
|
| 129 |
+
for waves in WAVES_PER_EU_OPTIONS:
|
| 130 |
+
for nw in NUM_WARPS_OPTIONS:
|
| 131 |
+
for ns in NUM_STAGES_OPTIONS:
|
| 132 |
+
splitk_configs.append(
|
| 133 |
+
triton.Config(
|
| 134 |
+
{
|
| 135 |
+
"BLOCK_M": bm,
|
| 136 |
+
"BLOCK_N": bn,
|
| 137 |
+
"waves_per_eu": waves,
|
| 138 |
+
},
|
| 139 |
+
num_stages=ns,
|
| 140 |
+
num_warps=nw,
|
| 141 |
+
)
|
| 142 |
+
)
|
| 143 |
+
|
| 144 |
+
# Reduce kernel configs - sweep num_warps
|
| 145 |
+
NUM_WARPS_REDUCE_OPTIONS = [2, 4]
|
| 146 |
+
reduce_configs = [
|
| 147 |
+
triton.Config({}, num_stages=1, num_warps=nw)
|
| 148 |
+
for nw in NUM_WARPS_REDUCE_OPTIONS
|
| 149 |
+
]
|
| 150 |
+
|
| 151 |
+
return splitk_configs, reduce_configs
|
| 152 |
+
|
| 153 |
+
|
| 154 |
+
fwd_decode_splitk_configs, fwd_decode_reduce_configs = get_fwd_decode_configs(AUTOTUNE)
|
| 155 |
+
|
| 156 |
+
|
| 157 |
+
@triton.jit
|
| 158 |
+
def _attn_fwd_inner(
|
| 159 |
+
q,
|
| 160 |
+
kT,
|
| 161 |
+
v,
|
| 162 |
+
pos,
|
| 163 |
+
col_mask,
|
| 164 |
+
m_i,
|
| 165 |
+
l_i,
|
| 166 |
+
acc,
|
| 167 |
+
pid_m,
|
| 168 |
+
q_descale,
|
| 169 |
+
k_descale,
|
| 170 |
+
v_descale, # FP8 scaling factors
|
| 171 |
+
alibi_slope,
|
| 172 |
+
apply_col_mask,
|
| 173 |
+
IS_FP8: tl.constexpr, # FP8 flag
|
| 174 |
+
BLOCK_M: tl.constexpr,
|
| 175 |
+
BLOCK_N: tl.constexpr,
|
| 176 |
+
N_CTX_Q: tl.constexpr,
|
| 177 |
+
N_CTX_K_FINAL: tl.constexpr,
|
| 178 |
+
USE_ALIBI: tl.constexpr,
|
| 179 |
+
USE_SLIDING_WINDOW: tl.constexpr,
|
| 180 |
+
IS_CAUSAL: tl.constexpr,
|
| 181 |
+
WINDOW_SIZE_LEFT: tl.constexpr,
|
| 182 |
+
WINDOW_SIZE_RIGHT: tl.constexpr,
|
| 183 |
+
):
|
| 184 |
+
# -- compute qk ---
|
| 185 |
+
qk = tl.zeros([BLOCK_M, BLOCK_N], dtype=tl.float32)
|
| 186 |
+
if IS_FP8:
|
| 187 |
+
qk += tl.dot(q, kT) * q_descale * k_descale # Apply FP8 scaling
|
| 188 |
+
else:
|
| 189 |
+
qk = tl.dot(q, kT, acc=qk) # noqa: F821
|
| 190 |
+
|
| 191 |
+
if USE_ALIBI:
|
| 192 |
+
row_idx = pid_m * BLOCK_M + tl.arange(0, BLOCK_M)
|
| 193 |
+
col_idx = pos + tl.arange(0, BLOCK_N)
|
| 194 |
+
|
| 195 |
+
# Compute relative positions
|
| 196 |
+
relative_pos = row_idx[:, None] + N_CTX_K_FINAL - (N_CTX_Q + col_idx[None, :])
|
| 197 |
+
relative_pos = tl.abs(relative_pos)
|
| 198 |
+
|
| 199 |
+
# Compute ALiBi bias
|
| 200 |
+
alibi_bias = -1 * alibi_slope * relative_pos
|
| 201 |
+
qk += alibi_bias * 1.44269504
|
| 202 |
+
|
| 203 |
+
# ------------------------------------------------------------------
|
| 204 |
+
# masking
|
| 205 |
+
# ------------------------------------------------------------------
|
| 206 |
+
if USE_SLIDING_WINDOW:
|
| 207 |
+
row_idx = pid_m * BLOCK_M + tl.arange(0, BLOCK_M) # q positions
|
| 208 |
+
col_idx = pos + tl.arange(0, BLOCK_N) # k positions
|
| 209 |
+
row = row_idx[:, None] # [M,1]
|
| 210 |
+
col = col_idx[None, :] # [1,N]
|
| 211 |
+
|
| 212 |
+
if IS_CAUSAL:
|
| 213 |
+
# -------- causal + window --------
|
| 214 |
+
diag = N_CTX_K_FINAL - N_CTX_Q # sk-sq
|
| 215 |
+
causal_ok = col <= row + diag
|
| 216 |
+
if WINDOW_SIZE_LEFT < 0: # only right window
|
| 217 |
+
win_ok = col <= row + diag + WINDOW_SIZE_RIGHT
|
| 218 |
+
else: # both sides
|
| 219 |
+
win_ok = (col >= row + diag - WINDOW_SIZE_LEFT) & (
|
| 220 |
+
col <= row + diag + WINDOW_SIZE_RIGHT
|
| 221 |
+
)
|
| 222 |
+
mask = ~(causal_ok & win_ok) # True => -inf
|
| 223 |
+
else:
|
| 224 |
+
# -------- non-causal window --------
|
| 225 |
+
sk, sq = N_CTX_K_FINAL, N_CTX_Q
|
| 226 |
+
if WINDOW_SIZE_LEFT < 0:
|
| 227 |
+
mask = col > row + (sk - sq) + WINDOW_SIZE_RIGHT
|
| 228 |
+
else:
|
| 229 |
+
right = tl.minimum(row + (sk - sq) + WINDOW_SIZE_RIGHT, sk)
|
| 230 |
+
left = row + (sk - sq) - WINDOW_SIZE_LEFT
|
| 231 |
+
mask = (col > right) | (col < left)
|
| 232 |
+
qk = tl.where(mask, float("-inf"), qk)
|
| 233 |
+
else:
|
| 234 |
+
if IS_CAUSAL:
|
| 235 |
+
row_idx = pid_m * BLOCK_M + tl.arange(0, BLOCK_M)
|
| 236 |
+
col_idx = pos + tl.arange(0, BLOCK_N)
|
| 237 |
+
|
| 238 |
+
# create a N_CTX_Q x kv_len causal mask
|
| 239 |
+
col_offset = N_CTX_K_FINAL - N_CTX_Q
|
| 240 |
+
causal_mask = row_idx[:, None] >= (col_idx[None, :] - col_offset)
|
| 241 |
+
|
| 242 |
+
# Apply the mask
|
| 243 |
+
qk = tl.where(causal_mask, qk, float("-inf"))
|
| 244 |
+
|
| 245 |
+
# Column mask (tail / variable-length). Instead of recomputing an arange each time,
|
| 246 |
+
# we accept a precomputed mask from the caller (col_valid_mask).
|
| 247 |
+
if apply_col_mask:
|
| 248 |
+
# Expect col_mask shape: [BLOCK_N]. True where column is within sequence.
|
| 249 |
+
qk = tl.where(col_mask[None, :], qk, float("-inf"))
|
| 250 |
+
|
| 251 |
+
m_i_new = tl.maximum(m_i, tl.max(qk, 1)) # per-row max so far
|
| 252 |
+
|
| 253 |
+
# rows that are *all* -inf after masking
|
| 254 |
+
valid = m_i_new > float("-inf")
|
| 255 |
+
|
| 256 |
+
# scale previous partial sums safely
|
| 257 |
+
alpha = tl.where(valid, tl.math.exp2(m_i - m_i_new), 0.0)
|
| 258 |
+
|
| 259 |
+
# subtract the row max only on valid rows
|
| 260 |
+
qk = tl.where(valid[:, None], qk - m_i_new[:, None], float("-inf"))
|
| 261 |
+
p = tl.math.exp2(qk)
|
| 262 |
+
|
| 263 |
+
# -- update m_i and l_i --
|
| 264 |
+
l_i = l_i * alpha + tl.sum(p, 1)
|
| 265 |
+
m_i = m_i_new
|
| 266 |
+
p = p.to(q.dtype)
|
| 267 |
+
|
| 268 |
+
# -- scale and update acc --
|
| 269 |
+
acc *= alpha[:, None]
|
| 270 |
+
if IS_FP8:
|
| 271 |
+
acc += tl.dot(p.to(v.dtype), v) * v_descale # Apply FP8 scaling for V
|
| 272 |
+
else:
|
| 273 |
+
acc = tl.dot(p.to(v.dtype), v, acc=acc)
|
| 274 |
+
|
| 275 |
+
return m_i, l_i, acc
|
| 276 |
+
|
| 277 |
+
|
| 278 |
+
@triton.autotune(
|
| 279 |
+
configs=fwd_decode_splitk_configs,
|
| 280 |
+
key=FWD_DECODE_AUTOTUNE_KEYS,
|
| 281 |
+
use_cuda_graph=True,
|
| 282 |
+
)
|
| 283 |
+
@triton.jit
|
| 284 |
+
def _fwd_kernel_splitK(
|
| 285 |
+
Q,
|
| 286 |
+
K,
|
| 287 |
+
V,
|
| 288 |
+
Q_Descale, # FP8 descale factors for Q
|
| 289 |
+
K_Descale, # FP8 descale factors for K
|
| 290 |
+
V_Descale, # FP8 descale factors for V
|
| 291 |
+
sm_scale,
|
| 292 |
+
Out_splitK, # [B*H*G, split_k, Mq, K]
|
| 293 |
+
Metadata, # [B*H*G, 2, split_k, M_ceil] contains [mi, li]
|
| 294 |
+
K_new,
|
| 295 |
+
V_new,
|
| 296 |
+
Cache_seqlens,
|
| 297 |
+
Cache_batch_idx,
|
| 298 |
+
Block_table,
|
| 299 |
+
Alibi_slopes,
|
| 300 |
+
stride_qz,
|
| 301 |
+
stride_qm,
|
| 302 |
+
stride_qg,
|
| 303 |
+
stride_qh,
|
| 304 |
+
stride_qd,
|
| 305 |
+
stride_kz,
|
| 306 |
+
stride_kn,
|
| 307 |
+
stride_kg,
|
| 308 |
+
stride_kh,
|
| 309 |
+
stride_kd,
|
| 310 |
+
stride_vz,
|
| 311 |
+
stride_vn,
|
| 312 |
+
stride_vg,
|
| 313 |
+
stride_vh,
|
| 314 |
+
stride_vd,
|
| 315 |
+
stride_osk_zhg,
|
| 316 |
+
stride_osk_s,
|
| 317 |
+
stride_osk_m,
|
| 318 |
+
stride_osk_d,
|
| 319 |
+
stride_mzhg,
|
| 320 |
+
stride_m2,
|
| 321 |
+
stride_ms,
|
| 322 |
+
stride_mm,
|
| 323 |
+
stride_kn_z,
|
| 324 |
+
stride_kn_n,
|
| 325 |
+
stride_kn_g,
|
| 326 |
+
stride_kn_h,
|
| 327 |
+
stride_kn_d,
|
| 328 |
+
stride_vn_z,
|
| 329 |
+
stride_vn_n,
|
| 330 |
+
stride_vn_g,
|
| 331 |
+
stride_vn_h,
|
| 332 |
+
stride_vn_d,
|
| 333 |
+
stride_bt_b,
|
| 334 |
+
stride_bt_s,
|
| 335 |
+
stride_az,
|
| 336 |
+
stride_ah,
|
| 337 |
+
stride_q_descale_z, # FP8 descale strides
|
| 338 |
+
stride_q_descale_h,
|
| 339 |
+
stride_k_descale_z,
|
| 340 |
+
stride_k_descale_h,
|
| 341 |
+
stride_v_descale_z,
|
| 342 |
+
stride_v_descale_h,
|
| 343 |
+
Z,
|
| 344 |
+
N_CTX_Q,
|
| 345 |
+
N_CTX_K,
|
| 346 |
+
N_CTX_NEW,
|
| 347 |
+
BLOCK_N_PER_SPLIT: tl.constexpr,
|
| 348 |
+
BLOCK_SIZE_K: tl.constexpr,
|
| 349 |
+
H_q: tl.constexpr,
|
| 350 |
+
H_kv: tl.constexpr,
|
| 351 |
+
G_q: tl.constexpr,
|
| 352 |
+
BLOCK_M: tl.constexpr,
|
| 353 |
+
BLOCK_DMODEL: tl.constexpr,
|
| 354 |
+
ACTUAL_BLOCK_DMODEL: tl.constexpr,
|
| 355 |
+
BLOCK_N: tl.constexpr,
|
| 356 |
+
USE_CACHE_SEQLENs: tl.constexpr,
|
| 357 |
+
USE_CACHE_BATCH_IDX: tl.constexpr,
|
| 358 |
+
NEW_KV: tl.constexpr,
|
| 359 |
+
IS_GQA: tl.constexpr,
|
| 360 |
+
IS_CAUSAL: tl.constexpr,
|
| 361 |
+
USE_ALIBI: tl.constexpr,
|
| 362 |
+
PADDED_HEAD: tl.constexpr,
|
| 363 |
+
GROUP_SIZE: tl.constexpr,
|
| 364 |
+
USE_SLIDING_WINDOW: tl.constexpr,
|
| 365 |
+
WINDOW_SIZE_LEFT: tl.constexpr,
|
| 366 |
+
WINDOW_SIZE_RIGHT: tl.constexpr,
|
| 367 |
+
USE_BLOCK_TABLE: tl.constexpr,
|
| 368 |
+
IS_FP8: tl.constexpr, # FP8 flag
|
| 369 |
+
):
|
| 370 |
+
# Cast strides to int64 to prevent overflow in pointer arithmetic.
|
| 371 |
+
# stride * index can exceed int32 range for large tensors (e.g. batch=64, sk=8192, d=56).
|
| 372 |
+
# See mha.py USE_INT64_STRIDES for the same pattern used in the prefill kernel.
|
| 373 |
+
stride_qz_i64 = tl.cast(stride_qz, tl.int64)
|
| 374 |
+
stride_qm_i64 = tl.cast(stride_qm, tl.int64)
|
| 375 |
+
stride_qg_i64 = tl.cast(stride_qg, tl.int64)
|
| 376 |
+
stride_qh_i64 = tl.cast(stride_qh, tl.int64)
|
| 377 |
+
stride_qd_i64 = tl.cast(stride_qd, tl.int64)
|
| 378 |
+
stride_kz_i64 = tl.cast(stride_kz, tl.int64)
|
| 379 |
+
stride_kn_i64 = tl.cast(stride_kn, tl.int64)
|
| 380 |
+
stride_kg_i64 = tl.cast(stride_kg, tl.int64)
|
| 381 |
+
stride_kh_i64 = tl.cast(stride_kh, tl.int64)
|
| 382 |
+
stride_kd_i64 = tl.cast(stride_kd, tl.int64)
|
| 383 |
+
stride_vz_i64 = tl.cast(stride_vz, tl.int64)
|
| 384 |
+
stride_vn_i64 = tl.cast(stride_vn, tl.int64)
|
| 385 |
+
stride_vg_i64 = tl.cast(stride_vg, tl.int64)
|
| 386 |
+
stride_vh_i64 = tl.cast(stride_vh, tl.int64)
|
| 387 |
+
stride_vd_i64 = tl.cast(stride_vd, tl.int64)
|
| 388 |
+
stride_osk_zhg_i64 = tl.cast(stride_osk_zhg, tl.int64)
|
| 389 |
+
stride_osk_s_i64 = tl.cast(stride_osk_s, tl.int64)
|
| 390 |
+
stride_osk_m_i64 = tl.cast(stride_osk_m, tl.int64)
|
| 391 |
+
stride_osk_d_i64 = tl.cast(stride_osk_d, tl.int64)
|
| 392 |
+
stride_mzhg_i64 = tl.cast(stride_mzhg, tl.int64)
|
| 393 |
+
stride_ms_i64 = tl.cast(stride_ms, tl.int64)
|
| 394 |
+
stride_m2_i64 = tl.cast(stride_m2, tl.int64)
|
| 395 |
+
|
| 396 |
+
# get program ids
|
| 397 |
+
pid_m = tl.program_id(0)
|
| 398 |
+
pid_zhg = tl.program_id(1)
|
| 399 |
+
pid_splitk = tl.program_id(2)
|
| 400 |
+
|
| 401 |
+
# compute z, h and g ids
|
| 402 |
+
z_id = pid_zhg // (H_q * G_q)
|
| 403 |
+
hq_id = (pid_zhg // G_q) % H_q
|
| 404 |
+
g_id = pid_zhg % G_q
|
| 405 |
+
|
| 406 |
+
# is gqa
|
| 407 |
+
if IS_GQA:
|
| 408 |
+
hk_id = hq_id // GROUP_SIZE
|
| 409 |
+
hv_id = hk_id
|
| 410 |
+
else:
|
| 411 |
+
hk_id = hq_id
|
| 412 |
+
hv_id = hq_id
|
| 413 |
+
|
| 414 |
+
# Load FP8 descale factors if needed
|
| 415 |
+
if IS_FP8:
|
| 416 |
+
if IS_GQA:
|
| 417 |
+
# For MQA/GQA, q_descale uses the same indexing as k/v (hk_id)
|
| 418 |
+
q_descale = tl.load(
|
| 419 |
+
Q_Descale + z_id * stride_q_descale_z + hk_id * stride_q_descale_h
|
| 420 |
+
)
|
| 421 |
+
else:
|
| 422 |
+
# For MHA, q_descale uses hq_id
|
| 423 |
+
q_descale = tl.load(
|
| 424 |
+
Q_Descale + z_id * stride_q_descale_z + hq_id * stride_q_descale_h
|
| 425 |
+
)
|
| 426 |
+
k_descale = tl.load(
|
| 427 |
+
K_Descale + z_id * stride_k_descale_z + hk_id * stride_k_descale_h
|
| 428 |
+
)
|
| 429 |
+
v_descale = tl.load(
|
| 430 |
+
V_Descale + z_id * stride_v_descale_z + hv_id * stride_v_descale_h
|
| 431 |
+
)
|
| 432 |
+
else:
|
| 433 |
+
q_descale, k_descale, v_descale = 1.0, 1.0, 1.0
|
| 434 |
+
|
| 435 |
+
# figure out seqlens
|
| 436 |
+
lo = pid_splitk * BLOCK_N_PER_SPLIT
|
| 437 |
+
if USE_CACHE_SEQLENs:
|
| 438 |
+
cache_seqlen_last_idx = tl.load(Cache_seqlens + z_id)
|
| 439 |
+
N_CTX_K_FINAL = cache_seqlen_last_idx
|
| 440 |
+
else:
|
| 441 |
+
N_CTX_K_FINAL = N_CTX_K
|
| 442 |
+
hi = tl.minimum((pid_splitk + 1) * BLOCK_N_PER_SPLIT, N_CTX_K_FINAL)
|
| 443 |
+
|
| 444 |
+
# pick batch index
|
| 445 |
+
if USE_CACHE_BATCH_IDX:
|
| 446 |
+
cache_batch_idx = tl.load(Cache_batch_idx + z_id)
|
| 447 |
+
else:
|
| 448 |
+
cache_batch_idx = z_id
|
| 449 |
+
|
| 450 |
+
# compute offsets
|
| 451 |
+
offs_m = pid_m * BLOCK_M + tl.arange(0, BLOCK_M)
|
| 452 |
+
offs_n = tl.arange(0, BLOCK_N)
|
| 453 |
+
offs_d = tl.arange(0, BLOCK_DMODEL)
|
| 454 |
+
|
| 455 |
+
# compute ptrs
|
| 456 |
+
q_offset = Q + hq_id * stride_qh_i64 + z_id * stride_qz_i64 + g_id * stride_qg_i64
|
| 457 |
+
q_ptrs = (
|
| 458 |
+
q_offset + offs_m[:, None] * stride_qm_i64 + offs_d[None, :] * stride_qd_i64
|
| 459 |
+
)
|
| 460 |
+
|
| 461 |
+
# Handle block table for paged attention
|
| 462 |
+
if USE_BLOCK_TABLE:
|
| 463 |
+
# K and V now point to paged cache
|
| 464 |
+
# Each batch has its own block table row
|
| 465 |
+
block_table_ptr = Block_table + z_id * stride_bt_b
|
| 466 |
+
else:
|
| 467 |
+
k_offset = (
|
| 468 |
+
K
|
| 469 |
+
+ hk_id * stride_kh_i64
|
| 470 |
+
+ cache_batch_idx * stride_kz_i64
|
| 471 |
+
+ g_id * stride_kg_i64
|
| 472 |
+
)
|
| 473 |
+
v_offset = (
|
| 474 |
+
V
|
| 475 |
+
+ hv_id * stride_vh_i64
|
| 476 |
+
+ cache_batch_idx * stride_vz_i64
|
| 477 |
+
+ g_id * stride_vg_i64
|
| 478 |
+
)
|
| 479 |
+
|
| 480 |
+
# compute masks
|
| 481 |
+
if PADDED_HEAD:
|
| 482 |
+
q_mask = (offs_m < N_CTX_Q)[:, None] & (offs_d < ACTUAL_BLOCK_DMODEL)[None, :]
|
| 483 |
+
kT_mask = (offs_d < ACTUAL_BLOCK_DMODEL)[:, None] & (offs_n < N_CTX_K_FINAL)[
|
| 484 |
+
None, :
|
| 485 |
+
]
|
| 486 |
+
v_mask = (offs_n < N_CTX_K_FINAL)[:, None] & (offs_d < ACTUAL_BLOCK_DMODEL)[
|
| 487 |
+
None, :
|
| 488 |
+
]
|
| 489 |
+
osk_mask = (offs_m < N_CTX_Q)[:, None] & (offs_d < ACTUAL_BLOCK_DMODEL)[None, :]
|
| 490 |
+
else:
|
| 491 |
+
q_mask = (offs_m < N_CTX_Q)[:, None]
|
| 492 |
+
kT_mask = (offs_n < N_CTX_K_FINAL)[None, :]
|
| 493 |
+
v_mask = (offs_n < N_CTX_K_FINAL)[:, None]
|
| 494 |
+
osk_mask = (offs_m < N_CTX_Q)[:, None]
|
| 495 |
+
|
| 496 |
+
# scale sm_scale by log_2(e) and use
|
| 497 |
+
# 2^x instead of exp in the loop because CSE and LICM
|
| 498 |
+
# don't work as expected with `exp` in the loop
|
| 499 |
+
qk_scale = sm_scale * 1.44269504
|
| 500 |
+
|
| 501 |
+
# load q: it will stay in SRAM throughout
|
| 502 |
+
q = tl.load(q_ptrs, mask=q_mask, other=0.0)
|
| 503 |
+
q = (q * qk_scale).to(q.dtype)
|
| 504 |
+
|
| 505 |
+
# load ALiBi slope if enabled
|
| 506 |
+
if USE_ALIBI:
|
| 507 |
+
a_offset = z_id * stride_az + hq_id * stride_ah
|
| 508 |
+
alibi_slope = tl.load(Alibi_slopes + a_offset)
|
| 509 |
+
else:
|
| 510 |
+
alibi_slope = None
|
| 511 |
+
|
| 512 |
+
# initialize pointer to m and l
|
| 513 |
+
m_i = tl.full([BLOCK_M], float("-inf"), dtype=tl.float32)
|
| 514 |
+
l_i = tl.zeros([BLOCK_M], dtype=tl.float32)
|
| 515 |
+
acc = tl.zeros([BLOCK_M, BLOCK_DMODEL], dtype=tl.float32) # noqa: F821
|
| 516 |
+
|
| 517 |
+
# loop over k, v and update accumulator
|
| 518 |
+
if USE_BLOCK_TABLE:
|
| 519 |
+
# Paged attention: process all KV blocks from cache
|
| 520 |
+
# Note: Cache should be updated externally before calling this kernel
|
| 521 |
+
num_kv_blocks = (N_CTX_K_FINAL + BLOCK_SIZE_K - 1) // BLOCK_SIZE_K
|
| 522 |
+
|
| 523 |
+
for block_idx in range(num_kv_blocks):
|
| 524 |
+
# Calculate sequence range for this block
|
| 525 |
+
block_start = block_idx * BLOCK_SIZE_K
|
| 526 |
+
block_end = tl.minimum(block_start + BLOCK_SIZE_K, N_CTX_K_FINAL)
|
| 527 |
+
|
| 528 |
+
# Check if block overlaps with our split-k range [lo, hi)
|
| 529 |
+
if block_end > lo and block_start < hi:
|
| 530 |
+
# Load physical block number
|
| 531 |
+
physical_block = tl.load(block_table_ptr + block_idx * stride_bt_s)
|
| 532 |
+
|
| 533 |
+
# Calculate the range within this block that overlaps with [lo, hi)
|
| 534 |
+
process_start = tl.maximum(lo - block_start, 0)
|
| 535 |
+
process_end = tl.minimum(hi - block_start, BLOCK_SIZE_K)
|
| 536 |
+
process_end = tl.minimum(process_end, block_end - block_start)
|
| 537 |
+
|
| 538 |
+
# Instead of forcing a floor alignment to BLOCK_N (which can still skip
|
| 539 |
+
# part of the intended range if start falls mid-tile for small splits),
|
| 540 |
+
# start from the raw (possibly unaligned) process_start rounded *down* but
|
| 541 |
+
# allow the loop to begin earlier (at most BLOCK_N before) so that any
|
| 542 |
+
# partial tile overlapping [lo, hi) is covered. Masking below will remove
|
| 543 |
+
# columns < lo or >= hi ensuring numerically identical coverage without
|
| 544 |
+
# duplication.
|
| 545 |
+
aligned_start = (process_start // BLOCK_N) * BLOCK_N
|
| 546 |
+
if aligned_start > 0 and aligned_start + BLOCK_N > process_start:
|
| 547 |
+
# ensure we include the tile that contains process_start
|
| 548 |
+
process_start = aligned_start
|
| 549 |
+
else:
|
| 550 |
+
process_start = aligned_start
|
| 551 |
+
|
| 552 |
+
for offset in range(process_start, process_end, BLOCK_N):
|
| 553 |
+
# Current position (may begin slightly before logical split range; masking fixes it)
|
| 554 |
+
pos = block_start + offset
|
| 555 |
+
# Proceed unconditionally; masking below enforces [lo, hi)
|
| 556 |
+
# Calculate base addresses for K and V in this physical block
|
| 557 |
+
k_base = (
|
| 558 |
+
K
|
| 559 |
+
+ physical_block * BLOCK_SIZE_K * stride_kn_i64
|
| 560 |
+
+ hk_id * stride_kh_i64
|
| 561 |
+
+ g_id * stride_kg_i64
|
| 562 |
+
)
|
| 563 |
+
v_base = (
|
| 564 |
+
V
|
| 565 |
+
+ physical_block * BLOCK_SIZE_K * stride_vn_i64
|
| 566 |
+
+ hv_id * stride_vh_i64
|
| 567 |
+
+ g_id * stride_vg_i64
|
| 568 |
+
)
|
| 569 |
+
|
| 570 |
+
# Offsets within the current block
|
| 571 |
+
block_offs = offset + offs_n
|
| 572 |
+
|
| 573 |
+
# Masks for valid data respecting:
|
| 574 |
+
# (1) global key length (seq_mask)
|
| 575 |
+
# (2) block bounds (block_mask)
|
| 576 |
+
# (3) current split range [lo, hi)
|
| 577 |
+
seq_mask = (pos + offs_n) < N_CTX_K_FINAL
|
| 578 |
+
block_mask = block_offs < BLOCK_SIZE_K
|
| 579 |
+
end_mask = block_offs < process_end
|
| 580 |
+
split_mask = ((pos + offs_n) >= lo) & ((pos + offs_n) < hi)
|
| 581 |
+
col_mask = seq_mask & block_mask & end_mask & split_mask
|
| 582 |
+
|
| 583 |
+
# Apply masks
|
| 584 |
+
kT_mask_final = kT_mask & col_mask[None, :]
|
| 585 |
+
v_mask_final = v_mask & col_mask[:, None]
|
| 586 |
+
|
| 587 |
+
# Load K and V
|
| 588 |
+
kT_ptrs = (
|
| 589 |
+
k_base
|
| 590 |
+
+ offs_d[:, None] * stride_kd_i64
|
| 591 |
+
+ block_offs[None, :] * stride_kn_i64
|
| 592 |
+
)
|
| 593 |
+
v_ptrs = (
|
| 594 |
+
v_base
|
| 595 |
+
+ block_offs[:, None] * stride_vn_i64
|
| 596 |
+
+ offs_d[None, :] * stride_vd_i64
|
| 597 |
+
)
|
| 598 |
+
|
| 599 |
+
kT = tl.load(kT_ptrs, mask=kT_mask_final, other=0.0)
|
| 600 |
+
v = tl.load(v_ptrs, mask=v_mask_final, other=0.0)
|
| 601 |
+
|
| 602 |
+
# Unified inner function handles both paged and contiguous
|
| 603 |
+
m_i, l_i, acc = _attn_fwd_inner(
|
| 604 |
+
q,
|
| 605 |
+
kT,
|
| 606 |
+
v,
|
| 607 |
+
pos,
|
| 608 |
+
col_mask,
|
| 609 |
+
m_i,
|
| 610 |
+
l_i,
|
| 611 |
+
acc,
|
| 612 |
+
pid_m,
|
| 613 |
+
q_descale,
|
| 614 |
+
k_descale,
|
| 615 |
+
v_descale,
|
| 616 |
+
alibi_slope,
|
| 617 |
+
True,
|
| 618 |
+
IS_FP8,
|
| 619 |
+
BLOCK_M,
|
| 620 |
+
BLOCK_N,
|
| 621 |
+
N_CTX_Q,
|
| 622 |
+
N_CTX_K_FINAL,
|
| 623 |
+
USE_ALIBI,
|
| 624 |
+
USE_SLIDING_WINDOW,
|
| 625 |
+
IS_CAUSAL,
|
| 626 |
+
WINDOW_SIZE_LEFT,
|
| 627 |
+
WINDOW_SIZE_RIGHT,
|
| 628 |
+
)
|
| 629 |
+
else:
|
| 630 |
+
# Non-paged attention: process KV from cache
|
| 631 |
+
# Note: Cache should be updated externally before calling this kernel
|
| 632 |
+
# Compute bounds check flag once: needed if split size not aligned to BLOCK_N or variable seqlens
|
| 633 |
+
bounds_checks_n = ((BLOCK_N_PER_SPLIT % BLOCK_N) > 0) | USE_CACHE_SEQLENs
|
| 634 |
+
# loop over k, v and update accumulator
|
| 635 |
+
for start_n in range(lo, hi, BLOCK_N):
|
| 636 |
+
kT_ptrs = (
|
| 637 |
+
k_offset
|
| 638 |
+
+ offs_d[:, None] * stride_kd_i64
|
| 639 |
+
+ (start_n + offs_n)[None, :] * stride_kn_i64
|
| 640 |
+
)
|
| 641 |
+
V_ptrs = (
|
| 642 |
+
v_offset
|
| 643 |
+
+ (start_n + offs_n)[:, None] * stride_vn_i64
|
| 644 |
+
+ offs_d[None, :] * stride_vd_i64
|
| 645 |
+
)
|
| 646 |
+
|
| 647 |
+
# load k
|
| 648 |
+
kT = tl.load(kT_ptrs, mask=kT_mask, other=0.0)
|
| 649 |
+
v = tl.load(V_ptrs, mask=v_mask, other=0.0)
|
| 650 |
+
|
| 651 |
+
# Use the same inner loop logic
|
| 652 |
+
# Precompute column validity mask for this tile (all True for full tiles).
|
| 653 |
+
# hi is the upper bound of the overall split range; start_n marks this tile's base.
|
| 654 |
+
col_valid_mask = offs_n < (hi - start_n)
|
| 655 |
+
|
| 656 |
+
m_i, l_i, acc = _attn_fwd_inner(
|
| 657 |
+
q,
|
| 658 |
+
kT,
|
| 659 |
+
v,
|
| 660 |
+
start_n,
|
| 661 |
+
col_valid_mask,
|
| 662 |
+
m_i,
|
| 663 |
+
l_i,
|
| 664 |
+
acc,
|
| 665 |
+
pid_m,
|
| 666 |
+
q_descale,
|
| 667 |
+
k_descale,
|
| 668 |
+
v_descale,
|
| 669 |
+
alibi_slope,
|
| 670 |
+
bounds_checks_n,
|
| 671 |
+
IS_FP8,
|
| 672 |
+
BLOCK_M,
|
| 673 |
+
BLOCK_N,
|
| 674 |
+
N_CTX_Q,
|
| 675 |
+
N_CTX_K_FINAL,
|
| 676 |
+
USE_ALIBI,
|
| 677 |
+
USE_SLIDING_WINDOW,
|
| 678 |
+
IS_CAUSAL,
|
| 679 |
+
WINDOW_SIZE_LEFT,
|
| 680 |
+
WINDOW_SIZE_RIGHT,
|
| 681 |
+
)
|
| 682 |
+
|
| 683 |
+
# write back O
|
| 684 |
+
osk_offset = (
|
| 685 |
+
Out_splitK + pid_zhg * stride_osk_zhg_i64 + pid_splitk * stride_osk_s_i64
|
| 686 |
+
)
|
| 687 |
+
osk_ptrs = (
|
| 688 |
+
osk_offset
|
| 689 |
+
+ offs_m[:, None] * stride_osk_m_i64
|
| 690 |
+
+ offs_d[None, :] * stride_osk_d_i64
|
| 691 |
+
)
|
| 692 |
+
tl.store(
|
| 693 |
+
osk_ptrs,
|
| 694 |
+
acc,
|
| 695 |
+
mask=osk_mask,
|
| 696 |
+
)
|
| 697 |
+
|
| 698 |
+
# write metadata for split-K reduction
|
| 699 |
+
metadata_offset = Metadata + pid_zhg * stride_mzhg_i64 + pid_splitk * stride_ms_i64
|
| 700 |
+
metadata_ptr = metadata_offset + offs_m
|
| 701 |
+
tl.store(metadata_ptr, m_i)
|
| 702 |
+
tl.store(metadata_ptr + stride_m2_i64, l_i)
|
| 703 |
+
|
| 704 |
+
|
| 705 |
+
FWD_DECODE_REDUCE_AUTOTUNE_KEYS = [
|
| 706 |
+
"BLOCK_DMODEL",
|
| 707 |
+
"split_k",
|
| 708 |
+
]
|
| 709 |
+
|
| 710 |
+
|
| 711 |
+
@triton.autotune(
|
| 712 |
+
configs=fwd_decode_reduce_configs,
|
| 713 |
+
key=FWD_DECODE_REDUCE_AUTOTUNE_KEYS,
|
| 714 |
+
use_cuda_graph=True,
|
| 715 |
+
)
|
| 716 |
+
@triton.jit
|
| 717 |
+
def _splitK_reduce(
|
| 718 |
+
Out_splitK, # [B*H*G, split_k, Mq, K]
|
| 719 |
+
Metadata, # [B*H*G, 2, split_k, M_ceil] contains [mi, li]
|
| 720 |
+
Out, # [B, H, G, M, K]
|
| 721 |
+
LSE, # [B*H*G, M]
|
| 722 |
+
stride_osk_zhg,
|
| 723 |
+
stride_osk_s,
|
| 724 |
+
stride_osk_m,
|
| 725 |
+
stride_osk_k,
|
| 726 |
+
stride_mzhg,
|
| 727 |
+
stride_m2,
|
| 728 |
+
stride_ms,
|
| 729 |
+
stride_mm,
|
| 730 |
+
stride_oz,
|
| 731 |
+
stride_oh,
|
| 732 |
+
stride_og,
|
| 733 |
+
stride_om,
|
| 734 |
+
stride_ok,
|
| 735 |
+
stride_lse_zhg,
|
| 736 |
+
stride_lse_m,
|
| 737 |
+
K_BLOCK_SIZE: tl.constexpr,
|
| 738 |
+
BLOCK_DMODEL: tl.constexpr,
|
| 739 |
+
ACTUAL_BLOCK_DMODEL: tl.constexpr,
|
| 740 |
+
H: tl.constexpr,
|
| 741 |
+
G: tl.constexpr,
|
| 742 |
+
split_k: tl.constexpr,
|
| 743 |
+
splitK_pow2: tl.constexpr,
|
| 744 |
+
MASK_SPLITK: tl.constexpr,
|
| 745 |
+
PADDED_HEAD: tl.constexpr,
|
| 746 |
+
):
|
| 747 |
+
# Cast strides to int64 to prevent overflow in pointer arithmetic.
|
| 748 |
+
stride_osk_zhg_i64 = tl.cast(stride_osk_zhg, tl.int64)
|
| 749 |
+
stride_osk_s_i64 = tl.cast(stride_osk_s, tl.int64)
|
| 750 |
+
stride_osk_m_i64 = tl.cast(stride_osk_m, tl.int64)
|
| 751 |
+
stride_osk_k_i64 = tl.cast(stride_osk_k, tl.int64)
|
| 752 |
+
stride_mzhg_i64 = tl.cast(stride_mzhg, tl.int64)
|
| 753 |
+
stride_m2_i64 = tl.cast(stride_m2, tl.int64)
|
| 754 |
+
stride_ms_i64 = tl.cast(stride_ms, tl.int64)
|
| 755 |
+
stride_mm_i64 = tl.cast(stride_mm, tl.int64)
|
| 756 |
+
stride_oz_i64 = tl.cast(stride_oz, tl.int64)
|
| 757 |
+
stride_oh_i64 = tl.cast(stride_oh, tl.int64)
|
| 758 |
+
stride_og_i64 = tl.cast(stride_og, tl.int64)
|
| 759 |
+
stride_om_i64 = tl.cast(stride_om, tl.int64)
|
| 760 |
+
stride_lse_zhg_i64 = tl.cast(stride_lse_zhg, tl.int64)
|
| 761 |
+
|
| 762 |
+
# get pids
|
| 763 |
+
pid_zhg = tl.program_id(0)
|
| 764 |
+
pid_m = tl.program_id(1)
|
| 765 |
+
pid_k = tl.program_id(2)
|
| 766 |
+
|
| 767 |
+
# compute offsets
|
| 768 |
+
offs_splitK = tl.arange(0, splitK_pow2)
|
| 769 |
+
offs_k = pid_k * K_BLOCK_SIZE + tl.arange(0, K_BLOCK_SIZE)
|
| 770 |
+
|
| 771 |
+
# compute masks
|
| 772 |
+
if PADDED_HEAD:
|
| 773 |
+
o_mask = offs_k < ACTUAL_BLOCK_DMODEL
|
| 774 |
+
else:
|
| 775 |
+
o_mask = None
|
| 776 |
+
|
| 777 |
+
# compute ptrs
|
| 778 |
+
metadata_offset = Metadata + pid_zhg * stride_mzhg_i64
|
| 779 |
+
metadata_ptr = metadata_offset + offs_splitK * stride_ms_i64 + pid_m * stride_mm_i64
|
| 780 |
+
|
| 781 |
+
osk_offset = Out_splitK + pid_zhg * stride_osk_zhg_i64 + pid_m * stride_osk_m_i64
|
| 782 |
+
osk_ptr = (
|
| 783 |
+
osk_offset
|
| 784 |
+
+ offs_splitK[:, None] * stride_osk_s_i64
|
| 785 |
+
+ offs_k[None, :] * stride_osk_k_i64
|
| 786 |
+
)
|
| 787 |
+
|
| 788 |
+
# read max values of each splitK
|
| 789 |
+
if MASK_SPLITK:
|
| 790 |
+
splitK_mask = offs_splitK < split_k
|
| 791 |
+
l_m = tl.load(metadata_ptr, mask=splitK_mask, other=float("-inf"))
|
| 792 |
+
l_sum = tl.load(metadata_ptr + stride_m2_i64, mask=splitK_mask, other=0.0)
|
| 793 |
+
acc = tl.load(osk_ptr, mask=splitK_mask[:, None], other=0.0)
|
| 794 |
+
else:
|
| 795 |
+
l_m = tl.load(metadata_ptr)
|
| 796 |
+
l_sum = tl.load(metadata_ptr + stride_m2_i64)
|
| 797 |
+
acc = tl.load(osk_ptr)
|
| 798 |
+
|
| 799 |
+
g_m = tl.max(l_m, axis=0)
|
| 800 |
+
|
| 801 |
+
alpha = tl.where(l_m > float("-inf"), tl.math.exp2(l_m - g_m), 0.0)
|
| 802 |
+
|
| 803 |
+
# read sum
|
| 804 |
+
l_sum *= alpha
|
| 805 |
+
g_sum = tl.sum(l_sum, axis=0)
|
| 806 |
+
acc = acc * alpha[:, None]
|
| 807 |
+
|
| 808 |
+
g_sum_safe = tl.where(g_sum > 0, g_sum, 1.0)
|
| 809 |
+
acc_out = tl.sum(acc, axis=0) / g_sum_safe
|
| 810 |
+
|
| 811 |
+
# Store output
|
| 812 |
+
z_id = pid_zhg // (H * G)
|
| 813 |
+
h_id = (pid_zhg // G) % H
|
| 814 |
+
g_id = pid_zhg % G
|
| 815 |
+
out_offset = (
|
| 816 |
+
Out + z_id * stride_oz_i64 + h_id * stride_oh_i64 + g_id * stride_og_i64
|
| 817 |
+
)
|
| 818 |
+
out_ptr = out_offset + pid_m * stride_om_i64 + offs_k
|
| 819 |
+
tl.store(out_ptr, acc_out, mask=o_mask)
|
| 820 |
+
|
| 821 |
+
# Store lse
|
| 822 |
+
l_ptrs = LSE + pid_zhg * stride_lse_zhg_i64 + pid_m
|
| 823 |
+
lse_val = tl.where(g_sum > 0, (g_m + tl.math.log2(g_sum)) / 1.44269504, g_m)
|
| 824 |
+
tl.store(l_ptrs, lse_val)
|
| 825 |
+
|
| 826 |
+
|
| 827 |
+
@triton.jit
|
| 828 |
+
def cast_uint32_to_half2(scale_shift):
|
| 829 |
+
# Extract two float16 packed into one int32
|
| 830 |
+
scale = scale_shift & 0xFFFF
|
| 831 |
+
shift = scale_shift >> 16
|
| 832 |
+
scale = scale.to(tl.uint16).to(tl.float16, bitcast=True)
|
| 833 |
+
shift = shift.to(tl.uint16).to(tl.float16, bitcast=True)
|
| 834 |
+
return scale, shift
|
| 835 |
+
|
| 836 |
+
|
| 837 |
+
@triton.jit
|
| 838 |
+
def dequantize(
|
| 839 |
+
x_,
|
| 840 |
+
scale,
|
| 841 |
+
shift,
|
| 842 |
+
PACKED_PER_VAL: tl.constexpr = 8,
|
| 843 |
+
):
|
| 844 |
+
# PACKED_PER_VAL is the number of values packed into
|
| 845 |
+
# each element x_. For example, for int4 quantization
|
| 846 |
+
# and x_ of type int32, PACKED_PER_VAL is 8.
|
| 847 |
+
|
| 848 |
+
BLOCK_N: tl.constexpr = x_.shape[0]
|
| 849 |
+
BLOCK_DMODEL_PACKED: tl.constexpr = x_.shape[1]
|
| 850 |
+
offsets = tl.arange(0, PACKED_PER_VAL) * 4
|
| 851 |
+
quant_offset = (
|
| 852 |
+
x_[:, None, :] >> offsets[None, :, None]
|
| 853 |
+
) # (BLOCK_N, PACKED_PER_VAL, D // PACKED_PER_VAL)
|
| 854 |
+
|
| 855 |
+
quant_offset = tl.view(
|
| 856 |
+
quant_offset, (BLOCK_N, BLOCK_DMODEL_PACKED * PACKED_PER_VAL)
|
| 857 |
+
)
|
| 858 |
+
# Trick - instead of converting int4 to float16 we view it as float16
|
| 859 |
+
# and then multiply by 32768 * 512 == 2**24
|
| 860 |
+
quant_offset = (quant_offset & 0xF).to(tl.uint16).to(tl.float16, bitcast=True)
|
| 861 |
+
quant_offset = (quant_offset * 32768.0).to(tl.float16)
|
| 862 |
+
scale_512 = scale * 512
|
| 863 |
+
|
| 864 |
+
dequant = quant_offset * scale_512 + shift
|
| 865 |
+
return dequant
|
| 866 |
+
|
| 867 |
+
|
| 868 |
+
def quantize_kv_int4(k: torch.Tensor, num_groups: int = 1) -> torch.Tensor:
|
| 869 |
+
# Scale and shift are such that quantization linearly maps
|
| 870 |
+
# int4 values range [0..15] to input values range min(k)..max(k)
|
| 871 |
+
# individually for every row
|
| 872 |
+
k = k.reshape(*k.shape[:-1], num_groups, k.shape[-1] // num_groups)
|
| 873 |
+
max_vals = torch.max(k, dim=-1, keepdim=True).values
|
| 874 |
+
min_vals = torch.min(k, dim=-1, keepdim=True).values
|
| 875 |
+
scale_k: torch.Tensor = (max_vals - min_vals) / 15
|
| 876 |
+
|
| 877 |
+
shift_k = torch.min(k, dim=-1, keepdim=True).values
|
| 878 |
+
scale_k = scale_k.to(torch.float16)
|
| 879 |
+
shift_k = shift_k.to(torch.float16)
|
| 880 |
+
|
| 881 |
+
in_bytes = ((k - shift_k.expand(k.shape)) / scale_k.expand(k.shape)) + 0.5
|
| 882 |
+
in_bytes = in_bytes.to(torch.uint8)
|
| 883 |
+
in_int4 = in_bytes & 0xF
|
| 884 |
+
in_int4_packed = in_int4[..., ::2] + (in_int4[..., 1::2] << 4)
|
| 885 |
+
scale_shift = torch.concat(
|
| 886 |
+
[scale_k.view(torch.uint8), shift_k.view(torch.uint8)], dim=-1
|
| 887 |
+
)
|
| 888 |
+
k_quant = torch.concat(
|
| 889 |
+
[
|
| 890 |
+
scale_shift.flatten(start_dim=-2),
|
| 891 |
+
in_int4_packed.flatten(start_dim=-2),
|
| 892 |
+
],
|
| 893 |
+
dim=-1,
|
| 894 |
+
).view(torch.int16)
|
| 895 |
+
return k_quant
|
| 896 |
+
|
| 897 |
+
|
| 898 |
+
def dequantize_kv_fp16(quant_k: torch.Tensor, num_groups: int = 1) -> torch.Tensor:
|
| 899 |
+
k_i16 = quant_k.view(torch.int16)
|
| 900 |
+
k_ui8 = k_i16.view(torch.uint8)
|
| 901 |
+
|
| 902 |
+
ss_size = num_groups * 4
|
| 903 |
+
scale_shift_ui8 = k_ui8[..., 0:ss_size]
|
| 904 |
+
scale_shift_ui8 = scale_shift_ui8.reshape(
|
| 905 |
+
*scale_shift_ui8.shape[:-1], num_groups, 4
|
| 906 |
+
)
|
| 907 |
+
scale = scale_shift_ui8[..., 0:2].view(torch.float16)
|
| 908 |
+
shift = scale_shift_ui8[..., 2:4].view(torch.float16)
|
| 909 |
+
|
| 910 |
+
kv_ui8 = k_ui8[..., ss_size:]
|
| 911 |
+
k_ui8 = kv_ui8.reshape(*kv_ui8.shape[:-1], num_groups, -1)
|
| 912 |
+
k1_i4 = k_ui8 & 0xF
|
| 913 |
+
k2_i4 = (k_ui8 & 0xF0) >> 4
|
| 914 |
+
k_shape = k1_i4.shape
|
| 915 |
+
k1_f16 = k1_i4.to(torch.float16) * scale.expand(k_shape) + shift.expand(k_shape)
|
| 916 |
+
k2_f16 = k2_i4.to(torch.float16) * scale.expand(k_shape) + shift.expand(k_shape)
|
| 917 |
+
|
| 918 |
+
out = torch.empty(
|
| 919 |
+
(*k1_f16.shape[:-1], k1_f16.shape[-1] * 2),
|
| 920 |
+
dtype=torch.float16,
|
| 921 |
+
device=quant_k.device,
|
| 922 |
+
)
|
| 923 |
+
out[..., ::2] = k1_f16
|
| 924 |
+
out[..., 1::2] = k2_f16
|
| 925 |
+
out = out.reshape(*k_shape[:-2], -1)
|
| 926 |
+
|
| 927 |
+
return out
|
| 928 |
+
|
| 929 |
+
|
| 930 |
+
def get_split_k(B: int, G: int, H: int, Mk: int) -> int:
|
| 931 |
+
"""Heuristic for the number of splits"""
|
| 932 |
+
bh = max(B * H, 1) # NOTE: Handle B*h=0 case
|
| 933 |
+
split_k = max(Mk, 1024) // bh
|
| 934 |
+
max_chunk_size = 64
|
| 935 |
+
while split_k > 0 and Mk / split_k < max_chunk_size:
|
| 936 |
+
split_k = split_k // 2
|
| 937 |
+
while B * H * G * split_k >= 1024:
|
| 938 |
+
split_k = split_k // 2
|
| 939 |
+
split_k = min(split_k, 512)
|
| 940 |
+
split_k = max(split_k, 1)
|
| 941 |
+
return split_k
|
| 942 |
+
|
| 943 |
+
|
| 944 |
+
def attention_forward_decode_triton_impl(
|
| 945 |
+
q: torch.Tensor,
|
| 946 |
+
k_cache: torch.Tensor,
|
| 947 |
+
v_cache: torch.Tensor,
|
| 948 |
+
k_new: Optional[torch.Tensor],
|
| 949 |
+
v_new: Optional[torch.Tensor],
|
| 950 |
+
out: torch.Tensor,
|
| 951 |
+
softmax_lse: torch.Tensor,
|
| 952 |
+
sm_scale: float,
|
| 953 |
+
causal: bool,
|
| 954 |
+
window_size_left: int,
|
| 955 |
+
window_size_right: int,
|
| 956 |
+
alibi_slopes: Optional[torch.Tensor],
|
| 957 |
+
layout: Literal["bshd"],
|
| 958 |
+
cache_seqlens: Optional[torch.Tensor],
|
| 959 |
+
cache_batch_idx: Optional[torch.Tensor],
|
| 960 |
+
block_table: Optional[torch.Tensor] = None,
|
| 961 |
+
q_descale: Optional[torch.Tensor] = None,
|
| 962 |
+
k_descale: Optional[torch.Tensor] = None,
|
| 963 |
+
v_descale: Optional[torch.Tensor] = None,
|
| 964 |
+
# rotary (optional)
|
| 965 |
+
rotary_cos: Optional[torch.Tensor] = None,
|
| 966 |
+
rotary_sin: Optional[torch.Tensor] = None,
|
| 967 |
+
rotary_interleaved: bool = False,
|
| 968 |
+
seqlens_rotary: Optional[torch.Tensor] = None,
|
| 969 |
+
):
|
| 970 |
+
# Validate layout at entry
|
| 971 |
+
if layout != "bshd":
|
| 972 |
+
raise ValueError(f"{layout} layout is not supported, only 'bshd' is supported")
|
| 973 |
+
|
| 974 |
+
# apply rotary embedding
|
| 975 |
+
if rotary_cos is not None and rotary_sin is not None:
|
| 976 |
+
# Prefer explicitly provided rotary sequence start offsets if given; fall back to cache_seqlens.
|
| 977 |
+
seqlen_offsets = (
|
| 978 |
+
seqlens_rotary
|
| 979 |
+
if seqlens_rotary is not None
|
| 980 |
+
else (cache_seqlens if cache_seqlens is not None else 0)
|
| 981 |
+
)
|
| 982 |
+
local = (window_size_left != -1) or (window_size_right != -1)
|
| 983 |
+
q, k_new = apply_rotary(
|
| 984 |
+
q,
|
| 985 |
+
k_new,
|
| 986 |
+
rotary_cos,
|
| 987 |
+
rotary_sin,
|
| 988 |
+
causal=causal,
|
| 989 |
+
local=local,
|
| 990 |
+
interleaved=rotary_interleaved,
|
| 991 |
+
seqlen_offsets=seqlen_offsets,
|
| 992 |
+
)
|
| 993 |
+
|
| 994 |
+
# handle cache updates
|
| 995 |
+
if k_new is not None and v_new is not None:
|
| 996 |
+
batch_size = k_new.shape[0]
|
| 997 |
+
seqlen_new = k_new.shape[1]
|
| 998 |
+
# shared offset within the new block for every batch element
|
| 999 |
+
token_offsets = torch.arange(seqlen_new, device=k_new.device, dtype=torch.long)
|
| 1000 |
+
|
| 1001 |
+
if block_table is None:
|
| 1002 |
+
# Non-paged attention
|
| 1003 |
+
if cache_seqlens is not None:
|
| 1004 |
+
insert_positions = cache_seqlens.long()[:, None] + token_offsets
|
| 1005 |
+
batch_idx = torch.arange(
|
| 1006 |
+
batch_size, device=k_new.device, dtype=torch.long
|
| 1007 |
+
)[:, None]
|
| 1008 |
+
k_cache[batch_idx, insert_positions] = k_new
|
| 1009 |
+
v_cache[batch_idx, insert_positions] = v_new
|
| 1010 |
+
cache_seqlens.add_(seqlen_new)
|
| 1011 |
+
else:
|
| 1012 |
+
# Append at the end of existing cache
|
| 1013 |
+
seqlen_cache = k_cache.shape[1]
|
| 1014 |
+
k_cache[:, seqlen_cache - seqlen_new :] = k_new
|
| 1015 |
+
v_cache[:, seqlen_cache - seqlen_new :] = v_new
|
| 1016 |
+
else:
|
| 1017 |
+
# Paged attention
|
| 1018 |
+
block_size = k_cache.shape[1]
|
| 1019 |
+
|
| 1020 |
+
if cache_seqlens is not None:
|
| 1021 |
+
start_idx = cache_seqlens.long()
|
| 1022 |
+
else:
|
| 1023 |
+
# Find the first unoccupied position per batch from the block table.
|
| 1024 |
+
invalid_mask = block_table < 0
|
| 1025 |
+
has_invalid = invalid_mask.any(dim=1)
|
| 1026 |
+
first_invalid_idx = invalid_mask.to(torch.int32).argmax(dim=1).long()
|
| 1027 |
+
start_idx = torch.where(
|
| 1028 |
+
has_invalid,
|
| 1029 |
+
first_invalid_idx * block_size,
|
| 1030 |
+
torch.full_like(
|
| 1031 |
+
first_invalid_idx, block_table.shape[1] * block_size
|
| 1032 |
+
),
|
| 1033 |
+
)
|
| 1034 |
+
|
| 1035 |
+
insert_positions = start_idx[:, None] + token_offsets
|
| 1036 |
+
block_indices = insert_positions // block_size
|
| 1037 |
+
within_block_indices = insert_positions % block_size
|
| 1038 |
+
batch_idx = torch.arange(batch_size, device=k_new.device, dtype=torch.long)[
|
| 1039 |
+
:, None
|
| 1040 |
+
]
|
| 1041 |
+
physical_blocks = block_table[batch_idx, block_indices]
|
| 1042 |
+
|
| 1043 |
+
flat_phys = physical_blocks.reshape(-1)
|
| 1044 |
+
flat_within = within_block_indices.reshape(-1)
|
| 1045 |
+
k_cache[flat_phys, flat_within] = k_new.reshape(-1, *k_new.shape[2:])
|
| 1046 |
+
v_cache[flat_phys, flat_within] = v_new.reshape(-1, *v_new.shape[2:])
|
| 1047 |
+
|
| 1048 |
+
if cache_seqlens is not None:
|
| 1049 |
+
cache_seqlens.add_(seqlen_new)
|
| 1050 |
+
|
| 1051 |
+
# kernel_configs
|
| 1052 |
+
is_new_kv = False # Cache has been updated, so no new KV in kernel
|
| 1053 |
+
use_alibi, (stride_az, stride_ah) = True if alibi_slopes is not None else False, (
|
| 1054 |
+
alibi_slopes.stride() if alibi_slopes is not None else (None, None)
|
| 1055 |
+
)
|
| 1056 |
+
use_cache_seqlens = cache_seqlens is not None
|
| 1057 |
+
use_sliding_window = window_size_left != -1 or window_size_right != -1
|
| 1058 |
+
use_block_table = block_table is not None
|
| 1059 |
+
|
| 1060 |
+
# get shapes and strides
|
| 1061 |
+
batch_size, seqlen_q, nheads_q, dim_q = get_shape_from_layout(q, layout)
|
| 1062 |
+
stride_qz, stride_qh, stride_qm, stride_qd = get_stride_from_layout(q, layout)
|
| 1063 |
+
|
| 1064 |
+
# Handle paged KV cache layout
|
| 1065 |
+
if use_block_table:
|
| 1066 |
+
# For paged attention, k_cache and v_cache have shape [num_blocks, block_size, nheads, head_dim]
|
| 1067 |
+
num_blocks_kc, block_size_k, nheads_kc, dim_kc = k_cache.shape
|
| 1068 |
+
num_blocks_vc, block_size_v, nheads_vc, dim_vc = v_cache.shape
|
| 1069 |
+
# Get the actual sequence length from cache_seqlens or block_table
|
| 1070 |
+
if cache_seqlens is not None:
|
| 1071 |
+
seqlen_kc = int(cache_seqlens.max().item())
|
| 1072 |
+
else:
|
| 1073 |
+
# Infer from block_table shape [batch_size, num_blocks_per_seq]
|
| 1074 |
+
assert block_table is not None
|
| 1075 |
+
num_blocks_per_seq = block_table.shape[1]
|
| 1076 |
+
seqlen_kc = num_blocks_per_seq * block_size_k
|
| 1077 |
+
seqlen_vc = seqlen_kc
|
| 1078 |
+
|
| 1079 |
+
# Strides for paged layout
|
| 1080 |
+
stride_kc_z = 0 # No batch dimension in paged cache
|
| 1081 |
+
stride_kc_n = k_cache.stride(1) # Sequence stride
|
| 1082 |
+
stride_kc_h = k_cache.stride(2) # Head stride
|
| 1083 |
+
stride_kc_d = k_cache.stride(3) # Dim stride
|
| 1084 |
+
|
| 1085 |
+
stride_vc_z = 0
|
| 1086 |
+
stride_vc_n = v_cache.stride(1)
|
| 1087 |
+
stride_vc_h = v_cache.stride(2)
|
| 1088 |
+
stride_vc_d = v_cache.stride(3)
|
| 1089 |
+
else:
|
| 1090 |
+
_, seqlen_kc, nheads_kc, dim_kc = get_shape_from_layout(k_cache, layout)
|
| 1091 |
+
stride_kc_z, stride_kc_h, stride_kc_n, stride_kc_d = get_stride_from_layout(
|
| 1092 |
+
k_cache, layout
|
| 1093 |
+
)
|
| 1094 |
+
_, seqlen_vc, nheads_vc, dim_vc = get_shape_from_layout(v_cache, layout)
|
| 1095 |
+
stride_vc_z, stride_vc_h, stride_vc_n, stride_vc_d = get_stride_from_layout(
|
| 1096 |
+
v_cache, layout
|
| 1097 |
+
)
|
| 1098 |
+
block_size_k = 0 # Not used
|
| 1099 |
+
if is_new_kv:
|
| 1100 |
+
_, seqlen_kn, nheads_kn, dim_kn = get_shape_from_layout(k_new, layout)
|
| 1101 |
+
stride_kn_z, stride_kn_h, stride_kn_n, stride_kn_d = get_stride_from_layout(
|
| 1102 |
+
k_new, layout
|
| 1103 |
+
)
|
| 1104 |
+
_, seqlen_vn, nheads_vn, dim_vn = get_shape_from_layout(v_new, layout)
|
| 1105 |
+
stride_vn_z, stride_vn_h, stride_vn_n, stride_vn_d = get_stride_from_layout(
|
| 1106 |
+
v_new, layout
|
| 1107 |
+
)
|
| 1108 |
+
else:
|
| 1109 |
+
_, _seqlen_kn, nheads_kn, _dim_kn = None, None, None, None
|
| 1110 |
+
stride_kn_z, stride_kn_h, stride_kn_n, stride_kn_d = None, None, None, None
|
| 1111 |
+
_, _seqlen_vn, nheads_vn, _dim_vn = None, None, None, None
|
| 1112 |
+
stride_vn_z, stride_vn_h, stride_vn_n, stride_vn_d = None, None, None, None
|
| 1113 |
+
_, seqlen_o, nheads_o, dim_o = get_shape_from_layout(out, layout)
|
| 1114 |
+
stride_oz, stride_oh, stride_om, stride_od = get_stride_from_layout(out, layout)
|
| 1115 |
+
assert (
|
| 1116 |
+
dim_q == dim_kc == dim_vc
|
| 1117 |
+
), f"Dimensions must match: {dim_q}, {dim_kc}, {dim_vc}"
|
| 1118 |
+
|
| 1119 |
+
# add extra information needed by the kernels
|
| 1120 |
+
(n_group_q, heads_per_group_q), stride_qg = (1, nheads_q), stride_qm
|
| 1121 |
+
(_n_group_k, heads_per_group_k), stride_kc_g = (1, nheads_kc), stride_kc_n
|
| 1122 |
+
(_n_group_v, _heads_per_group_v), stride_vc_g = (1, nheads_vc), stride_vc_n
|
| 1123 |
+
if is_new_kv:
|
| 1124 |
+
(_n_group_kn, _heads_per_group_kn), stride_kn_g = (1, nheads_kn), stride_kn_n
|
| 1125 |
+
(_n_group_vn, _heads_per_group_vn), stride_vn_g = (1, nheads_vn), stride_vn_n
|
| 1126 |
+
else:
|
| 1127 |
+
(_n_group_kn, _heads_per_group_kn), stride_kn_g = (None, None), None
|
| 1128 |
+
(_n_group_vn, _heads_per_group_vn), stride_vn_g = (None, None), None
|
| 1129 |
+
(_n_group_o, _heads_per_group_o), stride_og = (1, nheads_o), stride_om
|
| 1130 |
+
|
| 1131 |
+
# get padded size
|
| 1132 |
+
dim_padded = get_padded_headsize(dim_kc)
|
| 1133 |
+
is_padded_head = dim_padded != dim_kc
|
| 1134 |
+
|
| 1135 |
+
# Handle MQA/GQA case
|
| 1136 |
+
group_size = nheads_q // nheads_kc
|
| 1137 |
+
if group_size > 1:
|
| 1138 |
+
is_gqa = True
|
| 1139 |
+
else:
|
| 1140 |
+
is_gqa = False
|
| 1141 |
+
|
| 1142 |
+
# Use heuristics for split_k
|
| 1143 |
+
if use_block_table:
|
| 1144 |
+
# For paged attention, use the actual sequence length from cache_seqlens
|
| 1145 |
+
max_seqlen = (
|
| 1146 |
+
int(cache_seqlens.max().item())
|
| 1147 |
+
if cache_seqlens is not None
|
| 1148 |
+
else block_size_k
|
| 1149 |
+
)
|
| 1150 |
+
split_k = get_split_k(batch_size, n_group_q, heads_per_group_q, max_seqlen)
|
| 1151 |
+
else:
|
| 1152 |
+
split_k = get_split_k(batch_size, n_group_q, heads_per_group_q, seqlen_kc)
|
| 1153 |
+
split_size = (seqlen_kc + split_k - 1) // split_k
|
| 1154 |
+
|
| 1155 |
+
# setup grid - use lambda to get BLOCK_M from autotune
|
| 1156 |
+
# Use MAX_BLOCK_M for intermediate tensor allocation to ensure enough space
|
| 1157 |
+
seqlen_q_ceil = (seqlen_q + MAX_BLOCK_M - 1) // MAX_BLOCK_M * MAX_BLOCK_M
|
| 1158 |
+
|
| 1159 |
+
def grid(META):
|
| 1160 |
+
return (
|
| 1161 |
+
triton.cdiv(seqlen_q, META["BLOCK_M"]),
|
| 1162 |
+
batch_size * n_group_q * heads_per_group_q,
|
| 1163 |
+
split_k,
|
| 1164 |
+
)
|
| 1165 |
+
|
| 1166 |
+
# create intermediate tensors
|
| 1167 |
+
out_splitk = torch.empty(
|
| 1168 |
+
[
|
| 1169 |
+
batch_size * n_group_q * heads_per_group_q,
|
| 1170 |
+
split_k,
|
| 1171 |
+
seqlen_q_ceil,
|
| 1172 |
+
dim_padded,
|
| 1173 |
+
],
|
| 1174 |
+
dtype=torch.float32,
|
| 1175 |
+
device=q.device,
|
| 1176 |
+
)
|
| 1177 |
+
metadata = torch.empty(
|
| 1178 |
+
[batch_size * n_group_q * heads_per_group_q, 2, split_k, seqlen_q_ceil],
|
| 1179 |
+
dtype=torch.float32,
|
| 1180 |
+
device=q.device,
|
| 1181 |
+
)
|
| 1182 |
+
|
| 1183 |
+
# Validate pre-allocated softmax_lse tensor
|
| 1184 |
+
# Expected shape after view: (batch_size, n_group_q * heads_per_group_q, seqlen_q)
|
| 1185 |
+
# Internal shape: (batch_size * n_group_q * heads_per_group_q, seqlen_q)
|
| 1186 |
+
expected_h_total = batch_size * n_group_q * heads_per_group_q
|
| 1187 |
+
assert (
|
| 1188 |
+
softmax_lse.shape[0] == batch_size
|
| 1189 |
+
), f"softmax_lse.shape[0] ({softmax_lse.shape[0]}) must equal batch_size ({batch_size})"
|
| 1190 |
+
assert (
|
| 1191 |
+
softmax_lse.shape[1] == n_group_q * heads_per_group_q
|
| 1192 |
+
), f"softmax_lse.shape[1] ({softmax_lse.shape[1]}) must equal n_group_q * heads_per_group_q ({n_group_q * heads_per_group_q})"
|
| 1193 |
+
assert (
|
| 1194 |
+
softmax_lse.shape[2] >= seqlen_q
|
| 1195 |
+
), f"softmax_lse.shape[2] ({softmax_lse.shape[2]}) must be >= seqlen_q ({seqlen_q})"
|
| 1196 |
+
assert (
|
| 1197 |
+
softmax_lse.dtype == torch.float32
|
| 1198 |
+
), f"softmax_lse must be float32, got {softmax_lse.dtype}"
|
| 1199 |
+
assert softmax_lse.device == q.device, "softmax_lse must be on same device as q"
|
| 1200 |
+
|
| 1201 |
+
# Create internal lse view for kernel use
|
| 1202 |
+
lse = softmax_lse.view(expected_h_total, -1)[:, :seqlen_q].contiguous()
|
| 1203 |
+
|
| 1204 |
+
# get intermediate tensor strides
|
| 1205 |
+
stride_osk_zhg, stride_osk_s, stride_osk_m, stride_osk_d = out_splitk.stride()
|
| 1206 |
+
stride_mzhg, stride_m2, stride_ms, stride_mm = metadata.stride()
|
| 1207 |
+
stride_lse_zhg, stride_lse_m = lse.stride()
|
| 1208 |
+
|
| 1209 |
+
# Block table strides
|
| 1210 |
+
if use_block_table:
|
| 1211 |
+
assert block_table is not None
|
| 1212 |
+
stride_bt_b, stride_bt_s = block_table.stride()
|
| 1213 |
+
else:
|
| 1214 |
+
stride_bt_b, stride_bt_s = 0, 0
|
| 1215 |
+
|
| 1216 |
+
# FP8 support
|
| 1217 |
+
IS_FP8 = is_fp8([q, k_cache, v_cache])
|
| 1218 |
+
if IS_FP8:
|
| 1219 |
+
arch = get_arch()
|
| 1220 |
+
if not arch.supports_fp8:
|
| 1221 |
+
raise RuntimeError(f"{arch.name} does not support FP8")
|
| 1222 |
+
rec_dtype = arch.recommended_fp8_dtype(q.dtype)
|
| 1223 |
+
if (
|
| 1224 |
+
q.dtype != rec_dtype
|
| 1225 |
+
or k_cache.dtype != rec_dtype
|
| 1226 |
+
or v_cache.dtype != rec_dtype
|
| 1227 |
+
):
|
| 1228 |
+
warnings.warn(
|
| 1229 |
+
f"Use {rec_dtype} data type on {arch}. Got q: {q.dtype}, k: {k_cache.dtype}, v: {v_cache.dtype}",
|
| 1230 |
+
UserWarning,
|
| 1231 |
+
)
|
| 1232 |
+
if (q_descale is None) or (k_descale is None) or (v_descale is None):
|
| 1233 |
+
warnings.warn(
|
| 1234 |
+
"FP8 tensors detected but descale factors not provided. Using default scale of 1.0",
|
| 1235 |
+
UserWarning,
|
| 1236 |
+
)
|
| 1237 |
+
# Create default descale tensors if not provided
|
| 1238 |
+
if q_descale is None:
|
| 1239 |
+
q_descale = torch.ones(
|
| 1240 |
+
batch_size, nheads_q, dtype=torch.float32, device=q.device
|
| 1241 |
+
)
|
| 1242 |
+
if k_descale is None:
|
| 1243 |
+
k_descale = torch.ones(
|
| 1244 |
+
batch_size, nheads_kc, dtype=torch.float32, device=q.device
|
| 1245 |
+
)
|
| 1246 |
+
if v_descale is None:
|
| 1247 |
+
v_descale = torch.ones(
|
| 1248 |
+
batch_size, nheads_vc, dtype=torch.float32, device=q.device
|
| 1249 |
+
)
|
| 1250 |
+
else:
|
| 1251 |
+
# Enforce exact expected shapes; no reshaping or normalization.
|
| 1252 |
+
assert (
|
| 1253 |
+
q_descale.dim() == 2
|
| 1254 |
+
and q_descale.shape[0] == batch_size
|
| 1255 |
+
and q_descale.shape[1] == nheads_kc
|
| 1256 |
+
), f"q_descale expected shape ({batch_size}, {nheads_kc}) got {tuple(q_descale.shape)}"
|
| 1257 |
+
assert (
|
| 1258 |
+
k_descale.dim() == 2
|
| 1259 |
+
and k_descale.shape[0] == batch_size
|
| 1260 |
+
and k_descale.shape[1] == nheads_kc
|
| 1261 |
+
), f"k_descale expected shape ({batch_size}, {nheads_kc}) got {tuple(k_descale.shape)}"
|
| 1262 |
+
assert (
|
| 1263 |
+
v_descale.dim() == 2
|
| 1264 |
+
and v_descale.shape[0] == batch_size
|
| 1265 |
+
and v_descale.shape[1] == nheads_kc
|
| 1266 |
+
), f"v_descale expected shape ({batch_size}, {nheads_kc}) got {tuple(v_descale.shape)}"
|
| 1267 |
+
stride_q_descale_z, stride_q_descale_h = q_descale.stride()
|
| 1268 |
+
stride_k_descale_z, stride_k_descale_h = k_descale.stride()
|
| 1269 |
+
stride_v_descale_z, stride_v_descale_h = v_descale.stride()
|
| 1270 |
+
else:
|
| 1271 |
+
q_descale = None
|
| 1272 |
+
k_descale = None
|
| 1273 |
+
v_descale = None
|
| 1274 |
+
stride_q_descale_z = 0
|
| 1275 |
+
stride_q_descale_h = 0
|
| 1276 |
+
stride_k_descale_z = 0
|
| 1277 |
+
stride_k_descale_h = 0
|
| 1278 |
+
stride_v_descale_z = 0
|
| 1279 |
+
stride_v_descale_h = 0
|
| 1280 |
+
|
| 1281 |
+
if DEBUG:
|
| 1282 |
+
print(
|
| 1283 |
+
"batch_size, seqlen_q, nheads_q, dim_q",
|
| 1284 |
+
(batch_size, seqlen_q, nheads_q, dim_q),
|
| 1285 |
+
)
|
| 1286 |
+
print("_, seqlen_kc, nheads_kc, dim_kc", (_, seqlen_kc, nheads_kc, dim_kc))
|
| 1287 |
+
print("dim_padded:", dim_padded)
|
| 1288 |
+
print(
|
| 1289 |
+
"stride_qz, stride_qm, stride_qg, stride_qh, stride_qd",
|
| 1290 |
+
(stride_qz, stride_qm, stride_qg, stride_qh, stride_qd),
|
| 1291 |
+
)
|
| 1292 |
+
print(
|
| 1293 |
+
"stride_kc_z, stride_kc_n, stride_kc_g, stride_kc_h, stride_kc_d",
|
| 1294 |
+
(stride_kc_z, stride_kc_n, stride_kc_g, stride_kc_h, stride_kc_d),
|
| 1295 |
+
)
|
| 1296 |
+
print(
|
| 1297 |
+
"stride_vc_z, stride_vc_n, stride_vc_g, stride_vc_h, stride_vc_d",
|
| 1298 |
+
(stride_vc_z, stride_vc_n, stride_vc_g, stride_vc_h, stride_vc_d),
|
| 1299 |
+
)
|
| 1300 |
+
if is_new_kv:
|
| 1301 |
+
print(
|
| 1302 |
+
"stride_kn_z, stride_kn_n, stride_kn_g, stride_kn_h, stride_kn_d",
|
| 1303 |
+
(stride_kn_z, stride_kn_n, stride_kn_g, stride_kn_h, stride_kn_d),
|
| 1304 |
+
)
|
| 1305 |
+
print(
|
| 1306 |
+
"stride_vn_z, stride_vn_n, stride_vn_g, stride_vn_h, stride_vn_d",
|
| 1307 |
+
(stride_vn_z, stride_vn_n, stride_vn_g, stride_vn_h, stride_vn_d),
|
| 1308 |
+
)
|
| 1309 |
+
print(
|
| 1310 |
+
"stride_oz, stride_om, stride_og, stride_oh, stride_od",
|
| 1311 |
+
(stride_oz, stride_om, stride_og, stride_oh, stride_od),
|
| 1312 |
+
)
|
| 1313 |
+
print(
|
| 1314 |
+
"stride_osk_zhg, stride_osk_s, stride_osk_m, stride_osk_d",
|
| 1315 |
+
(stride_osk_zhg, stride_osk_s, stride_osk_m, stride_osk_d),
|
| 1316 |
+
)
|
| 1317 |
+
print(
|
| 1318 |
+
"stride_mzhg, stride_m2, stride_ms, stride_mm",
|
| 1319 |
+
(stride_mzhg, stride_m2, stride_ms, stride_mm),
|
| 1320 |
+
)
|
| 1321 |
+
print("stride_lse_zhg, stride_lse_m", (stride_lse_zhg, stride_lse_m))
|
| 1322 |
+
|
| 1323 |
+
_fwd_kernel_splitK[grid](
|
| 1324 |
+
Q=q,
|
| 1325 |
+
K=k_cache,
|
| 1326 |
+
V=v_cache,
|
| 1327 |
+
Q_Descale=q_descale,
|
| 1328 |
+
K_Descale=k_descale,
|
| 1329 |
+
V_Descale=v_descale,
|
| 1330 |
+
sm_scale=sm_scale,
|
| 1331 |
+
Out_splitK=out_splitk,
|
| 1332 |
+
Metadata=metadata,
|
| 1333 |
+
K_new=None,
|
| 1334 |
+
V_new=None,
|
| 1335 |
+
Cache_seqlens=cache_seqlens,
|
| 1336 |
+
Cache_batch_idx=cache_batch_idx,
|
| 1337 |
+
Block_table=block_table,
|
| 1338 |
+
Alibi_slopes=alibi_slopes,
|
| 1339 |
+
# q strides
|
| 1340 |
+
stride_qz=stride_qz,
|
| 1341 |
+
stride_qm=stride_qm,
|
| 1342 |
+
stride_qg=stride_qg,
|
| 1343 |
+
stride_qh=stride_qh,
|
| 1344 |
+
stride_qd=stride_qd,
|
| 1345 |
+
# k strides
|
| 1346 |
+
stride_kz=stride_kc_z,
|
| 1347 |
+
stride_kn=stride_kc_n,
|
| 1348 |
+
stride_kg=stride_kc_g,
|
| 1349 |
+
stride_kh=stride_kc_h,
|
| 1350 |
+
stride_kd=stride_kc_d,
|
| 1351 |
+
# v strides
|
| 1352 |
+
stride_vz=stride_vc_z,
|
| 1353 |
+
stride_vn=stride_vc_n,
|
| 1354 |
+
stride_vg=stride_vc_g,
|
| 1355 |
+
stride_vh=stride_vc_h,
|
| 1356 |
+
stride_vd=stride_vc_d,
|
| 1357 |
+
# out_splitk strides
|
| 1358 |
+
stride_osk_zhg=stride_osk_zhg,
|
| 1359 |
+
stride_osk_s=stride_osk_s,
|
| 1360 |
+
stride_osk_m=stride_osk_m,
|
| 1361 |
+
stride_osk_d=stride_osk_d,
|
| 1362 |
+
# metadata strides
|
| 1363 |
+
stride_mzhg=stride_mzhg,
|
| 1364 |
+
stride_m2=stride_m2,
|
| 1365 |
+
stride_ms=stride_ms,
|
| 1366 |
+
stride_mm=stride_mm,
|
| 1367 |
+
# k_new strides
|
| 1368 |
+
stride_kn_z=stride_kn_z,
|
| 1369 |
+
stride_kn_n=stride_kn_n,
|
| 1370 |
+
stride_kn_g=stride_kn_g,
|
| 1371 |
+
stride_kn_h=stride_kn_h,
|
| 1372 |
+
stride_kn_d=stride_kn_d,
|
| 1373 |
+
# v_new strides
|
| 1374 |
+
stride_vn_z=stride_vn_z,
|
| 1375 |
+
stride_vn_n=stride_vn_n,
|
| 1376 |
+
stride_vn_g=stride_vn_g,
|
| 1377 |
+
stride_vn_h=stride_vn_h,
|
| 1378 |
+
stride_vn_d=stride_vn_d,
|
| 1379 |
+
# block table strides
|
| 1380 |
+
stride_bt_b=stride_bt_b,
|
| 1381 |
+
stride_bt_s=stride_bt_s,
|
| 1382 |
+
# alibi strides
|
| 1383 |
+
stride_az=stride_az,
|
| 1384 |
+
stride_ah=stride_ah,
|
| 1385 |
+
# FP8 descale strides
|
| 1386 |
+
stride_q_descale_z=stride_q_descale_z,
|
| 1387 |
+
stride_q_descale_h=stride_q_descale_h,
|
| 1388 |
+
stride_k_descale_z=stride_k_descale_z,
|
| 1389 |
+
stride_k_descale_h=stride_k_descale_h,
|
| 1390 |
+
stride_v_descale_z=stride_v_descale_z,
|
| 1391 |
+
stride_v_descale_h=stride_v_descale_h,
|
| 1392 |
+
Z=batch_size,
|
| 1393 |
+
H_q=heads_per_group_q,
|
| 1394 |
+
H_kv=heads_per_group_k,
|
| 1395 |
+
G_q=n_group_q,
|
| 1396 |
+
N_CTX_Q=seqlen_q,
|
| 1397 |
+
N_CTX_K=seqlen_kc,
|
| 1398 |
+
N_CTX_NEW=0, # No new KV, cache already updated
|
| 1399 |
+
BLOCK_N_PER_SPLIT=split_size,
|
| 1400 |
+
BLOCK_SIZE_K=block_size_k if use_block_table else 256,
|
| 1401 |
+
BLOCK_DMODEL=dim_padded,
|
| 1402 |
+
ACTUAL_BLOCK_DMODEL=dim_kc,
|
| 1403 |
+
USE_CACHE_SEQLENs=use_cache_seqlens,
|
| 1404 |
+
USE_CACHE_BATCH_IDX=cache_batch_idx is not None,
|
| 1405 |
+
NEW_KV=False, # Cache already updated
|
| 1406 |
+
IS_GQA=is_gqa,
|
| 1407 |
+
IS_CAUSAL=causal,
|
| 1408 |
+
USE_ALIBI=use_alibi,
|
| 1409 |
+
PADDED_HEAD=is_padded_head,
|
| 1410 |
+
GROUP_SIZE=group_size,
|
| 1411 |
+
USE_SLIDING_WINDOW=use_sliding_window,
|
| 1412 |
+
WINDOW_SIZE_LEFT=window_size_left,
|
| 1413 |
+
WINDOW_SIZE_RIGHT=window_size_right,
|
| 1414 |
+
USE_BLOCK_TABLE=use_block_table,
|
| 1415 |
+
IS_FP8=IS_FP8,
|
| 1416 |
+
)
|
| 1417 |
+
|
| 1418 |
+
if DEBUG:
|
| 1419 |
+
print("Out_splitK:", out_splitk, out_splitk.shape)
|
| 1420 |
+
print("metadata:", metadata, metadata.shape)
|
| 1421 |
+
print("lse:", lse, lse.shape)
|
| 1422 |
+
print("Out:", out, out.shape)
|
| 1423 |
+
|
| 1424 |
+
# Merge together
|
| 1425 |
+
splitK_pow2 = triton.next_power_of_2(split_k)
|
| 1426 |
+
mask_split_k = splitK_pow2 > split_k
|
| 1427 |
+
if batch_size * n_group_q * heads_per_group_q * seqlen_q >= 512:
|
| 1428 |
+
k_block_num = 1
|
| 1429 |
+
else:
|
| 1430 |
+
k_block_num = 2
|
| 1431 |
+
assert dim_padded % k_block_num == 0
|
| 1432 |
+
k_block_size = dim_padded // k_block_num
|
| 1433 |
+
reduce_grid = (batch_size * n_group_q * heads_per_group_q, seqlen_q, k_block_num)
|
| 1434 |
+
|
| 1435 |
+
if DEBUG:
|
| 1436 |
+
print("splitK_pow2:", splitK_pow2)
|
| 1437 |
+
print("k_block_num:", k_block_num)
|
| 1438 |
+
print("k_block_size:", k_block_size)
|
| 1439 |
+
print("grid:", reduce_grid)
|
| 1440 |
+
|
| 1441 |
+
_splitK_reduce[reduce_grid](
|
| 1442 |
+
out_splitk,
|
| 1443 |
+
metadata,
|
| 1444 |
+
out,
|
| 1445 |
+
lse,
|
| 1446 |
+
# Split-K output strides
|
| 1447 |
+
stride_osk_zhg=stride_osk_zhg,
|
| 1448 |
+
stride_osk_s=stride_osk_s,
|
| 1449 |
+
stride_osk_m=stride_osk_m,
|
| 1450 |
+
stride_osk_k=stride_osk_d,
|
| 1451 |
+
# Metadata strides
|
| 1452 |
+
stride_mzhg=stride_mzhg,
|
| 1453 |
+
stride_m2=stride_m2,
|
| 1454 |
+
stride_ms=stride_ms,
|
| 1455 |
+
stride_mm=stride_mm,
|
| 1456 |
+
# Output tensor strides
|
| 1457 |
+
stride_oz=stride_oz,
|
| 1458 |
+
stride_oh=stride_oh,
|
| 1459 |
+
stride_og=stride_og,
|
| 1460 |
+
stride_om=stride_om,
|
| 1461 |
+
stride_ok=stride_od,
|
| 1462 |
+
# LSE strides
|
| 1463 |
+
stride_lse_zhg=stride_lse_zhg,
|
| 1464 |
+
stride_lse_m=stride_lse_m,
|
| 1465 |
+
K_BLOCK_SIZE=k_block_size,
|
| 1466 |
+
BLOCK_DMODEL=dim_padded,
|
| 1467 |
+
ACTUAL_BLOCK_DMODEL=dim_kc,
|
| 1468 |
+
G=n_group_q,
|
| 1469 |
+
H=heads_per_group_q,
|
| 1470 |
+
# TODO: Tune num_warps
|
| 1471 |
+
split_k=split_k,
|
| 1472 |
+
splitK_pow2=splitK_pow2,
|
| 1473 |
+
MASK_SPLITK=mask_split_k,
|
| 1474 |
+
PADDED_HEAD=is_padded_head,
|
| 1475 |
+
)
|
build/torch-rocm/_kernels/flash_attn_triton_amd/fwd_prefill.py
ADDED
|
@@ -0,0 +1,1875 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import warnings
|
| 2 |
+
import torch
|
| 3 |
+
import triton
|
| 4 |
+
import triton.language as tl
|
| 5 |
+
from typing import Literal, Optional
|
| 6 |
+
from .common import compute_alibi_block, compute_fp8_scaling_factors, apply_rotary
|
| 7 |
+
from .utils import (
|
| 8 |
+
AUTOTUNE,
|
| 9 |
+
AutotuneMode,
|
| 10 |
+
DEBUG,
|
| 11 |
+
FWD_CONF_OVERRIDE,
|
| 12 |
+
get_arch,
|
| 13 |
+
is_fp8,
|
| 14 |
+
remap_xcd,
|
| 15 |
+
)
|
| 16 |
+
|
| 17 |
+
FWD_PREFILL_AUTOTUNE_KEYS = [
|
| 18 |
+
"IS_CAUSAL",
|
| 19 |
+
"dropout_p",
|
| 20 |
+
"MAX_SEQLENS_Q",
|
| 21 |
+
"MAX_SEQLENS_K",
|
| 22 |
+
"ACTUAL_BLOCK_DMODEL_QK",
|
| 23 |
+
"ACTUAL_BLOCK_DMODEL_V",
|
| 24 |
+
"IS_VARLEN",
|
| 25 |
+
"HQ",
|
| 26 |
+
"HK",
|
| 27 |
+
]
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
def get_fwd_prefill_configs(mode: AutotuneMode):
|
| 31 |
+
# NOTE: Tests expect specific BLOCK_N sizes for attention score renormalization:
|
| 32 |
+
# - CDNA: BLOCK_N=64
|
| 33 |
+
# - RDNA: BLOCK_N=32
|
| 34 |
+
# See _get_block_size_n_triton() in test_flash_attn_triton_amd.py
|
| 35 |
+
|
| 36 |
+
if mode == "off":
|
| 37 |
+
if FWD_CONF_OVERRIDE:
|
| 38 |
+
return [FWD_CONF_OVERRIDE]
|
| 39 |
+
arch = get_arch()
|
| 40 |
+
if arch.name == "gfx950":
|
| 41 |
+
return [
|
| 42 |
+
triton.Config(
|
| 43 |
+
{
|
| 44 |
+
"BLOCK_M": 128,
|
| 45 |
+
"BLOCK_N": 64,
|
| 46 |
+
"waves_per_eu": 2,
|
| 47 |
+
"PRE_LOAD_V": False,
|
| 48 |
+
},
|
| 49 |
+
num_stages=1,
|
| 50 |
+
num_warps=4,
|
| 51 |
+
),
|
| 52 |
+
]
|
| 53 |
+
elif arch.name == "gfx942":
|
| 54 |
+
return [
|
| 55 |
+
triton.Config(
|
| 56 |
+
{
|
| 57 |
+
"BLOCK_M": 128,
|
| 58 |
+
"BLOCK_N": 64,
|
| 59 |
+
"waves_per_eu": 2,
|
| 60 |
+
"PRE_LOAD_V": False,
|
| 61 |
+
},
|
| 62 |
+
num_stages=1,
|
| 63 |
+
num_warps=4,
|
| 64 |
+
),
|
| 65 |
+
]
|
| 66 |
+
elif arch.is_rdna:
|
| 67 |
+
BLOCK_N = 64 if arch.name == "gfx1100" else 32
|
| 68 |
+
return [
|
| 69 |
+
triton.Config(
|
| 70 |
+
{
|
| 71 |
+
"BLOCK_M": 128,
|
| 72 |
+
"BLOCK_N": BLOCK_N,
|
| 73 |
+
"PRE_LOAD_V": False,
|
| 74 |
+
"waves_per_eu": 6,
|
| 75 |
+
},
|
| 76 |
+
num_stages=1,
|
| 77 |
+
num_warps=8,
|
| 78 |
+
),
|
| 79 |
+
]
|
| 80 |
+
else:
|
| 81 |
+
return [
|
| 82 |
+
triton.Config(
|
| 83 |
+
{
|
| 84 |
+
"BLOCK_M": 64,
|
| 85 |
+
"BLOCK_N": 64,
|
| 86 |
+
"waves_per_eu": 2,
|
| 87 |
+
"PRE_LOAD_V": False,
|
| 88 |
+
},
|
| 89 |
+
num_stages=1,
|
| 90 |
+
num_warps=4,
|
| 91 |
+
)
|
| 92 |
+
]
|
| 93 |
+
|
| 94 |
+
elif mode == "on":
|
| 95 |
+
if FWD_CONF_OVERRIDE:
|
| 96 |
+
return [FWD_CONF_OVERRIDE]
|
| 97 |
+
arch = get_arch()
|
| 98 |
+
if arch.name == "gfx950":
|
| 99 |
+
return [
|
| 100 |
+
triton.Config(
|
| 101 |
+
{
|
| 102 |
+
"BLOCK_M": 128,
|
| 103 |
+
"BLOCK_N": 64,
|
| 104 |
+
"waves_per_eu": 2,
|
| 105 |
+
"PRE_LOAD_V": False,
|
| 106 |
+
},
|
| 107 |
+
num_stages=1,
|
| 108 |
+
num_warps=4,
|
| 109 |
+
),
|
| 110 |
+
triton.Config(
|
| 111 |
+
{
|
| 112 |
+
"BLOCK_M": 128,
|
| 113 |
+
"BLOCK_N": 64,
|
| 114 |
+
"waves_per_eu": 2,
|
| 115 |
+
"PRE_LOAD_V": False,
|
| 116 |
+
},
|
| 117 |
+
num_stages=1,
|
| 118 |
+
num_warps=2,
|
| 119 |
+
),
|
| 120 |
+
triton.Config(
|
| 121 |
+
{
|
| 122 |
+
"BLOCK_M": 128,
|
| 123 |
+
"BLOCK_N": 64,
|
| 124 |
+
"waves_per_eu": 2,
|
| 125 |
+
"PRE_LOAD_V": False,
|
| 126 |
+
},
|
| 127 |
+
num_stages=2,
|
| 128 |
+
num_warps=4,
|
| 129 |
+
),
|
| 130 |
+
triton.Config(
|
| 131 |
+
{
|
| 132 |
+
"BLOCK_M": 128,
|
| 133 |
+
"BLOCK_N": 128,
|
| 134 |
+
"waves_per_eu": 2,
|
| 135 |
+
"PRE_LOAD_V": False,
|
| 136 |
+
},
|
| 137 |
+
num_stages=2,
|
| 138 |
+
num_warps=4,
|
| 139 |
+
),
|
| 140 |
+
]
|
| 141 |
+
elif arch.name == "gfx942":
|
| 142 |
+
if arch.cu_count < 304:
|
| 143 |
+
return [
|
| 144 |
+
triton.Config(
|
| 145 |
+
{
|
| 146 |
+
"BLOCK_M": 128,
|
| 147 |
+
"BLOCK_N": 64,
|
| 148 |
+
"waves_per_eu": 2,
|
| 149 |
+
"PRE_LOAD_V": False,
|
| 150 |
+
},
|
| 151 |
+
num_stages=1,
|
| 152 |
+
num_warps=4,
|
| 153 |
+
),
|
| 154 |
+
]
|
| 155 |
+
else:
|
| 156 |
+
return [
|
| 157 |
+
triton.Config(
|
| 158 |
+
{
|
| 159 |
+
"BLOCK_M": 128,
|
| 160 |
+
"BLOCK_N": 64,
|
| 161 |
+
"waves_per_eu": 2,
|
| 162 |
+
"PRE_LOAD_V": False,
|
| 163 |
+
},
|
| 164 |
+
num_stages=1,
|
| 165 |
+
num_warps=4,
|
| 166 |
+
)
|
| 167 |
+
]
|
| 168 |
+
elif arch.is_rdna:
|
| 169 |
+
BLOCK_N = 64 if arch.name == "gfx1100" else 32
|
| 170 |
+
return [
|
| 171 |
+
triton.Config(
|
| 172 |
+
{
|
| 173 |
+
"BLOCK_M": 128,
|
| 174 |
+
"BLOCK_N": BLOCK_N,
|
| 175 |
+
"PRE_LOAD_V": False,
|
| 176 |
+
"waves_per_eu": 6,
|
| 177 |
+
},
|
| 178 |
+
num_stages=1,
|
| 179 |
+
num_warps=8,
|
| 180 |
+
),
|
| 181 |
+
]
|
| 182 |
+
else:
|
| 183 |
+
return [
|
| 184 |
+
triton.Config(
|
| 185 |
+
{
|
| 186 |
+
"BLOCK_M": 64,
|
| 187 |
+
"BLOCK_N": 64,
|
| 188 |
+
"waves_per_eu": 2,
|
| 189 |
+
"PRE_LOAD_V": False,
|
| 190 |
+
},
|
| 191 |
+
num_stages=1,
|
| 192 |
+
num_warps=4,
|
| 193 |
+
)
|
| 194 |
+
]
|
| 195 |
+
|
| 196 |
+
else: # sweep
|
| 197 |
+
configs = []
|
| 198 |
+
BLOCK_M_OPTIONS = [128, 64, 32, 16]
|
| 199 |
+
BLOCK_N_OPTIONS = [128, 64, 32, 16]
|
| 200 |
+
NUM_WARPS_OPTIONS = [2, 4, 8]
|
| 201 |
+
NUM_STAGES_OPTIONS = [1, 2]
|
| 202 |
+
WAVES_PER_EU_OPTIONS = [4, 2, 1]
|
| 203 |
+
PRE_LOAD_V_OPTIONS = [False]
|
| 204 |
+
for bm in BLOCK_M_OPTIONS:
|
| 205 |
+
for bn in BLOCK_N_OPTIONS:
|
| 206 |
+
for waves in WAVES_PER_EU_OPTIONS:
|
| 207 |
+
for nw in NUM_WARPS_OPTIONS:
|
| 208 |
+
for ns in NUM_STAGES_OPTIONS:
|
| 209 |
+
for preload_v in PRE_LOAD_V_OPTIONS:
|
| 210 |
+
configs.append(
|
| 211 |
+
triton.Config(
|
| 212 |
+
{
|
| 213 |
+
"BLOCK_M": bm,
|
| 214 |
+
"BLOCK_N": bn,
|
| 215 |
+
"waves_per_eu": waves,
|
| 216 |
+
"PRE_LOAD_V": preload_v,
|
| 217 |
+
},
|
| 218 |
+
num_stages=ns,
|
| 219 |
+
num_warps=nw,
|
| 220 |
+
)
|
| 221 |
+
)
|
| 222 |
+
return configs
|
| 223 |
+
|
| 224 |
+
|
| 225 |
+
fwd_prefill_autotune_configs = get_fwd_prefill_configs(AUTOTUNE)
|
| 226 |
+
|
| 227 |
+
|
| 228 |
+
@triton.jit
|
| 229 |
+
def _attn_fwd_inner(
|
| 230 |
+
acc,
|
| 231 |
+
l_i,
|
| 232 |
+
m_i,
|
| 233 |
+
q,
|
| 234 |
+
k_base_ptrs,
|
| 235 |
+
v_base_ptrs,
|
| 236 |
+
bias_base_ptrs,
|
| 237 |
+
stride_kn,
|
| 238 |
+
stride_vk,
|
| 239 |
+
stride_bn,
|
| 240 |
+
stride_sn,
|
| 241 |
+
stride_sm,
|
| 242 |
+
start_m,
|
| 243 |
+
seqlen_k,
|
| 244 |
+
seqlen_q,
|
| 245 |
+
dropout_p,
|
| 246 |
+
philox_seed,
|
| 247 |
+
philox_offset_base,
|
| 248 |
+
sd_mask,
|
| 249 |
+
stride_sz,
|
| 250 |
+
stride_sh,
|
| 251 |
+
off_z,
|
| 252 |
+
off_h_q,
|
| 253 |
+
offs_m,
|
| 254 |
+
offs_n,
|
| 255 |
+
offs_d_qk,
|
| 256 |
+
offs_d_v,
|
| 257 |
+
block_min,
|
| 258 |
+
block_max,
|
| 259 |
+
n_extra_tokens,
|
| 260 |
+
alibi_slope,
|
| 261 |
+
q_descale,
|
| 262 |
+
k_descale,
|
| 263 |
+
v_descale,
|
| 264 |
+
IS_FP8: tl.constexpr,
|
| 265 |
+
FP8_MAX: tl.constexpr,
|
| 266 |
+
FP8_P_DESCALE: tl.constexpr,
|
| 267 |
+
APPLY_MASK: tl.constexpr, # True for masked blocks, False for full blocks
|
| 268 |
+
IS_CAUSAL: tl.constexpr,
|
| 269 |
+
BLOCK_M: tl.constexpr,
|
| 270 |
+
BLOCK_DMODEL_QK: tl.constexpr,
|
| 271 |
+
BLOCK_DMODEL_V: tl.constexpr,
|
| 272 |
+
BLOCK_N: tl.constexpr,
|
| 273 |
+
PRE_LOAD_V: tl.constexpr,
|
| 274 |
+
ENABLE_DROPOUT: tl.constexpr,
|
| 275 |
+
PADDED_HEAD_QK: tl.constexpr,
|
| 276 |
+
PADDED_HEAD_V: tl.constexpr,
|
| 277 |
+
ACTUAL_BLOCK_DMODEL_QK: tl.constexpr,
|
| 278 |
+
ACTUAL_BLOCK_DMODEL_V: tl.constexpr,
|
| 279 |
+
SM_SCALE: tl.constexpr,
|
| 280 |
+
USE_ALIBI: tl.constexpr,
|
| 281 |
+
USE_EXP2: tl.constexpr,
|
| 282 |
+
RETURN_SCORES: tl.constexpr,
|
| 283 |
+
USE_SLIDING_WINDOW: tl.constexpr,
|
| 284 |
+
WINDOW_SIZE_LEFT: tl.constexpr,
|
| 285 |
+
WINDOW_SIZE_RIGHT: tl.constexpr,
|
| 286 |
+
ACCUMULATOR_TYPE,
|
| 287 |
+
):
|
| 288 |
+
"""
|
| 289 |
+
Unified attention forward inner loop.
|
| 290 |
+
|
| 291 |
+
APPLY_MASK controls whether causal/window masking is applied:
|
| 292 |
+
- False: Fast path for full blocks (no masking overhead)
|
| 293 |
+
- True: Masked path with causal/window masking support
|
| 294 |
+
"""
|
| 295 |
+
if USE_EXP2:
|
| 296 |
+
RCP_LN2: tl.constexpr = 1.4426950408889634
|
| 297 |
+
|
| 298 |
+
# seqlen diff (only used when APPLY_MASK=True)
|
| 299 |
+
seqlen_delta_qk = seqlen_k - seqlen_q
|
| 300 |
+
|
| 301 |
+
# loop over k, v, and update accumulator
|
| 302 |
+
for start_n in range(block_min, block_max, BLOCK_N):
|
| 303 |
+
# get ptrs
|
| 304 |
+
k_ptrs = k_base_ptrs + start_n * stride_kn
|
| 305 |
+
v_ptrs = v_base_ptrs + start_n * stride_vk
|
| 306 |
+
|
| 307 |
+
kv_offs_n = start_n + tl.arange(0, BLOCK_N)
|
| 308 |
+
|
| 309 |
+
# Load K - different masking for APPLY_MASK vs non-masked
|
| 310 |
+
if APPLY_MASK:
|
| 311 |
+
# For masked blocks, check seqlen bounds
|
| 312 |
+
k_mask = kv_offs_n[None, :] < seqlen_k
|
| 313 |
+
v_mask = kv_offs_n[:, None] < seqlen_k
|
| 314 |
+
if PADDED_HEAD_QK:
|
| 315 |
+
k_mask = k_mask & (offs_d_qk[:, None] < ACTUAL_BLOCK_DMODEL_QK)
|
| 316 |
+
if PADDED_HEAD_V:
|
| 317 |
+
v_mask = v_mask & (offs_d_v[None, :] < ACTUAL_BLOCK_DMODEL_V)
|
| 318 |
+
k = tl.load(k_ptrs, mask=k_mask, other=0.0)
|
| 319 |
+
if PRE_LOAD_V:
|
| 320 |
+
v = tl.load(v_ptrs, mask=v_mask, other=0.0)
|
| 321 |
+
else:
|
| 322 |
+
# For full blocks, only check head dimension padding
|
| 323 |
+
if PADDED_HEAD_QK:
|
| 324 |
+
k_mask = offs_d_qk[:, None] < ACTUAL_BLOCK_DMODEL_QK
|
| 325 |
+
k = tl.load(k_ptrs, mask=k_mask, other=0.0)
|
| 326 |
+
else:
|
| 327 |
+
k = tl.load(k_ptrs)
|
| 328 |
+
if PRE_LOAD_V:
|
| 329 |
+
if PADDED_HEAD_V:
|
| 330 |
+
v_mask = offs_d_v[None, :] < ACTUAL_BLOCK_DMODEL_V
|
| 331 |
+
v = tl.load(v_ptrs, mask=v_mask, other=0.0)
|
| 332 |
+
else:
|
| 333 |
+
v = tl.load(v_ptrs)
|
| 334 |
+
|
| 335 |
+
# setup qk accumulator
|
| 336 |
+
qk = tl.zeros([BLOCK_M, BLOCK_N], dtype=ACCUMULATOR_TYPE)
|
| 337 |
+
|
| 338 |
+
# Apply extra token masking for partial blocks (only when APPLY_MASK=True)
|
| 339 |
+
if APPLY_MASK:
|
| 340 |
+
if (n_extra_tokens != 0) and (start_n + BLOCK_N == block_max):
|
| 341 |
+
boundary_m = tl.full([BLOCK_M], seqlen_k, dtype=tl.int32)
|
| 342 |
+
size_n = start_n + offs_n[None, :]
|
| 343 |
+
mask = size_n < boundary_m[:, None]
|
| 344 |
+
qk = tl.where(mask, qk, float("-inf"))
|
| 345 |
+
|
| 346 |
+
# -- compute qk ----
|
| 347 |
+
if IS_FP8:
|
| 348 |
+
qk += tl.dot(q, k) * q_descale * k_descale
|
| 349 |
+
else:
|
| 350 |
+
qk = tl.dot(q, k, acc=qk)
|
| 351 |
+
qk_scaled = qk * SM_SCALE
|
| 352 |
+
|
| 353 |
+
if USE_ALIBI:
|
| 354 |
+
# compute the global position of each token within the sequence
|
| 355 |
+
q_offs_m = start_m * BLOCK_M + tl.arange(0, BLOCK_M)
|
| 356 |
+
alibi_block = compute_alibi_block(
|
| 357 |
+
alibi_slope, seqlen_q, seqlen_k, q_offs_m, kv_offs_n
|
| 358 |
+
)
|
| 359 |
+
qk_scaled += alibi_block
|
| 360 |
+
|
| 361 |
+
# Apply causal/sliding window masking (only when APPLY_MASK=True)
|
| 362 |
+
if APPLY_MASK:
|
| 363 |
+
if USE_SLIDING_WINDOW:
|
| 364 |
+
if IS_CAUSAL:
|
| 365 |
+
# ========== CAUSAL SLIDING WINDOW MASKING ==========
|
| 366 |
+
row_idx = offs_m
|
| 367 |
+
col_idx = kv_offs_n
|
| 368 |
+
row_idx_expanded = row_idx[:, None]
|
| 369 |
+
col_idx_expanded = col_idx[None, :]
|
| 370 |
+
|
| 371 |
+
causal_offset = seqlen_k - seqlen_q
|
| 372 |
+
causal_mask = col_idx_expanded > (row_idx_expanded + causal_offset)
|
| 373 |
+
|
| 374 |
+
if WINDOW_SIZE_LEFT < 0:
|
| 375 |
+
window_mask = col_idx_expanded > (
|
| 376 |
+
row_idx_expanded + causal_offset + WINDOW_SIZE_RIGHT
|
| 377 |
+
)
|
| 378 |
+
else:
|
| 379 |
+
left_bound = row_idx_expanded + causal_offset - WINDOW_SIZE_LEFT
|
| 380 |
+
right_bound = (
|
| 381 |
+
row_idx_expanded + causal_offset + WINDOW_SIZE_RIGHT
|
| 382 |
+
)
|
| 383 |
+
window_mask = (col_idx_expanded < left_bound) | (
|
| 384 |
+
col_idx_expanded > right_bound
|
| 385 |
+
)
|
| 386 |
+
|
| 387 |
+
mask = causal_mask | window_mask
|
| 388 |
+
qk_scaled = tl.where(mask, float("-inf"), qk_scaled)
|
| 389 |
+
else:
|
| 390 |
+
# ========== NON-CAUSAL SLIDING WINDOW MASKING ==========
|
| 391 |
+
row_idx = offs_m
|
| 392 |
+
col_idx = kv_offs_n
|
| 393 |
+
sk = seqlen_k
|
| 394 |
+
sq = seqlen_q
|
| 395 |
+
row_idx_expanded = row_idx[:, None]
|
| 396 |
+
col_idx_expanded = col_idx[None, :]
|
| 397 |
+
|
| 398 |
+
if WINDOW_SIZE_LEFT < 0:
|
| 399 |
+
mask = col_idx_expanded > (
|
| 400 |
+
row_idx_expanded + sk - sq + WINDOW_SIZE_RIGHT
|
| 401 |
+
)
|
| 402 |
+
else:
|
| 403 |
+
sk_full = tl.full((1, BLOCK_N), sk, dtype=tl.int32)
|
| 404 |
+
right_bound_val = row_idx_expanded + sk - sq + WINDOW_SIZE_RIGHT
|
| 405 |
+
right_bound = tl.minimum(right_bound_val, sk_full)
|
| 406 |
+
left_bound = row_idx_expanded + sk - sq - WINDOW_SIZE_LEFT
|
| 407 |
+
mask = (col_idx_expanded > right_bound) | (
|
| 408 |
+
col_idx_expanded < left_bound
|
| 409 |
+
)
|
| 410 |
+
|
| 411 |
+
qk_scaled = tl.where(mask, float("-inf"), qk_scaled)
|
| 412 |
+
else:
|
| 413 |
+
if IS_CAUSAL:
|
| 414 |
+
causal_boundary = start_n + offs_n - seqlen_delta_qk
|
| 415 |
+
causal_mask = offs_m[:, None] >= causal_boundary[None, :]
|
| 416 |
+
qk_scaled = tl.where(causal_mask, qk_scaled, float("-inf"))
|
| 417 |
+
|
| 418 |
+
# compute qk mask for bounds checking
|
| 419 |
+
qk_mask = (offs_m[:, None] < seqlen_q) & (kv_offs_n[None, :] < seqlen_k)
|
| 420 |
+
|
| 421 |
+
# compute bias
|
| 422 |
+
if bias_base_ptrs is not None:
|
| 423 |
+
bias_ptrs = bias_base_ptrs + start_n * stride_bn
|
| 424 |
+
bias = tl.load(bias_ptrs, mask=qk_mask, other=0.0)
|
| 425 |
+
qk_scaled += bias
|
| 426 |
+
|
| 427 |
+
# get max scores so far
|
| 428 |
+
m_ij = tl.maximum(m_i, tl.max(qk_scaled, 1))
|
| 429 |
+
|
| 430 |
+
# scale and subtract max
|
| 431 |
+
# Handle the case where all values are -inf
|
| 432 |
+
q_shifted = tl.where(
|
| 433 |
+
m_ij[:, None] == float("-inf"), float("-inf"), qk_scaled - m_ij[:, None]
|
| 434 |
+
)
|
| 435 |
+
|
| 436 |
+
# Compute scaled QK and softmax probabilities
|
| 437 |
+
if USE_EXP2:
|
| 438 |
+
p = tl.math.exp2(q_shifted * RCP_LN2)
|
| 439 |
+
else:
|
| 440 |
+
p = tl.math.exp(q_shifted)
|
| 441 |
+
|
| 442 |
+
# CAVEAT: Must update l_ij before applying dropout
|
| 443 |
+
l_ij = tl.sum(p, 1)
|
| 444 |
+
if ENABLE_DROPOUT:
|
| 445 |
+
# Compute pointers for this block
|
| 446 |
+
philox_base = philox_offset_base + off_z * stride_sz + off_h_q * stride_sh
|
| 447 |
+
philox_ptrs = (
|
| 448 |
+
philox_base
|
| 449 |
+
+ offs_m[:, None] * stride_sm
|
| 450 |
+
+ kv_offs_n[None, :] * stride_sn
|
| 451 |
+
)
|
| 452 |
+
|
| 453 |
+
# compute dropout mask
|
| 454 |
+
rng_output = tl.rand(philox_seed, philox_ptrs)
|
| 455 |
+
dropout_mask = rng_output > dropout_p
|
| 456 |
+
|
| 457 |
+
# return scores with negative values for dropped vals (only if RETURN_SCORES is True)
|
| 458 |
+
if RETURN_SCORES:
|
| 459 |
+
sd_mask_value = tl.where(dropout_mask, p, -p)
|
| 460 |
+
sd_mask_base = sd_mask + off_z * stride_sz + off_h_q * stride_sh
|
| 461 |
+
sd_mask_ptrs = (
|
| 462 |
+
sd_mask_base
|
| 463 |
+
+ offs_m[:, None] * stride_sm
|
| 464 |
+
+ kv_offs_n[None, :] * stride_sn
|
| 465 |
+
)
|
| 466 |
+
|
| 467 |
+
sd_store_mask = (offs_m[:, None] < seqlen_q) & (
|
| 468 |
+
kv_offs_n[None, :] < seqlen_k
|
| 469 |
+
)
|
| 470 |
+
|
| 471 |
+
if APPLY_MASK and IS_CAUSAL:
|
| 472 |
+
causal_constraint = kv_offs_n[None, :] <= (
|
| 473 |
+
offs_m[:, None] + seqlen_delta_qk
|
| 474 |
+
)
|
| 475 |
+
sd_store_mask = sd_store_mask & causal_constraint
|
| 476 |
+
|
| 477 |
+
if APPLY_MASK and USE_SLIDING_WINDOW:
|
| 478 |
+
if WINDOW_SIZE_LEFT < 0:
|
| 479 |
+
window_constraint = kv_offs_n[None, :] <= (
|
| 480 |
+
offs_m[:, None] + seqlen_delta_qk + WINDOW_SIZE_RIGHT
|
| 481 |
+
)
|
| 482 |
+
else:
|
| 483 |
+
left_bound = (
|
| 484 |
+
offs_m[:, None] + seqlen_delta_qk - WINDOW_SIZE_LEFT
|
| 485 |
+
)
|
| 486 |
+
right_bound = (
|
| 487 |
+
offs_m[:, None] + seqlen_delta_qk + WINDOW_SIZE_RIGHT
|
| 488 |
+
)
|
| 489 |
+
window_constraint = (kv_offs_n[None, :] >= left_bound) & (
|
| 490 |
+
kv_offs_n[None, :] <= right_bound
|
| 491 |
+
)
|
| 492 |
+
sd_store_mask = sd_store_mask & window_constraint
|
| 493 |
+
|
| 494 |
+
tl.store(sd_mask_ptrs, sd_mask_value, mask=sd_store_mask)
|
| 495 |
+
|
| 496 |
+
# apply dropout mask in place
|
| 497 |
+
p = tl.where(dropout_mask, p, 0.0)
|
| 498 |
+
elif RETURN_SCORES:
|
| 499 |
+
sd_mask_base = sd_mask + off_z * stride_sz + off_h_q * stride_sh
|
| 500 |
+
sd_mask_ptrs = (
|
| 501 |
+
sd_mask_base
|
| 502 |
+
+ offs_m[:, None] * stride_sm
|
| 503 |
+
+ kv_offs_n[None, :] * stride_sn
|
| 504 |
+
)
|
| 505 |
+
|
| 506 |
+
sd_store_mask = (offs_m[:, None] < seqlen_q) & (
|
| 507 |
+
kv_offs_n[None, :] < seqlen_k
|
| 508 |
+
)
|
| 509 |
+
|
| 510 |
+
if APPLY_MASK and IS_CAUSAL:
|
| 511 |
+
causal_constraint = kv_offs_n[None, :] <= (
|
| 512 |
+
offs_m[:, None] + seqlen_delta_qk
|
| 513 |
+
)
|
| 514 |
+
sd_store_mask = sd_store_mask & causal_constraint
|
| 515 |
+
|
| 516 |
+
if APPLY_MASK and USE_SLIDING_WINDOW:
|
| 517 |
+
if WINDOW_SIZE_LEFT < 0:
|
| 518 |
+
window_constraint = kv_offs_n[None, :] <= (
|
| 519 |
+
offs_m[:, None] + seqlen_delta_qk + WINDOW_SIZE_RIGHT
|
| 520 |
+
)
|
| 521 |
+
else:
|
| 522 |
+
left_bound = offs_m[:, None] + seqlen_delta_qk - WINDOW_SIZE_LEFT
|
| 523 |
+
right_bound = offs_m[:, None] + seqlen_delta_qk + WINDOW_SIZE_RIGHT
|
| 524 |
+
window_constraint = (kv_offs_n[None, :] >= left_bound) & (
|
| 525 |
+
kv_offs_n[None, :] <= right_bound
|
| 526 |
+
)
|
| 527 |
+
sd_store_mask = sd_store_mask & window_constraint
|
| 528 |
+
|
| 529 |
+
tl.store(sd_mask_ptrs, p, mask=sd_store_mask)
|
| 530 |
+
|
| 531 |
+
# -- update output accumulator --
|
| 532 |
+
m_diff = tl.where(m_ij == float("-inf"), float("-inf"), m_i - m_ij)
|
| 533 |
+
if USE_EXP2:
|
| 534 |
+
alpha = tl.math.exp2(m_diff * RCP_LN2)
|
| 535 |
+
else:
|
| 536 |
+
alpha = tl.math.exp(m_diff)
|
| 537 |
+
acc = acc * alpha[:, None]
|
| 538 |
+
|
| 539 |
+
# Load V if not preloaded
|
| 540 |
+
if not PRE_LOAD_V:
|
| 541 |
+
if APPLY_MASK:
|
| 542 |
+
v_mask = kv_offs_n[:, None] < seqlen_k
|
| 543 |
+
if PADDED_HEAD_V:
|
| 544 |
+
v_mask = v_mask & (offs_d_v[None, :] < ACTUAL_BLOCK_DMODEL_V)
|
| 545 |
+
v = tl.load(v_ptrs, mask=v_mask, other=0.0)
|
| 546 |
+
else:
|
| 547 |
+
if PADDED_HEAD_V:
|
| 548 |
+
v_mask = offs_d_v[None, :] < ACTUAL_BLOCK_DMODEL_V
|
| 549 |
+
v = tl.load(v_ptrs, mask=v_mask, other=0.0)
|
| 550 |
+
else:
|
| 551 |
+
v = tl.load(v_ptrs)
|
| 552 |
+
|
| 553 |
+
# -- update m_i and l_i
|
| 554 |
+
l_i = l_i * alpha + l_ij
|
| 555 |
+
m_i = m_ij
|
| 556 |
+
|
| 557 |
+
if IS_FP8:
|
| 558 |
+
if FP8_P_DESCALE:
|
| 559 |
+
scale_p, descale_p = compute_fp8_scaling_factors(p, FP8_MAX)
|
| 560 |
+
acc += (
|
| 561 |
+
tl.dot((p * scale_p).to(v.type.element_ty), v)
|
| 562 |
+
* descale_p
|
| 563 |
+
* v_descale
|
| 564 |
+
)
|
| 565 |
+
else:
|
| 566 |
+
acc += tl.dot(p.to(v.type.element_ty), v) * v_descale
|
| 567 |
+
else:
|
| 568 |
+
acc = tl.dot(p.to(v.type.element_ty), v, acc=acc)
|
| 569 |
+
|
| 570 |
+
return acc, l_i, m_i
|
| 571 |
+
|
| 572 |
+
|
| 573 |
+
@triton.jit
|
| 574 |
+
def compute_window_bounds(
|
| 575 |
+
q_start,
|
| 576 |
+
q_end,
|
| 577 |
+
diag,
|
| 578 |
+
seqlen_k,
|
| 579 |
+
WINDOW_SIZE_LEFT: tl.constexpr,
|
| 580 |
+
WINDOW_SIZE_RIGHT: tl.constexpr,
|
| 581 |
+
IS_CAUSAL: tl.constexpr,
|
| 582 |
+
):
|
| 583 |
+
"""Calculate the window boundaries for a query block."""
|
| 584 |
+
# Left boundary
|
| 585 |
+
if WINDOW_SIZE_LEFT < 0:
|
| 586 |
+
left_min = 0
|
| 587 |
+
left_max = 0
|
| 588 |
+
else:
|
| 589 |
+
left_min = tl.maximum(0, q_start + diag - WINDOW_SIZE_LEFT)
|
| 590 |
+
left_max = tl.maximum(0, q_end + diag - WINDOW_SIZE_LEFT)
|
| 591 |
+
|
| 592 |
+
# Right boundary
|
| 593 |
+
if IS_CAUSAL:
|
| 594 |
+
# Causal cap: col <= row + diag
|
| 595 |
+
right_min = tl.minimum(seqlen_k - 1, q_start + diag)
|
| 596 |
+
right_max = tl.minimum(seqlen_k - 1, q_end + diag)
|
| 597 |
+
else:
|
| 598 |
+
if WINDOW_SIZE_RIGHT < 0:
|
| 599 |
+
right_min = tl.minimum(seqlen_k - 1, q_start + diag + WINDOW_SIZE_RIGHT)
|
| 600 |
+
right_max = tl.minimum(seqlen_k - 1, q_end + diag + WINDOW_SIZE_RIGHT)
|
| 601 |
+
else:
|
| 602 |
+
# Non-causal doesn't have the diagonal constraint
|
| 603 |
+
right_min = tl.minimum(seqlen_k - 1, q_start + diag + WINDOW_SIZE_RIGHT)
|
| 604 |
+
right_max = tl.minimum(seqlen_k - 1, q_end + diag + WINDOW_SIZE_RIGHT)
|
| 605 |
+
|
| 606 |
+
return left_min, left_max, right_min, right_max
|
| 607 |
+
|
| 608 |
+
|
| 609 |
+
@triton.jit
|
| 610 |
+
def classify_window_blocks(
|
| 611 |
+
left_min, left_max, right_min, right_max, BLOCK_N: tl.constexpr
|
| 612 |
+
):
|
| 613 |
+
"""Classify blocks based on window boundaries."""
|
| 614 |
+
# First and last blocks that have ANY overlap with window
|
| 615 |
+
first_block = left_min // BLOCK_N
|
| 616 |
+
last_block = right_max // BLOCK_N
|
| 617 |
+
|
| 618 |
+
# First block that is FULLY visible for all rows in Q block
|
| 619 |
+
full_left_block = left_max // BLOCK_N + (left_max % BLOCK_N != 0)
|
| 620 |
+
clipped_left = tl.minimum(full_left_block, last_block + 1)
|
| 621 |
+
|
| 622 |
+
# Last block that is FULLY visible for all rows in Q block
|
| 623 |
+
last_full_block_candidate = right_min // BLOCK_N
|
| 624 |
+
if (last_full_block_candidate + 1) * BLOCK_N - 1 > right_min:
|
| 625 |
+
last_full_block_candidate -= 1
|
| 626 |
+
full_right_block = tl.maximum(last_full_block_candidate, clipped_left - 1)
|
| 627 |
+
|
| 628 |
+
# Calculate counts
|
| 629 |
+
n_front_skip_blocks = first_block
|
| 630 |
+
n_front_masked_blocks = tl.maximum(0, clipped_left - first_block)
|
| 631 |
+
n_full_blocks = tl.maximum(0, full_right_block - clipped_left + 1)
|
| 632 |
+
n_back_masked_blocks = tl.maximum(0, last_block - full_right_block)
|
| 633 |
+
|
| 634 |
+
return (
|
| 635 |
+
n_front_skip_blocks,
|
| 636 |
+
n_front_masked_blocks,
|
| 637 |
+
n_full_blocks,
|
| 638 |
+
n_back_masked_blocks,
|
| 639 |
+
clipped_left,
|
| 640 |
+
) # Return clipped_left for padded block handling
|
| 641 |
+
|
| 642 |
+
|
| 643 |
+
@triton.jit
|
| 644 |
+
def handle_padded_last_block(
|
| 645 |
+
n_extra_tokens,
|
| 646 |
+
last_block,
|
| 647 |
+
total_k_blocks,
|
| 648 |
+
clipped_left,
|
| 649 |
+
n_front_masked_blocks,
|
| 650 |
+
n_full_blocks,
|
| 651 |
+
n_back_masked_blocks,
|
| 652 |
+
):
|
| 653 |
+
"""Ensure a padded last K-block is never classified as 'full'.
|
| 654 |
+
|
| 655 |
+
We move the padded last block (if visible) into the back-masked bucket.
|
| 656 |
+
If it's already back-masked, we do nothing. If it was counted in the
|
| 657 |
+
front-masked range, we decrement front-masked; if it was counted as full,
|
| 658 |
+
we decrement full. Then we increment back-masked.
|
| 659 |
+
"""
|
| 660 |
+
padded_last_k = (n_extra_tokens != 0) & (last_block == total_k_blocks - 1)
|
| 661 |
+
|
| 662 |
+
if padded_last_k:
|
| 663 |
+
# current 'full' range right edge
|
| 664 |
+
full_right_block = clipped_left + n_full_blocks - 1
|
| 665 |
+
|
| 666 |
+
# If last_block is already beyond full_right_block, it's already in back-masked -> nothing to do
|
| 667 |
+
last_already_back_masked = last_block > full_right_block
|
| 668 |
+
if not last_already_back_masked:
|
| 669 |
+
# If the window starts past last_block, it was counted in front-masked
|
| 670 |
+
if clipped_left > last_block:
|
| 671 |
+
n_front_masked_blocks = tl.maximum(0, n_front_masked_blocks - 1)
|
| 672 |
+
else:
|
| 673 |
+
# Otherwise it was counted 'full' -> move it out of full
|
| 674 |
+
n_full_blocks = tl.maximum(0, n_full_blocks - 1)
|
| 675 |
+
# In both cases we need one more back-masked block
|
| 676 |
+
n_back_masked_blocks = n_back_masked_blocks + 1
|
| 677 |
+
|
| 678 |
+
return n_front_masked_blocks, n_full_blocks, n_back_masked_blocks
|
| 679 |
+
|
| 680 |
+
|
| 681 |
+
@triton.jit
|
| 682 |
+
def compute_padding_info(seqlen_k, BLOCK_N: tl.constexpr):
|
| 683 |
+
"""Calculate padding information for the last K block."""
|
| 684 |
+
# check if we will need to do masking due either BLOCK_N being bigger than seqlen_k or seqlen_k not being a factor of BLOCK_N
|
| 685 |
+
# n_extra_tokens = 10 % 4 = 2
|
| 686 |
+
# This means the last K block has 2 valid tokens and 2 padding positions
|
| 687 |
+
# K blocks visualization:
|
| 688 |
+
# Block 0 Block 1 Block 2 (last)
|
| 689 |
+
# K0 K1 K2 K3 K4 K5 K6 K7 K8 K9 ?? ??
|
| 690 |
+
# ?---------? ?---------? ?---? ?---?
|
| 691 |
+
# full block full block valid pad
|
| 692 |
+
if seqlen_k < BLOCK_N:
|
| 693 |
+
n_extra_tokens = BLOCK_N - seqlen_k
|
| 694 |
+
elif seqlen_k % BLOCK_N:
|
| 695 |
+
n_extra_tokens = seqlen_k % BLOCK_N
|
| 696 |
+
else:
|
| 697 |
+
n_extra_tokens = 0
|
| 698 |
+
return n_extra_tokens
|
| 699 |
+
|
| 700 |
+
|
| 701 |
+
@triton.jit
|
| 702 |
+
def compute_block_masking(
|
| 703 |
+
seqlen_k,
|
| 704 |
+
seqlen_q,
|
| 705 |
+
start_m,
|
| 706 |
+
IS_CAUSAL: tl.constexpr,
|
| 707 |
+
USE_SLIDING_WINDOW: tl.constexpr,
|
| 708 |
+
WINDOW_SIZE_LEFT: tl.constexpr,
|
| 709 |
+
WINDOW_SIZE_RIGHT: tl.constexpr,
|
| 710 |
+
BLOCK_M: tl.constexpr,
|
| 711 |
+
BLOCK_N: tl.constexpr,
|
| 712 |
+
):
|
| 713 |
+
"""
|
| 714 |
+
Classify K blocks for attention computation with sliding window support.
|
| 715 |
+
|
| 716 |
+
Returns:
|
| 717 |
+
- n_front_skip_blocks: Blocks completely before the window
|
| 718 |
+
- n_front_masked_blocks: Blocks partially overlapping window front
|
| 719 |
+
- n_full_blocks: Blocks completely inside the window
|
| 720 |
+
- n_back_masked_blocks: Blocks partially overlapping window back
|
| 721 |
+
- n_extra_tokens: Padding tokens in last K block
|
| 722 |
+
"""
|
| 723 |
+
|
| 724 |
+
# common
|
| 725 |
+
q_start = start_m * BLOCK_M
|
| 726 |
+
q_end = tl.minimum((start_m + 1) * BLOCK_M - 1, seqlen_q - 1)
|
| 727 |
+
diag = seqlen_k - seqlen_q
|
| 728 |
+
total_k_blocks = tl.cdiv(seqlen_k, BLOCK_N)
|
| 729 |
+
n_extra_tokens = compute_padding_info(seqlen_k, BLOCK_N)
|
| 730 |
+
|
| 731 |
+
if USE_SLIDING_WINDOW:
|
| 732 |
+
# get window bounds
|
| 733 |
+
left_min, left_max, right_min, right_max = compute_window_bounds(
|
| 734 |
+
q_start,
|
| 735 |
+
q_end,
|
| 736 |
+
diag,
|
| 737 |
+
seqlen_k,
|
| 738 |
+
WINDOW_SIZE_LEFT,
|
| 739 |
+
WINDOW_SIZE_RIGHT,
|
| 740 |
+
IS_CAUSAL,
|
| 741 |
+
)
|
| 742 |
+
|
| 743 |
+
# window vanishes -> early exit
|
| 744 |
+
if right_max < left_min:
|
| 745 |
+
return 0, 0, 0, 0, n_extra_tokens
|
| 746 |
+
|
| 747 |
+
# classify blocks
|
| 748 |
+
(
|
| 749 |
+
n_front_skip_blocks,
|
| 750 |
+
n_front_masked_blocks,
|
| 751 |
+
n_full_blocks,
|
| 752 |
+
n_back_masked_blocks,
|
| 753 |
+
clipped_left,
|
| 754 |
+
) = classify_window_blocks(left_min, left_max, right_min, right_max, BLOCK_N)
|
| 755 |
+
|
| 756 |
+
# handle padded last block if needed
|
| 757 |
+
if n_extra_tokens != 0:
|
| 758 |
+
last_block = right_max // BLOCK_N
|
| 759 |
+
n_front_masked_blocks, n_full_blocks, n_back_masked_blocks = (
|
| 760 |
+
handle_padded_last_block(
|
| 761 |
+
n_extra_tokens,
|
| 762 |
+
last_block,
|
| 763 |
+
total_k_blocks,
|
| 764 |
+
clipped_left,
|
| 765 |
+
n_front_masked_blocks,
|
| 766 |
+
n_full_blocks,
|
| 767 |
+
n_back_masked_blocks,
|
| 768 |
+
)
|
| 769 |
+
)
|
| 770 |
+
return (
|
| 771 |
+
n_front_skip_blocks,
|
| 772 |
+
n_front_masked_blocks,
|
| 773 |
+
n_full_blocks,
|
| 774 |
+
n_back_masked_blocks,
|
| 775 |
+
n_extra_tokens,
|
| 776 |
+
)
|
| 777 |
+
else:
|
| 778 |
+
if IS_CAUSAL:
|
| 779 |
+
# ========== CAUSAL MODE: Classify K Blocks ==========
|
| 780 |
+
# Calculate causal boundary for this Q block
|
| 781 |
+
# [K0 K1 K2 K3] [K4 K5 K6 K7] [K8 K9 ?? ??]
|
| 782 |
+
# Q0-Q3: [ 1 0 0 0] [ 0 0 0 0] [ 0 0 -- --] <- Q0
|
| 783 |
+
# [ 1 1 0 0] [ 0 0 0 0] [ 0 0 -- --] <- Q1
|
| 784 |
+
# [ 1 1 1 0] [ 0 0 0 0] [ 0 0 -- --] <- Q2
|
| 785 |
+
# [ 1 1 1 1] [ 1 1 0 0] [ 0 0 -- --] <- Q3
|
| 786 |
+
# /\ can see up to K5
|
| 787 |
+
#
|
| 788 |
+
# Q4-Q7: [ 1 1 1 1] [ 1 1 1 0] [ 0 0 -- --] <- Q4
|
| 789 |
+
# [ 1 1 1 1] [ 1 1 1 1] [ 0 0 -- --] <- Q5
|
| 790 |
+
# [ 1 1 1 1] [ 1 1 1 1] [ 1 0 -- --] <- Q6
|
| 791 |
+
# [ 1 1 1 1] [ 1 1 1 1] [ 1 1 -- --] <- Q7
|
| 792 |
+
|
| 793 |
+
# ------------------------------------------------------------
|
| 794 |
+
# 1. figure out, in tokens, the right-most K position
|
| 795 |
+
# this Q-block may attend to
|
| 796 |
+
# ------------------------------------------------------------
|
| 797 |
+
k_max_token = q_end + diag # last visible K index
|
| 798 |
+
|
| 799 |
+
# this Q-block is entirely above the diagonal => nothing to do
|
| 800 |
+
if k_max_token < 0:
|
| 801 |
+
return 0, 0, 0, 0, n_extra_tokens
|
| 802 |
+
|
| 803 |
+
k_max_token = tl.minimum(k_max_token, seqlen_k - 1)
|
| 804 |
+
|
| 805 |
+
# ------------------------------------------------------------
|
| 806 |
+
# 2. translate token indices into K-block indices
|
| 807 |
+
# ------------------------------------------------------------
|
| 808 |
+
last_visible_k_block = k_max_token // BLOCK_N
|
| 809 |
+
n_visible_k_blocks = tl.minimum(last_visible_k_block + 1, total_k_blocks)
|
| 810 |
+
|
| 811 |
+
# ------------------------------------------------------------
|
| 812 |
+
# 3. classify those visible blocks
|
| 813 |
+
# - we *never* skip or mask blocks in front, because causal
|
| 814 |
+
# attention always starts at K0
|
| 815 |
+
# - the back side can require several masked blocks:
|
| 816 |
+
# o intersection of the causal diagonal with K-grid
|
| 817 |
+
# (at most ?BLOCK_M / BLOCK_N? blocks)
|
| 818 |
+
# o plus one for partial K blocks at the causal boundary
|
| 819 |
+
# ------------------------------------------------------------
|
| 820 |
+
n_back_masked_blocks = BLOCK_M // BLOCK_N + 1
|
| 821 |
+
n_back_masked_blocks = tl.minimum(n_back_masked_blocks, n_visible_k_blocks)
|
| 822 |
+
|
| 823 |
+
n_front_skip_blocks = 0 # causal never skips the left side
|
| 824 |
+
n_front_masked_blocks = 0 # ditto
|
| 825 |
+
n_full_blocks = n_visible_k_blocks - n_back_masked_blocks
|
| 826 |
+
else:
|
| 827 |
+
# ========== NON-CAUSAL MODE ==========
|
| 828 |
+
# Without causal mask, all positions can attend to all positions
|
| 829 |
+
# Only need to handle the padding in the last block
|
| 830 |
+
# [K0 K1 K2 K3] [K4 K5 K6 K7] [K8 K9 ?? ??]
|
| 831 |
+
# Q0-Q3: [ 1 1 1 1] [ 1 1 1 1] [ 1 1 -inf -inf]
|
| 832 |
+
# [ 1 1 1 1] [ 1 1 1 1] [ 1 1 -inf -inf]
|
| 833 |
+
# [ 1 1 1 1] [ 1 1 1 1] [ 1 1 -inf -inf]
|
| 834 |
+
# [ 1 1 1 1] [ 1 1 1 1] [ 1 1 -inf -inf]
|
| 835 |
+
#
|
| 836 |
+
# Q4-Q7: [ 1 1 1 1] [ 1 1 1 1] [ 1 1 -inf -inf]
|
| 837 |
+
# [ 1 1 1 1] [ 1 1 1 1] [ 1 1 -inf -inf]
|
| 838 |
+
# [ 1 1 1 1] [ 1 1 1 1] [ 1 1 -inf -inf]
|
| 839 |
+
# [ 1 1 1 1] [ 1 1 1 1] [ 1 1 -inf -inf]
|
| 840 |
+
|
| 841 |
+
n_front_skip_blocks = 0 # never skips the left side
|
| 842 |
+
n_front_masked_blocks = 0 # ditto
|
| 843 |
+
if n_extra_tokens != 0:
|
| 844 |
+
n_back_masked_blocks = 1 # Last block needs padding mask
|
| 845 |
+
n_full_blocks = total_k_blocks - 1
|
| 846 |
+
else:
|
| 847 |
+
n_back_masked_blocks = 0 # All blocks are aligned
|
| 848 |
+
n_full_blocks = total_k_blocks
|
| 849 |
+
|
| 850 |
+
return (
|
| 851 |
+
n_front_skip_blocks,
|
| 852 |
+
n_front_masked_blocks,
|
| 853 |
+
n_full_blocks,
|
| 854 |
+
n_back_masked_blocks,
|
| 855 |
+
n_extra_tokens,
|
| 856 |
+
)
|
| 857 |
+
|
| 858 |
+
|
| 859 |
+
@triton.autotune(
|
| 860 |
+
configs=fwd_prefill_autotune_configs,
|
| 861 |
+
key=FWD_PREFILL_AUTOTUNE_KEYS,
|
| 862 |
+
use_cuda_graph=True,
|
| 863 |
+
)
|
| 864 |
+
@triton.jit
|
| 865 |
+
def attn_fwd(
|
| 866 |
+
Q,
|
| 867 |
+
K,
|
| 868 |
+
V,
|
| 869 |
+
bias,
|
| 870 |
+
Q_Descale,
|
| 871 |
+
K_Descale,
|
| 872 |
+
V_Descale,
|
| 873 |
+
stride_q_descale_z,
|
| 874 |
+
stride_k_descale_z,
|
| 875 |
+
stride_v_descale_z,
|
| 876 |
+
LSE,
|
| 877 |
+
Out,
|
| 878 |
+
SD_MASK,
|
| 879 |
+
ALIBI_SLOPES,
|
| 880 |
+
stride_qz,
|
| 881 |
+
stride_qh,
|
| 882 |
+
stride_qm,
|
| 883 |
+
stride_qk,
|
| 884 |
+
stride_kz,
|
| 885 |
+
stride_kh,
|
| 886 |
+
stride_kn,
|
| 887 |
+
stride_kk,
|
| 888 |
+
stride_vz,
|
| 889 |
+
stride_vh,
|
| 890 |
+
stride_vk,
|
| 891 |
+
stride_vn,
|
| 892 |
+
stride_oz,
|
| 893 |
+
stride_oh,
|
| 894 |
+
stride_om,
|
| 895 |
+
stride_on,
|
| 896 |
+
stride_bz,
|
| 897 |
+
stride_bh,
|
| 898 |
+
stride_bm,
|
| 899 |
+
stride_bn,
|
| 900 |
+
stride_az,
|
| 901 |
+
stride_ah,
|
| 902 |
+
stride_sz,
|
| 903 |
+
stride_sh,
|
| 904 |
+
stride_sm,
|
| 905 |
+
stride_sn,
|
| 906 |
+
stride_lse_z,
|
| 907 |
+
stride_lse_h,
|
| 908 |
+
stride_lse_m,
|
| 909 |
+
cu_seqlens_q,
|
| 910 |
+
cu_seqlens_k,
|
| 911 |
+
seqused_q,
|
| 912 |
+
seqused_k, # Add seqused parameters
|
| 913 |
+
dropout_p,
|
| 914 |
+
philox_seed,
|
| 915 |
+
philox_offset_base,
|
| 916 |
+
HQ: tl.constexpr,
|
| 917 |
+
HK: tl.constexpr,
|
| 918 |
+
ACTUAL_BLOCK_DMODEL_QK: tl.constexpr,
|
| 919 |
+
ACTUAL_BLOCK_DMODEL_V: tl.constexpr,
|
| 920 |
+
MAX_SEQLENS_Q: tl.constexpr,
|
| 921 |
+
MAX_SEQLENS_K: tl.constexpr,
|
| 922 |
+
IS_VARLEN: tl.constexpr,
|
| 923 |
+
SM_SCALE: tl.constexpr,
|
| 924 |
+
IS_CAUSAL: tl.constexpr,
|
| 925 |
+
USE_SLIDING_WINDOW: tl.constexpr,
|
| 926 |
+
WINDOW_SIZE_LEFT: tl.constexpr,
|
| 927 |
+
WINDOW_SIZE_RIGHT: tl.constexpr,
|
| 928 |
+
BLOCK_M: tl.constexpr,
|
| 929 |
+
BLOCK_DMODEL_QK: tl.constexpr,
|
| 930 |
+
BLOCK_DMODEL_V: tl.constexpr,
|
| 931 |
+
BLOCK_N: tl.constexpr,
|
| 932 |
+
PRE_LOAD_V: tl.constexpr,
|
| 933 |
+
USE_BIAS: tl.constexpr,
|
| 934 |
+
ENABLE_DROPOUT: tl.constexpr,
|
| 935 |
+
RETURN_SCORES: tl.constexpr,
|
| 936 |
+
USE_ALIBI: tl.constexpr,
|
| 937 |
+
USE_EXP2: tl.constexpr,
|
| 938 |
+
IS_FP8: tl.constexpr,
|
| 939 |
+
FP8_MAX: tl.constexpr,
|
| 940 |
+
FP8_P_DESCALE: tl.constexpr,
|
| 941 |
+
USE_SEQUSED: tl.constexpr,
|
| 942 |
+
FORCE_MASKING: tl.constexpr,
|
| 943 |
+
NUM_XCD: tl.constexpr = 1,
|
| 944 |
+
):
|
| 945 |
+
# set params
|
| 946 |
+
ACCUMULATOR_TYPE = tl.float32
|
| 947 |
+
|
| 948 |
+
# compute offsets
|
| 949 |
+
off_h_q = tl.program_id(0)
|
| 950 |
+
# apply the xcd remapping for the hq dim
|
| 951 |
+
off_h_q = remap_xcd(off_h_q, HQ, NUM_XCD)
|
| 952 |
+
|
| 953 |
+
start_m = tl.program_id(1)
|
| 954 |
+
off_z = tl.program_id(2)
|
| 955 |
+
# If MQA / GQA, set the K and V head offsets appropriately.
|
| 956 |
+
GROUP_SIZE: tl.constexpr = HQ // HK
|
| 957 |
+
if GROUP_SIZE != 1:
|
| 958 |
+
off_h_k = off_h_q // GROUP_SIZE
|
| 959 |
+
else:
|
| 960 |
+
off_h_k = off_h_q
|
| 961 |
+
# Determine if we need to mask the heads
|
| 962 |
+
PADDED_HEAD_QK: tl.constexpr = ACTUAL_BLOCK_DMODEL_QK != BLOCK_DMODEL_QK
|
| 963 |
+
PADDED_HEAD_V: tl.constexpr = ACTUAL_BLOCK_DMODEL_V != BLOCK_DMODEL_V
|
| 964 |
+
|
| 965 |
+
offs_m = start_m * BLOCK_M + tl.arange(0, BLOCK_M)
|
| 966 |
+
offs_n = tl.arange(0, BLOCK_N)
|
| 967 |
+
offs_d_qk = tl.arange(0, BLOCK_DMODEL_QK)
|
| 968 |
+
offs_d_v = tl.arange(0, BLOCK_DMODEL_V)
|
| 969 |
+
|
| 970 |
+
# handle seqlen
|
| 971 |
+
if IS_VARLEN:
|
| 972 |
+
cu_seqlens_q_start = tl.load(cu_seqlens_q + off_z)
|
| 973 |
+
cu_seqlens_q_end = tl.load(cu_seqlens_q + off_z + 1)
|
| 974 |
+
|
| 975 |
+
# If seqused is provided, use it to limit the actual sequence length
|
| 976 |
+
if USE_SEQUSED:
|
| 977 |
+
actual_seqlen_q = (
|
| 978 |
+
tl.load(seqused_q + off_z)
|
| 979 |
+
if seqused_q is not None
|
| 980 |
+
else cu_seqlens_q_end - cu_seqlens_q_start
|
| 981 |
+
)
|
| 982 |
+
seqlen_q = tl.minimum(
|
| 983 |
+
actual_seqlen_q, cu_seqlens_q_end - cu_seqlens_q_start
|
| 984 |
+
)
|
| 985 |
+
else:
|
| 986 |
+
seqlen_q = cu_seqlens_q_end - cu_seqlens_q_start
|
| 987 |
+
|
| 988 |
+
# we have a one-size-fits-all grid in id(0). Some seqlens might be too small for all start_m so for those we return early.
|
| 989 |
+
if start_m * BLOCK_M > seqlen_q:
|
| 990 |
+
return
|
| 991 |
+
cu_seqlens_k_start = tl.load(cu_seqlens_k + off_z)
|
| 992 |
+
cu_seqlens_k_end = tl.load(cu_seqlens_k + off_z + 1)
|
| 993 |
+
|
| 994 |
+
# If seqused is provided, use it to limit the actual sequence length for keys
|
| 995 |
+
if USE_SEQUSED:
|
| 996 |
+
actual_seqlen_k = (
|
| 997 |
+
tl.load(seqused_k + off_z)
|
| 998 |
+
if seqused_k is not None
|
| 999 |
+
else cu_seqlens_k_end - cu_seqlens_k_start
|
| 1000 |
+
)
|
| 1001 |
+
seqlen_k = tl.minimum(
|
| 1002 |
+
actual_seqlen_k, cu_seqlens_k_end - cu_seqlens_k_start
|
| 1003 |
+
)
|
| 1004 |
+
else:
|
| 1005 |
+
seqlen_k = cu_seqlens_k_end - cu_seqlens_k_start
|
| 1006 |
+
else:
|
| 1007 |
+
cu_seqlens_q_start = 0
|
| 1008 |
+
cu_seqlens_k_start = 0
|
| 1009 |
+
seqlen_q = MAX_SEQLENS_Q
|
| 1010 |
+
seqlen_k = MAX_SEQLENS_K
|
| 1011 |
+
|
| 1012 |
+
# Load scale factors if IS_FP8.
|
| 1013 |
+
if IS_FP8:
|
| 1014 |
+
# For MQA/GQA (GROUP_SIZE != 1), q_descale uses the same indexing as k/v (off_h_k)
|
| 1015 |
+
# For MHA (GROUP_SIZE == 1), q_descale uses off_h_q (same as off_h_k)
|
| 1016 |
+
if GROUP_SIZE != 1:
|
| 1017 |
+
q_descale = tl.load(
|
| 1018 |
+
Q_Descale + off_z * stride_q_descale_z + off_h_k
|
| 1019 |
+
) # MQA/GQA: broadcast using k/v head index
|
| 1020 |
+
else:
|
| 1021 |
+
q_descale = tl.load(
|
| 1022 |
+
Q_Descale + off_z * stride_q_descale_z + off_h_q
|
| 1023 |
+
) # MHA: use q head index
|
| 1024 |
+
k_descale = tl.load(K_Descale + off_z * stride_k_descale_z + off_h_k)
|
| 1025 |
+
v_descale = tl.load(V_Descale + off_z * stride_v_descale_z + off_h_k)
|
| 1026 |
+
else:
|
| 1027 |
+
q_descale, k_descale, v_descale = 1.0, 1.0, 1.0
|
| 1028 |
+
|
| 1029 |
+
# figure out masking pattern
|
| 1030 |
+
(
|
| 1031 |
+
n_front_skip_blocks,
|
| 1032 |
+
n_front_masked_blocks,
|
| 1033 |
+
n_full_blocks,
|
| 1034 |
+
n_back_masked_blocks,
|
| 1035 |
+
n_extra_tokens,
|
| 1036 |
+
) = compute_block_masking(
|
| 1037 |
+
seqlen_k,
|
| 1038 |
+
seqlen_q,
|
| 1039 |
+
start_m,
|
| 1040 |
+
IS_CAUSAL,
|
| 1041 |
+
USE_SLIDING_WINDOW,
|
| 1042 |
+
WINDOW_SIZE_LEFT,
|
| 1043 |
+
WINDOW_SIZE_RIGHT,
|
| 1044 |
+
BLOCK_M,
|
| 1045 |
+
BLOCK_N,
|
| 1046 |
+
)
|
| 1047 |
+
|
| 1048 |
+
# ============================================================
|
| 1049 |
+
# PROGRAM EARLY EXIT (All K Blocks Skipped)
|
| 1050 |
+
# ============================================================
|
| 1051 |
+
total_visible_blocks = n_front_masked_blocks + n_full_blocks + n_back_masked_blocks
|
| 1052 |
+
if total_visible_blocks == 0:
|
| 1053 |
+
"""
|
| 1054 |
+
No K blocks visible - write zeros and exit.
|
| 1055 |
+
"""
|
| 1056 |
+
# Write zeros to output
|
| 1057 |
+
o_offset = (
|
| 1058 |
+
Out
|
| 1059 |
+
+ off_z * stride_oz
|
| 1060 |
+
+ off_h_q * stride_oh
|
| 1061 |
+
+ cu_seqlens_q_start * stride_om
|
| 1062 |
+
)
|
| 1063 |
+
o_ptrs = o_offset + offs_m[:, None] * stride_om + offs_d_v[None, :] * stride_on
|
| 1064 |
+
o_mask = offs_m[:, None] < seqlen_q
|
| 1065 |
+
if PADDED_HEAD_V:
|
| 1066 |
+
o_mask = o_mask & (offs_d_v[None, :] < ACTUAL_BLOCK_DMODEL_V)
|
| 1067 |
+
tl.store(
|
| 1068 |
+
o_ptrs,
|
| 1069 |
+
tl.zeros([BLOCK_M, BLOCK_DMODEL_V], dtype=Out.type.element_ty),
|
| 1070 |
+
mask=o_mask,
|
| 1071 |
+
)
|
| 1072 |
+
|
| 1073 |
+
# Write zeros to LSE
|
| 1074 |
+
l_ptrs = (
|
| 1075 |
+
LSE
|
| 1076 |
+
+ off_z * stride_lse_z
|
| 1077 |
+
+ off_h_q * stride_lse_h
|
| 1078 |
+
+ cu_seqlens_q_start * stride_lse_m
|
| 1079 |
+
+ offs_m * stride_lse_m
|
| 1080 |
+
)
|
| 1081 |
+
tl.store(l_ptrs, tl.zeros([BLOCK_M], dtype=tl.float32), mask=offs_m < seqlen_q)
|
| 1082 |
+
return
|
| 1083 |
+
|
| 1084 |
+
# ============================================================
|
| 1085 |
+
# NORMAL PROCESSING (Some K Blocks Visible)
|
| 1086 |
+
# ============================================================
|
| 1087 |
+
"""
|
| 1088 |
+
This program has visible K blocks to process.
|
| 1089 |
+
We'll use two calls to handle different block types efficiently.
|
| 1090 |
+
"""
|
| 1091 |
+
|
| 1092 |
+
# Initialize for processing
|
| 1093 |
+
# Compute pointers for all the tensors used in this kernel.
|
| 1094 |
+
q_offset = (
|
| 1095 |
+
Q + off_z * stride_qz + off_h_q * stride_qh + cu_seqlens_q_start * stride_qm
|
| 1096 |
+
)
|
| 1097 |
+
q_ptrs = q_offset + offs_m[:, None] * stride_qm + offs_d_qk[None, :] * stride_qk
|
| 1098 |
+
k_offset = (
|
| 1099 |
+
K + off_z * stride_kz + off_h_k * stride_kh + cu_seqlens_k_start * stride_kn
|
| 1100 |
+
)
|
| 1101 |
+
k_ptrs = k_offset + offs_d_qk[:, None] * stride_kk + offs_n[None, :] * stride_kn
|
| 1102 |
+
v_offset = (
|
| 1103 |
+
V + off_z * stride_vz + off_h_k * stride_vh + cu_seqlens_k_start * stride_vk
|
| 1104 |
+
)
|
| 1105 |
+
v_ptrs = v_offset + offs_n[:, None] * stride_vk + offs_d_v[None, :] * stride_vn
|
| 1106 |
+
if USE_BIAS:
|
| 1107 |
+
# Note: this might get large enough to overflow on some configs
|
| 1108 |
+
bias_offset = off_h_q * stride_bh
|
| 1109 |
+
bias_ptrs = (
|
| 1110 |
+
bias
|
| 1111 |
+
+ bias_offset
|
| 1112 |
+
+ offs_m[:, None] * stride_bm
|
| 1113 |
+
+ offs_n[None, :] * stride_bn
|
| 1114 |
+
)
|
| 1115 |
+
else:
|
| 1116 |
+
bias_ptrs = None
|
| 1117 |
+
|
| 1118 |
+
if USE_ALIBI:
|
| 1119 |
+
a_offset = off_z * stride_az + off_h_q * stride_ah
|
| 1120 |
+
alibi_slope = tl.load(ALIBI_SLOPES + a_offset)
|
| 1121 |
+
else:
|
| 1122 |
+
alibi_slope = None
|
| 1123 |
+
|
| 1124 |
+
# initialize pointer to m and l
|
| 1125 |
+
m_i = tl.full([BLOCK_M], float("-inf"), dtype=ACCUMULATOR_TYPE)
|
| 1126 |
+
l_i = tl.full([BLOCK_M], 1.0, dtype=ACCUMULATOR_TYPE)
|
| 1127 |
+
acc = tl.zeros([BLOCK_M, BLOCK_DMODEL_V], dtype=ACCUMULATOR_TYPE)
|
| 1128 |
+
|
| 1129 |
+
# Q is loaded once at the beginning and shared by all N blocks.
|
| 1130 |
+
q_ptrs_mask = offs_m[:, None] < seqlen_q
|
| 1131 |
+
if PADDED_HEAD_QK:
|
| 1132 |
+
q_ptrs_mask = q_ptrs_mask & (offs_d_qk[None, :] < ACTUAL_BLOCK_DMODEL_QK)
|
| 1133 |
+
q = tl.load(q_ptrs, mask=q_ptrs_mask, other=0.0)
|
| 1134 |
+
|
| 1135 |
+
# ========== Process MASKED K Blocks in the front ==========
|
| 1136 |
+
# NOTE: we use USE_SLIDING_WINDOW as guard because the compiler will crash other wise. front masking is only for sliding window so that is fine.
|
| 1137 |
+
if n_front_masked_blocks > 0 and USE_SLIDING_WINDOW:
|
| 1138 |
+
block_min = n_front_skip_blocks * BLOCK_N
|
| 1139 |
+
block_max = (n_front_skip_blocks + n_front_masked_blocks) * BLOCK_N
|
| 1140 |
+
|
| 1141 |
+
acc, l_i, m_i = _attn_fwd_inner(
|
| 1142 |
+
acc,
|
| 1143 |
+
l_i,
|
| 1144 |
+
m_i,
|
| 1145 |
+
q,
|
| 1146 |
+
k_ptrs,
|
| 1147 |
+
v_ptrs,
|
| 1148 |
+
bias_ptrs,
|
| 1149 |
+
stride_kn,
|
| 1150 |
+
stride_vk,
|
| 1151 |
+
stride_bn,
|
| 1152 |
+
stride_sn,
|
| 1153 |
+
stride_sm,
|
| 1154 |
+
start_m,
|
| 1155 |
+
seqlen_k,
|
| 1156 |
+
seqlen_q,
|
| 1157 |
+
dropout_p,
|
| 1158 |
+
philox_seed,
|
| 1159 |
+
philox_offset_base,
|
| 1160 |
+
SD_MASK,
|
| 1161 |
+
stride_sz,
|
| 1162 |
+
stride_sh,
|
| 1163 |
+
off_z,
|
| 1164 |
+
off_h_q,
|
| 1165 |
+
offs_m,
|
| 1166 |
+
offs_n,
|
| 1167 |
+
offs_d_qk,
|
| 1168 |
+
offs_d_v,
|
| 1169 |
+
block_min, # Start of front masked blocks
|
| 1170 |
+
block_max, # End of front masked blocks
|
| 1171 |
+
0, # n_extra_tokens (0 for front blocks, only relevant for last block)
|
| 1172 |
+
alibi_slope,
|
| 1173 |
+
q_descale,
|
| 1174 |
+
k_descale,
|
| 1175 |
+
v_descale,
|
| 1176 |
+
IS_FP8,
|
| 1177 |
+
FP8_MAX,
|
| 1178 |
+
FP8_P_DESCALE,
|
| 1179 |
+
APPLY_MASK=True, # Masked blocks
|
| 1180 |
+
IS_CAUSAL=IS_CAUSAL,
|
| 1181 |
+
BLOCK_M=BLOCK_M,
|
| 1182 |
+
BLOCK_DMODEL_QK=BLOCK_DMODEL_QK,
|
| 1183 |
+
BLOCK_DMODEL_V=BLOCK_DMODEL_V,
|
| 1184 |
+
BLOCK_N=BLOCK_N,
|
| 1185 |
+
PRE_LOAD_V=PRE_LOAD_V,
|
| 1186 |
+
ENABLE_DROPOUT=ENABLE_DROPOUT,
|
| 1187 |
+
PADDED_HEAD_QK=PADDED_HEAD_QK,
|
| 1188 |
+
PADDED_HEAD_V=PADDED_HEAD_V,
|
| 1189 |
+
ACTUAL_BLOCK_DMODEL_QK=ACTUAL_BLOCK_DMODEL_QK,
|
| 1190 |
+
ACTUAL_BLOCK_DMODEL_V=ACTUAL_BLOCK_DMODEL_V,
|
| 1191 |
+
SM_SCALE=SM_SCALE,
|
| 1192 |
+
USE_ALIBI=USE_ALIBI,
|
| 1193 |
+
USE_EXP2=USE_EXP2,
|
| 1194 |
+
RETURN_SCORES=RETURN_SCORES,
|
| 1195 |
+
USE_SLIDING_WINDOW=USE_SLIDING_WINDOW,
|
| 1196 |
+
WINDOW_SIZE_LEFT=WINDOW_SIZE_LEFT,
|
| 1197 |
+
WINDOW_SIZE_RIGHT=WINDOW_SIZE_RIGHT,
|
| 1198 |
+
ACCUMULATOR_TYPE=ACCUMULATOR_TYPE,
|
| 1199 |
+
)
|
| 1200 |
+
|
| 1201 |
+
# ========== Process FULL K Blocks (Fast Path) ==========
|
| 1202 |
+
if n_full_blocks > 0:
|
| 1203 |
+
block_min = (n_front_skip_blocks + n_front_masked_blocks) * BLOCK_N
|
| 1204 |
+
block_max = (
|
| 1205 |
+
n_front_skip_blocks + n_front_masked_blocks + n_full_blocks
|
| 1206 |
+
) * BLOCK_N
|
| 1207 |
+
|
| 1208 |
+
acc, l_i, m_i = _attn_fwd_inner(
|
| 1209 |
+
acc,
|
| 1210 |
+
l_i,
|
| 1211 |
+
m_i,
|
| 1212 |
+
q,
|
| 1213 |
+
k_ptrs,
|
| 1214 |
+
v_ptrs,
|
| 1215 |
+
bias_ptrs,
|
| 1216 |
+
stride_kn,
|
| 1217 |
+
stride_vk,
|
| 1218 |
+
stride_bn,
|
| 1219 |
+
stride_sn,
|
| 1220 |
+
stride_sm,
|
| 1221 |
+
start_m,
|
| 1222 |
+
seqlen_k,
|
| 1223 |
+
seqlen_q,
|
| 1224 |
+
dropout_p,
|
| 1225 |
+
philox_seed,
|
| 1226 |
+
philox_offset_base,
|
| 1227 |
+
SD_MASK,
|
| 1228 |
+
stride_sz,
|
| 1229 |
+
stride_sh,
|
| 1230 |
+
off_z,
|
| 1231 |
+
off_h_q,
|
| 1232 |
+
offs_m,
|
| 1233 |
+
offs_n,
|
| 1234 |
+
offs_d_qk,
|
| 1235 |
+
offs_d_v,
|
| 1236 |
+
block_min, # Start of range: 0
|
| 1237 |
+
block_max, # End of range: n_full_blocks * BLOCK_N
|
| 1238 |
+
0, # n_extra_tokens (not used for full blocks)
|
| 1239 |
+
alibi_slope,
|
| 1240 |
+
q_descale,
|
| 1241 |
+
k_descale,
|
| 1242 |
+
v_descale,
|
| 1243 |
+
IS_FP8,
|
| 1244 |
+
FP8_MAX,
|
| 1245 |
+
FP8_P_DESCALE,
|
| 1246 |
+
APPLY_MASK=FORCE_MASKING,
|
| 1247 |
+
IS_CAUSAL=IS_CAUSAL,
|
| 1248 |
+
BLOCK_M=BLOCK_M,
|
| 1249 |
+
BLOCK_DMODEL_QK=BLOCK_DMODEL_QK,
|
| 1250 |
+
BLOCK_DMODEL_V=BLOCK_DMODEL_V,
|
| 1251 |
+
BLOCK_N=BLOCK_N,
|
| 1252 |
+
PRE_LOAD_V=PRE_LOAD_V,
|
| 1253 |
+
ENABLE_DROPOUT=ENABLE_DROPOUT,
|
| 1254 |
+
PADDED_HEAD_QK=PADDED_HEAD_QK,
|
| 1255 |
+
PADDED_HEAD_V=PADDED_HEAD_V,
|
| 1256 |
+
ACTUAL_BLOCK_DMODEL_QK=ACTUAL_BLOCK_DMODEL_QK,
|
| 1257 |
+
ACTUAL_BLOCK_DMODEL_V=ACTUAL_BLOCK_DMODEL_V,
|
| 1258 |
+
SM_SCALE=SM_SCALE,
|
| 1259 |
+
USE_ALIBI=USE_ALIBI,
|
| 1260 |
+
USE_EXP2=USE_EXP2,
|
| 1261 |
+
RETURN_SCORES=RETURN_SCORES,
|
| 1262 |
+
USE_SLIDING_WINDOW=USE_SLIDING_WINDOW,
|
| 1263 |
+
WINDOW_SIZE_LEFT=WINDOW_SIZE_LEFT,
|
| 1264 |
+
WINDOW_SIZE_RIGHT=WINDOW_SIZE_RIGHT,
|
| 1265 |
+
ACCUMULATOR_TYPE=ACCUMULATOR_TYPE,
|
| 1266 |
+
)
|
| 1267 |
+
|
| 1268 |
+
# ========== Process MASKED K Blocks in the back ==========
|
| 1269 |
+
if n_back_masked_blocks > 0:
|
| 1270 |
+
block_min = (
|
| 1271 |
+
n_front_skip_blocks + n_front_masked_blocks + n_full_blocks
|
| 1272 |
+
) * BLOCK_N
|
| 1273 |
+
block_max = (
|
| 1274 |
+
n_front_skip_blocks
|
| 1275 |
+
+ n_front_masked_blocks
|
| 1276 |
+
+ n_full_blocks
|
| 1277 |
+
+ n_back_masked_blocks
|
| 1278 |
+
) * BLOCK_N
|
| 1279 |
+
|
| 1280 |
+
acc, l_i, m_i = _attn_fwd_inner(
|
| 1281 |
+
acc,
|
| 1282 |
+
l_i,
|
| 1283 |
+
m_i,
|
| 1284 |
+
q,
|
| 1285 |
+
k_ptrs,
|
| 1286 |
+
v_ptrs,
|
| 1287 |
+
bias_ptrs,
|
| 1288 |
+
stride_kn,
|
| 1289 |
+
stride_vk,
|
| 1290 |
+
stride_bn,
|
| 1291 |
+
stride_sn,
|
| 1292 |
+
stride_sm,
|
| 1293 |
+
start_m,
|
| 1294 |
+
seqlen_k,
|
| 1295 |
+
seqlen_q,
|
| 1296 |
+
dropout_p,
|
| 1297 |
+
philox_seed,
|
| 1298 |
+
philox_offset_base,
|
| 1299 |
+
SD_MASK,
|
| 1300 |
+
stride_sz,
|
| 1301 |
+
stride_sh,
|
| 1302 |
+
off_z,
|
| 1303 |
+
off_h_q,
|
| 1304 |
+
offs_m,
|
| 1305 |
+
offs_n,
|
| 1306 |
+
offs_d_qk,
|
| 1307 |
+
offs_d_v,
|
| 1308 |
+
block_min, # Start of range: n_full_blocks * BLOCK_N
|
| 1309 |
+
block_max, # End of range: n_visible_k_blocks * BLOCK_N
|
| 1310 |
+
n_extra_tokens, # Padding tokens in last block
|
| 1311 |
+
alibi_slope,
|
| 1312 |
+
q_descale,
|
| 1313 |
+
k_descale,
|
| 1314 |
+
v_descale,
|
| 1315 |
+
IS_FP8,
|
| 1316 |
+
FP8_MAX,
|
| 1317 |
+
FP8_P_DESCALE,
|
| 1318 |
+
APPLY_MASK=True, # Masked blocks
|
| 1319 |
+
IS_CAUSAL=IS_CAUSAL, # Use actual causal flag
|
| 1320 |
+
BLOCK_M=BLOCK_M,
|
| 1321 |
+
BLOCK_DMODEL_QK=BLOCK_DMODEL_QK,
|
| 1322 |
+
BLOCK_DMODEL_V=BLOCK_DMODEL_V,
|
| 1323 |
+
BLOCK_N=BLOCK_N,
|
| 1324 |
+
PRE_LOAD_V=PRE_LOAD_V,
|
| 1325 |
+
ENABLE_DROPOUT=ENABLE_DROPOUT,
|
| 1326 |
+
PADDED_HEAD_QK=PADDED_HEAD_QK,
|
| 1327 |
+
PADDED_HEAD_V=PADDED_HEAD_V,
|
| 1328 |
+
ACTUAL_BLOCK_DMODEL_QK=ACTUAL_BLOCK_DMODEL_QK,
|
| 1329 |
+
ACTUAL_BLOCK_DMODEL_V=ACTUAL_BLOCK_DMODEL_V,
|
| 1330 |
+
SM_SCALE=SM_SCALE,
|
| 1331 |
+
USE_ALIBI=USE_ALIBI,
|
| 1332 |
+
USE_EXP2=USE_EXP2,
|
| 1333 |
+
RETURN_SCORES=RETURN_SCORES,
|
| 1334 |
+
USE_SLIDING_WINDOW=USE_SLIDING_WINDOW,
|
| 1335 |
+
WINDOW_SIZE_LEFT=WINDOW_SIZE_LEFT,
|
| 1336 |
+
WINDOW_SIZE_RIGHT=WINDOW_SIZE_RIGHT,
|
| 1337 |
+
ACCUMULATOR_TYPE=ACCUMULATOR_TYPE,
|
| 1338 |
+
)
|
| 1339 |
+
|
| 1340 |
+
# ============================================================
|
| 1341 |
+
# EPILOGUE
|
| 1342 |
+
# ============================================================
|
| 1343 |
+
# Handle invalid rows: rows with no valid keys to attend to.
|
| 1344 |
+
# This occurs with sliding window or causal attention (when seqlen_q > seqlen_k).
|
| 1345 |
+
# For invalid rows: m_i = -inf, l_i = 0, acc = 0.
|
| 1346 |
+
# We set l_i = 1.0 to avoid division by zero and ensure LSE = -inf.
|
| 1347 |
+
invalid_mask = m_i == float("-inf")
|
| 1348 |
+
l_i_safe = tl.where(invalid_mask, 1.0, l_i)
|
| 1349 |
+
l_recip = 1 / l_i_safe[:, None]
|
| 1350 |
+
acc = acc * l_recip
|
| 1351 |
+
|
| 1352 |
+
if ENABLE_DROPOUT:
|
| 1353 |
+
dropout_scale = 1 / (1 - dropout_p)
|
| 1354 |
+
acc = acc * dropout_scale
|
| 1355 |
+
|
| 1356 |
+
# compute log-sum-exp
|
| 1357 |
+
if USE_EXP2:
|
| 1358 |
+
RCP_LN2: tl.constexpr = 1.4426950408889634
|
| 1359 |
+
LN2: tl.constexpr = 0.6931471824645996
|
| 1360 |
+
softmax_lse = (m_i * RCP_LN2 + tl.math.log2(l_i)) * LN2
|
| 1361 |
+
else:
|
| 1362 |
+
softmax_lse = m_i + tl.math.log(l_i)
|
| 1363 |
+
|
| 1364 |
+
# Ensure invalid rows have LSE = -inf
|
| 1365 |
+
softmax_lse = tl.where(invalid_mask, float("-inf"), softmax_lse)
|
| 1366 |
+
|
| 1367 |
+
# write back LSE(Log Sum Exponents), the log of the normalization constant
|
| 1368 |
+
l_offset = (
|
| 1369 |
+
LSE
|
| 1370 |
+
+ off_z * stride_lse_z
|
| 1371 |
+
+ off_h_q * stride_lse_h
|
| 1372 |
+
+ cu_seqlens_q_start * stride_lse_m
|
| 1373 |
+
)
|
| 1374 |
+
l_ptrs = l_offset + offs_m * stride_lse_m
|
| 1375 |
+
|
| 1376 |
+
# If seqlen_q not multiple of BLOCK_M, we need to mask out the last few rows.
|
| 1377 |
+
# This is only true for the last Q block. For others, overflow_size will be -ve
|
| 1378 |
+
end_m_idx = (start_m + 1) * BLOCK_M
|
| 1379 |
+
overflow_size = end_m_idx - seqlen_q
|
| 1380 |
+
if overflow_size > 0:
|
| 1381 |
+
boundary = tl.full((BLOCK_M,), BLOCK_M - overflow_size, dtype=tl.int32)
|
| 1382 |
+
l_ptrs_mask = tl.arange(0, BLOCK_M) < boundary
|
| 1383 |
+
tl.store(l_ptrs, softmax_lse, mask=l_ptrs_mask)
|
| 1384 |
+
else:
|
| 1385 |
+
tl.store(l_ptrs, softmax_lse)
|
| 1386 |
+
|
| 1387 |
+
# write back O
|
| 1388 |
+
o_offset = (
|
| 1389 |
+
Out + off_z * stride_oz + off_h_q * stride_oh + cu_seqlens_q_start * stride_om
|
| 1390 |
+
)
|
| 1391 |
+
o_ptrs = o_offset + offs_m[:, None] * stride_om + offs_d_v[None, :] * stride_on
|
| 1392 |
+
o_ptrs_mask = tl.full([BLOCK_M, BLOCK_DMODEL_V], 1, dtype=tl.int1)
|
| 1393 |
+
if overflow_size > 0:
|
| 1394 |
+
o_ptrs_mask = o_ptrs_mask & (offs_m[:, None] < seqlen_q)
|
| 1395 |
+
if PADDED_HEAD_V:
|
| 1396 |
+
o_ptrs_mask = o_ptrs_mask & (offs_d_v[None, :] < ACTUAL_BLOCK_DMODEL_V)
|
| 1397 |
+
|
| 1398 |
+
tl.store(o_ptrs, acc.to(Out.dtype.element_ty), mask=o_ptrs_mask)
|
| 1399 |
+
|
| 1400 |
+
|
| 1401 |
+
def attention_forward_prefill_triton_impl(
|
| 1402 |
+
q: torch.Tensor,
|
| 1403 |
+
k: torch.Tensor,
|
| 1404 |
+
v: torch.Tensor,
|
| 1405 |
+
o: torch.Tensor,
|
| 1406 |
+
softmax_lse: torch.Tensor,
|
| 1407 |
+
sd_mask: Optional[torch.Tensor],
|
| 1408 |
+
sm_scale: float,
|
| 1409 |
+
alibi_slopes: Optional[torch.Tensor],
|
| 1410 |
+
causal: bool,
|
| 1411 |
+
window_size_left: int,
|
| 1412 |
+
window_size_right: int,
|
| 1413 |
+
bias: Optional[torch.Tensor],
|
| 1414 |
+
layout: Literal["bshd", "bhsd", "thd"],
|
| 1415 |
+
# varlen
|
| 1416 |
+
cu_seqlens_q: Optional[torch.Tensor],
|
| 1417 |
+
cu_seqlens_k: Optional[torch.Tensor],
|
| 1418 |
+
max_seqlens_q: int,
|
| 1419 |
+
max_seqlens_k: int,
|
| 1420 |
+
# dropout
|
| 1421 |
+
dropout_p: float,
|
| 1422 |
+
philox_seed: Optional[int],
|
| 1423 |
+
philox_offset: Optional[int],
|
| 1424 |
+
# misc
|
| 1425 |
+
return_scores: bool,
|
| 1426 |
+
use_exp2: bool,
|
| 1427 |
+
# fp8
|
| 1428 |
+
q_descale: Optional[torch.Tensor],
|
| 1429 |
+
k_descale: Optional[torch.Tensor],
|
| 1430 |
+
v_descale: Optional[torch.Tensor],
|
| 1431 |
+
# seqused for FA v3
|
| 1432 |
+
seqused_q: Optional[torch.Tensor] = None,
|
| 1433 |
+
seqused_k: Optional[torch.Tensor] = None,
|
| 1434 |
+
# rotary (optional)
|
| 1435 |
+
rotary_cos: Optional[torch.Tensor] = None,
|
| 1436 |
+
rotary_sin: Optional[torch.Tensor] = None,
|
| 1437 |
+
rotary_interleaved: bool = False,
|
| 1438 |
+
seqlens_rotary: Optional[torch.Tensor] = None,
|
| 1439 |
+
):
|
| 1440 |
+
# get params, strides and shape
|
| 1441 |
+
IS_VARLEN = layout == "thd"
|
| 1442 |
+
|
| 1443 |
+
# common assertions
|
| 1444 |
+
assert (
|
| 1445 |
+
0.0 <= dropout_p <= 1.0
|
| 1446 |
+
), f"dropout_p must be between 0 and 1, got {dropout_p}"
|
| 1447 |
+
assert (
|
| 1448 |
+
q.device == k.device == v.device == o.device
|
| 1449 |
+
), f"All tensors must be on the same device. Got: q={q.device}, k={k.device}, v={v.device}, o={o.device}"
|
| 1450 |
+
assert q.dtype == k.dtype == v.dtype, "q, k, v must have the same dtype"
|
| 1451 |
+
current_device = torch.cuda.current_device()
|
| 1452 |
+
assert (
|
| 1453 |
+
q.is_cuda and q.device.index == current_device
|
| 1454 |
+
), f"Device mismatch: Kernel will launch on cuda:{current_device}, but tensors are on {q.device}"
|
| 1455 |
+
|
| 1456 |
+
# get shapes and strides
|
| 1457 |
+
if IS_VARLEN:
|
| 1458 |
+
# shape
|
| 1459 |
+
total_seqlen_q, nheads_q, head_size_q = q.shape
|
| 1460 |
+
total_seqlen_k, nheads_k, head_size_k = k.shape
|
| 1461 |
+
total_seqlen_v, nheads_v, head_size_v = v.shape
|
| 1462 |
+
|
| 1463 |
+
# assert shapes
|
| 1464 |
+
assert (
|
| 1465 |
+
cu_seqlens_q is not None
|
| 1466 |
+
), "cu_seqlens_q must be provided for varlen layout"
|
| 1467 |
+
assert (
|
| 1468 |
+
cu_seqlens_k is not None
|
| 1469 |
+
), "cu_seqlens_k must be provided for varlen layout"
|
| 1470 |
+
assert (
|
| 1471 |
+
max_seqlens_q is not None and max_seqlens_q > 0
|
| 1472 |
+
), "max_seqlens_q must be provided and positive for varlen layout"
|
| 1473 |
+
assert (
|
| 1474 |
+
max_seqlens_k is not None and max_seqlens_k > 0
|
| 1475 |
+
), "max_seqlens_k must be provided and positive for varlen layout"
|
| 1476 |
+
|
| 1477 |
+
# assert head dimensions
|
| 1478 |
+
assert (
|
| 1479 |
+
head_size_q == head_size_k
|
| 1480 |
+
), f"head sizes must match: q={head_size_q}, k={head_size_k}"
|
| 1481 |
+
assert (
|
| 1482 |
+
nheads_k == nheads_v
|
| 1483 |
+
), f"k and v must have same number of heads: k={nheads_k}, v={nheads_v}"
|
| 1484 |
+
assert (
|
| 1485 |
+
nheads_q % nheads_k == 0
|
| 1486 |
+
), f"nheads_q {nheads_q} must be divisible by nheads_k {nheads_k} for GQA/MQA"
|
| 1487 |
+
|
| 1488 |
+
# assert output shapes
|
| 1489 |
+
assert o.shape == (
|
| 1490 |
+
total_seqlen_q,
|
| 1491 |
+
nheads_q,
|
| 1492 |
+
head_size_v,
|
| 1493 |
+
), f"o shape {o.shape} != expected {(total_seqlen_q, nheads_q, head_size_v)}"
|
| 1494 |
+
|
| 1495 |
+
# assert cu_seqlens
|
| 1496 |
+
assert (
|
| 1497 |
+
cu_seqlens_q.dtype == torch.int32
|
| 1498 |
+
), f"cu_seqlens_q must be int32, got {cu_seqlens_q.dtype}"
|
| 1499 |
+
assert (
|
| 1500 |
+
cu_seqlens_k.dtype == torch.int32
|
| 1501 |
+
), f"cu_seqlens_k must be int32, got {cu_seqlens_k.dtype}"
|
| 1502 |
+
assert cu_seqlens_q[0] == 0, "cu_seqlens_q must start with 0"
|
| 1503 |
+
assert cu_seqlens_k[0] == 0, "cu_seqlens_k must start with 0"
|
| 1504 |
+
assert (
|
| 1505 |
+
cu_seqlens_q[-1] == total_seqlen_q
|
| 1506 |
+
), f"cu_seqlens_q[-1] {cu_seqlens_q[-1]} != total_seqlen_q {total_seqlen_q}"
|
| 1507 |
+
assert (
|
| 1508 |
+
cu_seqlens_k[-1] == total_seqlen_k
|
| 1509 |
+
), f"cu_seqlens_k[-1] {cu_seqlens_k[-1]} != total_seqlen_k {total_seqlen_k}"
|
| 1510 |
+
|
| 1511 |
+
# set vars
|
| 1512 |
+
batch = len(cu_seqlens_q) - 1
|
| 1513 |
+
head_size_qk = head_size_q
|
| 1514 |
+
|
| 1515 |
+
# Assert softmax_lse tensor is large enough
|
| 1516 |
+
assert (
|
| 1517 |
+
softmax_lse.shape[0] >= nheads_q
|
| 1518 |
+
), f"softmax_lse.shape[0]={softmax_lse.shape[0]} must be >= nheads_q={nheads_q}"
|
| 1519 |
+
assert (
|
| 1520 |
+
softmax_lse.shape[1] >= total_seqlen_q
|
| 1521 |
+
), f"softmax_lse.shape[1]={softmax_lse.shape[1]} must be >= total_seqlen_q={total_seqlen_q}"
|
| 1522 |
+
assert (
|
| 1523 |
+
softmax_lse.dtype == torch.float32
|
| 1524 |
+
), f"softmax_lse must be float32, got {softmax_lse.dtype}"
|
| 1525 |
+
assert softmax_lse.device == q.device, "softmax_lse must be on same device as q"
|
| 1526 |
+
|
| 1527 |
+
# strides
|
| 1528 |
+
stride_qb, stride_qh, stride_qm, stride_qd = (
|
| 1529 |
+
0,
|
| 1530 |
+
q.stride(1),
|
| 1531 |
+
q.stride(0),
|
| 1532 |
+
q.stride(2),
|
| 1533 |
+
)
|
| 1534 |
+
stride_kb, stride_kh, stride_kn, stride_kd = (
|
| 1535 |
+
0,
|
| 1536 |
+
k.stride(1),
|
| 1537 |
+
k.stride(0),
|
| 1538 |
+
k.stride(2),
|
| 1539 |
+
)
|
| 1540 |
+
stride_vb, stride_vh, stride_vn, stride_vd = (
|
| 1541 |
+
0,
|
| 1542 |
+
v.stride(1),
|
| 1543 |
+
v.stride(0),
|
| 1544 |
+
v.stride(2),
|
| 1545 |
+
)
|
| 1546 |
+
stride_ob, stride_oh, stride_om, stride_od = (
|
| 1547 |
+
0,
|
| 1548 |
+
o.stride(1),
|
| 1549 |
+
o.stride(0),
|
| 1550 |
+
o.stride(2),
|
| 1551 |
+
)
|
| 1552 |
+
stride_lse_z, stride_lse_h, stride_lse_m = (
|
| 1553 |
+
0,
|
| 1554 |
+
softmax_lse.stride(0),
|
| 1555 |
+
softmax_lse.stride(1),
|
| 1556 |
+
)
|
| 1557 |
+
else:
|
| 1558 |
+
# shapes
|
| 1559 |
+
batch_q, seqlen_q, nheads_q, head_size_q = q.shape
|
| 1560 |
+
batch_k, seqlen_k, nheads_k, head_size_k = k.shape
|
| 1561 |
+
batch_v, seqlen_v, nheads_v, head_size_v = v.shape
|
| 1562 |
+
|
| 1563 |
+
# assert batch dimensions
|
| 1564 |
+
assert (
|
| 1565 |
+
batch_q == batch_k == batch_v
|
| 1566 |
+
), f"batch sizes must match: q={batch_q}, k={batch_k}, v={batch_v}"
|
| 1567 |
+
|
| 1568 |
+
# assert head dimensions
|
| 1569 |
+
assert (
|
| 1570 |
+
head_size_q == head_size_k
|
| 1571 |
+
), f"head sizes must match: q={head_size_q}, k={head_size_k}"
|
| 1572 |
+
assert (
|
| 1573 |
+
nheads_k == nheads_v
|
| 1574 |
+
), f"k and v must have same number of heads: k={nheads_k}, v={nheads_v}"
|
| 1575 |
+
assert (
|
| 1576 |
+
nheads_q % nheads_k == 0
|
| 1577 |
+
), f"nheads_q {nheads_q} must be divisible by nheads_k {nheads_k} for GQA/MQA"
|
| 1578 |
+
|
| 1579 |
+
# assert sequence lengths
|
| 1580 |
+
assert (
|
| 1581 |
+
seqlen_k == seqlen_v
|
| 1582 |
+
), f"k and v sequence lengths must match: k={seqlen_k}, v={seqlen_v}"
|
| 1583 |
+
|
| 1584 |
+
# assert output shapes
|
| 1585 |
+
assert o.shape == (
|
| 1586 |
+
batch_q,
|
| 1587 |
+
seqlen_q,
|
| 1588 |
+
nheads_q,
|
| 1589 |
+
head_size_v,
|
| 1590 |
+
), f"o shape {o.shape} != expected {(batch_q, seqlen_q, nheads_q, head_size_v)}"
|
| 1591 |
+
|
| 1592 |
+
# set vars
|
| 1593 |
+
batch = batch_q
|
| 1594 |
+
head_size_qk = head_size_q
|
| 1595 |
+
max_seqlens_q = seqlen_q
|
| 1596 |
+
max_seqlens_k = seqlen_k
|
| 1597 |
+
|
| 1598 |
+
# Assert softmax_lse tensor is large enough
|
| 1599 |
+
assert (
|
| 1600 |
+
softmax_lse.shape[0] >= batch
|
| 1601 |
+
), f"softmax_lse.shape[0]={softmax_lse.shape[0]} must be >= batch={batch}"
|
| 1602 |
+
assert (
|
| 1603 |
+
softmax_lse.shape[1] >= nheads_q
|
| 1604 |
+
), f"softmax_lse.shape[1]={softmax_lse.shape[1]} must be >= nheads_q={nheads_q}"
|
| 1605 |
+
assert (
|
| 1606 |
+
softmax_lse.shape[2] >= seqlen_q
|
| 1607 |
+
), f"softmax_lse.shape[2]={softmax_lse.shape[2]} must be >= seqlen_q={seqlen_q}"
|
| 1608 |
+
assert (
|
| 1609 |
+
softmax_lse.dtype == torch.float32
|
| 1610 |
+
), f"softmax_lse must be float32, got {softmax_lse.dtype}"
|
| 1611 |
+
assert softmax_lse.device == q.device, "softmax_lse must be on same device as q"
|
| 1612 |
+
|
| 1613 |
+
# strides
|
| 1614 |
+
stride_qb, stride_qh, stride_qm, stride_qd = (
|
| 1615 |
+
q.stride(0),
|
| 1616 |
+
q.stride(2),
|
| 1617 |
+
q.stride(1),
|
| 1618 |
+
q.stride(3),
|
| 1619 |
+
)
|
| 1620 |
+
stride_kb, stride_kh, stride_kn, stride_kd = (
|
| 1621 |
+
k.stride(0),
|
| 1622 |
+
k.stride(2),
|
| 1623 |
+
k.stride(1),
|
| 1624 |
+
k.stride(3),
|
| 1625 |
+
)
|
| 1626 |
+
stride_vb, stride_vh, stride_vn, stride_vd = (
|
| 1627 |
+
v.stride(0),
|
| 1628 |
+
v.stride(2),
|
| 1629 |
+
v.stride(1),
|
| 1630 |
+
v.stride(3),
|
| 1631 |
+
)
|
| 1632 |
+
stride_ob, stride_oh, stride_om, stride_od = (
|
| 1633 |
+
o.stride(0),
|
| 1634 |
+
o.stride(2),
|
| 1635 |
+
o.stride(1),
|
| 1636 |
+
o.stride(3),
|
| 1637 |
+
)
|
| 1638 |
+
stride_lse_z, stride_lse_h, stride_lse_m = softmax_lse.stride()
|
| 1639 |
+
|
| 1640 |
+
# apply rotary embeddings
|
| 1641 |
+
if rotary_cos is not None and rotary_sin is not None:
|
| 1642 |
+
if IS_VARLEN:
|
| 1643 |
+
raise NotImplementedError(
|
| 1644 |
+
"Rotary embeddings with varlen (thd layout) prefill are not implemented yet."
|
| 1645 |
+
)
|
| 1646 |
+
seqlen_offsets = seqlens_rotary if seqlens_rotary is not None else 0
|
| 1647 |
+
local = (window_size_left != -1) or (window_size_right != -1)
|
| 1648 |
+
q, _ = apply_rotary(
|
| 1649 |
+
q,
|
| 1650 |
+
None,
|
| 1651 |
+
rotary_cos,
|
| 1652 |
+
rotary_sin,
|
| 1653 |
+
causal=causal,
|
| 1654 |
+
local=local,
|
| 1655 |
+
interleaved=rotary_interleaved,
|
| 1656 |
+
seqlen_offsets=seqlen_offsets,
|
| 1657 |
+
)
|
| 1658 |
+
|
| 1659 |
+
# fp8 setup and assertions
|
| 1660 |
+
IS_FP8 = is_fp8([q, k, v])
|
| 1661 |
+
if IS_FP8:
|
| 1662 |
+
arch = get_arch()
|
| 1663 |
+
if not arch.supports_fp8:
|
| 1664 |
+
raise RuntimeError(f"{arch.name} does not support FP8")
|
| 1665 |
+
FP8_MAX = torch.finfo(q.dtype).max
|
| 1666 |
+
rec_dtype = arch.recommended_fp8_dtype(q.dtype)
|
| 1667 |
+
if q.dtype != rec_dtype or k.dtype != rec_dtype or v.dtype != rec_dtype:
|
| 1668 |
+
warnings.warn(
|
| 1669 |
+
f"Use {rec_dtype} data type on {arch}. Got q: {q.dtype}, k: {k.dtype}, v: {v.dtype}",
|
| 1670 |
+
UserWarning,
|
| 1671 |
+
)
|
| 1672 |
+
|
| 1673 |
+
if (q_descale is None) or (k_descale is None) or (v_descale is None):
|
| 1674 |
+
warnings.warn(
|
| 1675 |
+
"FP8 tensors detected but descale factors not provided. Using default scale of 1.0",
|
| 1676 |
+
UserWarning,
|
| 1677 |
+
)
|
| 1678 |
+
# Create default descale tensors if not provided
|
| 1679 |
+
if q_descale is None:
|
| 1680 |
+
q_descale = torch.ones(
|
| 1681 |
+
batch, nheads_q, dtype=torch.float32, device=q.device
|
| 1682 |
+
)
|
| 1683 |
+
if k_descale is None:
|
| 1684 |
+
k_descale = torch.ones(
|
| 1685 |
+
batch, nheads_k, dtype=torch.float32, device=q.device
|
| 1686 |
+
)
|
| 1687 |
+
if v_descale is None:
|
| 1688 |
+
v_descale = torch.ones(
|
| 1689 |
+
batch, nheads_k, dtype=torch.float32, device=q.device
|
| 1690 |
+
)
|
| 1691 |
+
else:
|
| 1692 |
+
# Enforce exact expected shapes; no reshaping or normalization.
|
| 1693 |
+
assert (
|
| 1694 |
+
q_descale.dim() == 2
|
| 1695 |
+
and q_descale.shape[0] == batch
|
| 1696 |
+
and q_descale.shape[1] == nheads_k
|
| 1697 |
+
), f"q_descale expected shape ({batch}, {nheads_k}) got {tuple(q_descale.shape)}"
|
| 1698 |
+
assert (
|
| 1699 |
+
k_descale.dim() == 2
|
| 1700 |
+
and k_descale.shape[0] == batch
|
| 1701 |
+
and k_descale.shape[1] == nheads_k
|
| 1702 |
+
), f"k_descale expected shape ({batch}, {nheads_k}) got {tuple(k_descale.shape)}"
|
| 1703 |
+
assert (
|
| 1704 |
+
v_descale.dim() == 2
|
| 1705 |
+
and v_descale.shape[0] == batch
|
| 1706 |
+
and v_descale.shape[1] == nheads_k
|
| 1707 |
+
), f"v_descale expected shape ({batch}, {nheads_k}) got {tuple(v_descale.shape)}"
|
| 1708 |
+
|
| 1709 |
+
# o should be fp32 or fp16/bf16
|
| 1710 |
+
assert o.dtype in [
|
| 1711 |
+
torch.float16,
|
| 1712 |
+
torch.bfloat16,
|
| 1713 |
+
torch.float32,
|
| 1714 |
+
], f"Output tensor o must be fp16, bf16, or fp32 when using fp8, got {o.dtype}"
|
| 1715 |
+
|
| 1716 |
+
stride_q_descale_z = q_descale.stride(0) if q_descale is not None else 0
|
| 1717 |
+
stride_k_descale_z = k_descale.stride(0) if k_descale is not None else 0
|
| 1718 |
+
stride_v_descale_z = v_descale.stride(0) if v_descale is not None else 0
|
| 1719 |
+
|
| 1720 |
+
if DEBUG:
|
| 1721 |
+
print("FP8 path triggered in fwd_prefill.py")
|
| 1722 |
+
else:
|
| 1723 |
+
FP8_MAX = None
|
| 1724 |
+
q_descale = k_descale = v_descale = None
|
| 1725 |
+
stride_q_descale_z = stride_k_descale_z = stride_v_descale_z = None
|
| 1726 |
+
|
| 1727 |
+
# check output dtype matches input dtype when not using fp8
|
| 1728 |
+
assert (
|
| 1729 |
+
o.dtype == q.dtype
|
| 1730 |
+
), f"Output dtype {o.dtype} must match input dtype {q.dtype} when not using fp8"
|
| 1731 |
+
|
| 1732 |
+
# check features
|
| 1733 |
+
use_sliding_window = window_size_left != -1 or window_size_right != -1
|
| 1734 |
+
use_alibi, (stride_az, stride_ah) = (
|
| 1735 |
+
(True, alibi_slopes.stride()) if alibi_slopes is not None else (False, (0, 0))
|
| 1736 |
+
)
|
| 1737 |
+
# NOTE: a large bias tensor leads to overflow during pointer arithmetic
|
| 1738 |
+
if bias is not None:
|
| 1739 |
+
assert bias.numel() < 2**31
|
| 1740 |
+
|
| 1741 |
+
# Get closest power of 2 over or equal to 32 for both QK and V dimensions
|
| 1742 |
+
padded_d_model_qk = 1 << (head_size_qk - 1).bit_length()
|
| 1743 |
+
padded_d_model_v = 1 << (head_size_v - 1).bit_length()
|
| 1744 |
+
# Smallest head_dim supported is 16. If smaller, the tile in the
|
| 1745 |
+
# kernel is padded - there is no padding in memory for any dims.
|
| 1746 |
+
padded_d_model_qk = max(padded_d_model_qk, 16)
|
| 1747 |
+
padded_d_model_v = max(padded_d_model_v, 16)
|
| 1748 |
+
|
| 1749 |
+
# sd_mask assertions and strides
|
| 1750 |
+
if sd_mask is not None:
|
| 1751 |
+
assert dropout_p > 0.0 or return_scores, "sd_mask provided but not used"
|
| 1752 |
+
assert (
|
| 1753 |
+
sd_mask is not None
|
| 1754 |
+
), "sd_mask must be provided when return_scores=True or dropout_p > 0"
|
| 1755 |
+
# Assert sd_mask tensor is large enough
|
| 1756 |
+
assert (
|
| 1757 |
+
sd_mask.shape[0] >= batch
|
| 1758 |
+
), f"sd_mask.shape[0]={sd_mask.shape[0]} must be >= batch={batch}"
|
| 1759 |
+
assert (
|
| 1760 |
+
sd_mask.shape[1] >= nheads_q
|
| 1761 |
+
), f"sd_mask.shape[1]={sd_mask.shape[1]} must be >= nheads_q={nheads_q}"
|
| 1762 |
+
assert (
|
| 1763 |
+
sd_mask.shape[2] >= max_seqlens_q
|
| 1764 |
+
), f"sd_mask.shape[2]={sd_mask.shape[2]} must be >= max_seqlens_q={max_seqlens_q}"
|
| 1765 |
+
assert (
|
| 1766 |
+
sd_mask.shape[3] >= max_seqlens_k
|
| 1767 |
+
), f"sd_mask.shape[3]={sd_mask.shape[3]} must be >= max_seqlens_k={max_seqlens_k}"
|
| 1768 |
+
assert sd_mask.device == q.device, "sd_mask must be on same device as q"
|
| 1769 |
+
|
| 1770 |
+
stride_sz, stride_sh, stride_sm, stride_sn = (
|
| 1771 |
+
sd_mask.stride(0),
|
| 1772 |
+
sd_mask.stride(1),
|
| 1773 |
+
sd_mask.stride(2),
|
| 1774 |
+
sd_mask.stride(3),
|
| 1775 |
+
)
|
| 1776 |
+
else:
|
| 1777 |
+
stride_sz, stride_sh, stride_sm, stride_sn = (0, 0, 0, 0)
|
| 1778 |
+
|
| 1779 |
+
if bias is not None:
|
| 1780 |
+
stride_bz, stride_bh, stride_bm, stride_bn = (
|
| 1781 |
+
bias.stride(0),
|
| 1782 |
+
bias.stride(1),
|
| 1783 |
+
bias.stride(2),
|
| 1784 |
+
bias.stride(3),
|
| 1785 |
+
)
|
| 1786 |
+
else:
|
| 1787 |
+
stride_bz, stride_bh, stride_bm, stride_bn = (0, 0, 0, 0)
|
| 1788 |
+
|
| 1789 |
+
# Detect if we need to force masking for all blocks (required on some architectures)
|
| 1790 |
+
arch = get_arch()
|
| 1791 |
+
force_masking = arch.is_rdna
|
| 1792 |
+
|
| 1793 |
+
num_xcd = 1 if arch.is_rdna else 8
|
| 1794 |
+
|
| 1795 |
+
# launch kernel
|
| 1796 |
+
def grid(META):
|
| 1797 |
+
return (nheads_q, triton.cdiv(max_seqlens_q, META["BLOCK_M"]), batch)
|
| 1798 |
+
|
| 1799 |
+
attn_fwd[grid](
|
| 1800 |
+
q,
|
| 1801 |
+
k,
|
| 1802 |
+
v,
|
| 1803 |
+
bias,
|
| 1804 |
+
q_descale,
|
| 1805 |
+
k_descale,
|
| 1806 |
+
v_descale,
|
| 1807 |
+
stride_q_descale_z,
|
| 1808 |
+
stride_k_descale_z,
|
| 1809 |
+
stride_v_descale_z,
|
| 1810 |
+
softmax_lse,
|
| 1811 |
+
o,
|
| 1812 |
+
sd_mask,
|
| 1813 |
+
alibi_slopes,
|
| 1814 |
+
stride_qb,
|
| 1815 |
+
stride_qh,
|
| 1816 |
+
stride_qm,
|
| 1817 |
+
stride_qd,
|
| 1818 |
+
stride_kb,
|
| 1819 |
+
stride_kh,
|
| 1820 |
+
stride_kn,
|
| 1821 |
+
stride_kd,
|
| 1822 |
+
stride_vb,
|
| 1823 |
+
stride_vh,
|
| 1824 |
+
stride_vn,
|
| 1825 |
+
stride_vd,
|
| 1826 |
+
stride_ob,
|
| 1827 |
+
stride_oh,
|
| 1828 |
+
stride_om,
|
| 1829 |
+
stride_od,
|
| 1830 |
+
stride_bz,
|
| 1831 |
+
stride_bh,
|
| 1832 |
+
stride_bm,
|
| 1833 |
+
stride_bn,
|
| 1834 |
+
stride_az,
|
| 1835 |
+
stride_ah,
|
| 1836 |
+
stride_sz,
|
| 1837 |
+
stride_sh,
|
| 1838 |
+
stride_sm,
|
| 1839 |
+
stride_sn,
|
| 1840 |
+
stride_lse_z,
|
| 1841 |
+
stride_lse_h,
|
| 1842 |
+
stride_lse_m,
|
| 1843 |
+
cu_seqlens_q,
|
| 1844 |
+
cu_seqlens_k,
|
| 1845 |
+
seqused_q,
|
| 1846 |
+
seqused_k, # Pass seqused tensors
|
| 1847 |
+
dropout_p=dropout_p,
|
| 1848 |
+
philox_seed=philox_seed,
|
| 1849 |
+
philox_offset_base=philox_offset,
|
| 1850 |
+
HQ=nheads_q,
|
| 1851 |
+
HK=nheads_k,
|
| 1852 |
+
ACTUAL_BLOCK_DMODEL_QK=head_size_qk,
|
| 1853 |
+
ACTUAL_BLOCK_DMODEL_V=head_size_v,
|
| 1854 |
+
MAX_SEQLENS_Q=max_seqlens_q,
|
| 1855 |
+
MAX_SEQLENS_K=max_seqlens_k,
|
| 1856 |
+
SM_SCALE=sm_scale,
|
| 1857 |
+
IS_CAUSAL=causal,
|
| 1858 |
+
USE_SLIDING_WINDOW=use_sliding_window,
|
| 1859 |
+
WINDOW_SIZE_LEFT=window_size_left,
|
| 1860 |
+
WINDOW_SIZE_RIGHT=window_size_right,
|
| 1861 |
+
IS_VARLEN=IS_VARLEN,
|
| 1862 |
+
BLOCK_DMODEL_QK=padded_d_model_qk,
|
| 1863 |
+
BLOCK_DMODEL_V=padded_d_model_v,
|
| 1864 |
+
USE_BIAS=False if bias is None else True,
|
| 1865 |
+
USE_ALIBI=use_alibi,
|
| 1866 |
+
ENABLE_DROPOUT=dropout_p > 0.0,
|
| 1867 |
+
USE_EXP2=use_exp2,
|
| 1868 |
+
RETURN_SCORES=return_scores,
|
| 1869 |
+
IS_FP8=IS_FP8,
|
| 1870 |
+
FP8_MAX=FP8_MAX,
|
| 1871 |
+
FP8_P_DESCALE=False,
|
| 1872 |
+
USE_SEQUSED=(seqused_q is not None or seqused_k is not None),
|
| 1873 |
+
FORCE_MASKING=force_masking,
|
| 1874 |
+
NUM_XCD=num_xcd,
|
| 1875 |
+
)
|
build/torch-rocm/_kernels/flash_attn_triton_amd/interface_v2.py
ADDED
|
@@ -0,0 +1,828 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
from typing import Literal, Optional, Union
|
| 3 |
+
from .fwd_prefill import attention_forward_prefill_triton_impl
|
| 4 |
+
from .fwd_decode import attention_forward_decode_triton_impl
|
| 5 |
+
from .bwd import attention_backward_triton_impl
|
| 6 |
+
from .utils import (
|
| 7 |
+
DEBUG,
|
| 8 |
+
USE_EXP2,
|
| 9 |
+
BWD_MODE,
|
| 10 |
+
PHILOX_SEED,
|
| 11 |
+
PHILOX_OFFSET,
|
| 12 |
+
SHAPE_EXPECTATIONS,
|
| 13 |
+
round_multiple,
|
| 14 |
+
)
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
def fwd(
|
| 18 |
+
q: torch.Tensor,
|
| 19 |
+
k: torch.Tensor,
|
| 20 |
+
v: torch.Tensor,
|
| 21 |
+
out: Optional[torch.Tensor],
|
| 22 |
+
alibi_slopes: Optional[torch.Tensor],
|
| 23 |
+
dropout_p: float,
|
| 24 |
+
softmax_scale: float,
|
| 25 |
+
causal: bool,
|
| 26 |
+
window_size_left: int,
|
| 27 |
+
window_size_right: int,
|
| 28 |
+
softcap: float,
|
| 29 |
+
return_softmax: bool,
|
| 30 |
+
gen_: Optional[torch.Tensor] = None,
|
| 31 |
+
) -> tuple[torch.Tensor, torch.Tensor, Optional[torch.Tensor], torch.Tensor]:
|
| 32 |
+
|
| 33 |
+
# Reject FP8 tensors (FA2 AMD path does not support FP8)
|
| 34 |
+
if str(q.dtype).startswith("torch.float8"):
|
| 35 |
+
raise NotImplementedError(
|
| 36 |
+
"FP8 tensors are not supported in the AMD Triton FA2 interface. Use the FA3 path instead."
|
| 37 |
+
)
|
| 38 |
+
|
| 39 |
+
# Unsupported features assertions (keep behavior explicit like v3 shim)
|
| 40 |
+
if softcap != 0.0:
|
| 41 |
+
raise NotImplementedError(
|
| 42 |
+
"softcap is not supported in the AMD Triton FA2 interface (expected 0.0)."
|
| 43 |
+
)
|
| 44 |
+
|
| 45 |
+
if DEBUG:
|
| 46 |
+
print()
|
| 47 |
+
print("flash_attn_triton_amd.py::fwd inputs")
|
| 48 |
+
print("q:", q.shape)
|
| 49 |
+
print("k:", k.shape)
|
| 50 |
+
print("v:", v.shape)
|
| 51 |
+
print("out:", out.shape if out is not None else None)
|
| 52 |
+
print("alibi_slopes:", alibi_slopes.shape if alibi_slopes is not None else None)
|
| 53 |
+
print("dropout_p:", dropout_p)
|
| 54 |
+
print("softmax_scale:", softmax_scale)
|
| 55 |
+
print("causal:", causal)
|
| 56 |
+
print("window_size_left:", window_size_left)
|
| 57 |
+
print("window_size_right:", window_size_right)
|
| 58 |
+
print("softcap:", softcap)
|
| 59 |
+
print("return_softmax:", return_softmax)
|
| 60 |
+
|
| 61 |
+
if out is None:
|
| 62 |
+
out = torch.zeros_like(q)
|
| 63 |
+
else:
|
| 64 |
+
out.zero_()
|
| 65 |
+
|
| 66 |
+
# Layout / shapes
|
| 67 |
+
layout: Literal["bshd", "bhsd", "thd"] = "bshd"
|
| 68 |
+
max_seqlen_q = q.shape[1]
|
| 69 |
+
max_seqlen_k = k.shape[1]
|
| 70 |
+
batch, _, nheads_q, _ = q.shape
|
| 71 |
+
|
| 72 |
+
# Normalize / validate alibi
|
| 73 |
+
if alibi_slopes is not None:
|
| 74 |
+
if alibi_slopes.dim() == 1:
|
| 75 |
+
alibi_slopes = alibi_slopes.unsqueeze(0).expand(batch, -1)
|
| 76 |
+
assert alibi_slopes.is_cuda and alibi_slopes.dim() == 2
|
| 77 |
+
assert alibi_slopes.shape == (batch, nheads_q)
|
| 78 |
+
|
| 79 |
+
# Dropout + RNG seed
|
| 80 |
+
philox_seed, philox_offset = PHILOX_SEED, PHILOX_OFFSET
|
| 81 |
+
rng_state = torch.as_tensor([philox_seed, philox_offset])
|
| 82 |
+
|
| 83 |
+
# argument checks
|
| 84 |
+
assert q.dim() == 4 and k.dim() == 4 and v.dim() == 4
|
| 85 |
+
assert q.shape[-1] == k.shape[-1] == v.shape[-1]
|
| 86 |
+
assert q.dtype == k.dtype == v.dtype
|
| 87 |
+
assert out.shape[:-1] == q.shape[:-1] and out.shape[-1] == v.shape[-1]
|
| 88 |
+
nheads_k = k.shape[2]
|
| 89 |
+
assert (nheads_q % nheads_k) == 0
|
| 90 |
+
|
| 91 |
+
# Create output tensors based on shape expectations
|
| 92 |
+
if SHAPE_EXPECTATIONS == "rounded":
|
| 93 |
+
softmax_lse = torch.zeros(
|
| 94 |
+
(batch, nheads_q, round_multiple(max_seqlen_q, 128)),
|
| 95 |
+
device=q.device,
|
| 96 |
+
dtype=torch.float32,
|
| 97 |
+
)
|
| 98 |
+
if dropout_p > 0.0 or return_softmax:
|
| 99 |
+
sd_mask = torch.zeros(
|
| 100 |
+
(
|
| 101 |
+
batch,
|
| 102 |
+
nheads_q,
|
| 103 |
+
round_multiple(max_seqlen_q, 128),
|
| 104 |
+
round_multiple(max_seqlen_k, 128),
|
| 105 |
+
),
|
| 106 |
+
device=q.device,
|
| 107 |
+
dtype=torch.float32,
|
| 108 |
+
)
|
| 109 |
+
else:
|
| 110 |
+
sd_mask = None
|
| 111 |
+
else:
|
| 112 |
+
softmax_lse = torch.zeros(
|
| 113 |
+
(batch, nheads_q, max_seqlen_q),
|
| 114 |
+
device=q.device,
|
| 115 |
+
dtype=torch.float32,
|
| 116 |
+
)
|
| 117 |
+
if dropout_p > 0.0 or return_softmax:
|
| 118 |
+
sd_mask = torch.zeros(
|
| 119 |
+
(batch, nheads_q, max_seqlen_q, max_seqlen_k),
|
| 120 |
+
device=q.device,
|
| 121 |
+
dtype=torch.float32,
|
| 122 |
+
)
|
| 123 |
+
else:
|
| 124 |
+
sd_mask = None
|
| 125 |
+
|
| 126 |
+
# call implementation
|
| 127 |
+
if DEBUG:
|
| 128 |
+
print("Using Triton implementation")
|
| 129 |
+
attention_forward_prefill_triton_impl(
|
| 130 |
+
q,
|
| 131 |
+
k,
|
| 132 |
+
v,
|
| 133 |
+
out,
|
| 134 |
+
softmax_lse,
|
| 135 |
+
sd_mask,
|
| 136 |
+
softmax_scale,
|
| 137 |
+
alibi_slopes,
|
| 138 |
+
causal,
|
| 139 |
+
window_size_left,
|
| 140 |
+
window_size_right,
|
| 141 |
+
None,
|
| 142 |
+
layout,
|
| 143 |
+
None,
|
| 144 |
+
None,
|
| 145 |
+
max_seqlen_q,
|
| 146 |
+
max_seqlen_k,
|
| 147 |
+
dropout_p,
|
| 148 |
+
philox_seed,
|
| 149 |
+
philox_offset,
|
| 150 |
+
return_softmax,
|
| 151 |
+
USE_EXP2,
|
| 152 |
+
None,
|
| 153 |
+
None,
|
| 154 |
+
None,
|
| 155 |
+
None,
|
| 156 |
+
None,
|
| 157 |
+
None,
|
| 158 |
+
None,
|
| 159 |
+
)
|
| 160 |
+
|
| 161 |
+
if DEBUG:
|
| 162 |
+
print("flash_attn_triton_amd.py::fwd outputs")
|
| 163 |
+
print("out:", out.shape)
|
| 164 |
+
print("softmax_lse:", softmax_lse.shape)
|
| 165 |
+
print("sd_mask:", sd_mask.shape if sd_mask is not None else None)
|
| 166 |
+
print("rng_state:", rng_state)
|
| 167 |
+
|
| 168 |
+
# --- Assertions (shape + dtype contracts) ---
|
| 169 |
+
# out: (B, Sq, Hq, D)
|
| 170 |
+
assert out.shape == q.shape, f"[fwd] out shape {out.shape} != q shape {q.shape}"
|
| 171 |
+
# softmax_lse dtype
|
| 172 |
+
assert (
|
| 173 |
+
softmax_lse.dtype == torch.float32
|
| 174 |
+
), f"[fwd] softmax_lse dtype {softmax_lse.dtype} != torch.float32"
|
| 175 |
+
# softmax_lse shape depends on SHAPE_EXPECTATIONS
|
| 176 |
+
if SHAPE_EXPECTATIONS == "rounded":
|
| 177 |
+
expected_lse_shape = (q.shape[0], q.shape[2], round_multiple(q.shape[1], 128))
|
| 178 |
+
else:
|
| 179 |
+
expected_lse_shape = (q.shape[0], q.shape[2], q.shape[1])
|
| 180 |
+
assert (
|
| 181 |
+
softmax_lse.shape == expected_lse_shape
|
| 182 |
+
), f"[fwd] softmax_lse shape {softmax_lse.shape} != {expected_lse_shape}"
|
| 183 |
+
if return_softmax:
|
| 184 |
+
# sd_mask: (B, Hq, Sq, Sk)
|
| 185 |
+
assert sd_mask is not None, "[fwd] return_softmax=True but sd_mask is None"
|
| 186 |
+
assert sd_mask.dim() == 4, f"[fwd] sd_mask dim {sd_mask.dim()} != 4"
|
| 187 |
+
if SHAPE_EXPECTATIONS == "rounded":
|
| 188 |
+
expected_sq = round_multiple(q.shape[1], 128)
|
| 189 |
+
expected_sk = round_multiple(k.shape[1], 128)
|
| 190 |
+
assert (
|
| 191 |
+
sd_mask.shape[0] == q.shape[0]
|
| 192 |
+
and sd_mask.shape[1] == q.shape[2]
|
| 193 |
+
and sd_mask.shape[2] == expected_sq
|
| 194 |
+
and sd_mask.shape[3] == expected_sk
|
| 195 |
+
), f"[fwd] sd_mask shape {sd_mask.shape} != (B={q.shape[0]}, Hq={q.shape[2]}, Sq={expected_sq}, Sk={expected_sk})"
|
| 196 |
+
else:
|
| 197 |
+
assert (
|
| 198 |
+
sd_mask.shape[0] == q.shape[0]
|
| 199 |
+
and sd_mask.shape[1] == q.shape[2]
|
| 200 |
+
and sd_mask.shape[2] == q.shape[1]
|
| 201 |
+
), f"[fwd] sd_mask leading dims {sd_mask.shape[:3]} mismatch (B,Hq,Sq) {(q.shape[0], q.shape[2], q.shape[1])}"
|
| 202 |
+
else:
|
| 203 |
+
assert sd_mask is None, "[fwd] return_softmax=False but sd_mask is not None"
|
| 204 |
+
|
| 205 |
+
return out, softmax_lse, sd_mask, rng_state
|
| 206 |
+
|
| 207 |
+
|
| 208 |
+
def bwd(
|
| 209 |
+
dout: torch.Tensor,
|
| 210 |
+
q: torch.Tensor,
|
| 211 |
+
k: torch.Tensor,
|
| 212 |
+
v: torch.Tensor,
|
| 213 |
+
out: torch.Tensor,
|
| 214 |
+
softmax_lse: torch.Tensor,
|
| 215 |
+
dq: Optional[torch.Tensor],
|
| 216 |
+
dk: Optional[torch.Tensor],
|
| 217 |
+
dv: Optional[torch.Tensor],
|
| 218 |
+
alibi_slopes: Optional[torch.Tensor],
|
| 219 |
+
dropout_p: float,
|
| 220 |
+
softmax_scale: float,
|
| 221 |
+
causal: bool,
|
| 222 |
+
window_size_left: int,
|
| 223 |
+
window_size_right: int,
|
| 224 |
+
softcap: float,
|
| 225 |
+
deterministic: bool,
|
| 226 |
+
gen_: Optional[torch.Tensor] = None,
|
| 227 |
+
rng_state: Optional[torch.Tensor] = None,
|
| 228 |
+
) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor]:
|
| 229 |
+
if softcap != 0.0:
|
| 230 |
+
raise NotImplementedError(
|
| 231 |
+
"softcap is not supported in the AMD Triton FA2 interface (expected 0.0)."
|
| 232 |
+
)
|
| 233 |
+
|
| 234 |
+
# Check for sliding window - backward doesn't support it yet
|
| 235 |
+
is_sliding_window = (window_size_left >= 0) or (window_size_right >= 0)
|
| 236 |
+
if is_sliding_window:
|
| 237 |
+
raise NotImplementedError(
|
| 238 |
+
f"Sliding window attention is not yet supported in the AMD Triton backward pass "
|
| 239 |
+
f"(window_size_left={window_size_left}, window_size_right={window_size_right}). "
|
| 240 |
+
f"Use window_size=(-1, -1) for full attention."
|
| 241 |
+
)
|
| 242 |
+
|
| 243 |
+
if DEBUG:
|
| 244 |
+
print()
|
| 245 |
+
print("flash_attn_triton_amd.py::bwd inputs")
|
| 246 |
+
print("dout:", dout.shape)
|
| 247 |
+
print("q:", q.shape)
|
| 248 |
+
print("k:", k.shape)
|
| 249 |
+
print("v:", v.shape)
|
| 250 |
+
print("out:", out.shape)
|
| 251 |
+
print("softmax_lse:", softmax_lse.shape)
|
| 252 |
+
print("dq:", dq.shape if dq is not None else None)
|
| 253 |
+
print("dk:", dk.shape if dk is not None else None)
|
| 254 |
+
print("dv:", dv.shape if dv is not None else None)
|
| 255 |
+
print("alibi_slopes:", alibi_slopes.shape if alibi_slopes is not None else None)
|
| 256 |
+
print("dropout_p:", dropout_p)
|
| 257 |
+
print("softmax_scale:", softmax_scale)
|
| 258 |
+
print("causal:", causal)
|
| 259 |
+
print("window_size_left:", window_size_left)
|
| 260 |
+
print("window_size_right:", window_size_right)
|
| 261 |
+
print("deterministic:", deterministic)
|
| 262 |
+
print("rng_state:", rng_state)
|
| 263 |
+
|
| 264 |
+
dq = torch.zeros_like(q) if dq is None else dq.zero_()
|
| 265 |
+
dk = torch.zeros_like(k) if dk is None else dk.zero_()
|
| 266 |
+
dv = torch.zeros_like(v) if dv is None else dv.zero_()
|
| 267 |
+
|
| 268 |
+
# get shape
|
| 269 |
+
batch, seqlen_q, nheads_q, _ = q.shape
|
| 270 |
+
|
| 271 |
+
# Create delta tensor with shape based on expectations
|
| 272 |
+
# delta (softmax_d) : (B, Hq, Sq) or (B, Hq, round_multiple(Sq, 128))
|
| 273 |
+
if SHAPE_EXPECTATIONS == "rounded":
|
| 274 |
+
delta = torch.zeros(
|
| 275 |
+
(batch, nheads_q, round_multiple(seqlen_q, 128)),
|
| 276 |
+
device=q.device,
|
| 277 |
+
dtype=torch.float32,
|
| 278 |
+
)
|
| 279 |
+
else:
|
| 280 |
+
delta = torch.zeros(
|
| 281 |
+
(batch, nheads_q, seqlen_q), device=q.device, dtype=torch.float32
|
| 282 |
+
)
|
| 283 |
+
|
| 284 |
+
# Upstream change: base seeding logic on provided rng_state instead of dropout probability.
|
| 285 |
+
if rng_state is not None:
|
| 286 |
+
philox_seed, philox_offset = rng_state[0].item(), rng_state[1].item()
|
| 287 |
+
else:
|
| 288 |
+
philox_seed, philox_offset = None, None
|
| 289 |
+
|
| 290 |
+
if alibi_slopes is not None:
|
| 291 |
+
if alibi_slopes.dim() == 2:
|
| 292 |
+
pass
|
| 293 |
+
elif alibi_slopes.dim() == 1:
|
| 294 |
+
alibi_slopes = alibi_slopes.unsqueeze(0).expand(batch, -1)
|
| 295 |
+
else:
|
| 296 |
+
raise ValueError("Alibi can be (nheads,) or (batch_size, nheads).")
|
| 297 |
+
|
| 298 |
+
# call implementation
|
| 299 |
+
if DEBUG:
|
| 300 |
+
print(f"Using Triton implementation in {BWD_MODE} mode")
|
| 301 |
+
attention_backward_triton_impl(
|
| 302 |
+
do=dout,
|
| 303 |
+
q=q,
|
| 304 |
+
k=k,
|
| 305 |
+
v=v,
|
| 306 |
+
o=out,
|
| 307 |
+
softmax_lse=softmax_lse,
|
| 308 |
+
dq=dq,
|
| 309 |
+
dk=dk,
|
| 310 |
+
dv=dv,
|
| 311 |
+
delta=delta,
|
| 312 |
+
sm_scale=softmax_scale,
|
| 313 |
+
alibi_slopes=alibi_slopes,
|
| 314 |
+
causal=causal,
|
| 315 |
+
layout="bshd",
|
| 316 |
+
cu_seqlens_q=None,
|
| 317 |
+
cu_seqlens_k=None,
|
| 318 |
+
max_seqlen_q=seqlen_q,
|
| 319 |
+
max_seqlen_k=k.shape[1],
|
| 320 |
+
seqused_q=None,
|
| 321 |
+
seqused_k=None,
|
| 322 |
+
dropout_p=dropout_p,
|
| 323 |
+
philox_seed=philox_seed,
|
| 324 |
+
philox_offset=philox_offset,
|
| 325 |
+
use_exp2=USE_EXP2,
|
| 326 |
+
mode=BWD_MODE,
|
| 327 |
+
)
|
| 328 |
+
|
| 329 |
+
if DEBUG:
|
| 330 |
+
print("flash_attn_triton_amd.py::bwd outputs")
|
| 331 |
+
print("dq:", dq.shape)
|
| 332 |
+
print("dk:", dk.shape)
|
| 333 |
+
print("dv:", dv.shape)
|
| 334 |
+
# --- Assertions ---
|
| 335 |
+
assert dq.shape == q.shape, f"[bwd] dq shape {dq.shape} != q shape {q.shape}"
|
| 336 |
+
assert dk.shape == k.shape, f"[bwd] dk shape {dk.shape} != k shape {k.shape}"
|
| 337 |
+
assert dv.shape == v.shape, f"[bwd] dv shape {dv.shape} != v shape {v.shape}"
|
| 338 |
+
# delta (softmax_d) : (B, Hq, Sq)
|
| 339 |
+
if SHAPE_EXPECTATIONS == "rounded":
|
| 340 |
+
expected_delta_shape = (q.shape[0], q.shape[2], round_multiple(q.shape[1], 128))
|
| 341 |
+
else:
|
| 342 |
+
expected_delta_shape = (q.shape[0], q.shape[2], q.shape[1])
|
| 343 |
+
assert (
|
| 344 |
+
delta.shape == expected_delta_shape
|
| 345 |
+
), f"[bwd] delta shape {delta.shape} != {expected_delta_shape}"
|
| 346 |
+
return dq, dk, dv, delta
|
| 347 |
+
|
| 348 |
+
|
| 349 |
+
def varlen_fwd(
|
| 350 |
+
q: torch.Tensor,
|
| 351 |
+
k: torch.Tensor,
|
| 352 |
+
v: torch.Tensor,
|
| 353 |
+
out: Optional[torch.Tensor],
|
| 354 |
+
cu_seqlens_q: torch.Tensor,
|
| 355 |
+
cu_seqlens_k: torch.Tensor,
|
| 356 |
+
seqused_k: Optional[torch.Tensor],
|
| 357 |
+
leftpad_k: Optional[torch.Tensor],
|
| 358 |
+
block_table_: Optional[torch.Tensor],
|
| 359 |
+
alibi_slopes: Optional[torch.Tensor],
|
| 360 |
+
max_seqlen_q: int,
|
| 361 |
+
max_seqlen_k: int,
|
| 362 |
+
dropout_p: float,
|
| 363 |
+
softmax_scale: float,
|
| 364 |
+
zero_tensors: bool,
|
| 365 |
+
causal: bool,
|
| 366 |
+
window_size_left: int,
|
| 367 |
+
window_size_right: int,
|
| 368 |
+
softcap: float,
|
| 369 |
+
return_softmax: bool,
|
| 370 |
+
gen_: Optional[torch.Tensor] = None,
|
| 371 |
+
num_splits: int = 0,
|
| 372 |
+
) -> tuple[torch.Tensor, torch.Tensor, Optional[torch.Tensor], torch.Tensor]:
|
| 373 |
+
|
| 374 |
+
if str(q.dtype).startswith("torch.float8"):
|
| 375 |
+
raise NotImplementedError(
|
| 376 |
+
"FP8 tensors are not supported in the AMD Triton FA2 interface (varlen_fwd). Use the FA3 path instead."
|
| 377 |
+
)
|
| 378 |
+
|
| 379 |
+
if softcap != 0.0:
|
| 380 |
+
raise NotImplementedError(
|
| 381 |
+
"softcap is not supported in varlen_fwd (expected 0.0)."
|
| 382 |
+
)
|
| 383 |
+
if leftpad_k is not None:
|
| 384 |
+
raise NotImplementedError(
|
| 385 |
+
"leftpad_k is not supported in AMD Triton FA2 varlen_fwd."
|
| 386 |
+
)
|
| 387 |
+
if block_table_ is not None:
|
| 388 |
+
raise NotImplementedError(
|
| 389 |
+
"block_table / paged attention is not supported in AMD Triton FA2 varlen_fwd."
|
| 390 |
+
)
|
| 391 |
+
if seqused_k is not None:
|
| 392 |
+
raise NotImplementedError(
|
| 393 |
+
"seqused_k is not supported in AMD Triton FA2 varlen_fwd."
|
| 394 |
+
)
|
| 395 |
+
if num_splits not in (0, 1):
|
| 396 |
+
raise NotImplementedError(
|
| 397 |
+
"num_splits > 1 not supported in AMD Triton FA2 varlen_fwd."
|
| 398 |
+
)
|
| 399 |
+
|
| 400 |
+
if DEBUG:
|
| 401 |
+
print()
|
| 402 |
+
print("flash_attn_triton_amd.py::varlen_fwd")
|
| 403 |
+
print("q:", q.shape)
|
| 404 |
+
print("k:", k.shape)
|
| 405 |
+
print("v:", v.shape)
|
| 406 |
+
print("cu_seqlens_q:", cu_seqlens_q, cu_seqlens_q.shape)
|
| 407 |
+
print("cu_seqlens_k:", cu_seqlens_k, cu_seqlens_k.shape)
|
| 408 |
+
print("alibi_slopes:", alibi_slopes)
|
| 409 |
+
print("max_seqlen_q:", max_seqlen_q)
|
| 410 |
+
print("max_seqlen_k:", max_seqlen_k)
|
| 411 |
+
print("dropout_p:", dropout_p)
|
| 412 |
+
print("softmax_scale:", softmax_scale)
|
| 413 |
+
print("causal:", causal)
|
| 414 |
+
print("window_size_left:", window_size_left)
|
| 415 |
+
print("window_size_right:", window_size_right)
|
| 416 |
+
print("gen_:", gen_)
|
| 417 |
+
out = torch.zeros_like(q) if out is None else out.zero_()
|
| 418 |
+
|
| 419 |
+
# Layout and basic info for varlen
|
| 420 |
+
layout: Literal["bshd", "bhsd", "thd"] = "thd"
|
| 421 |
+
batch = len(cu_seqlens_q) - 1
|
| 422 |
+
total_q, nheads_q, _ = q.shape
|
| 423 |
+
|
| 424 |
+
# Create softmax_lse tensor - varlen always uses exact shape (Hq, Total_Q)
|
| 425 |
+
softmax_lse = torch.zeros((nheads_q, total_q), device=q.device, dtype=torch.float32)
|
| 426 |
+
|
| 427 |
+
# Create sd_mask tensor if needed
|
| 428 |
+
if return_softmax:
|
| 429 |
+
# sd_mask: (B, Hq, Sq, Sk) - shape based on expectations
|
| 430 |
+
if SHAPE_EXPECTATIONS == "rounded":
|
| 431 |
+
sd_mask = torch.zeros(
|
| 432 |
+
(
|
| 433 |
+
batch,
|
| 434 |
+
nheads_q,
|
| 435 |
+
round_multiple(max_seqlen_q, 128),
|
| 436 |
+
round_multiple(max_seqlen_k, 128),
|
| 437 |
+
),
|
| 438 |
+
device=q.device,
|
| 439 |
+
dtype=q.dtype,
|
| 440 |
+
)
|
| 441 |
+
else:
|
| 442 |
+
sd_mask = torch.zeros(
|
| 443 |
+
(batch, nheads_q, max_seqlen_q, max_seqlen_k),
|
| 444 |
+
device=q.device,
|
| 445 |
+
dtype=q.dtype,
|
| 446 |
+
)
|
| 447 |
+
else:
|
| 448 |
+
sd_mask = None
|
| 449 |
+
|
| 450 |
+
if alibi_slopes is not None:
|
| 451 |
+
if alibi_slopes.dim() == 1:
|
| 452 |
+
alibi_slopes = alibi_slopes.unsqueeze(0).expand(batch, -1)
|
| 453 |
+
assert alibi_slopes.is_cuda and alibi_slopes.dim() == 2
|
| 454 |
+
assert alibi_slopes.shape == (batch, nheads_q)
|
| 455 |
+
|
| 456 |
+
philox_seed, philox_offset = PHILOX_SEED, PHILOX_OFFSET
|
| 457 |
+
rng_state = torch.as_tensor([philox_seed, philox_offset])
|
| 458 |
+
|
| 459 |
+
# Inline checks (subset appropriate for varlen)
|
| 460 |
+
assert q.dim() == 3 and k.dim() == 3 and v.dim() == 3
|
| 461 |
+
assert q.shape[-1] == k.shape[-1] == v.shape[-1]
|
| 462 |
+
assert q.dtype == k.dtype == v.dtype
|
| 463 |
+
assert out.shape == q.shape
|
| 464 |
+
nheads_k = k.shape[1]
|
| 465 |
+
assert (nheads_q % nheads_k) == 0
|
| 466 |
+
|
| 467 |
+
# call implementation
|
| 468 |
+
if DEBUG:
|
| 469 |
+
print("Using Triton implementation")
|
| 470 |
+
attention_forward_prefill_triton_impl(
|
| 471 |
+
q,
|
| 472 |
+
k,
|
| 473 |
+
v,
|
| 474 |
+
out,
|
| 475 |
+
softmax_lse,
|
| 476 |
+
sd_mask,
|
| 477 |
+
softmax_scale,
|
| 478 |
+
alibi_slopes,
|
| 479 |
+
causal,
|
| 480 |
+
window_size_left,
|
| 481 |
+
window_size_right,
|
| 482 |
+
None,
|
| 483 |
+
layout,
|
| 484 |
+
cu_seqlens_q,
|
| 485 |
+
cu_seqlens_k,
|
| 486 |
+
max_seqlen_q,
|
| 487 |
+
max_seqlen_k,
|
| 488 |
+
dropout_p,
|
| 489 |
+
philox_seed,
|
| 490 |
+
philox_offset,
|
| 491 |
+
return_softmax,
|
| 492 |
+
USE_EXP2,
|
| 493 |
+
None,
|
| 494 |
+
None,
|
| 495 |
+
None,
|
| 496 |
+
)
|
| 497 |
+
|
| 498 |
+
if DEBUG:
|
| 499 |
+
print("varlen_fwd outputs")
|
| 500 |
+
print("out:", out, out.shape)
|
| 501 |
+
print("softmax_lse:", softmax_lse, softmax_lse.shape)
|
| 502 |
+
print("sd_mask:", sd_mask, sd_mask.shape if sd_mask is not None else None)
|
| 503 |
+
# --- Assertions ---
|
| 504 |
+
# out: (Total_Q, Hq, D)
|
| 505 |
+
assert (
|
| 506 |
+
out.shape == q.shape
|
| 507 |
+
), f"[varlen_fwd] out shape {out.shape} != q shape {q.shape}"
|
| 508 |
+
# softmax_lse: (Hq, Total_Q)
|
| 509 |
+
expected_lse_shape = (q.shape[1], q.shape[0])
|
| 510 |
+
assert (
|
| 511 |
+
softmax_lse.shape == expected_lse_shape
|
| 512 |
+
), f"[varlen_fwd] softmax_lse shape {softmax_lse.shape} != {expected_lse_shape}"
|
| 513 |
+
assert (
|
| 514 |
+
softmax_lse.dtype == torch.float32
|
| 515 |
+
), f"[varlen_fwd] softmax_lse dtype {softmax_lse.dtype} != torch.float32"
|
| 516 |
+
if return_softmax:
|
| 517 |
+
# sd_mask expected: (B, Hq, max_seqlen_q, max_seqlen_k)
|
| 518 |
+
assert (
|
| 519 |
+
sd_mask is not None
|
| 520 |
+
), "[varlen_fwd] return_softmax=True but sd_mask is None"
|
| 521 |
+
assert sd_mask.dim() == 4, f"[varlen_fwd] sd_mask dim {sd_mask.dim()} != 4"
|
| 522 |
+
batch = len(cu_seqlens_q) - 1
|
| 523 |
+
assert (
|
| 524 |
+
sd_mask.shape[0] == batch
|
| 525 |
+
), f"[varlen_fwd] sd_mask batch {sd_mask.shape[0]} != {batch}"
|
| 526 |
+
assert (
|
| 527 |
+
sd_mask.shape[1] == q.shape[1]
|
| 528 |
+
), f"[varlen_fwd] sd_mask nheads {sd_mask.shape[1]} != {q.shape[1]}"
|
| 529 |
+
if SHAPE_EXPECTATIONS == "rounded":
|
| 530 |
+
expected_sq = round_multiple(max_seqlen_q, 128)
|
| 531 |
+
expected_sk = round_multiple(max_seqlen_k, 128)
|
| 532 |
+
assert (
|
| 533 |
+
sd_mask.shape[2] == expected_sq and sd_mask.shape[3] == expected_sk
|
| 534 |
+
), f"[varlen_fwd] sd_mask shape {sd_mask.shape} != (B={batch}, Hq={q.shape[1]}, Sq={expected_sq}, Sk={expected_sk})"
|
| 535 |
+
else:
|
| 536 |
+
assert (
|
| 537 |
+
sd_mask.shape[2] == max_seqlen_q and sd_mask.shape[3] == max_seqlen_k
|
| 538 |
+
), f"[varlen_fwd] sd_mask shape {sd_mask.shape} != (B={batch}, Hq={q.shape[1]}, Sq={max_seqlen_q}, Sk={max_seqlen_k})"
|
| 539 |
+
else:
|
| 540 |
+
assert (
|
| 541 |
+
sd_mask is None
|
| 542 |
+
), "[varlen_fwd] return_softmax=False but sd_mask is not None"
|
| 543 |
+
return out, softmax_lse, sd_mask, rng_state
|
| 544 |
+
|
| 545 |
+
|
| 546 |
+
def varlen_bwd(
|
| 547 |
+
dout: torch.Tensor,
|
| 548 |
+
q: torch.Tensor,
|
| 549 |
+
k: torch.Tensor,
|
| 550 |
+
v: torch.Tensor,
|
| 551 |
+
out: torch.Tensor,
|
| 552 |
+
softmax_lse: torch.Tensor,
|
| 553 |
+
dq: Optional[torch.Tensor],
|
| 554 |
+
dk: Optional[torch.Tensor],
|
| 555 |
+
dv: Optional[torch.Tensor],
|
| 556 |
+
cu_seqlens_q: torch.Tensor,
|
| 557 |
+
cu_seqlens_k: torch.Tensor,
|
| 558 |
+
alibi_slopes: Optional[torch.Tensor],
|
| 559 |
+
max_seqlen_q: int,
|
| 560 |
+
max_seqlen_k: int,
|
| 561 |
+
dropout_p: float,
|
| 562 |
+
softmax_scale: float,
|
| 563 |
+
zero_tensors: bool,
|
| 564 |
+
causal: bool,
|
| 565 |
+
window_size_left: int,
|
| 566 |
+
window_size_right: int,
|
| 567 |
+
softcap: float,
|
| 568 |
+
deterministic: bool,
|
| 569 |
+
gen_: Optional[torch.Tensor] = None,
|
| 570 |
+
rng_state: Optional[torch.Tensor] = None,
|
| 571 |
+
) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor]:
|
| 572 |
+
if str(q.dtype).startswith("torch.float8"):
|
| 573 |
+
raise NotImplementedError(
|
| 574 |
+
"FP8 tensors are not supported in the AMD Triton FA2 interface (varlen_bwd). Use the FA3 path instead."
|
| 575 |
+
)
|
| 576 |
+
if softcap != 0.0:
|
| 577 |
+
raise NotImplementedError(
|
| 578 |
+
"softcap is not supported in varlen_bwd (expected 0.0)."
|
| 579 |
+
)
|
| 580 |
+
|
| 581 |
+
if DEBUG:
|
| 582 |
+
print()
|
| 583 |
+
print("varlen_bwd")
|
| 584 |
+
print("dout:", dout.shape)
|
| 585 |
+
print("q:", q.shape)
|
| 586 |
+
print("k:", k.shape)
|
| 587 |
+
print("v:", v.shape)
|
| 588 |
+
print("out:", out)
|
| 589 |
+
print("softmax_lse:", softmax_lse.shape)
|
| 590 |
+
print("dq:", dq.shape if dq is not None else None)
|
| 591 |
+
print("dk:", dk.shape if dk is not None else None)
|
| 592 |
+
print("dv:", dv.shape if dv is not None else None)
|
| 593 |
+
print("cu_seqlens_q:", cu_seqlens_q, cu_seqlens_q.shape)
|
| 594 |
+
print("cu_seqlens_k:", cu_seqlens_k, cu_seqlens_k.shape)
|
| 595 |
+
print("alibi_slopes:", alibi_slopes)
|
| 596 |
+
print("max_seqlen_q:", max_seqlen_q)
|
| 597 |
+
print("max_seqlen_k:", max_seqlen_k)
|
| 598 |
+
print("dropout_p:", dropout_p)
|
| 599 |
+
print("softmax_scale:", softmax_scale)
|
| 600 |
+
print("causal:", causal)
|
| 601 |
+
print("window_size_left:", window_size_left)
|
| 602 |
+
print("window_size_right:", window_size_right)
|
| 603 |
+
print("deterministic:", deterministic)
|
| 604 |
+
print("gen_:", gen_)
|
| 605 |
+
print("rng_state:", rng_state)
|
| 606 |
+
|
| 607 |
+
dq = torch.zeros_like(q) if dq is None else dq.zero_()
|
| 608 |
+
dk = torch.zeros_like(k) if dk is None else dk.zero_()
|
| 609 |
+
dv = torch.zeros_like(v) if dv is None else dv.zero_()
|
| 610 |
+
|
| 611 |
+
# get shape
|
| 612 |
+
batch = len(cu_seqlens_q) - 1
|
| 613 |
+
total_q, nheads_q, _ = q.shape
|
| 614 |
+
|
| 615 |
+
# Create delta tensor with shape based on expectations
|
| 616 |
+
# delta (softmax_d) : (Hq, Total_Q) or (Hq, Total_Q + 128*batch)
|
| 617 |
+
if SHAPE_EXPECTATIONS == "rounded":
|
| 618 |
+
delta = torch.zeros(
|
| 619 |
+
(nheads_q, total_q + 128 * batch), device=q.device, dtype=torch.float32
|
| 620 |
+
)
|
| 621 |
+
else:
|
| 622 |
+
delta = torch.zeros((nheads_q, total_q), device=q.device, dtype=torch.float32)
|
| 623 |
+
|
| 624 |
+
# Upstream change: base seeding logic on provided rng_state instead of dropout probability.
|
| 625 |
+
if rng_state is not None:
|
| 626 |
+
philox_seed, philox_offset = rng_state[0].item(), rng_state[1].item()
|
| 627 |
+
else:
|
| 628 |
+
philox_seed, philox_offset = None, None
|
| 629 |
+
|
| 630 |
+
if alibi_slopes is not None:
|
| 631 |
+
if alibi_slopes.dim() == 2:
|
| 632 |
+
pass
|
| 633 |
+
elif alibi_slopes.dim() == 1:
|
| 634 |
+
alibi_slopes = alibi_slopes.unsqueeze(0).expand(batch, -1)
|
| 635 |
+
else:
|
| 636 |
+
raise ValueError("Alibi can be (nheads,) or (batch_size, nheads).")
|
| 637 |
+
|
| 638 |
+
# call implementation
|
| 639 |
+
if DEBUG:
|
| 640 |
+
print(f"Using Triton implementation in {BWD_MODE} mode")
|
| 641 |
+
attention_backward_triton_impl(
|
| 642 |
+
do=dout,
|
| 643 |
+
q=q,
|
| 644 |
+
k=k,
|
| 645 |
+
v=v,
|
| 646 |
+
o=out,
|
| 647 |
+
softmax_lse=softmax_lse,
|
| 648 |
+
dq=dq,
|
| 649 |
+
dk=dk,
|
| 650 |
+
dv=dv,
|
| 651 |
+
delta=delta,
|
| 652 |
+
sm_scale=softmax_scale,
|
| 653 |
+
alibi_slopes=alibi_slopes,
|
| 654 |
+
causal=causal,
|
| 655 |
+
layout="thd",
|
| 656 |
+
cu_seqlens_q=cu_seqlens_q,
|
| 657 |
+
cu_seqlens_k=cu_seqlens_k,
|
| 658 |
+
max_seqlen_q=max_seqlen_q,
|
| 659 |
+
max_seqlen_k=max_seqlen_k,
|
| 660 |
+
seqused_q=None,
|
| 661 |
+
seqused_k=None,
|
| 662 |
+
dropout_p=dropout_p,
|
| 663 |
+
philox_seed=philox_seed,
|
| 664 |
+
philox_offset=philox_offset,
|
| 665 |
+
use_exp2=USE_EXP2,
|
| 666 |
+
mode=BWD_MODE,
|
| 667 |
+
)
|
| 668 |
+
|
| 669 |
+
if DEBUG:
|
| 670 |
+
print("varlen_bwd outputs")
|
| 671 |
+
print("delta:", delta, delta.shape)
|
| 672 |
+
print("dv:", dv, dv.shape)
|
| 673 |
+
print("dk:", dk, dk.shape)
|
| 674 |
+
print("dq:", dq, dq.shape)
|
| 675 |
+
# --- Assertions ---
|
| 676 |
+
assert dq.shape == q.shape, f"[varlen_bwd] dq shape {dq.shape} != q shape {q.shape}"
|
| 677 |
+
assert dk.shape == k.shape, f"[varlen_bwd] dk shape {dk.shape} != k shape {k.shape}"
|
| 678 |
+
assert dv.shape == v.shape, f"[varlen_bwd] dv shape {dv.shape} != v shape {v.shape}"
|
| 679 |
+
if SHAPE_EXPECTATIONS == "rounded":
|
| 680 |
+
batch = len(cu_seqlens_q) - 1
|
| 681 |
+
expected_delta_shape = (q.shape[1], q.shape[0] + 128 * batch)
|
| 682 |
+
else:
|
| 683 |
+
expected_delta_shape = (q.shape[1], q.shape[0]) # (Hq, Total_Q)
|
| 684 |
+
assert (
|
| 685 |
+
delta.shape == expected_delta_shape
|
| 686 |
+
), f"[varlen_bwd] delta shape {delta.shape} != {expected_delta_shape}"
|
| 687 |
+
return dq, dk, dv, delta
|
| 688 |
+
|
| 689 |
+
|
| 690 |
+
def fwd_kvcache(
|
| 691 |
+
q: torch.Tensor,
|
| 692 |
+
k_cache: torch.Tensor,
|
| 693 |
+
v_cache: torch.Tensor,
|
| 694 |
+
k: Optional[torch.Tensor],
|
| 695 |
+
v: Optional[torch.Tensor],
|
| 696 |
+
cache_seqlens: Optional[Union[int, torch.Tensor]],
|
| 697 |
+
rotary_cos: Optional[torch.Tensor],
|
| 698 |
+
rotary_sin: Optional[torch.Tensor],
|
| 699 |
+
cache_batch_idx: Optional[torch.Tensor],
|
| 700 |
+
cache_leftpad: Optional[torch.Tensor],
|
| 701 |
+
block_table: Optional[torch.Tensor],
|
| 702 |
+
alibi_slopes: Optional[torch.Tensor],
|
| 703 |
+
out: Optional[torch.Tensor],
|
| 704 |
+
softmax_scale: float,
|
| 705 |
+
causal: bool,
|
| 706 |
+
window_size_left: int,
|
| 707 |
+
window_size_right: int,
|
| 708 |
+
softcap: float,
|
| 709 |
+
rotary_interleaved: bool,
|
| 710 |
+
num_splits: int,
|
| 711 |
+
) -> tuple[torch.Tensor, torch.Tensor]:
|
| 712 |
+
|
| 713 |
+
if softcap != 0.0:
|
| 714 |
+
raise NotImplementedError(
|
| 715 |
+
"softcap is not supported in fwd_kvcache (expected 0.0)."
|
| 716 |
+
)
|
| 717 |
+
if num_splits not in (0, 1):
|
| 718 |
+
raise NotImplementedError(
|
| 719 |
+
"num_splits > 1 not supported in AMD Triton FA2 fwd_kvcache."
|
| 720 |
+
)
|
| 721 |
+
|
| 722 |
+
if DEBUG:
|
| 723 |
+
print()
|
| 724 |
+
print("flash_attn_triton_amd.py::fwd_kvcache inputs")
|
| 725 |
+
print("q:", q, q.shape)
|
| 726 |
+
print("k_cache:", k_cache, k_cache.shape)
|
| 727 |
+
print("v_cache:", v_cache, v_cache.shape)
|
| 728 |
+
print("k:", k, k.shape if k is not None else None)
|
| 729 |
+
print("v:", v, v.shape if v is not None else None)
|
| 730 |
+
print("cache_seqlens:", cache_seqlens)
|
| 731 |
+
print("rotary_cos:", rotary_cos)
|
| 732 |
+
print("rotary_sin:", rotary_sin)
|
| 733 |
+
print("cache_batch_idx:", cache_batch_idx)
|
| 734 |
+
print("cache_leftpad:", cache_leftpad)
|
| 735 |
+
print("block_table:", block_table)
|
| 736 |
+
print("alibi_slopes:", alibi_slopes)
|
| 737 |
+
print("out:", out)
|
| 738 |
+
print("softmax_scale:", softmax_scale)
|
| 739 |
+
print("causal:", causal)
|
| 740 |
+
print("window_size_left:", window_size_left)
|
| 741 |
+
print("window_size_right:", window_size_right)
|
| 742 |
+
print("softcap:", softcap)
|
| 743 |
+
print("rotary_interleaved:", rotary_interleaved)
|
| 744 |
+
print("num_splits:", num_splits)
|
| 745 |
+
|
| 746 |
+
# output
|
| 747 |
+
out = torch.zeros_like(q) if out is None else out.zero_()
|
| 748 |
+
|
| 749 |
+
# Basic layout info for decode path
|
| 750 |
+
layout: Literal["bshd"] = "bshd"
|
| 751 |
+
q.shape[1]
|
| 752 |
+
k_cache.shape[1]
|
| 753 |
+
cache_seqlens_tensor = (
|
| 754 |
+
torch.tensor(cache_seqlens, device=q.device)
|
| 755 |
+
if isinstance(cache_seqlens, int)
|
| 756 |
+
else cache_seqlens
|
| 757 |
+
)
|
| 758 |
+
window_left = (
|
| 759 |
+
int(window_size_left.item())
|
| 760 |
+
if isinstance(window_size_left, torch.Tensor)
|
| 761 |
+
else window_size_left
|
| 762 |
+
)
|
| 763 |
+
window_right = (
|
| 764 |
+
int(window_size_right.item())
|
| 765 |
+
if isinstance(window_size_right, torch.Tensor)
|
| 766 |
+
else window_size_right
|
| 767 |
+
)
|
| 768 |
+
|
| 769 |
+
k_new = k
|
| 770 |
+
v_new = v
|
| 771 |
+
|
| 772 |
+
# get shape
|
| 773 |
+
batch, seqlen_q, nheads_q, _ = q.shape
|
| 774 |
+
|
| 775 |
+
# Create softmax_lse tensor - decode always uses exact shape (B, Hq, Sq)
|
| 776 |
+
softmax_lse = torch.zeros(
|
| 777 |
+
(batch, nheads_q, seqlen_q), device=q.device, dtype=torch.float32
|
| 778 |
+
)
|
| 779 |
+
|
| 780 |
+
if alibi_slopes is not None:
|
| 781 |
+
if alibi_slopes.dim() == 1:
|
| 782 |
+
alibi_slopes = alibi_slopes.unsqueeze(0).expand(batch, -1)
|
| 783 |
+
assert alibi_slopes.is_cuda and alibi_slopes.dim() == 2
|
| 784 |
+
assert alibi_slopes.shape == (batch, nheads_q)
|
| 785 |
+
|
| 786 |
+
# launch kernel
|
| 787 |
+
if DEBUG:
|
| 788 |
+
print("Using Triton implementation")
|
| 789 |
+
attention_forward_decode_triton_impl(
|
| 790 |
+
q,
|
| 791 |
+
k_cache,
|
| 792 |
+
v_cache,
|
| 793 |
+
k_new,
|
| 794 |
+
v_new,
|
| 795 |
+
out,
|
| 796 |
+
softmax_lse,
|
| 797 |
+
softmax_scale,
|
| 798 |
+
causal,
|
| 799 |
+
window_left,
|
| 800 |
+
window_right,
|
| 801 |
+
alibi_slopes,
|
| 802 |
+
layout,
|
| 803 |
+
cache_seqlens_tensor,
|
| 804 |
+
cache_batch_idx,
|
| 805 |
+
block_table,
|
| 806 |
+
None,
|
| 807 |
+
None,
|
| 808 |
+
None,
|
| 809 |
+
rotary_cos=rotary_cos,
|
| 810 |
+
rotary_sin=rotary_sin,
|
| 811 |
+
rotary_interleaved=rotary_interleaved,
|
| 812 |
+
)
|
| 813 |
+
|
| 814 |
+
if DEBUG:
|
| 815 |
+
print("out:", out, out.shape)
|
| 816 |
+
print("softmax_lse:", softmax_lse, softmax_lse.shape)
|
| 817 |
+
# --- Assertions ---
|
| 818 |
+
assert (
|
| 819 |
+
out.shape == q.shape
|
| 820 |
+
), f"[fwd_kvcache] out shape {out.shape} != q shape {q.shape}"
|
| 821 |
+
expected_lse_shape = (q.shape[0], q.shape[2], q.shape[1])
|
| 822 |
+
assert (
|
| 823 |
+
softmax_lse.shape == expected_lse_shape
|
| 824 |
+
), f"[fwd_kvcache] softmax_lse shape {softmax_lse.shape} != {expected_lse_shape}"
|
| 825 |
+
assert (
|
| 826 |
+
softmax_lse.dtype == torch.float32
|
| 827 |
+
), f"[fwd_kvcache] softmax_lse dtype {softmax_lse.dtype} != torch.float32"
|
| 828 |
+
return out, softmax_lse
|
build/torch-rocm/_kernels/flash_attn_triton_amd/interface_v3.py
ADDED
|
@@ -0,0 +1,660 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
from typing import Literal, Optional, Tuple
|
| 3 |
+
from .fwd_prefill import attention_forward_prefill_triton_impl
|
| 4 |
+
from .fwd_decode import attention_forward_decode_triton_impl
|
| 5 |
+
from .bwd import attention_backward_triton_impl
|
| 6 |
+
from .utils import (
|
| 7 |
+
DEBUG,
|
| 8 |
+
USE_EXP2,
|
| 9 |
+
BWD_MODE,
|
| 10 |
+
PHILOX_SEED,
|
| 11 |
+
PHILOX_OFFSET,
|
| 12 |
+
is_fp8,
|
| 13 |
+
)
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
def fwd(
|
| 17 |
+
q: torch.Tensor,
|
| 18 |
+
k: torch.Tensor,
|
| 19 |
+
v: torch.Tensor,
|
| 20 |
+
k_new: Optional[torch.Tensor],
|
| 21 |
+
v_new: Optional[torch.Tensor],
|
| 22 |
+
qv: Optional[torch.Tensor],
|
| 23 |
+
out: Optional[torch.Tensor],
|
| 24 |
+
cu_seqlens_q: Optional[torch.Tensor],
|
| 25 |
+
cu_seqlens_k: Optional[torch.Tensor],
|
| 26 |
+
cu_seqlens_k_new: Optional[torch.Tensor],
|
| 27 |
+
seqused_q: Optional[torch.Tensor],
|
| 28 |
+
seqused_k: Optional[torch.Tensor],
|
| 29 |
+
max_seqlen_q: Optional[int],
|
| 30 |
+
max_seqlen_k: Optional[int],
|
| 31 |
+
page_table: Optional[torch.Tensor],
|
| 32 |
+
kv_batch_idx: Optional[torch.Tensor],
|
| 33 |
+
leftpad_k: Optional[torch.Tensor],
|
| 34 |
+
rotary_cos: Optional[torch.Tensor],
|
| 35 |
+
rotary_sin: Optional[torch.Tensor],
|
| 36 |
+
seqlens_rotary: Optional[torch.Tensor],
|
| 37 |
+
q_descale: Optional[torch.Tensor],
|
| 38 |
+
k_descale: Optional[torch.Tensor],
|
| 39 |
+
v_descale: Optional[torch.Tensor],
|
| 40 |
+
softmax_scale: float,
|
| 41 |
+
causal: bool,
|
| 42 |
+
window_size_left: int,
|
| 43 |
+
window_size_right: int,
|
| 44 |
+
attention_chunk: int,
|
| 45 |
+
softcap: float,
|
| 46 |
+
rotary_interleaved: bool,
|
| 47 |
+
scheduler_metadata: Optional[torch.Tensor] = None,
|
| 48 |
+
num_splits: int = 1,
|
| 49 |
+
pack_gqa: Optional[bool] = None,
|
| 50 |
+
sm_margin: int = 0,
|
| 51 |
+
) -> Tuple[torch.Tensor, torch.Tensor, Optional[torch.Tensor], Optional[torch.Tensor]]:
|
| 52 |
+
"""
|
| 53 |
+
Flash Attention v3 forward pass compatible interface for AMD Triton implementation.
|
| 54 |
+
|
| 55 |
+
This function maps v3 parameters to the existing AMD Triton implementation.
|
| 56 |
+
"""
|
| 57 |
+
|
| 58 |
+
if DEBUG:
|
| 59 |
+
print()
|
| 60 |
+
print("interface_fa_v3.py::fwd inputs")
|
| 61 |
+
print("q:", q.shape)
|
| 62 |
+
print("k:", k.shape)
|
| 63 |
+
print("v:", v.shape)
|
| 64 |
+
print("k_new:", k_new.shape if k_new is not None else None)
|
| 65 |
+
print("v_new:", v_new.shape if v_new is not None else None)
|
| 66 |
+
print("qv:", qv.shape if qv is not None else None)
|
| 67 |
+
print("out:", out.shape if out is not None else None)
|
| 68 |
+
print("cu_seqlens_q:", cu_seqlens_q.shape if cu_seqlens_q is not None else None)
|
| 69 |
+
print("cu_seqlens_k:", cu_seqlens_k.shape if cu_seqlens_k is not None else None)
|
| 70 |
+
print(
|
| 71 |
+
"cu_seqlens_k_new:",
|
| 72 |
+
cu_seqlens_k_new.shape if cu_seqlens_k_new is not None else None,
|
| 73 |
+
)
|
| 74 |
+
print("seqused_q:", seqused_q.shape if seqused_q is not None else None)
|
| 75 |
+
print("seqused_k:", seqused_k.shape if seqused_k is not None else None)
|
| 76 |
+
print("max_seqlen_q:", max_seqlen_q)
|
| 77 |
+
print("max_seqlen_k:", max_seqlen_k)
|
| 78 |
+
print("page_table:", page_table.shape if page_table is not None else None)
|
| 79 |
+
print("kv_batch_idx:", kv_batch_idx.shape if kv_batch_idx is not None else None)
|
| 80 |
+
print("leftpad_k:", leftpad_k.shape if leftpad_k is not None else None)
|
| 81 |
+
print("rotary_cos:", rotary_cos.shape if rotary_cos is not None else None)
|
| 82 |
+
print("rotary_sin:", rotary_sin.shape if rotary_sin is not None else None)
|
| 83 |
+
print(
|
| 84 |
+
"seqlens_rotary:",
|
| 85 |
+
seqlens_rotary.shape if seqlens_rotary is not None else None,
|
| 86 |
+
)
|
| 87 |
+
print("q_descale:", q_descale.shape if q_descale is not None else None)
|
| 88 |
+
print("k_descale:", k_descale.shape if k_descale is not None else None)
|
| 89 |
+
print("v_descale:", v_descale.shape if v_descale is not None else None)
|
| 90 |
+
print("softmax_scale:", softmax_scale)
|
| 91 |
+
print("causal:", causal)
|
| 92 |
+
print("window_size_left:", window_size_left)
|
| 93 |
+
print("window_size_right:", window_size_right)
|
| 94 |
+
print("attention_chunk:", attention_chunk)
|
| 95 |
+
print("softcap:", softcap)
|
| 96 |
+
print("rotary_interleaved:", rotary_interleaved)
|
| 97 |
+
print("scheduler_metadata:", scheduler_metadata)
|
| 98 |
+
print("num_splits:", num_splits)
|
| 99 |
+
print("pack_gqa:", pack_gqa)
|
| 100 |
+
print("sm_margin:", sm_margin)
|
| 101 |
+
|
| 102 |
+
# Handle qv packed input
|
| 103 |
+
if qv is not None:
|
| 104 |
+
raise NotImplementedError(
|
| 105 |
+
"QV packed input is not yet supported in the AMD Triton backend"
|
| 106 |
+
)
|
| 107 |
+
|
| 108 |
+
# Handle softcap
|
| 109 |
+
if softcap != 0.0:
|
| 110 |
+
raise NotImplementedError(
|
| 111 |
+
f"Softcap is not yet supported in the AMD Triton backend (got softcap={softcap}, expected 0.0)"
|
| 112 |
+
)
|
| 113 |
+
|
| 114 |
+
# Handle attention_chunk
|
| 115 |
+
if attention_chunk != 0 and attention_chunk != 1:
|
| 116 |
+
raise NotImplementedError(
|
| 117 |
+
f"attention_chunk is not yet supported in the AMD Triton backend (got attention_chunk={attention_chunk})"
|
| 118 |
+
)
|
| 119 |
+
|
| 120 |
+
# Handle scheduler metadata
|
| 121 |
+
if scheduler_metadata is not None:
|
| 122 |
+
raise NotImplementedError(
|
| 123 |
+
"Scheduler metadata is not yet supported in the AMD Triton backend"
|
| 124 |
+
)
|
| 125 |
+
|
| 126 |
+
# Handle pack_gqa
|
| 127 |
+
if pack_gqa is not None and pack_gqa is not False:
|
| 128 |
+
raise NotImplementedError(
|
| 129 |
+
f"pack_gqa is not yet supported in the AMD Triton backend (got pack_gqa={pack_gqa})"
|
| 130 |
+
)
|
| 131 |
+
|
| 132 |
+
# Handle num_splits
|
| 133 |
+
if num_splits != 1:
|
| 134 |
+
raise NotImplementedError(
|
| 135 |
+
f"Split attention (num_splits > 1) is not yet supported in the AMD Triton backend (got num_splits={num_splits})"
|
| 136 |
+
)
|
| 137 |
+
|
| 138 |
+
# Handle sm_margin
|
| 139 |
+
if sm_margin != 0:
|
| 140 |
+
raise NotImplementedError(
|
| 141 |
+
f"sm_margin is not yet supported in the AMD Triton backend (got sm_margin={sm_margin}, expected 0)"
|
| 142 |
+
)
|
| 143 |
+
|
| 144 |
+
# Handle leftpad_k
|
| 145 |
+
if leftpad_k is not None:
|
| 146 |
+
raise NotImplementedError(
|
| 147 |
+
"Left padding (leftpad_k) is not yet supported in the AMD Triton backend"
|
| 148 |
+
)
|
| 149 |
+
|
| 150 |
+
# Handle cu_seqlens_k_new
|
| 151 |
+
if cu_seqlens_k_new is not None:
|
| 152 |
+
raise NotImplementedError(
|
| 153 |
+
"cu_seqlens_k_new is not yet supported in the AMD Triton backend"
|
| 154 |
+
)
|
| 155 |
+
|
| 156 |
+
# establish layout / varlen & max seq lens
|
| 157 |
+
if cu_seqlens_q is not None:
|
| 158 |
+
if len(q.shape) != 3:
|
| 159 |
+
raise ValueError(
|
| 160 |
+
f"cu_seqlens_q provided but q has shape {q.shape}, expected 3D tensor for varlen"
|
| 161 |
+
)
|
| 162 |
+
layout: Literal["bshd", "thd"] = "thd"
|
| 163 |
+
cu_seqlens_q_local = cu_seqlens_q
|
| 164 |
+
assert max_seqlen_q is not None, "max_seqlen_q required for varlen mode"
|
| 165 |
+
max_seqlens_q_local = max_seqlen_q
|
| 166 |
+
if cu_seqlens_k is not None:
|
| 167 |
+
cu_seqlens_k_local = cu_seqlens_k
|
| 168 |
+
assert (
|
| 169 |
+
max_seqlen_k is not None
|
| 170 |
+
), "max_seqlen_k required when cu_seqlens_k provided"
|
| 171 |
+
max_seqlens_k_local = max_seqlen_k
|
| 172 |
+
else:
|
| 173 |
+
cu_seqlens_k_local = None
|
| 174 |
+
if len(k.shape) == 4:
|
| 175 |
+
max_seqlens_k_local = k.shape[1]
|
| 176 |
+
else:
|
| 177 |
+
assert max_seqlen_k is not None, "max_seqlen_k required for varlen mode"
|
| 178 |
+
max_seqlens_k_local = max_seqlen_k
|
| 179 |
+
else:
|
| 180 |
+
layout = "bshd"
|
| 181 |
+
cu_seqlens_q_local = None
|
| 182 |
+
cu_seqlens_k_local = None
|
| 183 |
+
max_seqlens_q_local = q.shape[1] if max_seqlen_q is None else max_seqlen_q
|
| 184 |
+
max_seqlens_k_local = k.shape[1] if max_seqlen_k is None else max_seqlen_k
|
| 185 |
+
|
| 186 |
+
# Now determine if we should use decode or prefill kernel
|
| 187 |
+
# Decode kernel should be used for KV cache scenarios where:
|
| 188 |
+
# 1. k_new/v_new are provided - incremental KV cache update (primary KV cache indicator)
|
| 189 |
+
# 2. kv_batch_idx is provided - KV cache batch indexing (primary KV cache indicator)
|
| 190 |
+
# 3. seqused_k without seqused_q - indicates KV cache fill levels (not varlen masking)
|
| 191 |
+
# Note: In varlen, both seqused_q and seqused_k are used for sequence masking
|
| 192 |
+
# In KV cache, only seqused_k is used to track cache fill levels
|
| 193 |
+
# Detect KV cache scenarios:
|
| 194 |
+
# - Clear KV cache indicators (k_new, v_new, kv_batch_idx)
|
| 195 |
+
# - OR seqused_k without seqused_q (KV cache fill tracking, not varlen masking)
|
| 196 |
+
use_decode = (
|
| 197 |
+
k_new is not None # Have new KV to append (KV cache indicator)
|
| 198 |
+
or v_new is not None # Have new KV to append (KV cache indicator)
|
| 199 |
+
or kv_batch_idx is not None # Have KV cache batch indexing (KV cache indicator)
|
| 200 |
+
or (
|
| 201 |
+
seqused_k is not None and seqused_q is None
|
| 202 |
+
) # KV cache fill levels (not varlen)
|
| 203 |
+
)
|
| 204 |
+
|
| 205 |
+
# Check for unsupported features with decode kernel
|
| 206 |
+
if use_decode:
|
| 207 |
+
if layout == "thd":
|
| 208 |
+
raise NotImplementedError(
|
| 209 |
+
"Varlen is not yet supported with the decode kernel in the AMD Triton backend"
|
| 210 |
+
)
|
| 211 |
+
if kv_batch_idx is not None:
|
| 212 |
+
raise NotImplementedError(
|
| 213 |
+
"kv_batch_idx is not yet supported with the decode kernel in the AMD Triton backend"
|
| 214 |
+
)
|
| 215 |
+
|
| 216 |
+
if out is None:
|
| 217 |
+
# NOTE: Using types that are lower precision than float32 such as bfloat16 for fp8 causes mismatches on a small set of tests.
|
| 218 |
+
out_dtype = torch.float32 if is_fp8([q, k, v]) else q.dtype
|
| 219 |
+
if layout == "bshd":
|
| 220 |
+
out = torch.zeros(
|
| 221 |
+
q.shape[0],
|
| 222 |
+
q.shape[1],
|
| 223 |
+
q.shape[2],
|
| 224 |
+
v.shape[-1],
|
| 225 |
+
dtype=out_dtype,
|
| 226 |
+
device=q.device,
|
| 227 |
+
)
|
| 228 |
+
elif layout == "thd":
|
| 229 |
+
out = torch.zeros(
|
| 230 |
+
q.shape[0], q.shape[1], v.shape[-1], dtype=out_dtype, device=q.device
|
| 231 |
+
)
|
| 232 |
+
else:
|
| 233 |
+
raise ValueError(
|
| 234 |
+
f"Unsupported layout: {layout}. Only 'bshd' and 'thd' layouts are supported."
|
| 235 |
+
)
|
| 236 |
+
else:
|
| 237 |
+
out = out.zero_()
|
| 238 |
+
|
| 239 |
+
# Handle causal mask
|
| 240 |
+
causal_flag = bool(causal)
|
| 241 |
+
|
| 242 |
+
# Handle alibi slopes
|
| 243 |
+
alibi_slopes = None
|
| 244 |
+
|
| 245 |
+
# Handle dropout
|
| 246 |
+
dropout_p = 0.0
|
| 247 |
+
return_softmax = False
|
| 248 |
+
philox_seed = PHILOX_SEED
|
| 249 |
+
philox_offset = PHILOX_OFFSET
|
| 250 |
+
|
| 251 |
+
# Call implementation
|
| 252 |
+
if DEBUG:
|
| 253 |
+
print("Using Triton implementation")
|
| 254 |
+
|
| 255 |
+
if use_decode:
|
| 256 |
+
if DEBUG:
|
| 257 |
+
print(
|
| 258 |
+
f"Using Decode Triton implementation (cache_seqlens={seqused_k is not None}, k_new={k_new is not None}, v_new={v_new is not None}, kv_batch_idx={kv_batch_idx is not None})"
|
| 259 |
+
)
|
| 260 |
+
|
| 261 |
+
# Create softmax_lse tensor for decode - always exact shape (B, Hq, Sq)
|
| 262 |
+
batch, seqlen_q, nheads_q, _ = q.shape
|
| 263 |
+
softmax_lse = torch.zeros(
|
| 264 |
+
(batch, nheads_q, seqlen_q), device=q.device, dtype=torch.float32
|
| 265 |
+
)
|
| 266 |
+
|
| 267 |
+
# Decode only supports bshd layout
|
| 268 |
+
assert layout == "bshd", f"decode requires bshd layout, got {layout}"
|
| 269 |
+
attention_forward_decode_triton_impl(
|
| 270 |
+
q,
|
| 271 |
+
k,
|
| 272 |
+
v,
|
| 273 |
+
k_new,
|
| 274 |
+
v_new,
|
| 275 |
+
out,
|
| 276 |
+
softmax_lse,
|
| 277 |
+
softmax_scale,
|
| 278 |
+
causal_flag,
|
| 279 |
+
window_size_left,
|
| 280 |
+
window_size_right,
|
| 281 |
+
alibi_slopes,
|
| 282 |
+
layout,
|
| 283 |
+
seqused_k,
|
| 284 |
+
kv_batch_idx,
|
| 285 |
+
page_table,
|
| 286 |
+
q_descale,
|
| 287 |
+
k_descale,
|
| 288 |
+
v_descale,
|
| 289 |
+
rotary_cos=rotary_cos,
|
| 290 |
+
rotary_sin=rotary_sin,
|
| 291 |
+
rotary_interleaved=rotary_interleaved,
|
| 292 |
+
seqlens_rotary=seqlens_rotary,
|
| 293 |
+
)
|
| 294 |
+
else:
|
| 295 |
+
if DEBUG:
|
| 296 |
+
print("Using Prefill Triton implementation")
|
| 297 |
+
|
| 298 |
+
# Create softmax_lse tensor - FA3 always uses exact shapes
|
| 299 |
+
if layout == "thd":
|
| 300 |
+
# varlen: (Hq, Total_Q)
|
| 301 |
+
total_q, nheads_q, _ = q.shape
|
| 302 |
+
softmax_lse = torch.zeros(
|
| 303 |
+
(nheads_q, total_q), device=q.device, dtype=torch.float32
|
| 304 |
+
)
|
| 305 |
+
else:
|
| 306 |
+
# bshd: (B, Hq, Sq)
|
| 307 |
+
batch, seqlen_q, nheads_q, _ = q.shape
|
| 308 |
+
softmax_lse = torch.zeros(
|
| 309 |
+
(batch, nheads_q, seqlen_q), device=q.device, dtype=torch.float32
|
| 310 |
+
)
|
| 311 |
+
|
| 312 |
+
# sd_mask is not returned in v3 interface
|
| 313 |
+
sd_mask = None
|
| 314 |
+
|
| 315 |
+
attention_forward_prefill_triton_impl(
|
| 316 |
+
q,
|
| 317 |
+
k,
|
| 318 |
+
v,
|
| 319 |
+
out,
|
| 320 |
+
softmax_lse,
|
| 321 |
+
sd_mask,
|
| 322 |
+
softmax_scale,
|
| 323 |
+
alibi_slopes,
|
| 324 |
+
causal_flag,
|
| 325 |
+
window_size_left,
|
| 326 |
+
window_size_right,
|
| 327 |
+
None,
|
| 328 |
+
layout,
|
| 329 |
+
cu_seqlens_q_local,
|
| 330 |
+
cu_seqlens_k_local,
|
| 331 |
+
max_seqlens_q_local,
|
| 332 |
+
max_seqlens_k_local,
|
| 333 |
+
dropout_p,
|
| 334 |
+
philox_seed,
|
| 335 |
+
philox_offset,
|
| 336 |
+
return_softmax,
|
| 337 |
+
USE_EXP2,
|
| 338 |
+
q_descale,
|
| 339 |
+
k_descale,
|
| 340 |
+
v_descale,
|
| 341 |
+
seqused_q,
|
| 342 |
+
seqused_k,
|
| 343 |
+
rotary_cos=rotary_cos,
|
| 344 |
+
rotary_sin=rotary_sin,
|
| 345 |
+
rotary_interleaved=rotary_interleaved,
|
| 346 |
+
seqlens_rotary=seqlens_rotary,
|
| 347 |
+
)
|
| 348 |
+
|
| 349 |
+
if DEBUG:
|
| 350 |
+
print("interface_fa_v3.py::fwd outputs")
|
| 351 |
+
print("out:", out.shape)
|
| 352 |
+
print("softmax_lse:", softmax_lse.shape)
|
| 353 |
+
|
| 354 |
+
# --- Assertions (FA3 always expects exact shapes) ---
|
| 355 |
+
# out: same shape as q except last dim is v's head_dim
|
| 356 |
+
if layout == "thd":
|
| 357 |
+
# varlen: (Total_Q, Hq, Dv)
|
| 358 |
+
assert (
|
| 359 |
+
out.shape[0] == q.shape[0]
|
| 360 |
+
), f"[fwd_v3] out.shape[0] {out.shape[0]} != q.shape[0] {q.shape[0]}"
|
| 361 |
+
assert (
|
| 362 |
+
out.shape[1] == q.shape[1]
|
| 363 |
+
), f"[fwd_v3] out.shape[1] {out.shape[1]} != q.shape[1] {q.shape[1]}"
|
| 364 |
+
assert (
|
| 365 |
+
out.shape[2] == v.shape[-1]
|
| 366 |
+
), f"[fwd_v3] out.shape[2] {out.shape[2]} != v.shape[-1] {v.shape[-1]}"
|
| 367 |
+
else:
|
| 368 |
+
# bshd: (B, Sq, Hq, Dv)
|
| 369 |
+
assert (
|
| 370 |
+
out.shape[0] == q.shape[0]
|
| 371 |
+
), f"[fwd_v3] out.shape[0] {out.shape[0]} != q.shape[0] {q.shape[0]}"
|
| 372 |
+
assert (
|
| 373 |
+
out.shape[1] == q.shape[1]
|
| 374 |
+
), f"[fwd_v3] out.shape[1] {out.shape[1]} != q.shape[1] {q.shape[1]}"
|
| 375 |
+
assert (
|
| 376 |
+
out.shape[2] == q.shape[2]
|
| 377 |
+
), f"[fwd_v3] out.shape[2] {out.shape[2]} != q.shape[2] {q.shape[2]}"
|
| 378 |
+
assert (
|
| 379 |
+
out.shape[3] == v.shape[-1]
|
| 380 |
+
), f"[fwd_v3] out.shape[3] {out.shape[3]} != v.shape[-1] {v.shape[-1]}"
|
| 381 |
+
|
| 382 |
+
# softmax_lse dtype
|
| 383 |
+
assert (
|
| 384 |
+
softmax_lse.dtype == torch.float32
|
| 385 |
+
), f"[fwd_v3] softmax_lse dtype {softmax_lse.dtype} != torch.float32"
|
| 386 |
+
# softmax_lse shape depends on layout
|
| 387 |
+
expected_lse_shape: tuple[int, ...]
|
| 388 |
+
if layout == "thd":
|
| 389 |
+
# varlen: (Hq, Total_Q)
|
| 390 |
+
expected_lse_shape = (q.shape[1], q.shape[0])
|
| 391 |
+
else:
|
| 392 |
+
# bshd: (B, Hq, Sq)
|
| 393 |
+
expected_lse_shape = (q.shape[0], q.shape[2], q.shape[1])
|
| 394 |
+
assert (
|
| 395 |
+
softmax_lse.shape == expected_lse_shape
|
| 396 |
+
), f"[fwd_v3] softmax_lse shape {softmax_lse.shape} != {expected_lse_shape}"
|
| 397 |
+
|
| 398 |
+
# Return format compatible with v3
|
| 399 |
+
# V3 returns (out, softmax_lse, out_accum, softmax_lse_accum)
|
| 400 |
+
# out_accum and softmax_lse_accum are None for Triton AMD (no split-k accumulation)
|
| 401 |
+
return out, softmax_lse, None, None
|
| 402 |
+
|
| 403 |
+
|
| 404 |
+
def bwd(
|
| 405 |
+
dout: torch.Tensor,
|
| 406 |
+
q: torch.Tensor,
|
| 407 |
+
k: torch.Tensor,
|
| 408 |
+
v: torch.Tensor,
|
| 409 |
+
out: torch.Tensor,
|
| 410 |
+
softmax_lse: torch.Tensor,
|
| 411 |
+
dq: Optional[torch.Tensor],
|
| 412 |
+
dk: Optional[torch.Tensor],
|
| 413 |
+
dv: Optional[torch.Tensor],
|
| 414 |
+
cu_seqlens_q: Optional[torch.Tensor],
|
| 415 |
+
cu_seqlens_k: Optional[torch.Tensor],
|
| 416 |
+
seqused_q: Optional[torch.Tensor],
|
| 417 |
+
seqused_k: Optional[torch.Tensor],
|
| 418 |
+
max_seqlen_q: Optional[int],
|
| 419 |
+
max_seqlen_k: Optional[int],
|
| 420 |
+
softmax_scale: float,
|
| 421 |
+
causal: bool,
|
| 422 |
+
window_size_left: int,
|
| 423 |
+
window_size_right: int,
|
| 424 |
+
softcap: float,
|
| 425 |
+
deterministic: bool,
|
| 426 |
+
sm_margin: int = 0,
|
| 427 |
+
*,
|
| 428 |
+
q_descale: Optional[torch.Tensor] = None,
|
| 429 |
+
k_descale: Optional[torch.Tensor] = None,
|
| 430 |
+
v_descale: Optional[torch.Tensor] = None,
|
| 431 |
+
) -> Tuple[torch.Tensor]:
|
| 432 |
+
"""
|
| 433 |
+
Flash Attention v3 backward pass compatible interface for AMD Triton implementation.
|
| 434 |
+
|
| 435 |
+
This function maps v3 parameters to the existing AMD Triton implementation.
|
| 436 |
+
Gradients are written in-place to dq, dk, dv if provided (or allocated internally).
|
| 437 |
+
Returns (delta,) matching the upstream FA3 API.
|
| 438 |
+
|
| 439 |
+
Args:
|
| 440 |
+
q_descale: Optional descale tensor for FP8 query (batch, nheads_k)
|
| 441 |
+
k_descale: Optional descale tensor for FP8 key (batch, nheads_k)
|
| 442 |
+
v_descale: Optional descale tensor for FP8 value (batch, nheads_k)
|
| 443 |
+
"""
|
| 444 |
+
|
| 445 |
+
if DEBUG:
|
| 446 |
+
print()
|
| 447 |
+
print("interface_fa_v3.py::bwd inputs")
|
| 448 |
+
print("dout:", dout.shape)
|
| 449 |
+
print("q:", q.shape)
|
| 450 |
+
print("k:", k.shape)
|
| 451 |
+
print("v:", v.shape)
|
| 452 |
+
print("out:", out.shape)
|
| 453 |
+
print("softmax_lse:", softmax_lse.shape)
|
| 454 |
+
print("dq:", dq.shape if dq is not None else None)
|
| 455 |
+
print("dk:", dk.shape if dk is not None else None)
|
| 456 |
+
print("dv:", dv.shape if dv is not None else None)
|
| 457 |
+
print("cu_seqlens_q:", cu_seqlens_q.shape if cu_seqlens_q is not None else None)
|
| 458 |
+
print("cu_seqlens_k:", cu_seqlens_k.shape if cu_seqlens_k is not None else None)
|
| 459 |
+
print("seqused_q:", seqused_q.shape if seqused_q is not None else None)
|
| 460 |
+
print("seqused_k:", seqused_k.shape if seqused_k is not None else None)
|
| 461 |
+
print("max_seqlen_q:", max_seqlen_q)
|
| 462 |
+
print("max_seqlen_k:", max_seqlen_k)
|
| 463 |
+
print("softmax_scale:", softmax_scale)
|
| 464 |
+
print("causal:", causal)
|
| 465 |
+
print("window_size_left:", window_size_left)
|
| 466 |
+
print("window_size_right:", window_size_right)
|
| 467 |
+
print("softcap:", softcap)
|
| 468 |
+
print("deterministic:", deterministic)
|
| 469 |
+
print("sm_margin:", sm_margin)
|
| 470 |
+
|
| 471 |
+
# Check for unsupported features in backward pass
|
| 472 |
+
|
| 473 |
+
# Handle sliding window - backward doesn't support it yet
|
| 474 |
+
is_sliding_window = (window_size_left >= 0) or (window_size_right >= 0)
|
| 475 |
+
if is_sliding_window:
|
| 476 |
+
raise NotImplementedError(
|
| 477 |
+
f"Sliding window attention is not yet supported in the AMD Triton backward pass "
|
| 478 |
+
f"(window_size_left={window_size_left}, window_size_right={window_size_right}). "
|
| 479 |
+
f"Use window_size=(-1, -1) for full attention."
|
| 480 |
+
)
|
| 481 |
+
|
| 482 |
+
# Handle softcap
|
| 483 |
+
if softcap != 0.0:
|
| 484 |
+
raise NotImplementedError(
|
| 485 |
+
f"Softcap is not yet supported in the AMD Triton backend backward pass (got softcap={softcap}, expected 0.0)"
|
| 486 |
+
)
|
| 487 |
+
|
| 488 |
+
# Handle sm_margin
|
| 489 |
+
if sm_margin != 0:
|
| 490 |
+
raise NotImplementedError(
|
| 491 |
+
f"sm_margin is not yet supported in the AMD Triton backend backward pass (got sm_margin={sm_margin}, expected 0)"
|
| 492 |
+
)
|
| 493 |
+
|
| 494 |
+
# Initialize gradient tensors if not provided
|
| 495 |
+
# NOTE: Using types that are lower precision than float32 such as bfloat16 for fp8 causes mismatches on a small set of tests.
|
| 496 |
+
grad_dtype = torch.float32 if is_fp8([q, k, v]) else q.dtype
|
| 497 |
+
dq = torch.zeros_like(q, dtype=grad_dtype) if dq is None else dq.zero_()
|
| 498 |
+
dk = torch.zeros_like(k, dtype=grad_dtype) if dk is None else dk.zero_()
|
| 499 |
+
dv = torch.zeros_like(v, dtype=grad_dtype) if dv is None else dv.zero_()
|
| 500 |
+
|
| 501 |
+
# Determine layout and set max sequence lengths
|
| 502 |
+
layout: Literal["bshd", "bhsd", "thd"]
|
| 503 |
+
if cu_seqlens_q is not None and cu_seqlens_k is not None:
|
| 504 |
+
# Variable length sequence mode
|
| 505 |
+
layout = "thd"
|
| 506 |
+
batch = len(cu_seqlens_q) - 1
|
| 507 |
+
total_q, nheads_q, _ = q.shape
|
| 508 |
+
# Create delta tensor - varlen: (Hq, Total_Q)
|
| 509 |
+
delta = torch.zeros((nheads_q, total_q), device=q.device, dtype=torch.float32)
|
| 510 |
+
else:
|
| 511 |
+
# Regular batch mode
|
| 512 |
+
layout = "bshd"
|
| 513 |
+
batch, seqlen_q, nheads_q, _ = q.shape
|
| 514 |
+
# Create delta tensor - bshd: (B, Hq, Sq)
|
| 515 |
+
delta = torch.zeros(
|
| 516 |
+
(batch, nheads_q, seqlen_q), device=q.device, dtype=torch.float32
|
| 517 |
+
)
|
| 518 |
+
|
| 519 |
+
# Set max sequence lengths (infer from tensor shapes if not provided)
|
| 520 |
+
max_seqlen_q = q.shape[1] if max_seqlen_q is None else max_seqlen_q
|
| 521 |
+
max_seqlen_k = k.shape[1] if max_seqlen_k is None else max_seqlen_k
|
| 522 |
+
|
| 523 |
+
# V3 backward doesn't have dropout or alibi slopes
|
| 524 |
+
dropout_p = 0.0
|
| 525 |
+
philox_seed, philox_offset = None, None
|
| 526 |
+
alibi_slopes = None
|
| 527 |
+
|
| 528 |
+
# Call implementation
|
| 529 |
+
if DEBUG:
|
| 530 |
+
print(f"Using Triton implementation in {BWD_MODE} mode")
|
| 531 |
+
attention_backward_triton_impl(
|
| 532 |
+
do=dout,
|
| 533 |
+
q=q,
|
| 534 |
+
k=k,
|
| 535 |
+
v=v,
|
| 536 |
+
o=out,
|
| 537 |
+
softmax_lse=softmax_lse,
|
| 538 |
+
dq=dq,
|
| 539 |
+
dk=dk,
|
| 540 |
+
dv=dv,
|
| 541 |
+
delta=delta,
|
| 542 |
+
sm_scale=softmax_scale,
|
| 543 |
+
alibi_slopes=alibi_slopes,
|
| 544 |
+
causal=causal,
|
| 545 |
+
layout=layout,
|
| 546 |
+
cu_seqlens_q=cu_seqlens_q,
|
| 547 |
+
cu_seqlens_k=cu_seqlens_k,
|
| 548 |
+
max_seqlen_q=max_seqlen_q,
|
| 549 |
+
max_seqlen_k=max_seqlen_k,
|
| 550 |
+
seqused_q=seqused_q,
|
| 551 |
+
seqused_k=seqused_k,
|
| 552 |
+
dropout_p=dropout_p,
|
| 553 |
+
philox_seed=philox_seed,
|
| 554 |
+
philox_offset=philox_offset,
|
| 555 |
+
use_exp2=USE_EXP2,
|
| 556 |
+
mode=BWD_MODE,
|
| 557 |
+
q_descale=q_descale,
|
| 558 |
+
k_descale=k_descale,
|
| 559 |
+
v_descale=v_descale,
|
| 560 |
+
)
|
| 561 |
+
|
| 562 |
+
if DEBUG:
|
| 563 |
+
print("interface_fa_v3.py::bwd outputs")
|
| 564 |
+
print("dq:", dq.shape)
|
| 565 |
+
print("dk:", dk.shape)
|
| 566 |
+
print("dv:", dv.shape)
|
| 567 |
+
print("delta:", delta.shape)
|
| 568 |
+
|
| 569 |
+
# --- Assertions (FA3 always expects exact shapes) ---
|
| 570 |
+
# Gradients should match input shapes
|
| 571 |
+
assert dq.shape == q.shape, f"[bwd_v3] dq shape {dq.shape} != q shape {q.shape}"
|
| 572 |
+
assert dk.shape == k.shape, f"[bwd_v3] dk shape {dk.shape} != k shape {k.shape}"
|
| 573 |
+
assert dv.shape == v.shape, f"[bwd_v3] dv shape {dv.shape} != v shape {v.shape}"
|
| 574 |
+
# delta (softmax_d) should match softmax_lse shape
|
| 575 |
+
assert (
|
| 576 |
+
delta.dtype == torch.float32
|
| 577 |
+
), f"[bwd_v3] delta dtype {delta.dtype} != torch.float32"
|
| 578 |
+
expected_delta_shape: tuple[int, ...]
|
| 579 |
+
if layout == "thd":
|
| 580 |
+
# varlen: (Hq, Total_Q)
|
| 581 |
+
expected_delta_shape = (q.shape[1], q.shape[0])
|
| 582 |
+
else:
|
| 583 |
+
# bshd: (B, Hq, Sq)
|
| 584 |
+
expected_delta_shape = (q.shape[0], q.shape[2], q.shape[1])
|
| 585 |
+
assert (
|
| 586 |
+
delta.shape == expected_delta_shape
|
| 587 |
+
), f"[bwd_v3] delta shape {delta.shape} != {expected_delta_shape}"
|
| 588 |
+
|
| 589 |
+
# Return only delta to match upstream FA3 API
|
| 590 |
+
# dq, dk, dv are mutated in-place by attention_backward_triton_impl
|
| 591 |
+
return (delta,)
|
| 592 |
+
|
| 593 |
+
|
| 594 |
+
def fwd_combine(
|
| 595 |
+
out_partial: torch.Tensor,
|
| 596 |
+
lse_partial: torch.Tensor,
|
| 597 |
+
out: Optional[torch.Tensor] = None,
|
| 598 |
+
out_dtype: Optional[torch.dtype] = None,
|
| 599 |
+
) -> torch.Tensor:
|
| 600 |
+
"""
|
| 601 |
+
Combine partial outputs from split attention computation.
|
| 602 |
+
|
| 603 |
+
This is used when num_splits > 1 to combine the partial results.
|
| 604 |
+
|
| 605 |
+
Args:
|
| 606 |
+
out_partial: Partial output tensor from split computation
|
| 607 |
+
lse_partial: Partial log-sum-exp tensor
|
| 608 |
+
out: Optional output tensor to write to
|
| 609 |
+
out_dtype: Optional dtype for output
|
| 610 |
+
|
| 611 |
+
Returns:
|
| 612 |
+
Combined output tensor
|
| 613 |
+
"""
|
| 614 |
+
raise NotImplementedError(
|
| 615 |
+
"fwd_combine is not yet implemented in the AMD Triton backend"
|
| 616 |
+
)
|
| 617 |
+
|
| 618 |
+
|
| 619 |
+
def get_scheduler_metadata(
|
| 620 |
+
batch_size: int,
|
| 621 |
+
max_seqlen_q: int,
|
| 622 |
+
max_seqlen_k: int,
|
| 623 |
+
num_heads_q: int,
|
| 624 |
+
num_heads_kv: int,
|
| 625 |
+
headdim: int,
|
| 626 |
+
headdim_v: int,
|
| 627 |
+
qkv_dtype: torch.dtype,
|
| 628 |
+
cache_seqlens: torch.Tensor,
|
| 629 |
+
cu_seqlens_q: Optional[torch.Tensor] = None,
|
| 630 |
+
cu_seqlens_k: Optional[torch.Tensor] = None,
|
| 631 |
+
cu_seqlens_k_new: Optional[torch.Tensor] = None,
|
| 632 |
+
seqused_q: Optional[torch.Tensor] = None,
|
| 633 |
+
cache_leftpad: Optional[torch.Tensor] = None,
|
| 634 |
+
page_size: Optional[int] = None,
|
| 635 |
+
max_seqlen_k_new: int = 0,
|
| 636 |
+
causal: bool = False,
|
| 637 |
+
window_size_left: int = -1,
|
| 638 |
+
window_size_right: int = -1,
|
| 639 |
+
attention_chunk: int = 0,
|
| 640 |
+
has_softcap: bool = False,
|
| 641 |
+
num_splits: int = 0,
|
| 642 |
+
pack_gqa: Optional[bool] = None,
|
| 643 |
+
sm_margin: int = 0,
|
| 644 |
+
) -> None:
|
| 645 |
+
"""
|
| 646 |
+
Get scheduler metadata for optimized kernel selection.
|
| 647 |
+
|
| 648 |
+
This function is used to precompute metadata for kernel scheduling in FA3.
|
| 649 |
+
The AMD Triton backend currently doesn't use scheduler metadata, so this
|
| 650 |
+
raises an error.
|
| 651 |
+
|
| 652 |
+
Args:
|
| 653 |
+
Various attention parameters used for scheduling decisions
|
| 654 |
+
|
| 655 |
+
Returns:
|
| 656 |
+
None - scheduler metadata is not used in AMD Triton backend
|
| 657 |
+
"""
|
| 658 |
+
raise NotImplementedError(
|
| 659 |
+
"get_scheduler_metadata is not supported in the AMD Triton backend yet."
|
| 660 |
+
)
|
build/torch-rocm/_kernels/flash_attn_triton_amd/utils.py
ADDED
|
@@ -0,0 +1,338 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Utilities for Flash Attention Triton AMD backend.
|
| 3 |
+
|
| 4 |
+
This module contains essential runtime utilities:
|
| 5 |
+
- GPU architecture detection
|
| 6 |
+
- Global configuration flags
|
| 7 |
+
- Tensor shape/stride helpers
|
| 8 |
+
- FP8 type detection
|
| 9 |
+
"""
|
| 10 |
+
|
| 11 |
+
import functools
|
| 12 |
+
import os
|
| 13 |
+
import json
|
| 14 |
+
import logging
|
| 15 |
+
from dataclasses import dataclass
|
| 16 |
+
from typing import Literal, Optional, Union
|
| 17 |
+
import triton.language as tl
|
| 18 |
+
|
| 19 |
+
import torch
|
| 20 |
+
import triton
|
| 21 |
+
|
| 22 |
+
logger = logging.getLogger(__name__)
|
| 23 |
+
|
| 24 |
+
AutotuneMode = Literal["off", "on", "sweep"]
|
| 25 |
+
|
| 26 |
+
__all__ = [
|
| 27 |
+
# Runtime info
|
| 28 |
+
"get_arch",
|
| 29 |
+
"is_hip",
|
| 30 |
+
# Global config
|
| 31 |
+
"AutotuneMode",
|
| 32 |
+
"AUTOTUNE",
|
| 33 |
+
"DEBUG",
|
| 34 |
+
"USE_TRITON_ROCM",
|
| 35 |
+
"BWD_MODE",
|
| 36 |
+
"USE_EXP2",
|
| 37 |
+
"PHILOX_SEED",
|
| 38 |
+
"PHILOX_OFFSET",
|
| 39 |
+
"SHAPE_EXPECTATIONS",
|
| 40 |
+
# FP8
|
| 41 |
+
"is_fp8",
|
| 42 |
+
# Shape/stride helpers
|
| 43 |
+
"get_shape_from_layout",
|
| 44 |
+
"get_stride_from_layout",
|
| 45 |
+
"get_padded_headsize",
|
| 46 |
+
# Misc helpers
|
| 47 |
+
"round_multiple",
|
| 48 |
+
]
|
| 49 |
+
|
| 50 |
+
|
| 51 |
+
# -------------------------------
|
| 52 |
+
# GPU Architecture
|
| 53 |
+
# -------------------------------
|
| 54 |
+
ArchFamily = Literal["cdna", "rdna"]
|
| 55 |
+
|
| 56 |
+
CDNA_ARCHS = frozenset({"gfx908", "gfx90a", "gfx940", "gfx941", "gfx942", "gfx950"})
|
| 57 |
+
RDNA_ARCHS = frozenset(
|
| 58 |
+
{
|
| 59 |
+
"gfx1030",
|
| 60 |
+
"gfx1100",
|
| 61 |
+
"gfx1101",
|
| 62 |
+
"gfx1102",
|
| 63 |
+
"gfx1150",
|
| 64 |
+
"gfx1151",
|
| 65 |
+
"gfx1200",
|
| 66 |
+
"gfx1201",
|
| 67 |
+
}
|
| 68 |
+
)
|
| 69 |
+
FP8_ARCHS = frozenset({"gfx942", "gfx950", "gfx1200", "gfx1201"})
|
| 70 |
+
|
| 71 |
+
_RECOMMENDED_FP8_REPLACEMENTS: dict[str, dict[torch.dtype, torch.dtype]] = {
|
| 72 |
+
"gfx942": {
|
| 73 |
+
torch.float8_e4m3fn: torch.float8_e4m3fnuz,
|
| 74 |
+
torch.float8_e5m2: torch.float8_e5m2fnuz,
|
| 75 |
+
},
|
| 76 |
+
}
|
| 77 |
+
|
| 78 |
+
|
| 79 |
+
@dataclass(frozen=True)
|
| 80 |
+
class GpuArch:
|
| 81 |
+
"""GPU architecture information."""
|
| 82 |
+
|
| 83 |
+
name: str # e.g., "gfx942", "gfx1100"
|
| 84 |
+
family: Optional[ArchFamily] = None
|
| 85 |
+
|
| 86 |
+
@property
|
| 87 |
+
def is_cdna(self) -> bool:
|
| 88 |
+
return self.family == "cdna"
|
| 89 |
+
|
| 90 |
+
@property
|
| 91 |
+
def is_rdna(self) -> bool:
|
| 92 |
+
return self.family == "rdna"
|
| 93 |
+
|
| 94 |
+
@property
|
| 95 |
+
def supports_fp8(self) -> bool:
|
| 96 |
+
"""Check if this architecture supports FP8."""
|
| 97 |
+
return self.name in FP8_ARCHS
|
| 98 |
+
|
| 99 |
+
def recommended_fp8_dtype(self, dtype: torch.dtype) -> torch.dtype:
|
| 100 |
+
"""Get the recommended FP8 dtype for this architecture.
|
| 101 |
+
|
| 102 |
+
Some architectures prefer different FP8 variants (e.g., fnuz vs fn).
|
| 103 |
+
Returns the input dtype unchanged if no replacement is recommended.
|
| 104 |
+
"""
|
| 105 |
+
return _RECOMMENDED_FP8_REPLACEMENTS.get(self.name, {}).get(dtype, dtype)
|
| 106 |
+
|
| 107 |
+
@property
|
| 108 |
+
def cu_count(self) -> int:
|
| 109 |
+
"""Get the number of compute units on the current GPU."""
|
| 110 |
+
return int(
|
| 111 |
+
torch.cuda.get_device_properties(
|
| 112 |
+
torch.cuda.current_device()
|
| 113 |
+
).multi_processor_count
|
| 114 |
+
)
|
| 115 |
+
|
| 116 |
+
|
| 117 |
+
# -------------------------------
|
| 118 |
+
# Global Variables
|
| 119 |
+
# -------------------------------
|
| 120 |
+
USE_TRITON_ROCM = os.getenv("FLASH_ATTENTION_TRITON_AMD_ENABLE", "FALSE") == "TRUE"
|
| 121 |
+
AUTOTUNE: AutotuneMode = (
|
| 122 |
+
"on"
|
| 123 |
+
if os.environ.get("FLASH_ATTENTION_TRITON_AMD_AUTOTUNE", "1").lower()
|
| 124 |
+
in ("1", "true", "yes", "on")
|
| 125 |
+
else "off"
|
| 126 |
+
)
|
| 127 |
+
|
| 128 |
+
# User override config json for attn_fwd.
|
| 129 |
+
# Note: Ignored if FLASH_ATTENTION_TRITON_AMD_AUTOTUNE is enabled.
|
| 130 |
+
#
|
| 131 |
+
# e.g. FLASH_ATTENTION_FWD_TRITON_AMD_CONFIG_JSON='{"BLOCK_M":32,"BLOCK_N":32,"waves_per_eu":1,"PRE_LOAD_V":false,"num_stages":1,"num_warps":4}'
|
| 132 |
+
FWD_CONF_OVERRIDE = None
|
| 133 |
+
try:
|
| 134 |
+
conf_json = os.getenv("FLASH_ATTENTION_FWD_TRITON_AMD_CONFIG_JSON")
|
| 135 |
+
if conf_json:
|
| 136 |
+
conf = json.loads(conf_json)
|
| 137 |
+
FWD_CONF_OVERRIDE = triton.Config(
|
| 138 |
+
conf,
|
| 139 |
+
num_stages=conf.pop("num_stages", 1),
|
| 140 |
+
num_warps=conf.pop("num_warps", 4),
|
| 141 |
+
)
|
| 142 |
+
except Exception as e:
|
| 143 |
+
logger.warning(f"FLASH_ATTENTION_FWD_TRITON_AMD_CONFIG_JSON parse error: {e}")
|
| 144 |
+
|
| 145 |
+
# Unified debug level:
|
| 146 |
+
# 0 = off (default)
|
| 147 |
+
# 1 = basic debug info (shapes, tensor stats, kernel params)
|
| 148 |
+
# 2 = detailed debug (includes Triton interpreter prints in kernels)
|
| 149 |
+
#
|
| 150 |
+
# Set via: FLASH_ATTENTION_TRITON_AMD_DEBUG=0|1|2
|
| 151 |
+
DEBUG: int = int(os.environ.get("FLASH_ATTENTION_TRITON_AMD_DEBUG", "0"))
|
| 152 |
+
if AUTOTUNE != "off" or DEBUG > 0:
|
| 153 |
+
os.environ["TRITON_PRINT_AUTOTUNING"] = "1"
|
| 154 |
+
if DEBUG >= 2:
|
| 155 |
+
os.environ["TRITON_INTERPRET"] = "1"
|
| 156 |
+
BWD_MODE: Literal["fused", "fused_atomic", "split"] = "fused"
|
| 157 |
+
USE_EXP2 = True
|
| 158 |
+
PHILOX_SEED = 0x1BF58
|
| 159 |
+
PHILOX_OFFSET = 0x1D4B49
|
| 160 |
+
SHAPE_EXPECTATIONS: Literal["exact", "rounded"] = "exact"
|
| 161 |
+
|
| 162 |
+
|
| 163 |
+
# -------------------------------
|
| 164 |
+
# FP8
|
| 165 |
+
# -------------------------------
|
| 166 |
+
_FP8_DTYPES = frozenset(
|
| 167 |
+
{
|
| 168 |
+
torch.float8_e4m3fnuz,
|
| 169 |
+
torch.float8_e4m3fn,
|
| 170 |
+
torch.float8_e5m2,
|
| 171 |
+
torch.float8_e5m2fnuz,
|
| 172 |
+
}
|
| 173 |
+
)
|
| 174 |
+
|
| 175 |
+
|
| 176 |
+
def is_fp8(
|
| 177 |
+
x: Union[torch.dtype, torch.Tensor, list[torch.Tensor], tuple[torch.Tensor, ...]],
|
| 178 |
+
) -> bool:
|
| 179 |
+
"""Check if dtype/tensor(s) are FP8.
|
| 180 |
+
|
| 181 |
+
This is a pure function - it only checks dtypes, not architecture support.
|
| 182 |
+
Use `get_arch().supports_fp8` to check if the current GPU supports FP8.
|
| 183 |
+
|
| 184 |
+
Args:
|
| 185 |
+
x: A dtype, tensor, or list/tuple of tensors to check.
|
| 186 |
+
|
| 187 |
+
Returns:
|
| 188 |
+
True if FP8, False otherwise.
|
| 189 |
+
|
| 190 |
+
Rules for multiple tensors:
|
| 191 |
+
- If all tensors are FP8 -> return True.
|
| 192 |
+
- If none are FP8 -> return False.
|
| 193 |
+
- If a mix of FP8 and non-FP8 -> raise ValueError.
|
| 194 |
+
|
| 195 |
+
Empty list/tuple returns False.
|
| 196 |
+
"""
|
| 197 |
+
# Handle dtype directly
|
| 198 |
+
if isinstance(x, torch.dtype):
|
| 199 |
+
return x in _FP8_DTYPES
|
| 200 |
+
|
| 201 |
+
# Handle single tensor
|
| 202 |
+
if isinstance(x, torch.Tensor):
|
| 203 |
+
return x.dtype in _FP8_DTYPES
|
| 204 |
+
|
| 205 |
+
# Handle list/tuple of tensors
|
| 206 |
+
if isinstance(x, (list, tuple)):
|
| 207 |
+
if len(x) == 0:
|
| 208 |
+
return False
|
| 209 |
+
flags = [t.dtype in _FP8_DTYPES for t in x]
|
| 210 |
+
if all(flags):
|
| 211 |
+
return True
|
| 212 |
+
if not any(flags):
|
| 213 |
+
return False
|
| 214 |
+
raise ValueError(
|
| 215 |
+
"Mixed FP8 and non-FP8 tensors provided; either all or none must be FP8."
|
| 216 |
+
)
|
| 217 |
+
|
| 218 |
+
raise TypeError(f"Expected dtype, Tensor, or sequence of Tensors, got {type(x)}")
|
| 219 |
+
|
| 220 |
+
|
| 221 |
+
# -------------------------------
|
| 222 |
+
# Shape/Stride Helpers
|
| 223 |
+
# -------------------------------
|
| 224 |
+
def get_shape_from_layout(
|
| 225 |
+
x: torch.Tensor,
|
| 226 |
+
layout: Literal["bshd", "bhsd", "thd"],
|
| 227 |
+
cu_seqlens: Optional[torch.Tensor] = None,
|
| 228 |
+
max_seqlen: Optional[int] = None,
|
| 229 |
+
) -> tuple[int, int, int, int]:
|
| 230 |
+
"""Extract (batch, max_seqlen, num_heads, head_dim) from tensor based on layout."""
|
| 231 |
+
if layout == "bhsd":
|
| 232 |
+
batch, num_heads, max_seqlen_final, head_dim = x.shape
|
| 233 |
+
elif layout == "bshd":
|
| 234 |
+
batch, max_seqlen_final, num_heads, head_dim = x.shape
|
| 235 |
+
elif layout == "thd":
|
| 236 |
+
total_seqlen, num_heads, head_dim = x.shape
|
| 237 |
+
if cu_seqlens is None:
|
| 238 |
+
raise ValueError("cu_seqlens must be provided for varlen (thd) layout")
|
| 239 |
+
if max_seqlen is None:
|
| 240 |
+
raise ValueError("max_seqlen must be provided for varlen (thd) layout")
|
| 241 |
+
|
| 242 |
+
batch, max_seqlen_final, num_heads, head_dim = (
|
| 243 |
+
len(cu_seqlens) - 1,
|
| 244 |
+
max_seqlen,
|
| 245 |
+
num_heads,
|
| 246 |
+
head_dim,
|
| 247 |
+
)
|
| 248 |
+
else:
|
| 249 |
+
raise ValueError(f"Got unsupported layout: {layout}")
|
| 250 |
+
|
| 251 |
+
return batch, max_seqlen_final, num_heads, head_dim
|
| 252 |
+
|
| 253 |
+
|
| 254 |
+
def get_stride_from_layout(
|
| 255 |
+
x: torch.Tensor, layout: Literal["bshd", "bhsd", "thd"]
|
| 256 |
+
) -> tuple[int, int, int, int]:
|
| 257 |
+
"""Get strides in (batch, head, seq, dim) order for the given layout."""
|
| 258 |
+
if layout == "thd":
|
| 259 |
+
strides = (0, x.stride(1), x.stride(0), x.stride(2))
|
| 260 |
+
elif layout == "bhsd":
|
| 261 |
+
strides = (x.stride(0), x.stride(1), x.stride(2), x.stride(3))
|
| 262 |
+
elif layout == "bshd":
|
| 263 |
+
strides = (x.stride(0), x.stride(2), x.stride(1), x.stride(3))
|
| 264 |
+
else:
|
| 265 |
+
raise ValueError(f"Got unsupported layout: {layout}")
|
| 266 |
+
return strides
|
| 267 |
+
|
| 268 |
+
|
| 269 |
+
def get_padded_headsize(size: int) -> int:
|
| 270 |
+
"""Get closest power of 2 over or equal to 32."""
|
| 271 |
+
# Smallest head_dim supported is 16. If smaller, the tile in the
|
| 272 |
+
# kernel is padded - there is no padding in memory for any dims.
|
| 273 |
+
padded_d_model = 1 << (size - 1).bit_length()
|
| 274 |
+
padded_d_model = max(padded_d_model, 16)
|
| 275 |
+
return padded_d_model
|
| 276 |
+
|
| 277 |
+
|
| 278 |
+
# -------------------------------
|
| 279 |
+
# Misc helpers
|
| 280 |
+
# -------------------------------
|
| 281 |
+
def round_multiple(x: int, m: int) -> int:
|
| 282 |
+
"""Round x up to the nearest multiple of m."""
|
| 283 |
+
return (x + m - 1) // m * m
|
| 284 |
+
|
| 285 |
+
|
| 286 |
+
# -------------------------------
|
| 287 |
+
# Runtime info
|
| 288 |
+
# -------------------------------
|
| 289 |
+
@functools.cache
|
| 290 |
+
def is_hip() -> bool:
|
| 291 |
+
"""Check if running on HIP (AMD) backend."""
|
| 292 |
+
return bool(triton.runtime.driver.active.get_current_target().backend == "hip")
|
| 293 |
+
|
| 294 |
+
|
| 295 |
+
@functools.cache
|
| 296 |
+
def get_arch() -> GpuArch:
|
| 297 |
+
"""Get the current GPU architecture."""
|
| 298 |
+
try:
|
| 299 |
+
name: str = triton.runtime.driver.active.get_current_target().arch
|
| 300 |
+
except RuntimeError:
|
| 301 |
+
# No GPU available (e.g. import-only on Windows/CPU)
|
| 302 |
+
return GpuArch(name="unknown")
|
| 303 |
+
if name in CDNA_ARCHS:
|
| 304 |
+
return GpuArch(name=name, family="cdna")
|
| 305 |
+
elif name in RDNA_ARCHS:
|
| 306 |
+
return GpuArch(name=name, family="rdna")
|
| 307 |
+
else:
|
| 308 |
+
return GpuArch(name=name)
|
| 309 |
+
|
| 310 |
+
|
| 311 |
+
@triton.jit
|
| 312 |
+
def remap_xcd(pid, GRID_MN, NUM_XCDS: tl.constexpr = 8):
|
| 313 |
+
## pid remapping on xcds
|
| 314 |
+
# Number of pids per XCD in the new arrangement
|
| 315 |
+
pids_per_xcd = (GRID_MN + NUM_XCDS - 1) // NUM_XCDS
|
| 316 |
+
# When GRID_MN cannot divide NUM_XCDS, some xcds will have
|
| 317 |
+
# pids_per_xcd pids, the other will have pids_per_xcd - 1 pids.
|
| 318 |
+
# We calculate the number of xcds that have pids_per_xcd pids as
|
| 319 |
+
# tall_xcds
|
| 320 |
+
tall_xcds = GRID_MN % NUM_XCDS
|
| 321 |
+
tall_xcds = NUM_XCDS if tall_xcds == 0 else tall_xcds
|
| 322 |
+
# Compute current XCD and local pid within the XCD
|
| 323 |
+
xcd = pid % NUM_XCDS
|
| 324 |
+
local_pid = pid // NUM_XCDS
|
| 325 |
+
# Calculate new pid based on the new grouping
|
| 326 |
+
# Note that we need to consider the following two cases:
|
| 327 |
+
# 1. the current pid is on a tall xcd
|
| 328 |
+
# 2. the current pid is on a short xcd
|
| 329 |
+
if xcd < tall_xcds:
|
| 330 |
+
pid = xcd * pids_per_xcd + local_pid
|
| 331 |
+
else:
|
| 332 |
+
pid = (
|
| 333 |
+
tall_xcds * pids_per_xcd
|
| 334 |
+
+ (xcd - tall_xcds) * (pids_per_xcd - 1)
|
| 335 |
+
+ local_pid
|
| 336 |
+
)
|
| 337 |
+
|
| 338 |
+
return pid
|
build/torch-rocm/_kernels/mha.py
ADDED
|
@@ -0,0 +1,942 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# SPDX-License-Identifier: MIT
|
| 2 |
+
# Copyright (C) 2024-2026, Advanced Micro Devices, Inc. All rights reserved.
|
| 3 |
+
|
| 4 |
+
import functools
|
| 5 |
+
import json
|
| 6 |
+
import torch
|
| 7 |
+
import triton
|
| 8 |
+
import triton.language as tl
|
| 9 |
+
|
| 10 |
+
from ..utils._triton import arch_info
|
| 11 |
+
from ..utils.core import AITER_TRITON_CONFIGS_PATH
|
| 12 |
+
from ..utils._triton.pid_preprocessing import remap_xcd
|
| 13 |
+
from ..utils._triton.mha_kernel_utils import _compute_fp8_scaling_factors
|
| 14 |
+
from ..utils._triton.kernel_repr import make_kernel_repr
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
@triton.jit
|
| 18 |
+
def _cdiv_fn(x, y):
|
| 19 |
+
return (x + y - 1) // y
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
@triton.jit
|
| 23 |
+
def _load_fn(ptrs, offset_first, offset_second, boundary_first, boundary_second):
|
| 24 |
+
if offset_first is not None and offset_second is not None:
|
| 25 |
+
mask = (offset_first[:, None] < boundary_first) & (
|
| 26 |
+
offset_second[None, :] < boundary_second
|
| 27 |
+
)
|
| 28 |
+
tensor = tl.load(ptrs, mask=mask, other=0.0)
|
| 29 |
+
elif offset_first is not None:
|
| 30 |
+
mask = offset_first[:, None] < boundary_first
|
| 31 |
+
tensor = tl.load(ptrs, mask=mask, other=0.0)
|
| 32 |
+
elif offset_second is not None:
|
| 33 |
+
mask = offset_second[None, :] < boundary_second
|
| 34 |
+
tensor = tl.load(ptrs, mask=mask, other=0.0)
|
| 35 |
+
else:
|
| 36 |
+
tensor = tl.load(ptrs)
|
| 37 |
+
return tensor
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
@triton.jit
|
| 41 |
+
def _compute_alibi_block(
|
| 42 |
+
alibi_slope, seqlen_q, seqlen_k, offs_m, offs_n, transpose=False
|
| 43 |
+
):
|
| 44 |
+
# when seqlen_k and seqlen_q are different we want the diagonal to stick to the bottom right of the attention matrix
|
| 45 |
+
# for casual mask we want something like this where (1 is kept and 0 is masked)
|
| 46 |
+
# seqlen_q = 2 and seqlen_k = 5
|
| 47 |
+
# 1 1 1 1 0
|
| 48 |
+
# 1 1 1 1 1
|
| 49 |
+
# seqlen_q = 5 and seqlen_k = 2
|
| 50 |
+
# 0 0
|
| 51 |
+
# 0 0
|
| 52 |
+
# 0 0
|
| 53 |
+
# 1 0
|
| 54 |
+
# 1 1
|
| 55 |
+
# for alibi the diagonal is 0 indicating no penalty for attending to that spot and increasing penalty for attending further from the diagonal
|
| 56 |
+
# e.g. alibi_slope = 1, seqlen_q = 2, seqlen_k = 5, offs_m = [0, 1, 2, 3], offs_n = [0, 1, 2, 3, 4], transpose = False
|
| 57 |
+
# 1. offs_m[:,None] = [[0],
|
| 58 |
+
# [1],
|
| 59 |
+
# 2. offs_m[:,None] + seqlen_k = [[5],
|
| 60 |
+
# [6],
|
| 61 |
+
# 3. offs_m[:,None] + seqlen_k - seqlen_q = [[3],
|
| 62 |
+
# [4],
|
| 63 |
+
# 4. offs_m[:,None] + seqlen_k - seqlen_q - offs_n[None,:] = [[3], - [[0, 1, 2, 3, 4]] = [[ 3, 2, 1, 0,-1],
|
| 64 |
+
# [4], [ 4, 3, 2, 1, 0]]
|
| 65 |
+
# 5. -1 * alibi_slope * tl.abs(relative_pos_block) = [[ -3, -2, -1, 0,-1],
|
| 66 |
+
# [ -4, -3, -2, -1, 0]],
|
| 67 |
+
relative_pos_block = offs_m[:, None] + seqlen_k - seqlen_q - offs_n[None, :]
|
| 68 |
+
alibi_block = -1 * alibi_slope * tl.abs(relative_pos_block)
|
| 69 |
+
if transpose:
|
| 70 |
+
return alibi_block.T
|
| 71 |
+
else:
|
| 72 |
+
return alibi_block
|
| 73 |
+
|
| 74 |
+
|
| 75 |
+
@triton.jit
|
| 76 |
+
def _attn_fwd_inner(
|
| 77 |
+
acc,
|
| 78 |
+
l_i,
|
| 79 |
+
m_i,
|
| 80 |
+
q,
|
| 81 |
+
q_pe,
|
| 82 |
+
k_ptrs,
|
| 83 |
+
k_pe_ptrs,
|
| 84 |
+
v_ptrs,
|
| 85 |
+
stride_kn,
|
| 86 |
+
stride_vk,
|
| 87 |
+
stride_sn,
|
| 88 |
+
start_m,
|
| 89 |
+
seqlen_k,
|
| 90 |
+
seqlen_q,
|
| 91 |
+
dropout_p,
|
| 92 |
+
sd_mask_ptrs,
|
| 93 |
+
dropout_mask_ptrs,
|
| 94 |
+
philox_seed,
|
| 95 |
+
philox_ptrs,
|
| 96 |
+
block_min,
|
| 97 |
+
block_max,
|
| 98 |
+
offs_n_causal,
|
| 99 |
+
masked_blocks,
|
| 100 |
+
n_extra_tokens,
|
| 101 |
+
alibi_slope,
|
| 102 |
+
descale_q,
|
| 103 |
+
descale_k,
|
| 104 |
+
descale_v,
|
| 105 |
+
OFFS_M: tl.constexpr,
|
| 106 |
+
OFFS_N: tl.constexpr,
|
| 107 |
+
PRELOAD_V: tl.constexpr,
|
| 108 |
+
BLOCK_M: tl.constexpr,
|
| 109 |
+
BLOCK_N: tl.constexpr,
|
| 110 |
+
BLOCK_DMODEL: tl.constexpr,
|
| 111 |
+
BLOCK_DMODEL_POW2: tl.constexpr,
|
| 112 |
+
BLOCK_DMODEL_PE: tl.constexpr, # it's zero or a power of 2
|
| 113 |
+
SM_SCALE: tl.constexpr,
|
| 114 |
+
IS_CAUSAL: tl.constexpr,
|
| 115 |
+
MASK_STEPS: tl.constexpr,
|
| 116 |
+
ENABLE_DROPOUT: tl.constexpr,
|
| 117 |
+
RETURN_SCORES: tl.constexpr,
|
| 118 |
+
PADDED_HEAD: tl.constexpr,
|
| 119 |
+
IS_FP8: tl.constexpr,
|
| 120 |
+
FP8_MAX: tl.constexpr,
|
| 121 |
+
ENABLE_PIPELINING: tl.constexpr,
|
| 122 |
+
SLIDING_WINDOW: tl.constexpr,
|
| 123 |
+
):
|
| 124 |
+
RCP_LN2: tl.constexpr = 1.4426950408889634
|
| 125 |
+
HAS_PE: tl.constexpr = BLOCK_DMODEL_PE > 0
|
| 126 |
+
|
| 127 |
+
# loop over k, v, and update accumulator
|
| 128 |
+
|
| 129 |
+
num_stages: tl.constexpr = (
|
| 130 |
+
None if ENABLE_PIPELINING else 1
|
| 131 |
+
) # Set num_stages==1 if we want to disable pipelining
|
| 132 |
+
for start_n in tl.range(block_min, block_max, BLOCK_N, num_stages=num_stages):
|
| 133 |
+
# For padded blocks, we will overrun the tensor size if
|
| 134 |
+
# we load all BLOCK_N. For others, the blocks are all within range.
|
| 135 |
+
if MASK_STEPS:
|
| 136 |
+
k_offs_n = start_n + tl.arange(0, BLOCK_N)
|
| 137 |
+
else:
|
| 138 |
+
k_offs_n = None
|
| 139 |
+
k_offs_k = None if not PADDED_HEAD else tl.arange(0, BLOCK_DMODEL_POW2)
|
| 140 |
+
k = _load_fn(k_ptrs, k_offs_k, k_offs_n, BLOCK_DMODEL, seqlen_k)
|
| 141 |
+
if HAS_PE:
|
| 142 |
+
k_pe = _load_fn(
|
| 143 |
+
k_pe_ptrs,
|
| 144 |
+
None,
|
| 145 |
+
k_offs_n,
|
| 146 |
+
(BLOCK_DMODEL + BLOCK_DMODEL_PE),
|
| 147 |
+
seqlen_k,
|
| 148 |
+
)
|
| 149 |
+
if PRELOAD_V:
|
| 150 |
+
v = _load_fn(v_ptrs, k_offs_n, k_offs_k, seqlen_k, BLOCK_DMODEL)
|
| 151 |
+
|
| 152 |
+
# We start from end of seqlen_k so only the first iteration would need
|
| 153 |
+
# to be checked for padding if it is not a multiple of block_n
|
| 154 |
+
# TODO: This can be optimized to only be true for the padded block.
|
| 155 |
+
mask = tl.full([BLOCK_M, BLOCK_N], True, dtype=tl.int1)
|
| 156 |
+
if MASK_STEPS:
|
| 157 |
+
# If this is the last block / iteration, we want to
|
| 158 |
+
# mask if the sequence length is not a multiple of block size
|
| 159 |
+
# a solution is to always do BLOCK_M // BLOCK_N + 1 steps if not is_modulo_mn.
|
| 160 |
+
# last step might get wasted but that is okay. check if this masking works For
|
| 161 |
+
# that case.
|
| 162 |
+
|
| 163 |
+
# remove the old if condition
|
| 164 |
+
# if (start_n + BLOCK_N == block_max) and (n_extra_tokens != 0):
|
| 165 |
+
# Though this will unconditionally compute mask_partial at runtime,
|
| 166 |
+
# the causal for loop does not have the if-else block any more, which
|
| 167 |
+
# helps instruction scheduling and register pressure.
|
| 168 |
+
bound_cond = (start_n + BLOCK_N == block_max) and (n_extra_tokens != 0)
|
| 169 |
+
size_n = start_n + OFFS_N[None, :]
|
| 170 |
+
mask_partial = size_n < seqlen_k
|
| 171 |
+
mask = tl.where(bound_cond, mask_partial, mask)
|
| 172 |
+
|
| 173 |
+
# compute masks
|
| 174 |
+
q_mask = OFFS_M[:, None] < seqlen_q
|
| 175 |
+
k_mask = (start_n + tl.arange(0, BLOCK_N))[None, :] < seqlen_k
|
| 176 |
+
p_mask = q_mask & k_mask
|
| 177 |
+
qk_scale = SM_SCALE * RCP_LN2
|
| 178 |
+
# -- compute qk ----
|
| 179 |
+
qk = tl.zeros([BLOCK_M, BLOCK_N], dtype=tl.float32)
|
| 180 |
+
if HAS_PE:
|
| 181 |
+
qk = tl.dot(q_pe, k_pe, acc=qk)
|
| 182 |
+
qk = tl.dot(q, k, acc=qk)
|
| 183 |
+
if IS_FP8:
|
| 184 |
+
qk = qk * (qk_scale * descale_q * descale_k)
|
| 185 |
+
else:
|
| 186 |
+
qk = qk * qk_scale
|
| 187 |
+
if IS_CAUSAL:
|
| 188 |
+
causal_boundary = start_n + offs_n_causal
|
| 189 |
+
causal_mask = OFFS_M[:, None] >= causal_boundary[None, :]
|
| 190 |
+
mask = mask & causal_mask
|
| 191 |
+
|
| 192 |
+
if SLIDING_WINDOW > 0:
|
| 193 |
+
k_pos = start_n + tl.arange(0, BLOCK_N)
|
| 194 |
+
q_adj = OFFS_M + seqlen_k - seqlen_q
|
| 195 |
+
window_mask = k_pos[None, :] >= (q_adj[:, None] - SLIDING_WINDOW)
|
| 196 |
+
mask = mask & window_mask
|
| 197 |
+
|
| 198 |
+
qk = tl.where(mask, qk, float("-inf"))
|
| 199 |
+
|
| 200 |
+
if alibi_slope is not None:
|
| 201 |
+
# Compute the global position of each token within the sequence
|
| 202 |
+
global_m_positions = start_m * BLOCK_M + tl.arange(0, BLOCK_M)
|
| 203 |
+
global_n_positions = start_n + tl.arange(0, BLOCK_N)
|
| 204 |
+
alibi_block = _compute_alibi_block(
|
| 205 |
+
alibi_slope, seqlen_q, seqlen_k, global_m_positions, global_n_positions
|
| 206 |
+
)
|
| 207 |
+
qk += alibi_block * RCP_LN2
|
| 208 |
+
# get max scores so far
|
| 209 |
+
m_ij = tl.maximum(m_i, tl.max(qk, 1))
|
| 210 |
+
|
| 211 |
+
# Compute scaled QK and softmax probabilities
|
| 212 |
+
p = tl.math.exp2(qk - m_ij[:, None])
|
| 213 |
+
|
| 214 |
+
if SLIDING_WINDOW > 0:
|
| 215 |
+
# When all qk in a row are -inf (fully out-of-window block) and m_i was -inf,
|
| 216 |
+
# exp2(-inf - (-inf)) = NaN. Sanitize by zeroing masked elements.
|
| 217 |
+
p = tl.where(mask, p, 0.0)
|
| 218 |
+
|
| 219 |
+
# CAVEAT: Must update l_ij before applying dropout
|
| 220 |
+
l_ij = tl.sum(p, 1)
|
| 221 |
+
if ENABLE_DROPOUT:
|
| 222 |
+
rng_output = tl.rand(
|
| 223 |
+
philox_seed, philox_ptrs
|
| 224 |
+
) # TODO: use tl.randint for better performance
|
| 225 |
+
dropout_mask = rng_output > dropout_p
|
| 226 |
+
tl.store(dropout_mask_ptrs, dropout_mask, mask=p_mask)
|
| 227 |
+
|
| 228 |
+
# return scores with negative values for dropped vals
|
| 229 |
+
sd_mask = tl.where(dropout_mask, p, -p)
|
| 230 |
+
tl.store(sd_mask_ptrs, sd_mask, mask=p_mask)
|
| 231 |
+
|
| 232 |
+
# apply dropout mask in place
|
| 233 |
+
p = tl.where(dropout_mask, p, 0.0)
|
| 234 |
+
elif RETURN_SCORES:
|
| 235 |
+
# NOTE: the returned score is not the same as the reference because we need to adjust as we find new maxes per block. We are not doing that
|
| 236 |
+
tl.store(sd_mask_ptrs, p, mask=p_mask)
|
| 237 |
+
|
| 238 |
+
# -- update output accumulator --
|
| 239 |
+
# alpha is an adjustment factor for acc and li as we loop and find new maxes
|
| 240 |
+
# store the diff in maxes to adjust acc and li as we discover new maxes
|
| 241 |
+
alpha = tl.math.exp2(m_i - m_ij)
|
| 242 |
+
if SLIDING_WINDOW > 0:
|
| 243 |
+
# When m_i == m_ij == -inf, exp2(-inf - (-inf)) = NaN. alpha should be 1.0
|
| 244 |
+
# (no rescaling needed since max didn't change).
|
| 245 |
+
alpha = tl.where(m_i == m_ij, 1.0, alpha)
|
| 246 |
+
acc = acc * alpha[:, None]
|
| 247 |
+
# -- update m_i and l_i
|
| 248 |
+
l_i = l_i * alpha + l_ij
|
| 249 |
+
# update m_i and l_i
|
| 250 |
+
m_i = m_ij
|
| 251 |
+
if not PRELOAD_V:
|
| 252 |
+
v = _load_fn(v_ptrs, k_offs_n, k_offs_k, seqlen_k, BLOCK_DMODEL)
|
| 253 |
+
if IS_FP8:
|
| 254 |
+
scale_p, descale_p = _compute_fp8_scaling_factors(p, FP8_MAX)
|
| 255 |
+
acc += (
|
| 256 |
+
tl.dot((p * scale_p).to(v.type.element_ty), v) * descale_p * descale_v
|
| 257 |
+
)
|
| 258 |
+
else:
|
| 259 |
+
acc = tl.dot(p.to(v.type.element_ty), v, acc=acc)
|
| 260 |
+
|
| 261 |
+
k_ptrs += BLOCK_N * stride_kn
|
| 262 |
+
if HAS_PE:
|
| 263 |
+
k_pe_ptrs += BLOCK_N * stride_kn
|
| 264 |
+
v_ptrs += BLOCK_N * stride_vk
|
| 265 |
+
if RETURN_SCORES:
|
| 266 |
+
sd_mask_ptrs += BLOCK_N * stride_sn
|
| 267 |
+
|
| 268 |
+
if ENABLE_DROPOUT:
|
| 269 |
+
dropout_mask_ptrs += BLOCK_N * stride_sn
|
| 270 |
+
philox_ptrs += BLOCK_N * stride_sn
|
| 271 |
+
|
| 272 |
+
return acc, l_i, m_i
|
| 273 |
+
|
| 274 |
+
|
| 275 |
+
_attn_fwd_repr = make_kernel_repr(
|
| 276 |
+
"_attn_fwd",
|
| 277 |
+
[
|
| 278 |
+
"IS_CAUSAL",
|
| 279 |
+
"NUM_Q_HEADS",
|
| 280 |
+
"NUM_K_HEADS",
|
| 281 |
+
"BLOCK_M",
|
| 282 |
+
"BLOCK_N",
|
| 283 |
+
"BLOCK_DMODEL",
|
| 284 |
+
"RETURN_SCORES",
|
| 285 |
+
"ENABLE_DROPOUT",
|
| 286 |
+
"IS_FP8",
|
| 287 |
+
"VARLEN",
|
| 288 |
+
"NUM_XCD",
|
| 289 |
+
"USE_INT64_STRIDES",
|
| 290 |
+
"ENABLE_SINK",
|
| 291 |
+
"SLIDING_WINDOW",
|
| 292 |
+
],
|
| 293 |
+
)
|
| 294 |
+
|
| 295 |
+
|
| 296 |
+
@triton.jit(repr=_attn_fwd_repr)
|
| 297 |
+
def _attn_fwd(
|
| 298 |
+
q_ptr: torch.Tensor,
|
| 299 |
+
k_ptr: torch.Tensor,
|
| 300 |
+
v_ptr: torch.Tensor,
|
| 301 |
+
descale_q_ptr: torch.Tensor,
|
| 302 |
+
descale_k_ptr: torch.Tensor,
|
| 303 |
+
descale_v_ptr: torch.Tensor,
|
| 304 |
+
out_ptr: torch.Tensor,
|
| 305 |
+
alibi_slopes_ptr: torch.Tensor,
|
| 306 |
+
s_dmask_ptr: torch.Tensor,
|
| 307 |
+
dropout_mask_ptr: torch.Tensor,
|
| 308 |
+
softmax_lse_ptr: torch.Tensor,
|
| 309 |
+
sink_ptr: torch.Tensor,
|
| 310 |
+
stride_qz_in,
|
| 311 |
+
stride_qh_in,
|
| 312 |
+
stride_qm_in,
|
| 313 |
+
stride_qk_in,
|
| 314 |
+
stride_kz_in,
|
| 315 |
+
stride_kh_in,
|
| 316 |
+
stride_kn_in,
|
| 317 |
+
stride_kk_in,
|
| 318 |
+
stride_vz_in,
|
| 319 |
+
stride_vh_in,
|
| 320 |
+
stride_vn_in,
|
| 321 |
+
stride_vk_in,
|
| 322 |
+
stride_descale_q_z_in,
|
| 323 |
+
stride_descale_k_z_in,
|
| 324 |
+
stride_descale_v_z_in,
|
| 325 |
+
stride_oz_in,
|
| 326 |
+
stride_oh_in,
|
| 327 |
+
stride_om_in,
|
| 328 |
+
stride_on_in,
|
| 329 |
+
stride_alibi_z_in,
|
| 330 |
+
stride_alibi_h_in,
|
| 331 |
+
stride_sd_z_in,
|
| 332 |
+
stride_sd_h_in,
|
| 333 |
+
stride_sd_m_in,
|
| 334 |
+
stride_sd_n_in,
|
| 335 |
+
stride_lse_z_in,
|
| 336 |
+
stride_lse_h_in,
|
| 337 |
+
stride_lse_m_in,
|
| 338 |
+
sm_scale,
|
| 339 |
+
cu_seqlens_q,
|
| 340 |
+
cu_seqlens_k,
|
| 341 |
+
dropout_p,
|
| 342 |
+
philox_seed,
|
| 343 |
+
philox_offset_base_in,
|
| 344 |
+
SEQLEN_Q,
|
| 345 |
+
SEQLEN_K,
|
| 346 |
+
IS_CAUSAL: tl.constexpr,
|
| 347 |
+
NUM_Q_HEADS: tl.constexpr,
|
| 348 |
+
NUM_K_HEADS: tl.constexpr,
|
| 349 |
+
PRELOAD_V: tl.constexpr,
|
| 350 |
+
BLOCK_M: tl.constexpr,
|
| 351 |
+
BLOCK_N: tl.constexpr,
|
| 352 |
+
BLOCK_DMODEL: tl.constexpr,
|
| 353 |
+
BLOCK_DMODEL_POW2: tl.constexpr,
|
| 354 |
+
BLOCK_DMODEL_PE: tl.constexpr, # it's zero or a power of 2
|
| 355 |
+
RETURN_SCORES: tl.constexpr,
|
| 356 |
+
ENABLE_DROPOUT: tl.constexpr,
|
| 357 |
+
IS_FP8: tl.constexpr,
|
| 358 |
+
FP8_MAX: tl.constexpr,
|
| 359 |
+
VARLEN: tl.constexpr,
|
| 360 |
+
BATCH,
|
| 361 |
+
NUM_XCD: tl.constexpr,
|
| 362 |
+
USE_INT64_STRIDES: tl.constexpr,
|
| 363 |
+
ENABLE_SINK: tl.constexpr,
|
| 364 |
+
SLIDING_WINDOW: tl.constexpr,
|
| 365 |
+
):
|
| 366 |
+
NUM_BLOCKS = (SEQLEN_Q + BLOCK_M - 1) // BLOCK_M
|
| 367 |
+
# calculate offsets
|
| 368 |
+
wid = tl.program_id(
|
| 369 |
+
0
|
| 370 |
+
) # workgroup id ranging: 0,1,2,...., (BATCH * NUM_Q_HEADS * NUM_BLOCKS - 1)
|
| 371 |
+
# num blocks along seqlen
|
| 372 |
+
|
| 373 |
+
off_q_head = wid % NUM_Q_HEADS
|
| 374 |
+
off_q_head = remap_xcd(off_q_head, NUM_Q_HEADS, NUM_XCD)
|
| 375 |
+
start_m = (wid // NUM_Q_HEADS) % NUM_BLOCKS
|
| 376 |
+
off_z = (wid // (NUM_BLOCKS * NUM_Q_HEADS)) % BATCH
|
| 377 |
+
|
| 378 |
+
# offsets
|
| 379 |
+
offs_m = start_m * BLOCK_M + tl.arange(0, BLOCK_M)
|
| 380 |
+
offs_n = tl.arange(0, BLOCK_N)
|
| 381 |
+
offs_d = tl.arange(0, BLOCK_DMODEL_POW2)
|
| 382 |
+
HAS_PE: tl.constexpr = BLOCK_DMODEL_PE > 0
|
| 383 |
+
if HAS_PE:
|
| 384 |
+
offs_pe = BLOCK_DMODEL + tl.arange(0, BLOCK_DMODEL_PE)
|
| 385 |
+
|
| 386 |
+
# NOTE:
|
| 387 |
+
# Workaround for int64 strides, In the absence of strides being int64, parts of the offset
|
| 388 |
+
# computation is done in 32 bit and overflows resulting in segfaults
|
| 389 |
+
# If input strides are defined as int64, it disables vectorized loads which drops perf
|
| 390 |
+
# If we define new strides as stride_x = stride_x_in.to(tl.int64), that does not work
|
| 391 |
+
# because strides are tl.constexpr and cannot be upcasted
|
| 392 |
+
# If we define new strides as stride_x: tl.int64 = stride_x_in, segfault remains
|
| 393 |
+
# The permanent solution is to enable upcasting of tl.constexpr
|
| 394 |
+
# In the meantime, the following workaround provides correctness and does not drop perf
|
| 395 |
+
if USE_INT64_STRIDES:
|
| 396 |
+
stride_qz = tl.cast(stride_qz_in, tl.int64)
|
| 397 |
+
stride_qh = tl.cast(stride_qh_in, tl.int64)
|
| 398 |
+
stride_qm = tl.cast(stride_qm_in, tl.int64)
|
| 399 |
+
stride_qk = tl.cast(stride_qk_in, tl.int64)
|
| 400 |
+
stride_kz = tl.cast(stride_kz_in, tl.int64)
|
| 401 |
+
stride_kh = tl.cast(stride_kh_in, tl.int64)
|
| 402 |
+
stride_kn = tl.cast(stride_kn_in, tl.int64)
|
| 403 |
+
stride_kk = tl.cast(stride_kk_in, tl.int64)
|
| 404 |
+
stride_vz = tl.cast(stride_vz_in, tl.int64)
|
| 405 |
+
stride_vh = tl.cast(stride_vh_in, tl.int64)
|
| 406 |
+
stride_vn = tl.cast(stride_vn_in, tl.int64)
|
| 407 |
+
stride_vk = tl.cast(stride_vk_in, tl.int64)
|
| 408 |
+
if IS_FP8:
|
| 409 |
+
stride_descale_q_z = tl.cast(stride_descale_q_z_in, tl.int64)
|
| 410 |
+
stride_descale_k_z = tl.cast(stride_descale_k_z_in, tl.int64)
|
| 411 |
+
stride_descale_v_z = tl.cast(stride_descale_v_z_in, tl.int64)
|
| 412 |
+
stride_oz = tl.cast(stride_oz_in, tl.int64)
|
| 413 |
+
stride_oh = tl.cast(stride_oh_in, tl.int64)
|
| 414 |
+
stride_om = tl.cast(stride_om_in, tl.int64)
|
| 415 |
+
stride_on = tl.cast(stride_on_in, tl.int64)
|
| 416 |
+
stride_alibi_z = tl.cast(stride_alibi_z_in, tl.int64)
|
| 417 |
+
stride_alibi_h = tl.cast(stride_alibi_h_in, tl.int64)
|
| 418 |
+
|
| 419 |
+
# NOTE: philox offset is need in dropout pointer calculations
|
| 420 |
+
philox_offset_base = tl.cast(philox_offset_base_in, tl.int64)
|
| 421 |
+
stride_sd_z = tl.cast(stride_sd_z_in, tl.int64)
|
| 422 |
+
stride_sd_h = tl.cast(stride_sd_h_in, tl.int64)
|
| 423 |
+
stride_sd_m = tl.cast(stride_sd_m_in, tl.int64)
|
| 424 |
+
stride_sd_n = tl.cast(stride_sd_n_in, tl.int64)
|
| 425 |
+
stride_lse_z = tl.cast(stride_lse_z_in, tl.int64)
|
| 426 |
+
stride_lse_h = tl.cast(stride_lse_h_in, tl.int64)
|
| 427 |
+
stride_lse_m = tl.cast(stride_lse_m_in, tl.int64)
|
| 428 |
+
else:
|
| 429 |
+
stride_qz = stride_qz_in
|
| 430 |
+
stride_qm = stride_qm_in
|
| 431 |
+
stride_qk = stride_qk_in
|
| 432 |
+
stride_qh = stride_qh_in
|
| 433 |
+
stride_kz = stride_kz_in
|
| 434 |
+
stride_kh = stride_kh_in
|
| 435 |
+
stride_kn = stride_kn_in
|
| 436 |
+
stride_kk = stride_kk_in
|
| 437 |
+
stride_vz = stride_vz_in
|
| 438 |
+
stride_vh = stride_vh_in
|
| 439 |
+
stride_vn = stride_vn_in
|
| 440 |
+
stride_vk = stride_vk_in
|
| 441 |
+
stride_descale_q_z = stride_descale_q_z_in
|
| 442 |
+
stride_descale_k_z = stride_descale_k_z_in
|
| 443 |
+
stride_descale_v_z = stride_descale_v_z_in
|
| 444 |
+
stride_oz = stride_oz_in
|
| 445 |
+
stride_oh = stride_oh_in
|
| 446 |
+
stride_om = stride_om_in
|
| 447 |
+
stride_on = stride_on_in
|
| 448 |
+
stride_alibi_z = stride_alibi_z_in
|
| 449 |
+
stride_alibi_h = stride_alibi_h_in
|
| 450 |
+
philox_offset_base = philox_offset_base_in
|
| 451 |
+
stride_sd_z = stride_sd_z_in
|
| 452 |
+
stride_sd_h = stride_sd_h_in
|
| 453 |
+
stride_sd_m = stride_sd_m_in
|
| 454 |
+
stride_sd_n = stride_sd_n_in
|
| 455 |
+
stride_lse_z = stride_lse_z_in
|
| 456 |
+
stride_lse_h = stride_lse_h_in
|
| 457 |
+
stride_lse_m = stride_lse_m_in
|
| 458 |
+
|
| 459 |
+
tl.assume(stride_qz_in >= 0)
|
| 460 |
+
tl.assume(stride_qh_in >= 0)
|
| 461 |
+
tl.assume(stride_qm_in >= 0)
|
| 462 |
+
tl.assume(stride_qk_in >= 0)
|
| 463 |
+
tl.assume(stride_kz_in >= 0)
|
| 464 |
+
tl.assume(stride_kh_in >= 0)
|
| 465 |
+
tl.assume(stride_kn_in >= 0)
|
| 466 |
+
tl.assume(stride_kk_in >= 0)
|
| 467 |
+
tl.assume(stride_vz_in >= 0)
|
| 468 |
+
tl.assume(stride_vh_in >= 0)
|
| 469 |
+
tl.assume(stride_vn_in >= 0)
|
| 470 |
+
tl.assume(stride_vk_in >= 0)
|
| 471 |
+
if IS_FP8:
|
| 472 |
+
tl.assume(stride_descale_q_z_in >= 0)
|
| 473 |
+
tl.assume(stride_descale_k_z_in >= 0)
|
| 474 |
+
tl.assume(stride_descale_v_z_in >= 0)
|
| 475 |
+
tl.assume(stride_oz_in >= 0)
|
| 476 |
+
tl.assume(stride_oh_in >= 0)
|
| 477 |
+
tl.assume(stride_om_in >= 0)
|
| 478 |
+
tl.assume(stride_on_in >= 0)
|
| 479 |
+
tl.assume(stride_alibi_z_in >= 0)
|
| 480 |
+
tl.assume(stride_alibi_h_in >= 0)
|
| 481 |
+
# NOTE: philox offset is need in dropout pointer calculations
|
| 482 |
+
tl.assume(philox_offset_base_in >= 0)
|
| 483 |
+
tl.assume(stride_sd_z_in >= 0)
|
| 484 |
+
tl.assume(stride_sd_h_in >= 0)
|
| 485 |
+
tl.assume(stride_sd_m_in >= 0)
|
| 486 |
+
tl.assume(stride_sd_n_in >= 0)
|
| 487 |
+
tl.assume(stride_lse_z_in >= 0)
|
| 488 |
+
tl.assume(stride_lse_h_in >= 0)
|
| 489 |
+
tl.assume(stride_lse_m_in >= 0)
|
| 490 |
+
|
| 491 |
+
if VARLEN:
|
| 492 |
+
cu_seqlens_q_start = tl.load(cu_seqlens_q + off_z)
|
| 493 |
+
cu_seqlens_q_end = tl.load(cu_seqlens_q + off_z + 1)
|
| 494 |
+
|
| 495 |
+
seqlen_q = cu_seqlens_q_end - cu_seqlens_q_start
|
| 496 |
+
# We have a one-size-fits-all grid in id(0). Some seqlens might be too
|
| 497 |
+
# small for all start_m so for those we return early.
|
| 498 |
+
if start_m * BLOCK_M > seqlen_q:
|
| 499 |
+
return
|
| 500 |
+
cu_seqlens_k_start = tl.load(cu_seqlens_k + off_z)
|
| 501 |
+
cu_seqlens_k_end = tl.load(cu_seqlens_k + off_z + 1)
|
| 502 |
+
seqlen_k = cu_seqlens_k_end - cu_seqlens_k_start
|
| 503 |
+
else:
|
| 504 |
+
cu_seqlens_q_start = 0
|
| 505 |
+
cu_seqlens_k_start = 0
|
| 506 |
+
seqlen_q = SEQLEN_Q
|
| 507 |
+
seqlen_k = SEQLEN_K
|
| 508 |
+
|
| 509 |
+
n_blocks = _cdiv_fn(seqlen_k, BLOCK_N)
|
| 510 |
+
|
| 511 |
+
# Now we compute whether we need to exit early due to causal masking.
|
| 512 |
+
# This is because for seqlen_q > seqlen_k, M rows of the attn scores
|
| 513 |
+
# are completely masked, resulting in 0s written to the output, and
|
| 514 |
+
# inf written to LSE. We don't need to do any GEMMs in this case.
|
| 515 |
+
# This block of code determines what N is, and if this WG is operating
|
| 516 |
+
# on those M rows.
|
| 517 |
+
if IS_CAUSAL:
|
| 518 |
+
# If seqlen_q == seqlen_k, the attn scores are a square matrix.
|
| 519 |
+
# If seqlen_q != seqlen_k, attn scores are rectangular which means
|
| 520 |
+
# the causal mask boundary is bottom right aligned, and ends at either
|
| 521 |
+
# the top edge (seqlen_q < seqlen_k) or left edge.
|
| 522 |
+
|
| 523 |
+
# This captures the decrease in n_blocks if we have a rectangular attn matrix
|
| 524 |
+
n_blocks_seqlen = _cdiv_fn(
|
| 525 |
+
(start_m + 1) * BLOCK_M + seqlen_k - seqlen_q, BLOCK_N
|
| 526 |
+
)
|
| 527 |
+
|
| 528 |
+
# This is what adjusts the block_max for the current WG, only
|
| 529 |
+
# if IS_CAUSAL. Otherwise we want to always iterate through all n_blocks
|
| 530 |
+
n_blocks = min(n_blocks, n_blocks_seqlen)
|
| 531 |
+
|
| 532 |
+
# If we have no blocks after adjusting for seqlen deltas, this WG is part of
|
| 533 |
+
# the blocks that are all 0. We exit early.
|
| 534 |
+
if n_blocks <= 0:
|
| 535 |
+
offs_out = (
|
| 536 |
+
off_z * stride_oz
|
| 537 |
+
+ off_q_head * stride_oh
|
| 538 |
+
+ cu_seqlens_q_start * stride_om
|
| 539 |
+
+ offs_m[:, None] * stride_om
|
| 540 |
+
+ offs_d[None, :] * stride_on
|
| 541 |
+
)
|
| 542 |
+
acc = tl.zeros([BLOCK_M, BLOCK_DMODEL_POW2], dtype=out_ptr.type.element_ty)
|
| 543 |
+
out_mask = (offs_m[:, None] < seqlen_q) & (offs_d[None, :] < BLOCK_DMODEL)
|
| 544 |
+
tl.store(out_ptr + offs_out, acc, mask=out_mask)
|
| 545 |
+
|
| 546 |
+
if softmax_lse_ptr is not None:
|
| 547 |
+
offs_lse = (
|
| 548 |
+
off_z * stride_lse_z
|
| 549 |
+
+ off_q_head * stride_lse_h
|
| 550 |
+
+ cu_seqlens_q_start * stride_lse_m
|
| 551 |
+
+ offs_m * stride_lse_m
|
| 552 |
+
)
|
| 553 |
+
lse_mask = offs_m < SEQLEN_Q
|
| 554 |
+
lse = tl.full([BLOCK_M], value=0.0, dtype=tl.float32)
|
| 555 |
+
tl.store(softmax_lse_ptr + offs_lse, lse, mask=lse_mask)
|
| 556 |
+
# TODO: Should dropout and return encoded softmax be handled here too?
|
| 557 |
+
|
| 558 |
+
return
|
| 559 |
+
|
| 560 |
+
grp_sz: tl.constexpr = NUM_Q_HEADS // NUM_K_HEADS
|
| 561 |
+
if grp_sz != 1: # Grouped Query Attention
|
| 562 |
+
off_k_head = off_q_head // grp_sz
|
| 563 |
+
else:
|
| 564 |
+
off_k_head = off_q_head
|
| 565 |
+
|
| 566 |
+
# q,k,v offsets
|
| 567 |
+
q_offs = (
|
| 568 |
+
off_z * stride_qz
|
| 569 |
+
+ off_q_head * stride_qh
|
| 570 |
+
+ cu_seqlens_q_start * stride_qm
|
| 571 |
+
+ offs_m[:, None] * stride_qm
|
| 572 |
+
+ offs_d[None, :] * stride_qk
|
| 573 |
+
)
|
| 574 |
+
q_ptrs = q_ptr + q_offs
|
| 575 |
+
if HAS_PE:
|
| 576 |
+
q_pe_offs = (
|
| 577 |
+
off_z * stride_qz
|
| 578 |
+
+ off_q_head * stride_qh
|
| 579 |
+
+ cu_seqlens_q_start * stride_qm
|
| 580 |
+
+ offs_m[:, None] * stride_qm
|
| 581 |
+
+ offs_pe[None, :] * stride_qk
|
| 582 |
+
)
|
| 583 |
+
q_pe_ptrs = q_ptr + q_pe_offs
|
| 584 |
+
else:
|
| 585 |
+
q_pe_ptrs = None
|
| 586 |
+
|
| 587 |
+
k_offs = (
|
| 588 |
+
off_z * stride_kz
|
| 589 |
+
+ off_k_head * stride_kh
|
| 590 |
+
+ cu_seqlens_k_start * stride_kn
|
| 591 |
+
+ offs_d[:, None] * stride_kk
|
| 592 |
+
+ offs_n[None, :] * stride_kn
|
| 593 |
+
)
|
| 594 |
+
k_ptrs = k_ptr + k_offs
|
| 595 |
+
if HAS_PE:
|
| 596 |
+
k_pe_offs = (
|
| 597 |
+
off_z * stride_kz
|
| 598 |
+
+ off_k_head * stride_kh
|
| 599 |
+
+ cu_seqlens_k_start * stride_kn
|
| 600 |
+
+ offs_pe[:, None] * stride_kk
|
| 601 |
+
+ offs_n[None, :] * stride_kn
|
| 602 |
+
)
|
| 603 |
+
k_pe_ptrs = k_ptr + k_pe_offs
|
| 604 |
+
else:
|
| 605 |
+
k_pe_ptrs = None
|
| 606 |
+
|
| 607 |
+
v_offs = (
|
| 608 |
+
off_z * stride_vz
|
| 609 |
+
+ off_k_head * stride_vh
|
| 610 |
+
+ cu_seqlens_k_start * stride_vn
|
| 611 |
+
+ offs_n[:, None] * stride_vn
|
| 612 |
+
+ offs_d[None, :] * stride_vk
|
| 613 |
+
)
|
| 614 |
+
v_ptrs = v_ptr + v_offs
|
| 615 |
+
|
| 616 |
+
# alibi slopes
|
| 617 |
+
if alibi_slopes_ptr is not None:
|
| 618 |
+
alibi_offs = off_z * stride_alibi_z + off_q_head * stride_alibi_h
|
| 619 |
+
alibi_slope = tl.load(alibi_slopes_ptr + alibi_offs)
|
| 620 |
+
else:
|
| 621 |
+
alibi_slope = None
|
| 622 |
+
|
| 623 |
+
# s_dmask (return_scores)
|
| 624 |
+
if s_dmask_ptr is not None:
|
| 625 |
+
s_dmask_offs = (
|
| 626 |
+
off_z * stride_sd_z
|
| 627 |
+
+ off_q_head * stride_sd_h
|
| 628 |
+
+ offs_m[:, None] * stride_sd_m
|
| 629 |
+
+ offs_n[None, :] * stride_sd_n
|
| 630 |
+
)
|
| 631 |
+
s_dmask_ptrs = s_dmask_ptr + s_dmask_offs
|
| 632 |
+
else:
|
| 633 |
+
s_dmask_ptrs = None
|
| 634 |
+
|
| 635 |
+
# dropout
|
| 636 |
+
if dropout_mask_ptr is not None:
|
| 637 |
+
dropout_mask_offs = (
|
| 638 |
+
off_z * stride_sd_z
|
| 639 |
+
+ off_q_head * stride_sd_h
|
| 640 |
+
+ offs_m[:, None] * stride_sd_m
|
| 641 |
+
+ offs_n[None, :] * stride_sd_n
|
| 642 |
+
)
|
| 643 |
+
dropout_mask_ptrs = dropout_mask_ptr + dropout_mask_offs
|
| 644 |
+
philox_ptrs = (
|
| 645 |
+
philox_offset_base
|
| 646 |
+
+ off_z * stride_sd_z
|
| 647 |
+
+ off_q_head * stride_sd_h
|
| 648 |
+
+ offs_m[:, None] * stride_sd_m
|
| 649 |
+
+ offs_n[None, :] * stride_sd_n
|
| 650 |
+
)
|
| 651 |
+
else:
|
| 652 |
+
dropout_mask_ptrs = None
|
| 653 |
+
philox_ptrs = None
|
| 654 |
+
|
| 655 |
+
if ENABLE_SINK:
|
| 656 |
+
RCP_LN2: tl.constexpr = 1.4426950408889634
|
| 657 |
+
m_i_value = tl.load(sink_ptr + off_q_head).to(tl.float32) * RCP_LN2
|
| 658 |
+
else:
|
| 659 |
+
m_i_value = float("-inf")
|
| 660 |
+
|
| 661 |
+
m_i = tl.full([BLOCK_M], m_i_value, dtype=tl.float32)
|
| 662 |
+
l_i = tl.full([BLOCK_M], 1.0, dtype=tl.float32)
|
| 663 |
+
acc = tl.zeros([BLOCK_M, BLOCK_DMODEL_POW2], dtype=tl.float32)
|
| 664 |
+
if BLOCK_DMODEL == BLOCK_DMODEL_POW2:
|
| 665 |
+
q_mask = offs_m[:, None] < seqlen_q
|
| 666 |
+
else:
|
| 667 |
+
q_mask = (offs_m[:, None] < seqlen_q) & (offs_d[None, :] < BLOCK_DMODEL)
|
| 668 |
+
|
| 669 |
+
if BLOCK_M >= NUM_Q_HEADS:
|
| 670 |
+
q_cache_mod: tl.constexpr = ".cg"
|
| 671 |
+
else:
|
| 672 |
+
q_cache_mod: tl.constexpr = ""
|
| 673 |
+
|
| 674 |
+
if HAS_PE:
|
| 675 |
+
q_pe = tl.load(q_pe_ptrs, mask=q_mask, other=0.0, cache_modifier=q_cache_mod)
|
| 676 |
+
else:
|
| 677 |
+
q_pe = None
|
| 678 |
+
q = tl.load(q_ptrs, mask=q_mask, other=0.0, cache_modifier=q_cache_mod)
|
| 679 |
+
if IS_FP8:
|
| 680 |
+
descale_q = tl.load(descale_q_ptr + off_z * stride_descale_q_z + off_q_head)
|
| 681 |
+
descale_k = tl.load(descale_k_ptr + off_z * stride_descale_k_z + off_k_head)
|
| 682 |
+
descale_v = tl.load(descale_v_ptr + off_z * stride_descale_v_z + off_k_head)
|
| 683 |
+
else:
|
| 684 |
+
descale_q, descale_k, descale_v = 1.0, 1.0, 1.0
|
| 685 |
+
|
| 686 |
+
n_extra_tokens = 0
|
| 687 |
+
if seqlen_k < BLOCK_N:
|
| 688 |
+
n_extra_tokens = BLOCK_N - seqlen_k
|
| 689 |
+
elif seqlen_k % BLOCK_N:
|
| 690 |
+
n_extra_tokens = seqlen_k % BLOCK_N
|
| 691 |
+
|
| 692 |
+
# if CAUSAL, then determine masked_blocks and full blocks
|
| 693 |
+
# Here we compute how many full and masked blocks we have.
|
| 694 |
+
padded_block_k = n_extra_tokens != 0
|
| 695 |
+
is_modulo_mn = not padded_block_k and (seqlen_q % BLOCK_M == 0)
|
| 696 |
+
skipped_blocks = 0
|
| 697 |
+
if SLIDING_WINDOW > 0:
|
| 698 |
+
# Skip K blocks that are fully left of the earliest key position
|
| 699 |
+
# reachable by this Q block. The first retained block can still be
|
| 700 |
+
# partially outside the window, so we keep the per-element mask below.
|
| 701 |
+
window_start_n = start_m * BLOCK_M + seqlen_k - seqlen_q - SLIDING_WINDOW
|
| 702 |
+
skipped_blocks = tl.maximum(window_start_n, 0) // BLOCK_N
|
| 703 |
+
skipped_blocks = tl.minimum(skipped_blocks, n_blocks)
|
| 704 |
+
if IS_CAUSAL:
|
| 705 |
+
# There are always at least BLOCK_M // BLOCK_N masked blocks.
|
| 706 |
+
# Additionally there might be one more due to dissimilar seqlens.
|
| 707 |
+
masked_blocks = BLOCK_M // BLOCK_N + (not is_modulo_mn)
|
| 708 |
+
else:
|
| 709 |
+
# Padding on Q does not need to be masked in the FA loop.
|
| 710 |
+
masked_blocks = padded_block_k
|
| 711 |
+
# if IS_CAUSAL, not is_modulo_mn does not always result in an additional block.
|
| 712 |
+
# In this case we might exceed n_blocks so pick the min.
|
| 713 |
+
visible_blocks = n_blocks - skipped_blocks
|
| 714 |
+
masked_blocks = min(masked_blocks, visible_blocks)
|
| 715 |
+
n_full_blocks = visible_blocks - masked_blocks
|
| 716 |
+
block_min = skipped_blocks * BLOCK_N
|
| 717 |
+
block_max = n_blocks * BLOCK_N
|
| 718 |
+
if skipped_blocks > 0:
|
| 719 |
+
k_ptrs += skipped_blocks * BLOCK_N * stride_kn
|
| 720 |
+
if HAS_PE:
|
| 721 |
+
k_pe_ptrs += skipped_blocks * BLOCK_N * stride_kn
|
| 722 |
+
v_ptrs += skipped_blocks * BLOCK_N * stride_vn
|
| 723 |
+
if RETURN_SCORES:
|
| 724 |
+
s_dmask_ptrs += skipped_blocks * BLOCK_N * stride_sd_n
|
| 725 |
+
if ENABLE_DROPOUT:
|
| 726 |
+
dropout_mask_ptrs += skipped_blocks * BLOCK_N * stride_sd_n
|
| 727 |
+
philox_ptrs += skipped_blocks * BLOCK_N * stride_sd_n
|
| 728 |
+
# Compute for full blocks. Here we set causal to false regardless of its actual
|
| 729 |
+
# value because there is no masking. Similarly we do not need padding.
|
| 730 |
+
if n_full_blocks > 0:
|
| 731 |
+
block_max = block_min + n_full_blocks * BLOCK_N
|
| 732 |
+
acc, l_i, m_i = _attn_fwd_inner(
|
| 733 |
+
acc,
|
| 734 |
+
l_i,
|
| 735 |
+
m_i,
|
| 736 |
+
q,
|
| 737 |
+
q_pe,
|
| 738 |
+
k_ptrs,
|
| 739 |
+
k_pe_ptrs,
|
| 740 |
+
v_ptrs,
|
| 741 |
+
stride_kn,
|
| 742 |
+
stride_vn,
|
| 743 |
+
stride_sd_n,
|
| 744 |
+
start_m,
|
| 745 |
+
seqlen_k,
|
| 746 |
+
seqlen_q,
|
| 747 |
+
dropout_p,
|
| 748 |
+
s_dmask_ptrs,
|
| 749 |
+
dropout_mask_ptrs,
|
| 750 |
+
philox_seed,
|
| 751 |
+
philox_ptrs,
|
| 752 |
+
block_min,
|
| 753 |
+
block_max,
|
| 754 |
+
0,
|
| 755 |
+
0,
|
| 756 |
+
0,
|
| 757 |
+
alibi_slope,
|
| 758 |
+
descale_q,
|
| 759 |
+
descale_k,
|
| 760 |
+
descale_v,
|
| 761 |
+
offs_m,
|
| 762 |
+
offs_n,
|
| 763 |
+
PRELOAD_V,
|
| 764 |
+
BLOCK_M,
|
| 765 |
+
BLOCK_N,
|
| 766 |
+
BLOCK_DMODEL,
|
| 767 |
+
BLOCK_DMODEL_POW2,
|
| 768 |
+
BLOCK_DMODEL_PE,
|
| 769 |
+
sm_scale,
|
| 770 |
+
False,
|
| 771 |
+
MASK_STEPS=False,
|
| 772 |
+
ENABLE_DROPOUT=ENABLE_DROPOUT,
|
| 773 |
+
RETURN_SCORES=RETURN_SCORES,
|
| 774 |
+
PADDED_HEAD=BLOCK_DMODEL != BLOCK_DMODEL_POW2,
|
| 775 |
+
IS_FP8=IS_FP8,
|
| 776 |
+
FP8_MAX=FP8_MAX,
|
| 777 |
+
ENABLE_PIPELINING=True,
|
| 778 |
+
SLIDING_WINDOW=SLIDING_WINDOW,
|
| 779 |
+
)
|
| 780 |
+
block_min = block_max
|
| 781 |
+
block_max = n_blocks * BLOCK_N
|
| 782 |
+
|
| 783 |
+
# Remaining blocks, if any, are full / not masked.
|
| 784 |
+
if masked_blocks > 0:
|
| 785 |
+
if IS_CAUSAL:
|
| 786 |
+
offs_n_causal = offs_n + (seqlen_q - seqlen_k)
|
| 787 |
+
else:
|
| 788 |
+
offs_n_causal = 0
|
| 789 |
+
k_ptrs += n_full_blocks * BLOCK_N * stride_kn
|
| 790 |
+
if HAS_PE:
|
| 791 |
+
k_pe_ptrs += n_full_blocks * BLOCK_N * stride_kn
|
| 792 |
+
v_ptrs += n_full_blocks * BLOCK_N * stride_vn
|
| 793 |
+
if RETURN_SCORES:
|
| 794 |
+
s_dmask_ptrs += n_full_blocks * BLOCK_N * stride_sd_n
|
| 795 |
+
if ENABLE_DROPOUT:
|
| 796 |
+
dropout_mask_ptrs += n_full_blocks * BLOCK_N * stride_sd_n
|
| 797 |
+
acc, l_i, m_i = _attn_fwd_inner(
|
| 798 |
+
acc,
|
| 799 |
+
l_i,
|
| 800 |
+
m_i,
|
| 801 |
+
q,
|
| 802 |
+
q_pe,
|
| 803 |
+
k_ptrs,
|
| 804 |
+
k_pe_ptrs,
|
| 805 |
+
v_ptrs,
|
| 806 |
+
stride_kn,
|
| 807 |
+
stride_vn,
|
| 808 |
+
stride_sd_n,
|
| 809 |
+
start_m,
|
| 810 |
+
seqlen_k,
|
| 811 |
+
seqlen_q,
|
| 812 |
+
dropout_p,
|
| 813 |
+
s_dmask_ptrs,
|
| 814 |
+
dropout_mask_ptrs,
|
| 815 |
+
philox_seed,
|
| 816 |
+
philox_ptrs,
|
| 817 |
+
block_min,
|
| 818 |
+
block_max,
|
| 819 |
+
offs_n_causal,
|
| 820 |
+
masked_blocks,
|
| 821 |
+
n_extra_tokens,
|
| 822 |
+
alibi_slope,
|
| 823 |
+
descale_q,
|
| 824 |
+
descale_k,
|
| 825 |
+
descale_v,
|
| 826 |
+
offs_m,
|
| 827 |
+
offs_n,
|
| 828 |
+
PRELOAD_V,
|
| 829 |
+
BLOCK_M,
|
| 830 |
+
BLOCK_N,
|
| 831 |
+
BLOCK_DMODEL,
|
| 832 |
+
BLOCK_DMODEL_POW2,
|
| 833 |
+
BLOCK_DMODEL_PE,
|
| 834 |
+
sm_scale,
|
| 835 |
+
IS_CAUSAL,
|
| 836 |
+
MASK_STEPS=True,
|
| 837 |
+
ENABLE_DROPOUT=ENABLE_DROPOUT,
|
| 838 |
+
RETURN_SCORES=RETURN_SCORES,
|
| 839 |
+
PADDED_HEAD=BLOCK_DMODEL != BLOCK_DMODEL_POW2,
|
| 840 |
+
IS_FP8=IS_FP8,
|
| 841 |
+
FP8_MAX=FP8_MAX,
|
| 842 |
+
ENABLE_PIPELINING=False,
|
| 843 |
+
SLIDING_WINDOW=SLIDING_WINDOW,
|
| 844 |
+
)
|
| 845 |
+
# epilogue
|
| 846 |
+
# This helps the compiler do Newton Raphson on l_i vs on acc which is much larger.
|
| 847 |
+
l_recip = 1 / l_i[:, None]
|
| 848 |
+
acc = acc * l_recip
|
| 849 |
+
if ENABLE_DROPOUT:
|
| 850 |
+
dropout_scale = 1 / (1 - dropout_p)
|
| 851 |
+
acc = acc * dropout_scale
|
| 852 |
+
# If seqlen_q > seqlen_k but the delta is not a multiple of BLOCK_M,
|
| 853 |
+
# then we have one block with a row of all NaNs which come from computing
|
| 854 |
+
# softmax over a row of all -infs (-inf - inf = NaN). We check for that here
|
| 855 |
+
# and store 0s where there are NaNs as these rows should've been zeroed out.
|
| 856 |
+
end_m_idx = (start_m + 1) * BLOCK_M
|
| 857 |
+
start_m_idx = start_m * BLOCK_M
|
| 858 |
+
causal_start_idx = seqlen_q - seqlen_k
|
| 859 |
+
if IS_CAUSAL:
|
| 860 |
+
if causal_start_idx > start_m_idx and causal_start_idx < end_m_idx:
|
| 861 |
+
out_mask_boundary = tl.full(
|
| 862 |
+
(BLOCK_DMODEL_POW2,), causal_start_idx, dtype=tl.int32
|
| 863 |
+
)
|
| 864 |
+
mask_m_offsets = start_m_idx + tl.arange(0, BLOCK_M)
|
| 865 |
+
out_ptrs_mask = mask_m_offsets[:, None] >= out_mask_boundary[None, :]
|
| 866 |
+
z = 0.0
|
| 867 |
+
acc = tl.where(out_ptrs_mask, acc, z.to(acc.type.element_ty))
|
| 868 |
+
|
| 869 |
+
# write back LSE(Log Sum Exponents), the log of the normalization constant
|
| 870 |
+
overflow_size = end_m_idx - seqlen_q
|
| 871 |
+
if softmax_lse_ptr is not None:
|
| 872 |
+
LN2: tl.constexpr = 0.6931471824645996
|
| 873 |
+
# compute log-sum-exp in base 2 units
|
| 874 |
+
softmax_lse = m_i + tl.math.log2(l_i)
|
| 875 |
+
# convert back to natural units
|
| 876 |
+
softmax_lse *= LN2
|
| 877 |
+
|
| 878 |
+
if IS_CAUSAL:
|
| 879 |
+
# zero out nans caused by -infs when doing causal
|
| 880 |
+
lse_causal_mask = (start_m_idx + tl.arange(0, BLOCK_M)) < causal_start_idx
|
| 881 |
+
softmax_lse = tl.where(lse_causal_mask, 0.0, softmax_lse)
|
| 882 |
+
|
| 883 |
+
# If seqlen_q not multiple of BLOCK_M, we need to mask out the last few rows.
|
| 884 |
+
# This is only true for the last M block. For others, overflow_size will be -ve
|
| 885 |
+
offs_lse = (
|
| 886 |
+
off_z * stride_lse_z
|
| 887 |
+
+ off_q_head * stride_lse_h
|
| 888 |
+
+ cu_seqlens_q_start * stride_lse_m
|
| 889 |
+
+ offs_m * stride_lse_m
|
| 890 |
+
)
|
| 891 |
+
if overflow_size > 0:
|
| 892 |
+
boundary = tl.full((BLOCK_M,), BLOCK_M - overflow_size, dtype=tl.int32)
|
| 893 |
+
lse_mask = tl.arange(0, BLOCK_M) < boundary
|
| 894 |
+
tl.store(
|
| 895 |
+
softmax_lse_ptr + offs_lse, softmax_lse, mask=lse_mask
|
| 896 |
+
) # the log of the normalization constant
|
| 897 |
+
else:
|
| 898 |
+
tl.store(
|
| 899 |
+
softmax_lse_ptr + offs_lse, softmax_lse
|
| 900 |
+
) # the log of the normalization constant
|
| 901 |
+
|
| 902 |
+
# write back O
|
| 903 |
+
offs_out = (
|
| 904 |
+
off_z * stride_oz
|
| 905 |
+
+ off_q_head * stride_oh
|
| 906 |
+
+ cu_seqlens_q_start * stride_om
|
| 907 |
+
+ offs_m[:, None] * stride_om
|
| 908 |
+
+ offs_d[None, :] * stride_on
|
| 909 |
+
)
|
| 910 |
+
out_mask = tl.full([BLOCK_M, 1], 1, dtype=tl.int1)
|
| 911 |
+
if overflow_size > 0:
|
| 912 |
+
out_mask = out_mask & (offs_m[:, None] < seqlen_q)
|
| 913 |
+
if BLOCK_DMODEL != BLOCK_DMODEL_POW2:
|
| 914 |
+
out_mask = out_mask & (offs_d[None, :] < BLOCK_DMODEL)
|
| 915 |
+
op = acc.to(out_ptr.dtype.element_ty)
|
| 916 |
+
tl.store(out_ptr + offs_out, op, mask=out_mask)
|
| 917 |
+
|
| 918 |
+
|
| 919 |
+
@functools.lru_cache(maxsize=1024)
|
| 920 |
+
def _get_config(
|
| 921 |
+
enable_dropout: bool,
|
| 922 |
+
dtype: torch.dtype,
|
| 923 |
+
has_pe: bool = False,
|
| 924 |
+
):
|
| 925 |
+
if not hasattr(_get_config, "_config_dict"):
|
| 926 |
+
dev = arch_info.get_arch()
|
| 927 |
+
_get_config._config_dict = {}
|
| 928 |
+
fpath = f"{AITER_TRITON_CONFIGS_PATH}/{dev}-MHA-DEFAULT.json"
|
| 929 |
+
with open(fpath, "r") as file:
|
| 930 |
+
config = json.load(file)
|
| 931 |
+
_get_config._config_dict["default"] = config
|
| 932 |
+
fwd_cfg = _get_config._config_dict["default"]["fwd"]
|
| 933 |
+
has_dropout_or_fp32 = enable_dropout or dtype == torch.float32
|
| 934 |
+
# TODO: pe + dropout is not tuned
|
| 935 |
+
if has_pe and has_dropout_or_fp32 and "pe_dropout_or_fp32" in fwd_cfg:
|
| 936 |
+
return fwd_cfg["pe_dropout_or_fp32"]
|
| 937 |
+
elif has_pe and "pe" in fwd_cfg:
|
| 938 |
+
return fwd_cfg["pe"]
|
| 939 |
+
elif enable_dropout or dtype == torch.float32:
|
| 940 |
+
return fwd_cfg["dropout_or_fp32"]
|
| 941 |
+
else:
|
| 942 |
+
return fwd_cfg["default"]
|
build/torch-rocm/_kernels/mha_fused_bwd.py
ADDED
|
@@ -0,0 +1,1073 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# SPDX-License-Identifier: MIT
|
| 2 |
+
# Copyright (C) 2024-2026, Advanced Micro Devices, Inc. All rights reserved.
|
| 3 |
+
|
| 4 |
+
import functools
|
| 5 |
+
import json
|
| 6 |
+
import triton
|
| 7 |
+
import triton.language as tl
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
from ..utils._triton import arch_info
|
| 11 |
+
from ..utils.core import AITER_TRITON_CONFIGS_PATH
|
| 12 |
+
from ..utils._triton.pid_preprocessing import remap_xcd
|
| 13 |
+
from ..utils._triton.mha_kernel_utils import _compute_fp8_scaling_factors
|
| 14 |
+
from ..utils._triton.kernel_repr import make_kernel_repr
|
| 15 |
+
|
| 16 |
+
# This function computes delta given output Out and gradient DO
|
| 17 |
+
# Here is the I/O shape:
|
| 18 |
+
# Out: (batch, nhead_q, max_seqlens_q, headDim)
|
| 19 |
+
# DO: (batch, nhead_q, max_seqlens_q, headDim)
|
| 20 |
+
# Delta: (batch, nheads_q, max_seqlens_q), same as softmax_lse defined at
|
| 21 |
+
_bwd_preprocess_repr = make_kernel_repr(
|
| 22 |
+
"_bwd_preprocess",
|
| 23 |
+
[
|
| 24 |
+
"BLOCK_M",
|
| 25 |
+
"BLOCK_D_MODEL",
|
| 26 |
+
"IS_VARLEN",
|
| 27 |
+
"IS_FP8",
|
| 28 |
+
],
|
| 29 |
+
)
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
@triton.jit(repr=_bwd_preprocess_repr)
|
| 33 |
+
def _bwd_preprocess(
|
| 34 |
+
o_ptr,
|
| 35 |
+
do_ptr, # noqa: E741
|
| 36 |
+
delta_ptr,
|
| 37 |
+
stride_o_b,
|
| 38 |
+
stride_o_h,
|
| 39 |
+
stride_o_m,
|
| 40 |
+
stride_o_k,
|
| 41 |
+
stride_do_b,
|
| 42 |
+
stride_do_h,
|
| 43 |
+
stride_do_m,
|
| 44 |
+
stride_do_k,
|
| 45 |
+
stride_delta_b,
|
| 46 |
+
stride_delta_h,
|
| 47 |
+
stride_delta_m,
|
| 48 |
+
stride_descale_do_z,
|
| 49 |
+
cu_seqlens_q,
|
| 50 |
+
max_seqlen_q,
|
| 51 |
+
descale_do_ptr,
|
| 52 |
+
BLOCK_M: tl.constexpr,
|
| 53 |
+
BLOCK_D_MODEL: tl.constexpr,
|
| 54 |
+
BLOCK_D_MODEL_POW2: tl.constexpr,
|
| 55 |
+
IS_VARLEN: tl.constexpr,
|
| 56 |
+
IS_FP8: tl.constexpr,
|
| 57 |
+
):
|
| 58 |
+
pid_m = tl.program_id(0) # seqlen
|
| 59 |
+
bid = tl.program_id(1) # batch
|
| 60 |
+
hid = tl.program_id(2) # head
|
| 61 |
+
|
| 62 |
+
# Handle varlen
|
| 63 |
+
q_start = 0
|
| 64 |
+
seqlen_q = max_seqlen_q
|
| 65 |
+
if IS_VARLEN:
|
| 66 |
+
q_start = tl.load(cu_seqlens_q + bid)
|
| 67 |
+
q_end = tl.load(cu_seqlens_q + bid + 1)
|
| 68 |
+
seqlen_q = q_end - q_start
|
| 69 |
+
else:
|
| 70 |
+
q_start = 0
|
| 71 |
+
seqlen_q = max_seqlen_q
|
| 72 |
+
|
| 73 |
+
# Compute offsets
|
| 74 |
+
offs_m = pid_m * BLOCK_M + tl.arange(0, BLOCK_M)
|
| 75 |
+
offs_k = tl.arange(0, BLOCK_D_MODEL_POW2)
|
| 76 |
+
|
| 77 |
+
# O and DO may have different strides (e.g. BSHD vs SBHD memory layout),
|
| 78 |
+
# so address each with its own strides.
|
| 79 |
+
offs_o = (
|
| 80 |
+
bid * stride_o_b
|
| 81 |
+
+ hid * stride_o_h
|
| 82 |
+
+ q_start * stride_o_m
|
| 83 |
+
+ offs_m[:, None] * stride_o_m
|
| 84 |
+
+ offs_k[None, :] * stride_o_k
|
| 85 |
+
)
|
| 86 |
+
offs_do = (
|
| 87 |
+
bid * stride_do_b
|
| 88 |
+
+ hid * stride_do_h
|
| 89 |
+
+ q_start * stride_do_m
|
| 90 |
+
+ offs_m[:, None] * stride_do_m
|
| 91 |
+
+ offs_k[None, :] * stride_do_k
|
| 92 |
+
)
|
| 93 |
+
|
| 94 |
+
# create masks
|
| 95 |
+
mask_m = offs_m < seqlen_q
|
| 96 |
+
mask = mask_m[:, None]
|
| 97 |
+
PADDED_HEAD: tl.constexpr = BLOCK_D_MODEL != BLOCK_D_MODEL_POW2
|
| 98 |
+
if PADDED_HEAD:
|
| 99 |
+
mask &= offs_k[None, :] < BLOCK_D_MODEL
|
| 100 |
+
|
| 101 |
+
# load [BLOCK_M, BLOCK_D_MODEL_POW2]
|
| 102 |
+
o = tl.load(o_ptr + offs_o, mask=mask, other=0.0)
|
| 103 |
+
do = tl.load(do_ptr + offs_do, mask=mask, other=0.0)
|
| 104 |
+
|
| 105 |
+
# compute and write-back to delta
|
| 106 |
+
if IS_FP8:
|
| 107 |
+
descale_do = tl.load(descale_do_ptr + bid * stride_descale_do_z + hid)
|
| 108 |
+
|
| 109 |
+
# NOTE: do is in the fp8 range and o is not in fp8
|
| 110 |
+
delta = tl.sum(o.to(tl.float32) * (do.to(tl.float32) * descale_do), axis=1)
|
| 111 |
+
else:
|
| 112 |
+
delta = tl.sum(o.to(tl.float32) * do.to(tl.float32), axis=1)
|
| 113 |
+
|
| 114 |
+
offs_delta = (
|
| 115 |
+
bid * stride_delta_b
|
| 116 |
+
+ hid * stride_delta_h
|
| 117 |
+
+ q_start * stride_delta_m
|
| 118 |
+
+ offs_m * stride_delta_m
|
| 119 |
+
)
|
| 120 |
+
tl.store(delta_ptr + offs_delta, delta, mask=mask_m)
|
| 121 |
+
|
| 122 |
+
|
| 123 |
+
@triton.jit
|
| 124 |
+
def _bwd_dkdvdq_inner(
|
| 125 |
+
dk,
|
| 126 |
+
dv,
|
| 127 |
+
Q,
|
| 128 |
+
k,
|
| 129 |
+
v,
|
| 130 |
+
DO,
|
| 131 |
+
DQ,
|
| 132 |
+
M,
|
| 133 |
+
D,
|
| 134 |
+
sm_scale,
|
| 135 |
+
stride_q_m,
|
| 136 |
+
stride_q_k,
|
| 137 |
+
stride_dq_m,
|
| 138 |
+
stride_dq_k,
|
| 139 |
+
stride_do_m,
|
| 140 |
+
stride_do_k,
|
| 141 |
+
stride_dropout_m,
|
| 142 |
+
stride_dropout_n,
|
| 143 |
+
stride_deltam,
|
| 144 |
+
dropout_p,
|
| 145 |
+
philox_seed,
|
| 146 |
+
batch_philox_offset,
|
| 147 |
+
dropout_offset,
|
| 148 |
+
seqlen_q,
|
| 149 |
+
seqlen_k,
|
| 150 |
+
start_n,
|
| 151 |
+
start_m,
|
| 152 |
+
num_steps,
|
| 153 |
+
descale_q,
|
| 154 |
+
descale_k,
|
| 155 |
+
descale_v,
|
| 156 |
+
descale_do,
|
| 157 |
+
BLOCK_M: tl.constexpr,
|
| 158 |
+
BLOCK_N: tl.constexpr,
|
| 159 |
+
BLOCK_D_MODEL: tl.constexpr,
|
| 160 |
+
BLOCK_D_MODEL_POW2: tl.constexpr,
|
| 161 |
+
MASK: tl.constexpr,
|
| 162 |
+
ENABLE_DROPOUT: tl.constexpr,
|
| 163 |
+
IS_FP8: tl.constexpr,
|
| 164 |
+
FP8_MAX: tl.constexpr,
|
| 165 |
+
workgroup_id,
|
| 166 |
+
):
|
| 167 |
+
tl.assume(stride_q_m >= 0)
|
| 168 |
+
tl.assume(stride_q_k >= 0)
|
| 169 |
+
tl.assume(stride_dq_m >= 0)
|
| 170 |
+
tl.assume(stride_dq_k >= 0)
|
| 171 |
+
tl.assume(stride_do_m >= 0)
|
| 172 |
+
tl.assume(stride_do_k >= 0)
|
| 173 |
+
tl.assume(stride_deltam >= 0)
|
| 174 |
+
|
| 175 |
+
PADDED_HEAD: tl.constexpr = BLOCK_D_MODEL != BLOCK_D_MODEL_POW2
|
| 176 |
+
delta_qk = seqlen_q - seqlen_k
|
| 177 |
+
offs_m = start_m + tl.arange(0, BLOCK_M)
|
| 178 |
+
offs_n = start_n + tl.arange(0, BLOCK_N)
|
| 179 |
+
offs_k = tl.arange(0, BLOCK_D_MODEL_POW2)
|
| 180 |
+
|
| 181 |
+
# mask to make sure not OOB of seqlen_q
|
| 182 |
+
mask_n = offs_n < seqlen_k
|
| 183 |
+
|
| 184 |
+
qT_ptrs_start = (
|
| 185 |
+
Q + offs_m[None, :] * stride_q_m + offs_k[:, None] * stride_q_k
|
| 186 |
+
) # [BLOCK_D_MODEL_POW2, BLOCK_M]
|
| 187 |
+
dq_ptrs_start = (
|
| 188 |
+
DQ + offs_m[:, None] * stride_dq_m + offs_k[None, :] * stride_dq_k
|
| 189 |
+
) # [BLOCK_M, BLOCK_D_MODEL_POW2]
|
| 190 |
+
|
| 191 |
+
do_ptrs_start = DO + offs_m[:, None] * stride_do_m + offs_k[None, :] * stride_do_k
|
| 192 |
+
curr_m = start_m
|
| 193 |
+
step_m = BLOCK_M
|
| 194 |
+
curr_philox_offset = batch_philox_offset
|
| 195 |
+
|
| 196 |
+
# Iterate over blocks(BLOCK_M size) of Q while calculating
|
| 197 |
+
# a fixed block(BLOCK_N) of dk and dv. Note, during backward
|
| 198 |
+
# pass P has to be recomputed. However, this kernel computes
|
| 199 |
+
# dV and dK, so we compute we need P^T and S^T. See backward pass
|
| 200 |
+
# equations
|
| 201 |
+
#
|
| 202 |
+
# From Flash Attention Paper:
|
| 203 |
+
# ForwardPass: S = QkT, P=softmax(S), O=PV
|
| 204 |
+
#
|
| 205 |
+
# BackwardPass equations
|
| 206 |
+
# dV = P^TdO
|
| 207 |
+
# dP = dOV^T
|
| 208 |
+
# dS = dsoftmax(dP)
|
| 209 |
+
# dQ = dSK
|
| 210 |
+
# dK = QdS^T
|
| 211 |
+
|
| 212 |
+
for iter in range(num_steps):
|
| 213 |
+
# Permute the iteration order to reduce the probability that concurrent workgroups (that share the same q head idx and batch idx) are at the same iteration
|
| 214 |
+
blk_idx = (iter + workgroup_id) % num_steps
|
| 215 |
+
|
| 216 |
+
curr_m = start_m + blk_idx * step_m
|
| 217 |
+
qT_ptrs = qT_ptrs_start + blk_idx * step_m * stride_q_m
|
| 218 |
+
dq_ptrs = dq_ptrs_start + blk_idx * step_m * stride_dq_m
|
| 219 |
+
do_ptrs = do_ptrs_start + blk_idx * step_m * stride_do_m
|
| 220 |
+
|
| 221 |
+
offs_m = curr_m + tl.arange(0, BLOCK_M)
|
| 222 |
+
mask_m = offs_m < seqlen_q
|
| 223 |
+
mask_qT = mask_m[None, :]
|
| 224 |
+
mask_do = mask_m[:, None]
|
| 225 |
+
mask_nm = mask_n[:, None] & (offs_m[None, :] < seqlen_q)
|
| 226 |
+
|
| 227 |
+
if PADDED_HEAD:
|
| 228 |
+
mask_qT &= offs_k[:, None] < BLOCK_D_MODEL
|
| 229 |
+
mask_do &= offs_k[None, :] < BLOCK_D_MODEL
|
| 230 |
+
|
| 231 |
+
# load qT
|
| 232 |
+
qT = tl.load(qT_ptrs, mask=mask_qT, other=0.0)
|
| 233 |
+
|
| 234 |
+
# dropout
|
| 235 |
+
if ENABLE_DROPOUT:
|
| 236 |
+
# NOTE: dropout is transposed because it is used to mask pT
|
| 237 |
+
philox_offs = (
|
| 238 |
+
curr_philox_offset
|
| 239 |
+
+ offs_m[None, :] * stride_dropout_m
|
| 240 |
+
+ offs_n[:, None] * stride_dropout_n
|
| 241 |
+
)
|
| 242 |
+
rand_vals = tl.rand(philox_seed, philox_offs)
|
| 243 |
+
dropout_mask = rand_vals > dropout_p
|
| 244 |
+
dropout_scale = 1.0 / (1 - dropout_p)
|
| 245 |
+
|
| 246 |
+
# Load M
|
| 247 |
+
m = tl.load(M + offs_m * stride_deltam, mask=mask_m, other=0.0)
|
| 248 |
+
|
| 249 |
+
# Compute qkT
|
| 250 |
+
if IS_FP8:
|
| 251 |
+
qkT = tl.dot(k, qT) * descale_q * descale_k
|
| 252 |
+
else:
|
| 253 |
+
qkT = tl.dot(k, qT)
|
| 254 |
+
|
| 255 |
+
# Compute pT(use m and also apply sm_scale)
|
| 256 |
+
pT = tl.math.exp(qkT * sm_scale - m[None, :])
|
| 257 |
+
|
| 258 |
+
if MASK:
|
| 259 |
+
causal_mask = (offs_m[None, :] - delta_qk) >= (offs_n[:, None])
|
| 260 |
+
mask = causal_mask & mask_nm
|
| 261 |
+
pT = tl.where(mask, pT, 0.0)
|
| 262 |
+
|
| 263 |
+
# load DO
|
| 264 |
+
do = tl.load(do_ptrs, mask=mask_do, other=0.0)
|
| 265 |
+
|
| 266 |
+
# dV
|
| 267 |
+
if ENABLE_DROPOUT:
|
| 268 |
+
pT_dropout = tl.where(dropout_mask, pT, 0.0) * dropout_scale
|
| 269 |
+
if IS_FP8:
|
| 270 |
+
scale_p_dropout, descale_p_dropout = _compute_fp8_scaling_factors(
|
| 271 |
+
pT_dropout, FP8_MAX
|
| 272 |
+
)
|
| 273 |
+
dv += (
|
| 274 |
+
tl.dot((pT_dropout * scale_p_dropout).to(do.type.element_ty), do)
|
| 275 |
+
* descale_p_dropout
|
| 276 |
+
* descale_do
|
| 277 |
+
)
|
| 278 |
+
else:
|
| 279 |
+
dv = tl.dot(pT_dropout.to(do.type.element_ty), do, acc=dv)
|
| 280 |
+
else:
|
| 281 |
+
if IS_FP8:
|
| 282 |
+
scale_pT, descale_pT = _compute_fp8_scaling_factors(pT, FP8_MAX)
|
| 283 |
+
dv += (
|
| 284 |
+
tl.dot((pT * scale_pT).to(do.type.element_ty), do)
|
| 285 |
+
* descale_pT
|
| 286 |
+
* descale_do
|
| 287 |
+
)
|
| 288 |
+
else:
|
| 289 |
+
dv = tl.dot(pT.to(do.type.element_ty), do, acc=dv)
|
| 290 |
+
|
| 291 |
+
# Load delta
|
| 292 |
+
Di = tl.load(D + offs_m * stride_deltam, mask=mask_m)
|
| 293 |
+
|
| 294 |
+
# Compute dP and dS
|
| 295 |
+
if IS_FP8:
|
| 296 |
+
dpT = tl.dot(v, tl.trans(do)) * descale_v * descale_do
|
| 297 |
+
else:
|
| 298 |
+
dpT = tl.dot(v, tl.trans(do))
|
| 299 |
+
|
| 300 |
+
if ENABLE_DROPOUT:
|
| 301 |
+
dpT = tl.where(dropout_mask, dpT, 0.0) * dropout_scale
|
| 302 |
+
|
| 303 |
+
delta_i = Di[None, :]
|
| 304 |
+
dsT = pT * (dpT - delta_i)
|
| 305 |
+
|
| 306 |
+
# compute dk
|
| 307 |
+
if IS_FP8:
|
| 308 |
+
scale_dsT, descale_dsT = _compute_fp8_scaling_factors(dsT, FP8_MAX)
|
| 309 |
+
dk += (
|
| 310 |
+
tl.dot((dsT * scale_dsT).to(qT.type.element_ty), tl.trans(qT))
|
| 311 |
+
* descale_dsT
|
| 312 |
+
* descale_q
|
| 313 |
+
)
|
| 314 |
+
else:
|
| 315 |
+
dk = tl.dot(dsT.to(qT.type.element_ty), tl.trans(qT), acc=dk)
|
| 316 |
+
|
| 317 |
+
# We can compute the dq_partial here and do a atomic add to the correct memory location
|
| 318 |
+
# NOTE: Possible problems with the atomic add: contention, is inside a loop which has achieved bad perf before
|
| 319 |
+
# (BLOCK_M, BLOCK_N) x (BLOCK_N, D)
|
| 320 |
+
if IS_FP8:
|
| 321 |
+
dq_partial = (
|
| 322 |
+
tl.dot((dsT * scale_dsT).to(k.dtype).T, k) * descale_dsT * descale_k
|
| 323 |
+
)
|
| 324 |
+
else:
|
| 325 |
+
dq_partial = tl.dot(dsT.to(k.dtype).T, k)
|
| 326 |
+
tl.atomic_add(
|
| 327 |
+
dq_ptrs,
|
| 328 |
+
dq_partial * sm_scale,
|
| 329 |
+
mask=mask_m[:, None] & (offs_k[None, :] < BLOCK_D_MODEL),
|
| 330 |
+
sem="relaxed",
|
| 331 |
+
)
|
| 332 |
+
|
| 333 |
+
return dk, dv
|
| 334 |
+
|
| 335 |
+
|
| 336 |
+
_bwd_kernel_dkdvdq_causal_repr = make_kernel_repr(
|
| 337 |
+
"_bwd_kernel_dkdvdq_causal",
|
| 338 |
+
[
|
| 339 |
+
"NUM_Q_HEADS",
|
| 340 |
+
"NUM_K_HEADS",
|
| 341 |
+
"BLOCK_M",
|
| 342 |
+
"BLOCK_N",
|
| 343 |
+
"BLK_SLICE_FACTOR",
|
| 344 |
+
"BLOCK_D_MODEL",
|
| 345 |
+
"ENABLE_DROPOUT",
|
| 346 |
+
"IS_VARLEN",
|
| 347 |
+
"IS_FP8",
|
| 348 |
+
"USE_INT64_STRIDES",
|
| 349 |
+
"NUM_XCD",
|
| 350 |
+
],
|
| 351 |
+
)
|
| 352 |
+
|
| 353 |
+
|
| 354 |
+
@triton.jit(repr=_bwd_kernel_dkdvdq_causal_repr)
|
| 355 |
+
def _bwd_kernel_dkdvdq_causal(
|
| 356 |
+
q_ptr,
|
| 357 |
+
k_ptr,
|
| 358 |
+
v_ptr,
|
| 359 |
+
sm_scale,
|
| 360 |
+
do_ptr,
|
| 361 |
+
dk_ptr,
|
| 362 |
+
dv_ptr,
|
| 363 |
+
dq_ptr,
|
| 364 |
+
m_ptr,
|
| 365 |
+
delta_ptr,
|
| 366 |
+
stride_q_b_in,
|
| 367 |
+
stride_q_h_in,
|
| 368 |
+
stride_q_m_in,
|
| 369 |
+
stride_q_k_in,
|
| 370 |
+
stride_k_b_in,
|
| 371 |
+
stride_k_h_in,
|
| 372 |
+
stride_k_n_in,
|
| 373 |
+
stride_k_k_in,
|
| 374 |
+
stride_v_b_in,
|
| 375 |
+
stride_v_h_in,
|
| 376 |
+
stride_v_n_in,
|
| 377 |
+
stride_v_k_in,
|
| 378 |
+
stride_dk_b_in,
|
| 379 |
+
stride_dk_h_in,
|
| 380 |
+
stride_dk_n_in,
|
| 381 |
+
stride_dk_k_in,
|
| 382 |
+
stride_dq_b_in,
|
| 383 |
+
stride_dq_h_in,
|
| 384 |
+
stride_dq_m_in,
|
| 385 |
+
stride_dq_k_in,
|
| 386 |
+
stride_delta_b_in,
|
| 387 |
+
stride_delta_h_in,
|
| 388 |
+
stride_delta_m_in,
|
| 389 |
+
stride_do_b_in,
|
| 390 |
+
stride_do_h_in,
|
| 391 |
+
stride_do_m_in,
|
| 392 |
+
stride_do_k_in,
|
| 393 |
+
stride_dropout_b_in,
|
| 394 |
+
stride_dropout_h_in,
|
| 395 |
+
stride_dropout_m_in,
|
| 396 |
+
stride_dropout_n_in,
|
| 397 |
+
stride_descale_q_z_in,
|
| 398 |
+
stride_descale_k_z_in,
|
| 399 |
+
stride_descale_v_z_in,
|
| 400 |
+
stride_descale_do_z_in,
|
| 401 |
+
cu_seqlens_q,
|
| 402 |
+
cu_seqlens_k,
|
| 403 |
+
max_seqlen_q,
|
| 404 |
+
max_seqlen_k,
|
| 405 |
+
dropout_mask,
|
| 406 |
+
dropout_p,
|
| 407 |
+
philox_seed,
|
| 408 |
+
philox_offset_base_in,
|
| 409 |
+
descale_q_ptr,
|
| 410 |
+
descale_k_ptr,
|
| 411 |
+
descale_v_ptr,
|
| 412 |
+
descale_do_ptr,
|
| 413 |
+
NUM_Q_HEADS: tl.constexpr,
|
| 414 |
+
NUM_K_HEADS: tl.constexpr,
|
| 415 |
+
BATCH,
|
| 416 |
+
NUM_K_PIDS,
|
| 417 |
+
BLOCK_M: tl.constexpr,
|
| 418 |
+
BLOCK_N: tl.constexpr,
|
| 419 |
+
BLK_SLICE_FACTOR: tl.constexpr,
|
| 420 |
+
BLOCK_D_MODEL: tl.constexpr,
|
| 421 |
+
BLOCK_D_MODEL_POW2: tl.constexpr,
|
| 422 |
+
ENABLE_DROPOUT: tl.constexpr,
|
| 423 |
+
IS_VARLEN: tl.constexpr,
|
| 424 |
+
IS_FP8: tl.constexpr,
|
| 425 |
+
FP8_MAX: tl.constexpr,
|
| 426 |
+
USE_INT64_STRIDES: tl.constexpr,
|
| 427 |
+
NUM_XCD: tl.constexpr,
|
| 428 |
+
):
|
| 429 |
+
if USE_INT64_STRIDES:
|
| 430 |
+
stride_q_b = tl.cast(stride_q_b_in, tl.int64)
|
| 431 |
+
stride_q_h = tl.cast(stride_q_h_in, tl.int64)
|
| 432 |
+
stride_q_m = tl.cast(stride_q_m_in, tl.int64)
|
| 433 |
+
stride_q_k = tl.cast(stride_q_k_in, tl.int64)
|
| 434 |
+
stride_k_b = tl.cast(stride_k_b_in, tl.int64)
|
| 435 |
+
stride_k_h = tl.cast(stride_k_h_in, tl.int64)
|
| 436 |
+
stride_k_n = tl.cast(stride_k_n_in, tl.int64)
|
| 437 |
+
stride_k_k = tl.cast(stride_k_k_in, tl.int64)
|
| 438 |
+
stride_v_b = tl.cast(stride_v_b_in, tl.int64)
|
| 439 |
+
stride_v_h = tl.cast(stride_v_h_in, tl.int64)
|
| 440 |
+
stride_v_n = tl.cast(stride_v_n_in, tl.int64)
|
| 441 |
+
stride_v_k = tl.cast(stride_v_k_in, tl.int64)
|
| 442 |
+
stride_dk_b = tl.cast(stride_dk_b_in, tl.int64)
|
| 443 |
+
stride_dk_h = tl.cast(stride_dk_h_in, tl.int64)
|
| 444 |
+
stride_dk_n = tl.cast(stride_dk_n_in, tl.int64)
|
| 445 |
+
stride_dk_k = tl.cast(stride_dk_k_in, tl.int64)
|
| 446 |
+
stride_dq_b = tl.cast(stride_dq_b_in, tl.int64)
|
| 447 |
+
stride_dq_h = tl.cast(stride_dq_h_in, tl.int64)
|
| 448 |
+
stride_dq_m = tl.cast(stride_dq_m_in, tl.int64)
|
| 449 |
+
stride_dq_k = tl.cast(stride_dq_k_in, tl.int64)
|
| 450 |
+
stride_delta_b = tl.cast(stride_delta_b_in, tl.int64)
|
| 451 |
+
stride_delta_h = tl.cast(stride_delta_h_in, tl.int64)
|
| 452 |
+
stride_delta_m = tl.cast(stride_delta_m_in, tl.int64)
|
| 453 |
+
stride_do_b = tl.cast(stride_do_b_in, tl.int64)
|
| 454 |
+
stride_do_h = tl.cast(stride_do_h_in, tl.int64)
|
| 455 |
+
stride_do_m = tl.cast(stride_do_m_in, tl.int64)
|
| 456 |
+
stride_do_k = tl.cast(stride_do_k_in, tl.int64)
|
| 457 |
+
stride_dropout_b = tl.cast(stride_dropout_b_in, tl.int64)
|
| 458 |
+
stride_dropout_h = tl.cast(stride_dropout_h_in, tl.int64)
|
| 459 |
+
stride_dropout_m = tl.cast(stride_dropout_m_in, tl.int64)
|
| 460 |
+
stride_dropout_n = tl.cast(stride_dropout_n_in, tl.int64)
|
| 461 |
+
philox_offset_base = tl.cast(philox_offset_base_in, tl.int64)
|
| 462 |
+
if IS_FP8:
|
| 463 |
+
stride_descale_q_z = tl.cast(stride_descale_q_z_in, tl.int64)
|
| 464 |
+
stride_descale_k_z = tl.cast(stride_descale_k_z_in, tl.int64)
|
| 465 |
+
stride_descale_v_z = tl.cast(stride_descale_v_z_in, tl.int64)
|
| 466 |
+
stride_descale_do_z = tl.cast(stride_descale_do_z_in, tl.int64)
|
| 467 |
+
else:
|
| 468 |
+
stride_q_b = stride_q_b_in
|
| 469 |
+
stride_q_h = stride_q_h_in
|
| 470 |
+
stride_q_m = stride_q_m_in
|
| 471 |
+
stride_q_k = stride_q_k_in
|
| 472 |
+
stride_k_b = stride_k_b_in
|
| 473 |
+
stride_k_h = stride_k_h_in
|
| 474 |
+
stride_k_n = stride_k_n_in
|
| 475 |
+
stride_k_k = stride_k_k_in
|
| 476 |
+
stride_v_b = stride_v_b_in
|
| 477 |
+
stride_v_h = stride_v_h_in
|
| 478 |
+
stride_v_n = stride_v_n_in
|
| 479 |
+
stride_v_k = stride_v_k_in
|
| 480 |
+
stride_dk_b = stride_dk_b_in
|
| 481 |
+
stride_dk_h = stride_dk_h_in
|
| 482 |
+
stride_dk_n = stride_dk_n_in
|
| 483 |
+
stride_dk_k = stride_dk_k_in
|
| 484 |
+
stride_dq_b = stride_dq_b_in
|
| 485 |
+
stride_dq_h = stride_dq_h_in
|
| 486 |
+
stride_dq_m = stride_dq_m_in
|
| 487 |
+
stride_dq_k = stride_dq_k_in
|
| 488 |
+
stride_delta_b = stride_delta_b_in
|
| 489 |
+
stride_delta_h = stride_delta_h_in
|
| 490 |
+
stride_delta_m = stride_delta_m_in
|
| 491 |
+
stride_do_b = stride_do_b_in
|
| 492 |
+
stride_do_h = stride_do_h_in
|
| 493 |
+
stride_do_m = stride_do_m_in
|
| 494 |
+
stride_do_k = stride_do_k_in
|
| 495 |
+
stride_dropout_b = stride_dropout_b_in
|
| 496 |
+
stride_dropout_h = stride_dropout_h_in
|
| 497 |
+
stride_dropout_m = stride_dropout_m_in
|
| 498 |
+
stride_dropout_n = stride_dropout_n_in
|
| 499 |
+
philox_offset_base = philox_offset_base_in
|
| 500 |
+
stride_descale_q_z = stride_descale_q_z_in
|
| 501 |
+
stride_descale_k_z = stride_descale_k_z_in
|
| 502 |
+
stride_descale_v_z = stride_descale_v_z_in
|
| 503 |
+
stride_descale_do_z = stride_descale_do_z_in
|
| 504 |
+
|
| 505 |
+
GROUP_SIZE = NUM_Q_HEADS // NUM_K_HEADS
|
| 506 |
+
wid = tl.program_id(0) # workgoup id: 0, ..., NUM_Q_PIDS * BATCH * NUM_K_HEADS - 1
|
| 507 |
+
|
| 508 |
+
head_q_idx = wid % NUM_Q_HEADS
|
| 509 |
+
head_q_idx = remap_xcd(head_q_idx, NUM_Q_HEADS, NUM_XCD)
|
| 510 |
+
seq_k_blk_idx = (wid // NUM_Q_HEADS) % NUM_K_PIDS
|
| 511 |
+
batch_idx = (wid // (NUM_K_PIDS * NUM_Q_HEADS)) % BATCH
|
| 512 |
+
|
| 513 |
+
# In the backward we dont want concurrent workgroups to handle consecutive heads or blocks, so remap them to be far apart.
|
| 514 |
+
head_q_idx = (head_q_idx * 29) % NUM_Q_HEADS
|
| 515 |
+
# seq_k_blk_idx = (seq_k_blk_idx * 29) % NUM_K_PIDS
|
| 516 |
+
|
| 517 |
+
head_k_idx = head_q_idx // GROUP_SIZE
|
| 518 |
+
|
| 519 |
+
# Determine q and k start along with seqlen_q and seqlen_k
|
| 520 |
+
q_start = 0
|
| 521 |
+
k_start = 0
|
| 522 |
+
seqlen_q = max_seqlen_q
|
| 523 |
+
seqlen_k = max_seqlen_k
|
| 524 |
+
if IS_VARLEN:
|
| 525 |
+
q_start = tl.load(cu_seqlens_q + batch_idx)
|
| 526 |
+
q_end = tl.load(cu_seqlens_q + batch_idx + 1)
|
| 527 |
+
k_start = tl.load(cu_seqlens_k + batch_idx)
|
| 528 |
+
k_end = tl.load(cu_seqlens_k + batch_idx + 1)
|
| 529 |
+
seqlen_q = q_end - q_start
|
| 530 |
+
seqlen_k = k_end - k_start
|
| 531 |
+
|
| 532 |
+
dk = tl.zeros([BLOCK_N, BLOCK_D_MODEL_POW2], dtype=tl.float32)
|
| 533 |
+
dv = tl.zeros([BLOCK_N, BLOCK_D_MODEL_POW2], dtype=tl.float32)
|
| 534 |
+
|
| 535 |
+
# Figure out causal starting block since we have seqlen_q >=< seqlen_k.
|
| 536 |
+
# Unlike forward pass where we tile on M dim and iterate on N dim, so that
|
| 537 |
+
# we can skip some M blocks, in backward pass, we tile on the N dim for kv
|
| 538 |
+
# and iterate over the M. In this way, we cannot skip N blocks, but only to
|
| 539 |
+
# determine the starting M blocks to skip some initial blocks masked by
|
| 540 |
+
# causal.
|
| 541 |
+
delta_qk = seqlen_q - seqlen_k
|
| 542 |
+
|
| 543 |
+
# q < k: some blocks will have no Masked block, other needs to re-calc
|
| 544 |
+
# starting position
|
| 545 |
+
# delta_qk is negative so flip it, only multiple of BLOCK_N can skip the
|
| 546 |
+
# masked op
|
| 547 |
+
num_blocks_skip = -delta_qk // BLOCK_N
|
| 548 |
+
delta_aligned = (num_blocks_skip + 1) * BLOCK_N + delta_qk
|
| 549 |
+
start_delta_q_lt_k = delta_aligned // BLOCK_M * BLOCK_M
|
| 550 |
+
if delta_qk >= 0:
|
| 551 |
+
start_delta = delta_qk
|
| 552 |
+
else:
|
| 553 |
+
start_delta = start_delta_q_lt_k
|
| 554 |
+
|
| 555 |
+
start_n = seq_k_blk_idx * BLOCK_N
|
| 556 |
+
|
| 557 |
+
offs_k = tl.arange(0, BLOCK_D_MODEL_POW2)
|
| 558 |
+
offs_n = start_n + tl.arange(0, BLOCK_N)
|
| 559 |
+
# Mask for loading K and V
|
| 560 |
+
mask_kv = offs_n[:, None] < seqlen_k
|
| 561 |
+
PADDED_HEAD: tl.constexpr = BLOCK_D_MODEL != BLOCK_D_MODEL_POW2
|
| 562 |
+
if PADDED_HEAD:
|
| 563 |
+
mask_k = offs_k < BLOCK_D_MODEL
|
| 564 |
+
mask_kv &= mask_k[None, :]
|
| 565 |
+
|
| 566 |
+
GROUP_SIZE = NUM_Q_HEADS // NUM_K_HEADS
|
| 567 |
+
adj_k = (
|
| 568 |
+
batch_idx * stride_k_b
|
| 569 |
+
+ head_k_idx * stride_k_h
|
| 570 |
+
+ k_start * stride_k_n
|
| 571 |
+
+ offs_n[:, None] * stride_k_n
|
| 572 |
+
+ offs_k[None, :] * stride_k_k
|
| 573 |
+
)
|
| 574 |
+
adj_v = (
|
| 575 |
+
batch_idx * stride_v_b
|
| 576 |
+
+ head_k_idx * stride_v_h
|
| 577 |
+
+ k_start * stride_v_n
|
| 578 |
+
+ offs_n[:, None] * stride_v_n
|
| 579 |
+
+ offs_k[None, :] * stride_v_k
|
| 580 |
+
)
|
| 581 |
+
# load K and V: they stay in SRAM throughout the inner loop.
|
| 582 |
+
k = tl.load(k_ptr + adj_k, mask=mask_kv, other=0.0)
|
| 583 |
+
v = tl.load(v_ptr + adj_v, mask=mask_kv, other=0.0)
|
| 584 |
+
|
| 585 |
+
# If MQA / GQA, set the K and V head offsets appropriately.
|
| 586 |
+
# for head_q_idx in range(head_k_idx * GROUP_SIZE, head_k_idx * GROUP_SIZE + GROUP_SIZE):
|
| 587 |
+
if delta_qk >= 0:
|
| 588 |
+
start_m = start_n + start_delta
|
| 589 |
+
len_m = BLOCK_N
|
| 590 |
+
else:
|
| 591 |
+
start_m = max(start_n + delta_qk, 0)
|
| 592 |
+
start_m = (start_m // BLOCK_M) * BLOCK_M
|
| 593 |
+
# because we might shift the masked blocks up, we are deeper into
|
| 594 |
+
# the masked out region, so we would potentially increase the total
|
| 595 |
+
# steps with masked operation to get out of it
|
| 596 |
+
residue_m = max(start_n + delta_qk - start_m, 0)
|
| 597 |
+
len_m = BLOCK_N + residue_m
|
| 598 |
+
|
| 599 |
+
# offset input and output tensor by batch and Q/K heads
|
| 600 |
+
adj_q = batch_idx * stride_q_b + head_q_idx * stride_q_h + q_start * stride_q_m
|
| 601 |
+
adj_dq = batch_idx * stride_dq_b + head_q_idx * stride_dq_h + q_start * stride_dq_m
|
| 602 |
+
|
| 603 |
+
q_ptr_adj = q_ptr + adj_q
|
| 604 |
+
dq_ptr_adj = dq_ptr + adj_dq
|
| 605 |
+
|
| 606 |
+
adj_do = batch_idx * stride_do_b + head_q_idx * stride_do_h + q_start * stride_do_m
|
| 607 |
+
do_ptr_adj = do_ptr + adj_do
|
| 608 |
+
adj_delta = (
|
| 609 |
+
batch_idx * stride_delta_b
|
| 610 |
+
+ head_q_idx * stride_delta_h
|
| 611 |
+
+ q_start * stride_delta_m
|
| 612 |
+
)
|
| 613 |
+
m_ptr_adj = m_ptr + adj_delta
|
| 614 |
+
delta_ptr_adj = delta_ptr + adj_delta
|
| 615 |
+
|
| 616 |
+
# batch_philox_offset is the ACTUALLY dropout offset
|
| 617 |
+
# dropout_offset is for debug purpose and will be removed later
|
| 618 |
+
batch_philox_offset = 0
|
| 619 |
+
dropout_offset = 0
|
| 620 |
+
if ENABLE_DROPOUT:
|
| 621 |
+
batch_philox_offset = (
|
| 622 |
+
philox_offset_base
|
| 623 |
+
+ batch_idx * stride_dropout_b
|
| 624 |
+
+ head_q_idx * stride_dropout_h
|
| 625 |
+
)
|
| 626 |
+
dropout_offset = (
|
| 627 |
+
dropout_mask + batch_idx * stride_dropout_b + head_q_idx * stride_dropout_h
|
| 628 |
+
)
|
| 629 |
+
|
| 630 |
+
MASK_BLOCK_M: tl.constexpr = BLOCK_M // BLK_SLICE_FACTOR
|
| 631 |
+
# bound the masked operation to q len so it does not have to wast cycles
|
| 632 |
+
len_m = min(len_m, seqlen_q)
|
| 633 |
+
num_steps = tl.cdiv(len_m, MASK_BLOCK_M)
|
| 634 |
+
|
| 635 |
+
# when q < k, we may skip the initial masked op
|
| 636 |
+
if seq_k_blk_idx < num_blocks_skip:
|
| 637 |
+
num_steps = 0
|
| 638 |
+
|
| 639 |
+
if IS_FP8:
|
| 640 |
+
descale_q = tl.load(descale_q_ptr + batch_idx * stride_descale_q_z + head_q_idx)
|
| 641 |
+
descale_k = tl.load(descale_k_ptr + batch_idx * stride_descale_k_z + head_k_idx)
|
| 642 |
+
descale_v = tl.load(descale_v_ptr + batch_idx * stride_descale_v_z + head_k_idx)
|
| 643 |
+
descale_do = tl.load(
|
| 644 |
+
descale_do_ptr + batch_idx * stride_descale_do_z + head_q_idx
|
| 645 |
+
)
|
| 646 |
+
else:
|
| 647 |
+
descale_q, descale_k, descale_v, descale_do = 1.0, 1.0, 1.0, 1.0
|
| 648 |
+
|
| 649 |
+
# if unaligned start_m is negative, the current N-tile has no block on the
|
| 650 |
+
# diagonal of causal mask, so everything have no causal mask
|
| 651 |
+
dk, dv = _bwd_dkdvdq_inner(
|
| 652 |
+
dk,
|
| 653 |
+
dv, # output tensors
|
| 654 |
+
q_ptr_adj,
|
| 655 |
+
k,
|
| 656 |
+
v,
|
| 657 |
+
do_ptr_adj,
|
| 658 |
+
dq_ptr_adj,
|
| 659 |
+
m_ptr_adj,
|
| 660 |
+
delta_ptr_adj,
|
| 661 |
+
sm_scale, # input tensors
|
| 662 |
+
stride_q_m,
|
| 663 |
+
stride_q_k, # strides for q
|
| 664 |
+
stride_dq_m,
|
| 665 |
+
stride_dq_k, # strides for q
|
| 666 |
+
stride_do_m,
|
| 667 |
+
stride_do_k, # strides for o
|
| 668 |
+
stride_dropout_m,
|
| 669 |
+
stride_dropout_n, # strides for dropout
|
| 670 |
+
stride_delta_m,
|
| 671 |
+
dropout_p,
|
| 672 |
+
philox_seed,
|
| 673 |
+
batch_philox_offset,
|
| 674 |
+
dropout_offset, #
|
| 675 |
+
seqlen_q,
|
| 676 |
+
seqlen_k, # max sequence length for q and k
|
| 677 |
+
start_n,
|
| 678 |
+
start_m,
|
| 679 |
+
num_steps, # iteration numbers
|
| 680 |
+
descale_q,
|
| 681 |
+
descale_k,
|
| 682 |
+
descale_v,
|
| 683 |
+
descale_do, # fp8 descale factors from user
|
| 684 |
+
MASK_BLOCK_M,
|
| 685 |
+
BLOCK_N, # block dim
|
| 686 |
+
BLOCK_D_MODEL,
|
| 687 |
+
BLOCK_D_MODEL_POW2, # head dim
|
| 688 |
+
MASK=True, # causal masking
|
| 689 |
+
ENABLE_DROPOUT=ENABLE_DROPOUT, # activate dropout
|
| 690 |
+
IS_FP8=IS_FP8,
|
| 691 |
+
FP8_MAX=FP8_MAX,
|
| 692 |
+
workgroup_id=seq_k_blk_idx,
|
| 693 |
+
)
|
| 694 |
+
|
| 695 |
+
start_m += num_steps * MASK_BLOCK_M
|
| 696 |
+
num_steps = tl.cdiv(seqlen_q - start_m, BLOCK_M)
|
| 697 |
+
|
| 698 |
+
dk, dv = _bwd_dkdvdq_inner(
|
| 699 |
+
dk,
|
| 700 |
+
dv, # output tensors
|
| 701 |
+
q_ptr_adj,
|
| 702 |
+
k,
|
| 703 |
+
v,
|
| 704 |
+
do_ptr_adj,
|
| 705 |
+
dq_ptr_adj,
|
| 706 |
+
m_ptr_adj,
|
| 707 |
+
delta_ptr_adj,
|
| 708 |
+
sm_scale, # input tensors
|
| 709 |
+
stride_q_m,
|
| 710 |
+
stride_q_k, # strides for q
|
| 711 |
+
stride_dq_m,
|
| 712 |
+
stride_dq_k, # strides for dq
|
| 713 |
+
stride_do_m,
|
| 714 |
+
stride_do_k, # strides for o
|
| 715 |
+
stride_dropout_m,
|
| 716 |
+
stride_dropout_n, # strides for dropout
|
| 717 |
+
stride_delta_m,
|
| 718 |
+
dropout_p,
|
| 719 |
+
philox_seed,
|
| 720 |
+
batch_philox_offset,
|
| 721 |
+
dropout_offset, #
|
| 722 |
+
seqlen_q,
|
| 723 |
+
seqlen_k, # max sequence length for q and k
|
| 724 |
+
start_n,
|
| 725 |
+
start_m,
|
| 726 |
+
num_steps, # iteration numbers
|
| 727 |
+
descale_q,
|
| 728 |
+
descale_k,
|
| 729 |
+
descale_v,
|
| 730 |
+
descale_do, # fp8 descale factors from user
|
| 731 |
+
BLOCK_M,
|
| 732 |
+
BLOCK_N, # block dim
|
| 733 |
+
BLOCK_D_MODEL,
|
| 734 |
+
BLOCK_D_MODEL_POW2, # head dim
|
| 735 |
+
MASK=False, # causal masking
|
| 736 |
+
ENABLE_DROPOUT=ENABLE_DROPOUT, # activate dropout
|
| 737 |
+
IS_FP8=IS_FP8,
|
| 738 |
+
FP8_MAX=FP8_MAX,
|
| 739 |
+
workgroup_id=seq_k_blk_idx,
|
| 740 |
+
)
|
| 741 |
+
|
| 742 |
+
# Write back dV and dK.
|
| 743 |
+
offs_dkdv = (
|
| 744 |
+
batch_idx * stride_dk_b
|
| 745 |
+
+ head_k_idx * stride_dk_h
|
| 746 |
+
+ k_start * stride_dk_n
|
| 747 |
+
+ offs_n[:, None] * stride_dk_n
|
| 748 |
+
+ offs_k[None, :] * stride_dk_k
|
| 749 |
+
)
|
| 750 |
+
tl.atomic_add(dv_ptr + offs_dkdv, dv, mask=mask_kv, sem="relaxed")
|
| 751 |
+
dk *= sm_scale
|
| 752 |
+
tl.atomic_add(dk_ptr + offs_dkdv, dk, mask=mask_kv, sem="relaxed")
|
| 753 |
+
|
| 754 |
+
|
| 755 |
+
_bwd_kernel_dkdvdq_noncausal_repr = make_kernel_repr(
|
| 756 |
+
"_bwd_kernel_dkdvdq_noncausal",
|
| 757 |
+
[
|
| 758 |
+
"NUM_Q_HEADS",
|
| 759 |
+
"NUM_K_HEADS",
|
| 760 |
+
"BLOCK_M",
|
| 761 |
+
"BLOCK_N",
|
| 762 |
+
"BLOCK_D_MODEL",
|
| 763 |
+
"ENABLE_DROPOUT",
|
| 764 |
+
"IS_VARLEN",
|
| 765 |
+
"IS_FP8",
|
| 766 |
+
"USE_INT64_STRIDES",
|
| 767 |
+
],
|
| 768 |
+
)
|
| 769 |
+
|
| 770 |
+
|
| 771 |
+
@triton.jit(repr=_bwd_kernel_dkdvdq_noncausal_repr)
|
| 772 |
+
def _bwd_kernel_dkdvdq_noncausal(
|
| 773 |
+
Q,
|
| 774 |
+
K,
|
| 775 |
+
V,
|
| 776 |
+
sm_scale,
|
| 777 |
+
DO,
|
| 778 |
+
DK,
|
| 779 |
+
DV,
|
| 780 |
+
DQ,
|
| 781 |
+
M,
|
| 782 |
+
Delta,
|
| 783 |
+
stride_qb_in,
|
| 784 |
+
stride_qh_in,
|
| 785 |
+
stride_qm_in,
|
| 786 |
+
stride_qk_in,
|
| 787 |
+
stride_kb_in,
|
| 788 |
+
stride_kh_in,
|
| 789 |
+
stride_kn_in,
|
| 790 |
+
stride_kk_in,
|
| 791 |
+
stride_vb_in,
|
| 792 |
+
stride_vh_in,
|
| 793 |
+
stride_vn_in,
|
| 794 |
+
stride_vk_in,
|
| 795 |
+
stride_dkb_in,
|
| 796 |
+
stride_dkh_in,
|
| 797 |
+
stride_dkn_in,
|
| 798 |
+
stride_dkk_in,
|
| 799 |
+
stride_dqb_in,
|
| 800 |
+
stride_dqh_in,
|
| 801 |
+
stride_dqm_in,
|
| 802 |
+
stride_dqk_in,
|
| 803 |
+
stride_deltab_in,
|
| 804 |
+
stride_deltah_in,
|
| 805 |
+
stride_deltam_in,
|
| 806 |
+
stride_dob_in,
|
| 807 |
+
stride_doh_in,
|
| 808 |
+
stride_dom_in,
|
| 809 |
+
stride_dok_in,
|
| 810 |
+
stride_dropoutb_in,
|
| 811 |
+
stride_dropouth_in,
|
| 812 |
+
stride_dropoutm_in,
|
| 813 |
+
stride_dropoutn_in,
|
| 814 |
+
stride_descale_q_z_in,
|
| 815 |
+
stride_descale_k_z_in,
|
| 816 |
+
stride_descale_v_z_in,
|
| 817 |
+
stride_descale_do_z_in,
|
| 818 |
+
cu_seqlens_q,
|
| 819 |
+
cu_seqlens_k,
|
| 820 |
+
max_seqlen_q,
|
| 821 |
+
max_seqlen_k,
|
| 822 |
+
dropout_mask,
|
| 823 |
+
dropout_p,
|
| 824 |
+
philox_seed,
|
| 825 |
+
philox_offset,
|
| 826 |
+
descale_q_ptr,
|
| 827 |
+
descale_k_ptr,
|
| 828 |
+
descale_v_ptr,
|
| 829 |
+
descale_do_ptr,
|
| 830 |
+
NUM_Q_HEADS: tl.constexpr,
|
| 831 |
+
NUM_K_HEADS: tl.constexpr,
|
| 832 |
+
BATCH,
|
| 833 |
+
NUM_K_PIDS,
|
| 834 |
+
BLOCK_M: tl.constexpr,
|
| 835 |
+
BLOCK_N: tl.constexpr,
|
| 836 |
+
BLK_SLICE_FACTOR: tl.constexpr,
|
| 837 |
+
BLOCK_D_MODEL: tl.constexpr,
|
| 838 |
+
BLOCK_D_MODEL_POW2: tl.constexpr,
|
| 839 |
+
ENABLE_DROPOUT: tl.constexpr,
|
| 840 |
+
IS_VARLEN: tl.constexpr,
|
| 841 |
+
IS_FP8: tl.constexpr,
|
| 842 |
+
FP8_MAX: tl.constexpr,
|
| 843 |
+
USE_INT64_STRIDES: tl.constexpr,
|
| 844 |
+
):
|
| 845 |
+
if USE_INT64_STRIDES:
|
| 846 |
+
stride_qb = tl.cast(stride_qb_in, tl.int64)
|
| 847 |
+
stride_qh = tl.cast(stride_qh_in, tl.int64)
|
| 848 |
+
stride_qm = tl.cast(stride_qm_in, tl.int64)
|
| 849 |
+
stride_qk = tl.cast(stride_qk_in, tl.int64)
|
| 850 |
+
stride_kb = tl.cast(stride_kb_in, tl.int64)
|
| 851 |
+
stride_kh = tl.cast(stride_kh_in, tl.int64)
|
| 852 |
+
stride_kn = tl.cast(stride_kn_in, tl.int64)
|
| 853 |
+
stride_kk = tl.cast(stride_kk_in, tl.int64)
|
| 854 |
+
stride_vb = tl.cast(stride_vb_in, tl.int64)
|
| 855 |
+
stride_vh = tl.cast(stride_vh_in, tl.int64)
|
| 856 |
+
stride_vn = tl.cast(stride_vn_in, tl.int64)
|
| 857 |
+
stride_vk = tl.cast(stride_vk_in, tl.int64)
|
| 858 |
+
stride_dkb = tl.cast(stride_dkb_in, tl.int64)
|
| 859 |
+
stride_dkh = tl.cast(stride_dkh_in, tl.int64)
|
| 860 |
+
stride_dkn = tl.cast(stride_dkn_in, tl.int64)
|
| 861 |
+
stride_dkk = tl.cast(stride_dkk_in, tl.int64)
|
| 862 |
+
stride_dqb = tl.cast(stride_dqb_in, tl.int64)
|
| 863 |
+
stride_dqh = tl.cast(stride_dqh_in, tl.int64)
|
| 864 |
+
stride_dqm = tl.cast(stride_dqm_in, tl.int64)
|
| 865 |
+
stride_dqk = tl.cast(stride_dqk_in, tl.int64)
|
| 866 |
+
stride_deltab = tl.cast(stride_deltab_in, tl.int64)
|
| 867 |
+
stride_deltah = tl.cast(stride_deltah_in, tl.int64)
|
| 868 |
+
stride_deltam = tl.cast(stride_deltam_in, tl.int64)
|
| 869 |
+
stride_dob = tl.cast(stride_dob_in, tl.int64)
|
| 870 |
+
stride_doh = tl.cast(stride_doh_in, tl.int64)
|
| 871 |
+
stride_dom = tl.cast(stride_dom_in, tl.int64)
|
| 872 |
+
stride_dok = tl.cast(stride_dok_in, tl.int64)
|
| 873 |
+
stride_dropoutb = tl.cast(stride_dropoutb_in, tl.int64)
|
| 874 |
+
stride_dropouth = tl.cast(stride_dropouth_in, tl.int64)
|
| 875 |
+
stride_dropoutm = tl.cast(stride_dropoutm_in, tl.int64)
|
| 876 |
+
stride_dropoutn = tl.cast(stride_dropoutn_in, tl.int64)
|
| 877 |
+
if IS_FP8:
|
| 878 |
+
stride_descale_q_z = tl.cast(stride_descale_q_z_in, tl.int64)
|
| 879 |
+
stride_descale_k_z = tl.cast(stride_descale_k_z_in, tl.int64)
|
| 880 |
+
stride_descale_v_z = tl.cast(stride_descale_v_z_in, tl.int64)
|
| 881 |
+
stride_descale_do_z = tl.cast(stride_descale_do_z_in, tl.int64)
|
| 882 |
+
else:
|
| 883 |
+
stride_qb = stride_qb_in
|
| 884 |
+
stride_qh = stride_qh_in
|
| 885 |
+
stride_qm = stride_qm_in
|
| 886 |
+
stride_qk = stride_qk_in
|
| 887 |
+
stride_kb = stride_kb_in
|
| 888 |
+
stride_kh = stride_kh_in
|
| 889 |
+
stride_kn = stride_kn_in
|
| 890 |
+
stride_kk = stride_kk_in
|
| 891 |
+
stride_vb = stride_vb_in
|
| 892 |
+
stride_vh = stride_vh_in
|
| 893 |
+
stride_vn = stride_vn_in
|
| 894 |
+
stride_vk = stride_vk_in
|
| 895 |
+
stride_dkb = stride_dkb_in
|
| 896 |
+
stride_dkh = stride_dkh_in
|
| 897 |
+
stride_dkn = stride_dkn_in
|
| 898 |
+
stride_dkk = stride_dkk_in
|
| 899 |
+
stride_dqb = stride_dqb_in
|
| 900 |
+
stride_dqh = stride_dqh_in
|
| 901 |
+
stride_dqm = stride_dqm_in
|
| 902 |
+
stride_dqk = stride_dqk_in
|
| 903 |
+
stride_deltab = stride_deltab_in
|
| 904 |
+
stride_deltah = stride_deltah_in
|
| 905 |
+
stride_deltam = stride_deltam_in
|
| 906 |
+
stride_dob = stride_dob_in
|
| 907 |
+
stride_doh = stride_doh_in
|
| 908 |
+
stride_dom = stride_dom_in
|
| 909 |
+
stride_dok = stride_dok_in
|
| 910 |
+
stride_dropoutb = stride_dropoutb_in
|
| 911 |
+
stride_dropouth = stride_dropouth_in
|
| 912 |
+
stride_dropoutm = stride_dropoutm_in
|
| 913 |
+
stride_dropoutn = stride_dropoutn_in
|
| 914 |
+
stride_descale_q_z = stride_descale_q_z_in
|
| 915 |
+
stride_descale_k_z = stride_descale_k_z_in
|
| 916 |
+
stride_descale_v_z = stride_descale_v_z_in
|
| 917 |
+
stride_descale_do_z = stride_descale_do_z_in
|
| 918 |
+
|
| 919 |
+
# workgroup id
|
| 920 |
+
wid = tl.program_id(0) # 0, ..., NUM_K_PIDS * BATCH * NUM_K_HEADS - 1
|
| 921 |
+
|
| 922 |
+
# Workgroups get launched first along batch dim, then in head_k dim, and then in seq k block dim
|
| 923 |
+
# This is in order to avoid contention for the tl.atomic_add (inside _bwd_dkdvdq_inner) that happens between workgroups that share the same batch and head_k.
|
| 924 |
+
bid = wid % BATCH
|
| 925 |
+
hkid = wid // BATCH % NUM_K_HEADS
|
| 926 |
+
pid = wid // (BATCH * NUM_K_HEADS) % NUM_K_PIDS
|
| 927 |
+
|
| 928 |
+
q_start = 0
|
| 929 |
+
k_start = 0
|
| 930 |
+
seqlen_q = max_seqlen_q
|
| 931 |
+
seqlen_k = max_seqlen_k
|
| 932 |
+
|
| 933 |
+
if IS_VARLEN:
|
| 934 |
+
q_start = tl.load(cu_seqlens_q + bid)
|
| 935 |
+
q_end = tl.load(cu_seqlens_q + bid + 1)
|
| 936 |
+
k_start = tl.load(cu_seqlens_k + bid)
|
| 937 |
+
k_end = tl.load(cu_seqlens_k + bid + 1)
|
| 938 |
+
seqlen_q = q_end - q_start
|
| 939 |
+
seqlen_k = k_end - k_start
|
| 940 |
+
|
| 941 |
+
dk = tl.zeros([BLOCK_N, BLOCK_D_MODEL_POW2], dtype=tl.float32)
|
| 942 |
+
dv = tl.zeros([BLOCK_N, BLOCK_D_MODEL_POW2], dtype=tl.float32)
|
| 943 |
+
|
| 944 |
+
start_n = pid * BLOCK_N
|
| 945 |
+
|
| 946 |
+
offs_k = tl.arange(0, BLOCK_D_MODEL_POW2)
|
| 947 |
+
offs_n = start_n + tl.arange(0, BLOCK_N)
|
| 948 |
+
mask_kv = offs_n[:, None] < seqlen_k
|
| 949 |
+
PADDED_HEAD: tl.constexpr = BLOCK_D_MODEL != BLOCK_D_MODEL_POW2
|
| 950 |
+
if PADDED_HEAD:
|
| 951 |
+
mask_kv &= offs_k < BLOCK_D_MODEL
|
| 952 |
+
|
| 953 |
+
GROUP_SIZE = NUM_Q_HEADS // NUM_K_HEADS
|
| 954 |
+
adj_k = (
|
| 955 |
+
bid * stride_kb
|
| 956 |
+
+ hkid * stride_kh
|
| 957 |
+
+ k_start * stride_kn
|
| 958 |
+
+ offs_n[:, None] * stride_kn
|
| 959 |
+
+ offs_k[None, :] * stride_kk
|
| 960 |
+
)
|
| 961 |
+
adj_v = (
|
| 962 |
+
bid * stride_vb
|
| 963 |
+
+ hkid * stride_vh
|
| 964 |
+
+ k_start * stride_vn
|
| 965 |
+
+ offs_n[:, None] * stride_vn
|
| 966 |
+
+ offs_k[None, :] * stride_vk
|
| 967 |
+
)
|
| 968 |
+
|
| 969 |
+
k = tl.load(K + adj_k, mask=mask_kv, other=0.0)
|
| 970 |
+
v = tl.load(V + adj_v, mask=mask_kv, other=0.0)
|
| 971 |
+
|
| 972 |
+
for hqid in range(hkid * GROUP_SIZE, hkid * GROUP_SIZE + GROUP_SIZE):
|
| 973 |
+
adj_q = bid * stride_qb + hqid * stride_qh + q_start * stride_qm
|
| 974 |
+
adj_dq = bid * stride_dqb + hqid * stride_dqh + q_start * stride_dqm
|
| 975 |
+
|
| 976 |
+
Q_ptr = Q + adj_q
|
| 977 |
+
DQ_ptr = DQ + adj_dq
|
| 978 |
+
|
| 979 |
+
adj_do = bid * stride_dob + hqid * stride_doh + q_start * stride_dom
|
| 980 |
+
DO_ptr = DO + adj_do
|
| 981 |
+
adj_delta = bid * stride_deltab + hqid * stride_deltah + q_start * stride_deltam
|
| 982 |
+
M_ptr = M + adj_delta
|
| 983 |
+
Delta_ptr = Delta + adj_delta
|
| 984 |
+
|
| 985 |
+
# dropout
|
| 986 |
+
batch_philox_offset = 0
|
| 987 |
+
dropout_offset = 0
|
| 988 |
+
if ENABLE_DROPOUT:
|
| 989 |
+
batch_philox_offset = (
|
| 990 |
+
philox_offset + bid * stride_dropoutb + hqid * stride_dropouth
|
| 991 |
+
)
|
| 992 |
+
dropout_offset = (
|
| 993 |
+
dropout_mask + bid * stride_dropoutb + hqid * stride_dropouth
|
| 994 |
+
)
|
| 995 |
+
|
| 996 |
+
if IS_FP8:
|
| 997 |
+
descale_q = tl.load(descale_q_ptr + bid * stride_descale_q_z + hqid)
|
| 998 |
+
descale_k = tl.load(descale_k_ptr + bid * stride_descale_k_z + hkid)
|
| 999 |
+
descale_v = tl.load(descale_v_ptr + bid * stride_descale_v_z + hkid)
|
| 1000 |
+
descale_do = tl.load(descale_do_ptr + bid * stride_descale_do_z + hqid)
|
| 1001 |
+
else:
|
| 1002 |
+
descale_q, descale_k, descale_v, descale_do = 1.0, 1.0, 1.0, 1.0
|
| 1003 |
+
|
| 1004 |
+
start_m = 0
|
| 1005 |
+
num_steps = tl.cdiv(seqlen_q, BLOCK_M)
|
| 1006 |
+
|
| 1007 |
+
dk, dv = _bwd_dkdvdq_inner(
|
| 1008 |
+
dk,
|
| 1009 |
+
dv,
|
| 1010 |
+
Q_ptr,
|
| 1011 |
+
k,
|
| 1012 |
+
v,
|
| 1013 |
+
DO_ptr,
|
| 1014 |
+
DQ_ptr,
|
| 1015 |
+
M_ptr,
|
| 1016 |
+
Delta_ptr,
|
| 1017 |
+
sm_scale,
|
| 1018 |
+
stride_qm,
|
| 1019 |
+
stride_qk,
|
| 1020 |
+
stride_dqm,
|
| 1021 |
+
stride_dqk,
|
| 1022 |
+
stride_dom,
|
| 1023 |
+
stride_dok,
|
| 1024 |
+
stride_dropoutm,
|
| 1025 |
+
stride_dropoutn,
|
| 1026 |
+
stride_deltam,
|
| 1027 |
+
dropout_p,
|
| 1028 |
+
philox_seed,
|
| 1029 |
+
batch_philox_offset,
|
| 1030 |
+
dropout_offset,
|
| 1031 |
+
seqlen_q,
|
| 1032 |
+
seqlen_k,
|
| 1033 |
+
start_n,
|
| 1034 |
+
start_m,
|
| 1035 |
+
num_steps,
|
| 1036 |
+
descale_q,
|
| 1037 |
+
descale_k,
|
| 1038 |
+
descale_v,
|
| 1039 |
+
descale_do,
|
| 1040 |
+
BLOCK_M,
|
| 1041 |
+
BLOCK_N,
|
| 1042 |
+
BLOCK_D_MODEL,
|
| 1043 |
+
BLOCK_D_MODEL_POW2,
|
| 1044 |
+
MASK=False,
|
| 1045 |
+
ENABLE_DROPOUT=ENABLE_DROPOUT,
|
| 1046 |
+
IS_FP8=IS_FP8,
|
| 1047 |
+
FP8_MAX=FP8_MAX,
|
| 1048 |
+
workgroup_id=wid,
|
| 1049 |
+
)
|
| 1050 |
+
|
| 1051 |
+
adj_dkdv = (
|
| 1052 |
+
bid * stride_dkb
|
| 1053 |
+
+ hkid * stride_dkh
|
| 1054 |
+
+ k_start * stride_dkn
|
| 1055 |
+
+ offs_n[:, None] * stride_dkn
|
| 1056 |
+
+ offs_k[None, :] * stride_dkk
|
| 1057 |
+
)
|
| 1058 |
+
tl.store(DV + adj_dkdv, dv, mask=mask_kv)
|
| 1059 |
+
dk *= sm_scale
|
| 1060 |
+
tl.store(DK + adj_dkdv, dk, mask=mask_kv)
|
| 1061 |
+
|
| 1062 |
+
|
| 1063 |
+
@functools.lru_cache(maxsize=1024)
|
| 1064 |
+
def _get_config():
|
| 1065 |
+
if not hasattr(_get_config, "_config_dict"):
|
| 1066 |
+
dev = arch_info.get_arch()
|
| 1067 |
+
_get_config._config_dict = {}
|
| 1068 |
+
fpath = f"{AITER_TRITON_CONFIGS_PATH}/{dev}-MHA-DEFAULT.json"
|
| 1069 |
+
with open(fpath, "r") as file:
|
| 1070 |
+
config = json.load(file)
|
| 1071 |
+
_get_config._config_dict = config
|
| 1072 |
+
|
| 1073 |
+
return _get_config._config_dict["bkwd_fused"]
|
build/torch-rocm/_kernels/mha_onekernel_bwd.py
ADDED
|
@@ -0,0 +1,1783 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# SPDX-License-Identifier: MIT
|
| 2 |
+
# Copyright (C) 2024-2026, Advanced Micro Devices, Inc. All rights reserved.
|
| 3 |
+
|
| 4 |
+
import functools
|
| 5 |
+
import json
|
| 6 |
+
import triton # type: ignore
|
| 7 |
+
import triton.language as tl # type: ignore
|
| 8 |
+
from ..utils._triton import arch_info
|
| 9 |
+
from ..utils.core import AITER_TRITON_CONFIGS_PATH
|
| 10 |
+
from ..utils._triton.mha_kernel_utils import _compute_fp8_scaling_factors
|
| 11 |
+
from ..utils._triton.kernel_repr import make_kernel_repr
|
| 12 |
+
|
| 13 |
+
# NOTE: triton fails to import tl.constexprs so create them here for the file
|
| 14 |
+
DROPOUT_USE_PYTORCH = False
|
| 15 |
+
DROPOUT_DUMP = False
|
| 16 |
+
|
| 17 |
+
tl_DROPOUT_USE_PYTORCH: tl.constexpr = triton.language.constexpr(DROPOUT_USE_PYTORCH)
|
| 18 |
+
tl_DROPOUT_DUMP: tl.constexpr = triton.language.constexpr(DROPOUT_DUMP)
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
# This function computes delta given output Out and gradient DO
|
| 22 |
+
# Here is the I/O shape:
|
| 23 |
+
# Out: (batch, nhead_q, max_seqlens_q, headDim)
|
| 24 |
+
# DO: (batch, nhead_q, max_seqlens_q, headDim)
|
| 25 |
+
# Delta: (batch, nheads_q, max_seqlens_q), same as softmax_lse defined at
|
| 26 |
+
_bwd_preprocess_repr = make_kernel_repr(
|
| 27 |
+
"_bwd_preprocess",
|
| 28 |
+
[
|
| 29 |
+
"BLOCK_M",
|
| 30 |
+
"BLOCK_D_MODEL",
|
| 31 |
+
"IS_VARLEN",
|
| 32 |
+
"IS_FP8",
|
| 33 |
+
],
|
| 34 |
+
)
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
@triton.jit(repr=_bwd_preprocess_repr)
|
| 38 |
+
def _bwd_preprocess(
|
| 39 |
+
o_ptr,
|
| 40 |
+
do_ptr, # noqa: E741
|
| 41 |
+
delta_ptr,
|
| 42 |
+
stride_o_b,
|
| 43 |
+
stride_o_h,
|
| 44 |
+
stride_o_m,
|
| 45 |
+
stride_o_k,
|
| 46 |
+
stride_do_b,
|
| 47 |
+
stride_do_h,
|
| 48 |
+
stride_do_m,
|
| 49 |
+
stride_do_k,
|
| 50 |
+
stride_delta_b,
|
| 51 |
+
stride_delta_h,
|
| 52 |
+
stride_delta_m,
|
| 53 |
+
stride_descale_do_z,
|
| 54 |
+
cu_seqlens_q,
|
| 55 |
+
max_seqlen_q,
|
| 56 |
+
descale_do_ptr,
|
| 57 |
+
BLOCK_M: tl.constexpr,
|
| 58 |
+
BLOCK_D_MODEL: tl.constexpr,
|
| 59 |
+
BLOCK_D_MODEL_POW2: tl.constexpr,
|
| 60 |
+
IS_VARLEN: tl.constexpr,
|
| 61 |
+
IS_FP8: tl.constexpr,
|
| 62 |
+
):
|
| 63 |
+
pid_m = tl.program_id(0) # seqlen
|
| 64 |
+
bid = tl.program_id(1) # batch
|
| 65 |
+
hid = tl.program_id(2) # head
|
| 66 |
+
|
| 67 |
+
# Handle varlen
|
| 68 |
+
q_start = 0
|
| 69 |
+
seqlen_q = max_seqlen_q
|
| 70 |
+
if IS_VARLEN:
|
| 71 |
+
q_start = tl.load(cu_seqlens_q + bid)
|
| 72 |
+
q_end = tl.load(cu_seqlens_q + bid + 1)
|
| 73 |
+
seqlen_q = q_end - q_start
|
| 74 |
+
else:
|
| 75 |
+
q_start = 0
|
| 76 |
+
seqlen_q = max_seqlen_q
|
| 77 |
+
|
| 78 |
+
# Compute offsets
|
| 79 |
+
offs_m = pid_m * BLOCK_M + tl.arange(0, BLOCK_M)
|
| 80 |
+
offs_k = tl.arange(0, BLOCK_D_MODEL_POW2)
|
| 81 |
+
|
| 82 |
+
# O and DO may have different strides (e.g. BSHD vs SBHD memory layout),
|
| 83 |
+
# so address each with its own strides.
|
| 84 |
+
offs_o = (
|
| 85 |
+
bid * stride_o_b
|
| 86 |
+
+ hid * stride_o_h
|
| 87 |
+
+ q_start * stride_o_m
|
| 88 |
+
+ offs_m[:, None] * stride_o_m
|
| 89 |
+
+ offs_k[None, :] * stride_o_k
|
| 90 |
+
)
|
| 91 |
+
offs_do = (
|
| 92 |
+
bid * stride_do_b
|
| 93 |
+
+ hid * stride_do_h
|
| 94 |
+
+ q_start * stride_do_m
|
| 95 |
+
+ offs_m[:, None] * stride_do_m
|
| 96 |
+
+ offs_k[None, :] * stride_do_k
|
| 97 |
+
)
|
| 98 |
+
|
| 99 |
+
# create masks
|
| 100 |
+
mask_m = offs_m < seqlen_q
|
| 101 |
+
mask = mask_m[:, None]
|
| 102 |
+
PADDED_HEAD: tl.constexpr = BLOCK_D_MODEL != BLOCK_D_MODEL_POW2
|
| 103 |
+
if PADDED_HEAD:
|
| 104 |
+
mask &= offs_k[None, :] < BLOCK_D_MODEL
|
| 105 |
+
|
| 106 |
+
# load [BLOCK_M, BLOCK_D_MODEL_POW2]
|
| 107 |
+
o = tl.load(o_ptr + offs_o, mask=mask, other=0.0)
|
| 108 |
+
do = tl.load(do_ptr + offs_do, mask=mask, other=0.0)
|
| 109 |
+
|
| 110 |
+
# compute and write-back to delta
|
| 111 |
+
if IS_FP8:
|
| 112 |
+
descale_do = tl.load(descale_do_ptr + bid * stride_descale_do_z + hid)
|
| 113 |
+
|
| 114 |
+
# NOTE: do is in the fp8 range and o is not in fp8
|
| 115 |
+
delta = tl.sum(o.to(tl.float32) * (do.to(tl.float32) * descale_do), axis=1)
|
| 116 |
+
else:
|
| 117 |
+
delta = tl.sum(o.to(tl.float32) * do.to(tl.float32), axis=1)
|
| 118 |
+
|
| 119 |
+
offs_delta = (
|
| 120 |
+
bid * stride_delta_b
|
| 121 |
+
+ hid * stride_delta_h
|
| 122 |
+
+ q_start * stride_delta_m
|
| 123 |
+
+ offs_m * stride_delta_m
|
| 124 |
+
)
|
| 125 |
+
tl.store(delta_ptr + offs_delta, delta, mask=mask_m)
|
| 126 |
+
|
| 127 |
+
|
| 128 |
+
# The main inner-loop logic for computing dK and dV.
|
| 129 |
+
@triton.jit
|
| 130 |
+
def _bwd_dkdv_inner(
|
| 131 |
+
dk, # output
|
| 132 |
+
dk_pe, # optional output, pass None for non-PE case
|
| 133 |
+
dv, # output
|
| 134 |
+
Q,
|
| 135 |
+
k,
|
| 136 |
+
k_pe,
|
| 137 |
+
v,
|
| 138 |
+
DO,
|
| 139 |
+
M,
|
| 140 |
+
D,
|
| 141 |
+
sm_scale, # input tensor
|
| 142 |
+
stride_qm,
|
| 143 |
+
stride_qk,
|
| 144 |
+
stride_dom,
|
| 145 |
+
stride_dok,
|
| 146 |
+
stride_dropoutm,
|
| 147 |
+
stride_dropoutn,
|
| 148 |
+
stride_deltam,
|
| 149 |
+
BLOCK_M: tl.constexpr, # 16
|
| 150 |
+
BLOCK_N: tl.constexpr, # 128
|
| 151 |
+
HEAD_DIM: tl.constexpr, #
|
| 152 |
+
ACTUAL_HEAD_DIM: tl.constexpr, #
|
| 153 |
+
PE_HEAD_DIM: tl.constexpr,
|
| 154 |
+
dropout_p,
|
| 155 |
+
philox_seed,
|
| 156 |
+
batch_philox_offset,
|
| 157 |
+
dropout_offset,
|
| 158 |
+
alibi_slope,
|
| 159 |
+
seqlen_q,
|
| 160 |
+
seqlen_k, # max sequence length for q and k
|
| 161 |
+
# Filled in by the wrapper.
|
| 162 |
+
start_n,
|
| 163 |
+
start_m,
|
| 164 |
+
num_steps, # iteration numbers
|
| 165 |
+
descale_q,
|
| 166 |
+
descale_k,
|
| 167 |
+
descale_v,
|
| 168 |
+
descale_do, # fp8 descale factors from user
|
| 169 |
+
MASK: tl.constexpr, # causal masking, only apply to tiles on mask diagonal
|
| 170 |
+
ENABLE_DROPOUT: tl.constexpr, # activate dropout
|
| 171 |
+
USE_ALIBI: tl.constexpr,
|
| 172 |
+
USE_EXP2: tl.constexpr, # activate exp2
|
| 173 |
+
IS_FP8: tl.constexpr,
|
| 174 |
+
FP8_MAX: tl.constexpr,
|
| 175 |
+
DEBUG_TRITON: tl.constexpr,
|
| 176 |
+
DEBUG_TRITON_DETAIL: tl.constexpr,
|
| 177 |
+
SLIDING_WINDOW: tl.constexpr,
|
| 178 |
+
):
|
| 179 |
+
# if HEAD_DIM is padded
|
| 180 |
+
PADDED_HEAD: tl.constexpr = ACTUAL_HEAD_DIM != HEAD_DIM
|
| 181 |
+
HAS_PE: tl.constexpr = PE_HEAD_DIM > 0
|
| 182 |
+
delta_qk = seqlen_q - seqlen_k
|
| 183 |
+
offs_m = start_m + tl.arange(0, BLOCK_M) # start_m + (0, 15)
|
| 184 |
+
offs_n = start_n + tl.arange(0, BLOCK_N) # start_m + (0, 127)
|
| 185 |
+
offs_k = tl.arange(0, HEAD_DIM)
|
| 186 |
+
if HAS_PE:
|
| 187 |
+
offs_k_pe = HEAD_DIM + tl.arange(0, PE_HEAD_DIM)
|
| 188 |
+
# mask to make sure not OOB of seqlen_q
|
| 189 |
+
mask_n = offs_n < seqlen_k
|
| 190 |
+
# Q and DO are (seqlen_q, head_dim)
|
| 191 |
+
# qT_ptrs = (1, BLOCK_M) + (HEAD_DIM, 1), transpose of q
|
| 192 |
+
qT_ptrs = Q + offs_m[None, :] * stride_qm + offs_k[:, None] * stride_qk
|
| 193 |
+
if HAS_PE:
|
| 194 |
+
qT_pe_ptrs = Q + offs_m[None, :] * stride_qm + offs_k_pe[:, None] * stride_qk
|
| 195 |
+
# do_ptrs = (BLOCK_M, 1) + (1, HEAD_DIM), NOT transposed
|
| 196 |
+
do_ptrs = DO + offs_m[:, None] * stride_dom + offs_k[None, :] * stride_dok
|
| 197 |
+
# BLOCK_N must be a multiple of BLOCK_M, otherwise the code wouldn't work.
|
| 198 |
+
tl.static_assert(BLOCK_N % BLOCK_M == 0)
|
| 199 |
+
curr_m = start_m
|
| 200 |
+
step_m = BLOCK_M
|
| 201 |
+
curr_philox_offset = batch_philox_offset
|
| 202 |
+
curr_dropout_offset = dropout_offset
|
| 203 |
+
RCP_LN2: tl.constexpr = 1.4426950408889634 # = 1.0 / ln(2)
|
| 204 |
+
|
| 205 |
+
for blk_idx in range(num_steps):
|
| 206 |
+
if DEBUG_TRITON:
|
| 207 |
+
print(f"iter {blk_idx}: curr_m = {curr_m}") # noqa: E701
|
| 208 |
+
offs_m = curr_m + tl.arange(0, BLOCK_M)
|
| 209 |
+
# update the mask because offs_m advanced
|
| 210 |
+
mask_m = offs_m < seqlen_q
|
| 211 |
+
mask_qT = mask_m[None, :]
|
| 212 |
+
mask_do = mask_m[:, None]
|
| 213 |
+
mask_nm = mask_n[:, None] & (offs_m[None, :] < seqlen_q)
|
| 214 |
+
if PADDED_HEAD:
|
| 215 |
+
mask_qT &= offs_k[:, None] < ACTUAL_HEAD_DIM
|
| 216 |
+
mask_do &= offs_k[None, :] < ACTUAL_HEAD_DIM
|
| 217 |
+
qT = tl.load(qT_ptrs, mask=mask_qT, other=0.0)
|
| 218 |
+
if HAS_PE:
|
| 219 |
+
qT_pe = tl.load(qT_pe_ptrs, mask=mask_qT, other=0.0)
|
| 220 |
+
# generate dropout mask
|
| 221 |
+
if ENABLE_DROPOUT:
|
| 222 |
+
# NOTE: dropout is transposed because it is used to mask pT
|
| 223 |
+
philox_offs = (
|
| 224 |
+
curr_philox_offset
|
| 225 |
+
+ offs_m[None, :] * stride_dropoutm
|
| 226 |
+
+ offs_n[:, None] * stride_dropoutn
|
| 227 |
+
)
|
| 228 |
+
if tl_DROPOUT_USE_PYTORCH:
|
| 229 |
+
dropout_offs = (
|
| 230 |
+
offs_m[None, :] * stride_dropoutm
|
| 231 |
+
+ offs_n[:, None] * stride_dropoutn
|
| 232 |
+
)
|
| 233 |
+
dropout_mask = tl.load(curr_dropout_offset + dropout_offs, mask=mask_nm)
|
| 234 |
+
else:
|
| 235 |
+
rand_vals = tl.rand(philox_seed, philox_offs)
|
| 236 |
+
dropout_mask = rand_vals > dropout_p
|
| 237 |
+
dropout_scale = 1.0 / (1 - dropout_p)
|
| 238 |
+
# Load m before computing qk to reduce pipeline stall.
|
| 239 |
+
m = tl.load(M + offs_m * stride_deltam, mask=mask_m, other=0.0)
|
| 240 |
+
if IS_FP8:
|
| 241 |
+
qkT = tl.dot(k, qT) * descale_q * descale_k
|
| 242 |
+
else:
|
| 243 |
+
qkT = tl.dot(k, qT)
|
| 244 |
+
if HAS_PE:
|
| 245 |
+
qkT = tl.dot(k_pe, qT_pe, acc=qkT)
|
| 246 |
+
qkT_scaled = qkT * sm_scale
|
| 247 |
+
|
| 248 |
+
if USE_ALIBI:
|
| 249 |
+
relative_pos_block = offs_n[:, None] + seqlen_q - seqlen_k - offs_m[None, :]
|
| 250 |
+
alibi_block = -1 * alibi_slope * tl.abs(relative_pos_block)
|
| 251 |
+
qkT_scaled += alibi_block
|
| 252 |
+
|
| 253 |
+
if DEBUG_TRITON_DETAIL:
|
| 254 |
+
if start_n == 256:
|
| 255 |
+
print(f"qT: {qT.shape}\n", qT)
|
| 256 |
+
print(f"k: {k.shape}\n", k)
|
| 257 |
+
print(f"qkT scaled: {qkT.shape}\n", qkT_scaled)
|
| 258 |
+
# TODO: remove the scaling of m later when we removed re-scaling in fwd
|
| 259 |
+
if USE_EXP2:
|
| 260 |
+
pT = tl.math.exp2(qkT_scaled * RCP_LN2 - m[None, :] * RCP_LN2)
|
| 261 |
+
else:
|
| 262 |
+
pT = tl.math.exp(qkT_scaled - m[None, :])
|
| 263 |
+
|
| 264 |
+
# Autoregressive masking.
|
| 265 |
+
if MASK:
|
| 266 |
+
# offset offs_m with delta_qk since the causal mask starts at
|
| 267 |
+
# bottom right of the (seqlen_q, seqlen_k) matrix
|
| 268 |
+
causal_mask = (offs_m[None, :] - delta_qk) >= offs_n[:, None]
|
| 269 |
+
mask = causal_mask & mask_nm
|
| 270 |
+
if DEBUG_TRITON_DETAIL:
|
| 271 |
+
if start_n == 256:
|
| 272 |
+
print(f"causal_mask: {causal_mask.shape}\n", causal_mask)
|
| 273 |
+
print(
|
| 274 |
+
f"qkT after causal: {qkT.shape}\n",
|
| 275 |
+
tl.where(causal_mask, qkT * sm_scale, 0.0),
|
| 276 |
+
)
|
| 277 |
+
pT = tl.where(mask, pT, 0.0)
|
| 278 |
+
if SLIDING_WINDOW > 0:
|
| 279 |
+
window_mask = offs_n[:, None] >= (
|
| 280 |
+
offs_m[None, :] - delta_qk - SLIDING_WINDOW
|
| 281 |
+
)
|
| 282 |
+
pT = tl.where(window_mask, pT, 0.0)
|
| 283 |
+
do = tl.load(do_ptrs, mask=mask_do, other=0.0)
|
| 284 |
+
# Compute dV.
|
| 285 |
+
if ENABLE_DROPOUT:
|
| 286 |
+
pT_dropout = tl.where(dropout_mask, pT, 0.0) * dropout_scale
|
| 287 |
+
if IS_FP8:
|
| 288 |
+
scale_p_dropout, descale_p_dropout = _compute_fp8_scaling_factors(
|
| 289 |
+
pT_dropout, FP8_MAX
|
| 290 |
+
)
|
| 291 |
+
dv += (
|
| 292 |
+
tl.dot((pT_dropout * scale_p_dropout).to(do.type.element_ty), do)
|
| 293 |
+
* descale_p_dropout
|
| 294 |
+
* descale_do
|
| 295 |
+
)
|
| 296 |
+
else:
|
| 297 |
+
dv = tl.dot(pT_dropout.to(do.type.element_ty), do, acc=dv)
|
| 298 |
+
else:
|
| 299 |
+
if IS_FP8:
|
| 300 |
+
scale_pT, descale_pT = _compute_fp8_scaling_factors(pT, FP8_MAX)
|
| 301 |
+
dv += (
|
| 302 |
+
tl.dot((pT * scale_pT).to(do.type.element_ty), do)
|
| 303 |
+
* descale_pT
|
| 304 |
+
* descale_do
|
| 305 |
+
)
|
| 306 |
+
else:
|
| 307 |
+
dv = tl.dot(pT.to(do.type.element_ty), do, acc=dv)
|
| 308 |
+
|
| 309 |
+
if DEBUG_TRITON_DETAIL:
|
| 310 |
+
if start_n == 256:
|
| 311 |
+
print(f"pT: {pT.shape}\n", pT)
|
| 312 |
+
# D (= delta) is pre-divided by ds_scale.
|
| 313 |
+
Di = tl.load(D + offs_m * stride_deltam, mask=mask_m)
|
| 314 |
+
# Compute dP and dS.
|
| 315 |
+
if IS_FP8:
|
| 316 |
+
dpT = tl.dot(v, tl.trans(do)) * descale_v * descale_do
|
| 317 |
+
else:
|
| 318 |
+
dpT = tl.dot(v, tl.trans(do))
|
| 319 |
+
if ENABLE_DROPOUT:
|
| 320 |
+
dpT = tl.where(dropout_mask, dpT, 0.0) * dropout_scale
|
| 321 |
+
delta_i = Di[None, :]
|
| 322 |
+
dsT = pT * (dpT - delta_i)
|
| 323 |
+
if IS_FP8:
|
| 324 |
+
scale_dsT, descale_dsT = _compute_fp8_scaling_factors(dsT, FP8_MAX)
|
| 325 |
+
dk += (
|
| 326 |
+
tl.dot((dsT * scale_dsT).to(qT.type.element_ty), tl.trans(qT))
|
| 327 |
+
* descale_dsT
|
| 328 |
+
* descale_q
|
| 329 |
+
)
|
| 330 |
+
else:
|
| 331 |
+
dk = tl.dot(dsT.to(qT.type.element_ty), tl.trans(qT), acc=dk)
|
| 332 |
+
if HAS_PE:
|
| 333 |
+
dk_pe = tl.dot(
|
| 334 |
+
dsT.to(qT_pe.type.element_ty), tl.trans(qT_pe), acc=dk_pe
|
| 335 |
+
)
|
| 336 |
+
# Increment pointers.
|
| 337 |
+
curr_m += step_m
|
| 338 |
+
qT_ptrs += step_m * stride_qm
|
| 339 |
+
if HAS_PE:
|
| 340 |
+
qT_pe_ptrs += step_m * stride_qm
|
| 341 |
+
do_ptrs += step_m * stride_dom
|
| 342 |
+
return dk, dk_pe, dv
|
| 343 |
+
|
| 344 |
+
|
| 345 |
+
# the main inner-loop logic for computing dQ
|
| 346 |
+
@triton.jit
|
| 347 |
+
def _bwd_dq_inner(
|
| 348 |
+
dq, # output
|
| 349 |
+
dq_pe, # optional output, pass None for non-PE case
|
| 350 |
+
q,
|
| 351 |
+
q_pe,
|
| 352 |
+
K,
|
| 353 |
+
V,
|
| 354 |
+
do,
|
| 355 |
+
m,
|
| 356 |
+
Di, # D (= delta) is pre-divided by ds_scale.
|
| 357 |
+
sm_scale, # input
|
| 358 |
+
# shared by Q/K/V.
|
| 359 |
+
stride_qm,
|
| 360 |
+
stride_qk,
|
| 361 |
+
stride_kn,
|
| 362 |
+
stride_kk,
|
| 363 |
+
stride_vn,
|
| 364 |
+
stride_vk,
|
| 365 |
+
stride_dropoutm,
|
| 366 |
+
stride_dropoutn, # stride for dropout
|
| 367 |
+
seqlen_q,
|
| 368 |
+
seqlen_k, #
|
| 369 |
+
BLOCK_M2: tl.constexpr, #
|
| 370 |
+
BLOCK_N2: tl.constexpr, #
|
| 371 |
+
HEAD_DIM: tl.constexpr,
|
| 372 |
+
ACTUAL_HEAD_DIM: tl.constexpr, #
|
| 373 |
+
PE_HEAD_DIM: tl.constexpr,
|
| 374 |
+
dropout_p,
|
| 375 |
+
philox_seed,
|
| 376 |
+
batch_philox_offset,
|
| 377 |
+
dropout_offset,
|
| 378 |
+
alibi_slope,
|
| 379 |
+
# Filled in by the wrapper.
|
| 380 |
+
start_m,
|
| 381 |
+
start_n,
|
| 382 |
+
end_n,
|
| 383 |
+
num_steps, #
|
| 384 |
+
descale_q,
|
| 385 |
+
descale_k,
|
| 386 |
+
descale_v,
|
| 387 |
+
descale_do, # fp8 descale factors from user
|
| 388 |
+
MASK: tl.constexpr,
|
| 389 |
+
ENABLE_DROPOUT: tl.constexpr,
|
| 390 |
+
USE_ALIBI: tl.constexpr,
|
| 391 |
+
USE_EXP2: tl.constexpr,
|
| 392 |
+
IS_FP8: tl.constexpr,
|
| 393 |
+
FP8_MAX: tl.constexpr,
|
| 394 |
+
DEBUG_TRITON: tl.constexpr,
|
| 395 |
+
DEBUG_TRITON_DETAIL: tl.constexpr,
|
| 396 |
+
SLIDING_WINDOW: tl.constexpr,
|
| 397 |
+
ENABLE_SINK: tl.constexpr,
|
| 398 |
+
):
|
| 399 |
+
# if HEAD_DIM is padded
|
| 400 |
+
PADDED_HEAD: tl.constexpr = ACTUAL_HEAD_DIM != HEAD_DIM
|
| 401 |
+
HAS_PE: tl.constexpr = PE_HEAD_DIM > 0
|
| 402 |
+
delta_qk = seqlen_q - seqlen_k
|
| 403 |
+
offs_m = start_m + tl.arange(0, BLOCK_M2)
|
| 404 |
+
offs_n = start_n + tl.arange(0, BLOCK_N2)
|
| 405 |
+
offs_k = tl.arange(0, HEAD_DIM)
|
| 406 |
+
if HAS_PE:
|
| 407 |
+
offs_k_pe = HEAD_DIM + tl.arange(0, PE_HEAD_DIM)
|
| 408 |
+
|
| 409 |
+
# mask to make sure not OOB of seqlen_q
|
| 410 |
+
mask_m = offs_m < seqlen_q
|
| 411 |
+
|
| 412 |
+
kT_ptrs = K + offs_n[None, :] * stride_kn + offs_k[:, None] * stride_kk
|
| 413 |
+
if HAS_PE:
|
| 414 |
+
kT_pe_ptrs = K + offs_n[None, :] * stride_kn + offs_k_pe[:, None] * stride_kk
|
| 415 |
+
vT_ptrs = V + offs_n[None, :] * stride_vn + offs_k[:, None] * stride_vk
|
| 416 |
+
# BLOCK_M2 must be a multiple of BLOCK_N2, otherwise the code wouldn't work.
|
| 417 |
+
tl.static_assert(BLOCK_M2 % BLOCK_N2 == 0)
|
| 418 |
+
curr_n = start_n
|
| 419 |
+
step_n = BLOCK_N2
|
| 420 |
+
curr_philox_offset = batch_philox_offset
|
| 421 |
+
curr_dropout_offset = dropout_offset
|
| 422 |
+
RCP_LN2: tl.constexpr = 1.4426950408889634 # = 1.0 / ln(2)
|
| 423 |
+
delta_recomp = tl.zeros([BLOCK_M2], dtype=tl.float32)
|
| 424 |
+
for blk_idx in range(num_steps):
|
| 425 |
+
if DEBUG_TRITON:
|
| 426 |
+
print(f"iter {blk_idx}: curr_n = {curr_n}") # noqa: E701
|
| 427 |
+
offs_n = curr_n + tl.arange(0, BLOCK_N2)
|
| 428 |
+
# end_n is needed because the end of causal True might not be perfectly
|
| 429 |
+
# aligned with the end of the block
|
| 430 |
+
mask_n = offs_n < end_n
|
| 431 |
+
if DEBUG_TRITON_DETAIL:
|
| 432 |
+
print(
|
| 433 |
+
f"start_n = {start_n}, end_n = {end_n}, offs_n: {offs_n.shape}\n{offs_n}"
|
| 434 |
+
) # noqa: E701
|
| 435 |
+
if DEBUG_TRITON_DETAIL:
|
| 436 |
+
print(f"mask_n: {mask_n.shape}\n{mask_n}") # noqa: E701
|
| 437 |
+
mask_kT = mask_n[None, :]
|
| 438 |
+
mask_mn = mask_m[:, None] & (offs_n[None, :] < end_n)
|
| 439 |
+
if PADDED_HEAD:
|
| 440 |
+
mask_kT &= offs_k[:, None] < ACTUAL_HEAD_DIM
|
| 441 |
+
|
| 442 |
+
kT = tl.load(kT_ptrs, mask=mask_kT, other=0.0)
|
| 443 |
+
if HAS_PE:
|
| 444 |
+
kT_pe = tl.load(kT_pe_ptrs, mask=mask_kT, other=0.0)
|
| 445 |
+
vT = tl.load(vT_ptrs, mask=mask_kT, other=0.0)
|
| 446 |
+
|
| 447 |
+
if ENABLE_DROPOUT:
|
| 448 |
+
# NOTE: dropout is transposed because it is used to mask pT
|
| 449 |
+
philox_offs = (
|
| 450 |
+
curr_philox_offset
|
| 451 |
+
+ offs_m[:, None] * stride_dropoutm
|
| 452 |
+
+ offs_n[None, :] * stride_dropoutn
|
| 453 |
+
)
|
| 454 |
+
if tl_DROPOUT_USE_PYTORCH:
|
| 455 |
+
dropout_offs = (
|
| 456 |
+
offs_m[:, None] * stride_dropoutm
|
| 457 |
+
+ offs_n[None, :] * stride_dropoutn
|
| 458 |
+
)
|
| 459 |
+
dropout_mask = tl.load(curr_dropout_offset + dropout_offs, mask=mask_mn)
|
| 460 |
+
else:
|
| 461 |
+
rand_vals = tl.rand(philox_seed, philox_offs)
|
| 462 |
+
dropout_mask = rand_vals > dropout_p
|
| 463 |
+
dropout_scale = 1 / (1 - dropout_p)
|
| 464 |
+
|
| 465 |
+
if IS_FP8:
|
| 466 |
+
qk = tl.dot(q, kT) * descale_q * descale_k
|
| 467 |
+
else:
|
| 468 |
+
qk = tl.dot(q, kT)
|
| 469 |
+
if HAS_PE:
|
| 470 |
+
qk = tl.dot(q_pe, kT_pe, acc=qk)
|
| 471 |
+
qk_scaled = qk * sm_scale
|
| 472 |
+
|
| 473 |
+
if USE_ALIBI:
|
| 474 |
+
relative_pos_block = offs_m[:, None] + seqlen_k - seqlen_q - offs_n[None, :]
|
| 475 |
+
alibi_block = -1 * alibi_slope * tl.abs(relative_pos_block)
|
| 476 |
+
qk_scaled += alibi_block
|
| 477 |
+
|
| 478 |
+
if DEBUG_TRITON_DETAIL:
|
| 479 |
+
print(f"qk scaled: {qk.shape}\n", qk_scaled) # noqa: E701
|
| 480 |
+
if USE_EXP2:
|
| 481 |
+
p = tl.math.exp2(qk_scaled * RCP_LN2 - m * RCP_LN2)
|
| 482 |
+
else:
|
| 483 |
+
p = tl.math.exp(qk_scaled - m)
|
| 484 |
+
|
| 485 |
+
# Autoregressive masking.
|
| 486 |
+
if MASK:
|
| 487 |
+
causal_mask = (offs_m[:, None] - delta_qk) >= offs_n[None, :]
|
| 488 |
+
mask = causal_mask & mask_mn
|
| 489 |
+
p = tl.where(mask, p, 0.0)
|
| 490 |
+
if SLIDING_WINDOW > 0:
|
| 491 |
+
window_mask = offs_n[None, :] >= (
|
| 492 |
+
offs_m[:, None] - delta_qk - SLIDING_WINDOW
|
| 493 |
+
)
|
| 494 |
+
p = tl.where(window_mask, p, 0.0)
|
| 495 |
+
# Compute dP and dS.
|
| 496 |
+
if IS_FP8:
|
| 497 |
+
dp = tl.dot(do, vT) * descale_do * descale_v
|
| 498 |
+
else:
|
| 499 |
+
dp = tl.dot(do, vT)
|
| 500 |
+
if ENABLE_DROPOUT:
|
| 501 |
+
dp = tl.where(dropout_mask, dp, 0.0) * dropout_scale
|
| 502 |
+
if ENABLE_SINK:
|
| 503 |
+
# Equal to do_i . o_i but skips the bf16 round-trip through forward `o`.
|
| 504 |
+
delta_recomp += tl.sum(p * dp, axis=1)
|
| 505 |
+
delta_i = Di[:, None]
|
| 506 |
+
ds = p * (dp - delta_i)
|
| 507 |
+
# Compute dQ.
|
| 508 |
+
# NOTE: We need to de-scale dq in the end, because kT was pre-scaled.
|
| 509 |
+
if IS_FP8:
|
| 510 |
+
scale_ds, descale_ds = _compute_fp8_scaling_factors(ds, FP8_MAX)
|
| 511 |
+
dq += (
|
| 512 |
+
tl.dot((ds * scale_ds).to(kT.type.element_ty), tl.trans(kT))
|
| 513 |
+
* descale_ds
|
| 514 |
+
* descale_k
|
| 515 |
+
)
|
| 516 |
+
else:
|
| 517 |
+
dq = tl.dot(ds.to(kT.type.element_ty), tl.trans(kT), acc=dq)
|
| 518 |
+
if HAS_PE:
|
| 519 |
+
dq_pe = tl.dot(ds.to(kT_pe.type.element_ty), tl.trans(kT_pe), acc=dq_pe)
|
| 520 |
+
# Increment pointers.
|
| 521 |
+
curr_n += step_n
|
| 522 |
+
kT_ptrs += step_n * stride_kn
|
| 523 |
+
if HAS_PE:
|
| 524 |
+
kT_pe_ptrs += step_n * stride_kn
|
| 525 |
+
vT_ptrs += step_n * stride_vn
|
| 526 |
+
return dq, dq_pe, delta_recomp
|
| 527 |
+
|
| 528 |
+
|
| 529 |
+
_bwd_kernel_causal_repr = make_kernel_repr(
|
| 530 |
+
"bwd_kernel_causal",
|
| 531 |
+
[
|
| 532 |
+
"BLOCK_M1",
|
| 533 |
+
"BLOCK_N1",
|
| 534 |
+
"BLOCK_M2",
|
| 535 |
+
"BLOCK_N2",
|
| 536 |
+
"BLK_SLICE_FACTOR",
|
| 537 |
+
"HEAD_DIM",
|
| 538 |
+
"ENABLE_DROPOUT",
|
| 539 |
+
"IS_VARLEN",
|
| 540 |
+
"USE_ALIBI",
|
| 541 |
+
"USE_EXP2",
|
| 542 |
+
"IS_FP8",
|
| 543 |
+
"USE_INT64_STRIDES",
|
| 544 |
+
"ENABLE_SINK",
|
| 545 |
+
"SLIDING_WINDOW",
|
| 546 |
+
],
|
| 547 |
+
)
|
| 548 |
+
|
| 549 |
+
|
| 550 |
+
@triton.jit(repr=_bwd_kernel_causal_repr)
|
| 551 |
+
def bwd_kernel_causal( # grid = (tl.cdiv(max_seqlen_q // BLOCK_M2), batch, nheads_q)
|
| 552 |
+
Q,
|
| 553 |
+
K,
|
| 554 |
+
V,
|
| 555 |
+
Sink,
|
| 556 |
+
sm_scale,
|
| 557 |
+
DO,
|
| 558 |
+
DQ,
|
| 559 |
+
DK,
|
| 560 |
+
DV,
|
| 561 |
+
DSink,
|
| 562 |
+
M,
|
| 563 |
+
Delta,
|
| 564 |
+
stride_qb_in,
|
| 565 |
+
stride_qh_in,
|
| 566 |
+
stride_qm_in,
|
| 567 |
+
stride_qd_in,
|
| 568 |
+
stride_kb_in,
|
| 569 |
+
stride_kh_in,
|
| 570 |
+
stride_kn_in,
|
| 571 |
+
stride_kd_in,
|
| 572 |
+
stride_vb_in,
|
| 573 |
+
stride_vh_in,
|
| 574 |
+
stride_vn_in,
|
| 575 |
+
stride_vd_in,
|
| 576 |
+
stride_dqb_in,
|
| 577 |
+
stride_dqh_in,
|
| 578 |
+
stride_dqm_in,
|
| 579 |
+
stride_dqd_in,
|
| 580 |
+
stride_dkb_in,
|
| 581 |
+
stride_dkh_in,
|
| 582 |
+
stride_dkn_in,
|
| 583 |
+
stride_dkd_in,
|
| 584 |
+
stride_dvb_in,
|
| 585 |
+
stride_dvh_in,
|
| 586 |
+
stride_dvn_in,
|
| 587 |
+
stride_dvd_in,
|
| 588 |
+
stride_deltab_in,
|
| 589 |
+
stride_deltah_in,
|
| 590 |
+
stride_deltam_in,
|
| 591 |
+
stride_dob_in,
|
| 592 |
+
stride_doh_in,
|
| 593 |
+
stride_dom_in,
|
| 594 |
+
stride_dod_in,
|
| 595 |
+
stride_dropoutb_in,
|
| 596 |
+
stride_dropouth_in,
|
| 597 |
+
stride_dropoutm_in,
|
| 598 |
+
stride_dropoutn_in,
|
| 599 |
+
stride_descale_q_z_in,
|
| 600 |
+
stride_descale_k_z_in,
|
| 601 |
+
stride_descale_v_z_in,
|
| 602 |
+
stride_descale_do_z_in,
|
| 603 |
+
stride_az_in,
|
| 604 |
+
stride_ah_in,
|
| 605 |
+
HQ,
|
| 606 |
+
HK,
|
| 607 |
+
cu_seqlens_q,
|
| 608 |
+
cu_seqlens_k,
|
| 609 |
+
max_seqlen_q,
|
| 610 |
+
max_seqlen_k,
|
| 611 |
+
Dropout_mask,
|
| 612 |
+
dropout_p,
|
| 613 |
+
philox_seed,
|
| 614 |
+
philox_offset_base_in,
|
| 615 |
+
Alibi_slopes,
|
| 616 |
+
Descale_q,
|
| 617 |
+
Descale_k,
|
| 618 |
+
Descale_v,
|
| 619 |
+
Descale_do,
|
| 620 |
+
BLOCK_M1: tl.constexpr,
|
| 621 |
+
BLOCK_N1: tl.constexpr,
|
| 622 |
+
BLOCK_M2: tl.constexpr,
|
| 623 |
+
BLOCK_N2: tl.constexpr,
|
| 624 |
+
BLK_SLICE_FACTOR: tl.constexpr,
|
| 625 |
+
HEAD_DIM: tl.constexpr,
|
| 626 |
+
ACTUAL_HEAD_DIM: tl.constexpr,
|
| 627 |
+
PE_HEAD_DIM: tl.constexpr,
|
| 628 |
+
ENABLE_DROPOUT: tl.constexpr,
|
| 629 |
+
IS_VARLEN: tl.constexpr,
|
| 630 |
+
USE_ALIBI: tl.constexpr,
|
| 631 |
+
USE_EXP2: tl.constexpr,
|
| 632 |
+
IS_FP8: tl.constexpr,
|
| 633 |
+
FP8_MAX: tl.constexpr,
|
| 634 |
+
DEBUG_TRITON: tl.constexpr,
|
| 635 |
+
DEBUG_TRITON_DETAIL: tl.constexpr,
|
| 636 |
+
USE_INT64_STRIDES: tl.constexpr,
|
| 637 |
+
ENABLE_SINK: tl.constexpr,
|
| 638 |
+
SLIDING_WINDOW: tl.constexpr,
|
| 639 |
+
):
|
| 640 |
+
if USE_INT64_STRIDES:
|
| 641 |
+
stride_qb = tl.cast(stride_qb_in, tl.int64)
|
| 642 |
+
stride_qh = tl.cast(stride_qh_in, tl.int64)
|
| 643 |
+
stride_qm = tl.cast(stride_qm_in, tl.int64)
|
| 644 |
+
stride_qd = tl.cast(stride_qd_in, tl.int64)
|
| 645 |
+
stride_kb = tl.cast(stride_kb_in, tl.int64)
|
| 646 |
+
stride_kh = tl.cast(stride_kh_in, tl.int64)
|
| 647 |
+
stride_kn = tl.cast(stride_kn_in, tl.int64)
|
| 648 |
+
stride_kd = tl.cast(stride_kd_in, tl.int64)
|
| 649 |
+
stride_vb = tl.cast(stride_vb_in, tl.int64)
|
| 650 |
+
stride_vh = tl.cast(stride_vh_in, tl.int64)
|
| 651 |
+
stride_vn = tl.cast(stride_vn_in, tl.int64)
|
| 652 |
+
stride_vd = tl.cast(stride_vd_in, tl.int64)
|
| 653 |
+
stride_dqb = tl.cast(stride_dqb_in, tl.int64)
|
| 654 |
+
stride_dqh = tl.cast(stride_dqh_in, tl.int64)
|
| 655 |
+
stride_dqm = tl.cast(stride_dqm_in, tl.int64)
|
| 656 |
+
stride_dqd = tl.cast(stride_dqd_in, tl.int64)
|
| 657 |
+
stride_dkb = tl.cast(stride_dkb_in, tl.int64)
|
| 658 |
+
stride_dkh = tl.cast(stride_dkh_in, tl.int64)
|
| 659 |
+
stride_dkn = tl.cast(stride_dkn_in, tl.int64)
|
| 660 |
+
stride_dkd = tl.cast(stride_dkd_in, tl.int64)
|
| 661 |
+
stride_dvb = tl.cast(stride_dvb_in, tl.int64)
|
| 662 |
+
stride_dvh = tl.cast(stride_dvh_in, tl.int64)
|
| 663 |
+
stride_dvn = tl.cast(stride_dvn_in, tl.int64)
|
| 664 |
+
stride_dvd = tl.cast(stride_dvd_in, tl.int64)
|
| 665 |
+
stride_deltab = tl.cast(stride_deltab_in, tl.int64)
|
| 666 |
+
stride_deltah = tl.cast(stride_deltah_in, tl.int64)
|
| 667 |
+
stride_deltam = tl.cast(stride_deltam_in, tl.int64)
|
| 668 |
+
stride_dob = tl.cast(stride_dob_in, tl.int64)
|
| 669 |
+
stride_doh = tl.cast(stride_doh_in, tl.int64)
|
| 670 |
+
stride_dom = tl.cast(stride_dom_in, tl.int64)
|
| 671 |
+
stride_dod = tl.cast(stride_dod_in, tl.int64)
|
| 672 |
+
philox_offset_base = tl.cast(philox_offset_base_in, tl.int64)
|
| 673 |
+
stride_dropoutb = tl.cast(stride_dropoutb_in, tl.int64)
|
| 674 |
+
stride_dropouth = tl.cast(stride_dropouth_in, tl.int64)
|
| 675 |
+
stride_dropoutm = tl.cast(stride_dropoutm_in, tl.int64)
|
| 676 |
+
stride_dropoutn = tl.cast(stride_dropoutn_in, tl.int64)
|
| 677 |
+
if IS_FP8:
|
| 678 |
+
stride_descale_q_z = tl.cast(stride_descale_q_z_in, tl.int64)
|
| 679 |
+
stride_descale_k_z = tl.cast(stride_descale_k_z_in, tl.int64)
|
| 680 |
+
stride_descale_v_z = tl.cast(stride_descale_v_z_in, tl.int64)
|
| 681 |
+
stride_descale_do_z = tl.cast(stride_descale_do_z_in, tl.int64)
|
| 682 |
+
stride_az = tl.cast(stride_az_in, tl.int64)
|
| 683 |
+
stride_ah = tl.cast(stride_ah_in, tl.int64)
|
| 684 |
+
else:
|
| 685 |
+
stride_qb = stride_qb_in
|
| 686 |
+
stride_qh = stride_qh_in
|
| 687 |
+
stride_qm = stride_qm_in
|
| 688 |
+
stride_qd = stride_qd_in
|
| 689 |
+
stride_kb = stride_kb_in
|
| 690 |
+
stride_kh = stride_kh_in
|
| 691 |
+
stride_kn = stride_kn_in
|
| 692 |
+
stride_kd = stride_kd_in
|
| 693 |
+
stride_vb = stride_vb_in
|
| 694 |
+
stride_vh = stride_vh_in
|
| 695 |
+
stride_vn = stride_vn_in
|
| 696 |
+
stride_vd = stride_vd_in
|
| 697 |
+
stride_dqb = stride_dqb_in
|
| 698 |
+
stride_dqh = stride_dqh_in
|
| 699 |
+
stride_dqm = stride_dqm_in
|
| 700 |
+
stride_dqd = stride_dqd_in
|
| 701 |
+
stride_dkb = stride_dkb_in
|
| 702 |
+
stride_dkh = stride_dkh_in
|
| 703 |
+
stride_dkn = stride_dkn_in
|
| 704 |
+
stride_dkd = stride_dkd_in
|
| 705 |
+
stride_dvb = stride_dvb_in
|
| 706 |
+
stride_dvh = stride_dvh_in
|
| 707 |
+
stride_dvn = stride_dvn_in
|
| 708 |
+
stride_dvd = stride_dvd_in
|
| 709 |
+
stride_deltab = stride_deltab_in
|
| 710 |
+
stride_deltah = stride_deltah_in
|
| 711 |
+
stride_deltam = stride_deltam_in
|
| 712 |
+
stride_dob = stride_dob_in
|
| 713 |
+
stride_doh = stride_doh_in
|
| 714 |
+
stride_dom = stride_dom_in
|
| 715 |
+
stride_dod = stride_dod_in
|
| 716 |
+
philox_offset_base = philox_offset_base_in
|
| 717 |
+
stride_dropoutb = stride_dropoutb_in
|
| 718 |
+
stride_dropouth = stride_dropouth_in
|
| 719 |
+
stride_dropoutm = stride_dropoutm_in
|
| 720 |
+
stride_dropoutn = stride_dropoutn_in
|
| 721 |
+
stride_descale_q_z = stride_descale_q_z_in
|
| 722 |
+
stride_descale_k_z = stride_descale_k_z_in
|
| 723 |
+
stride_descale_v_z = stride_descale_v_z_in
|
| 724 |
+
stride_descale_do_z = stride_descale_do_z_in
|
| 725 |
+
stride_az = stride_az_in
|
| 726 |
+
stride_ah = stride_ah_in
|
| 727 |
+
|
| 728 |
+
# program ids
|
| 729 |
+
hkid = tl.program_id(0)
|
| 730 |
+
pid = tl.program_id(1)
|
| 731 |
+
bid = tl.program_id(2)
|
| 732 |
+
if DEBUG_TRITON:
|
| 733 |
+
print(f"\npid: {pid}, bid: {bid}, hkid: {hkid}") # noqa: E701
|
| 734 |
+
# figure out varlen start and end
|
| 735 |
+
q_start = 0
|
| 736 |
+
k_start = 0
|
| 737 |
+
seqlen_q = max_seqlen_q
|
| 738 |
+
seqlen_k = max_seqlen_k
|
| 739 |
+
if IS_VARLEN:
|
| 740 |
+
# Compute actual sequence lengths
|
| 741 |
+
q_start = tl.load(cu_seqlens_q + bid)
|
| 742 |
+
q_end = tl.load(cu_seqlens_q + bid + 1)
|
| 743 |
+
k_start = tl.load(cu_seqlens_k + bid)
|
| 744 |
+
k_end = tl.load(cu_seqlens_k + bid + 1)
|
| 745 |
+
seqlen_q = q_end - q_start
|
| 746 |
+
seqlen_k = k_end - k_start
|
| 747 |
+
|
| 748 |
+
delta_qk = seqlen_q - seqlen_k
|
| 749 |
+
if DEBUG_TRITON:
|
| 750 |
+
print(f"delta_qk = {delta_qk}") # noqa: E701
|
| 751 |
+
PADDED_HEAD: tl.constexpr = ACTUAL_HEAD_DIM != HEAD_DIM
|
| 752 |
+
HAS_PE: tl.constexpr = PE_HEAD_DIM > 0
|
| 753 |
+
offs_d = tl.arange(0, HEAD_DIM)
|
| 754 |
+
if HAS_PE:
|
| 755 |
+
offs_d_pe = HEAD_DIM + tl.arange(0, PE_HEAD_DIM)
|
| 756 |
+
GROUP_SIZE: tl.constexpr = HQ // HK
|
| 757 |
+
|
| 758 |
+
# align the delta_qk
|
| 759 |
+
start_n = pid * BLOCK_N1
|
| 760 |
+
if start_n < seqlen_k:
|
| 761 |
+
# This section does dk and dv
|
| 762 |
+
dk = tl.zeros([BLOCK_N1, HEAD_DIM], dtype=tl.float32)
|
| 763 |
+
if HAS_PE:
|
| 764 |
+
dk_pe = tl.zeros([BLOCK_N1, PE_HEAD_DIM], dtype=tl.float32)
|
| 765 |
+
else:
|
| 766 |
+
# Couldn't assign None to dk_pe because _bwd_dkdv_inner can't return None.
|
| 767 |
+
dk_pe = dk
|
| 768 |
+
dv = tl.zeros([BLOCK_N1, HEAD_DIM], dtype=tl.float32)
|
| 769 |
+
|
| 770 |
+
# q > k: diretcly skip all the way until the start of causal block
|
| 771 |
+
start_delta_q_gt_k = delta_qk
|
| 772 |
+
# q < k: some blocks will have no Masked block, other needs to re-calc
|
| 773 |
+
# starting position
|
| 774 |
+
# delta_qk is negative so flip it, only multiple of BLOCK_N can skip the
|
| 775 |
+
# masked op
|
| 776 |
+
num_blocks_skip = -delta_qk // BLOCK_N1
|
| 777 |
+
delta_aligned = (num_blocks_skip + 1) * BLOCK_N1 + delta_qk
|
| 778 |
+
start_delta_q_lt_k = delta_aligned // BLOCK_M1 * BLOCK_M1
|
| 779 |
+
if delta_qk >= 0:
|
| 780 |
+
start_delta = delta_qk
|
| 781 |
+
if DEBUG_TRITON:
|
| 782 |
+
print(
|
| 783 |
+
f"q >= k: start_delta = delta_qk aligned to BLOCK_M = {start_delta_q_gt_k}"
|
| 784 |
+
) # noqa: E701
|
| 785 |
+
else:
|
| 786 |
+
start_delta = start_delta_q_lt_k
|
| 787 |
+
if DEBUG_TRITON:
|
| 788 |
+
print(
|
| 789 |
+
f"q < k: start_delta = residue btw multiple BLOCK_N and delta_qk = {delta_aligned} = aligned to BLOCK_M = {start_delta_q_lt_k}"
|
| 790 |
+
) # noqa: E701
|
| 791 |
+
|
| 792 |
+
offs_n = start_n + tl.arange(0, BLOCK_N1)
|
| 793 |
+
# Mask for loading K and V
|
| 794 |
+
mask_kv = offs_n[:, None] < seqlen_k
|
| 795 |
+
if PADDED_HEAD:
|
| 796 |
+
mask_d = offs_d < ACTUAL_HEAD_DIM
|
| 797 |
+
mask_kv &= mask_d[None, :]
|
| 798 |
+
|
| 799 |
+
# K/V tensors not changed for the group
|
| 800 |
+
adj_k = (
|
| 801 |
+
bid * stride_kb
|
| 802 |
+
+ hkid * stride_kh
|
| 803 |
+
+ k_start * stride_kn
|
| 804 |
+
+ offs_n[:, None] * stride_kn
|
| 805 |
+
+ offs_d[None, :] * stride_kd
|
| 806 |
+
)
|
| 807 |
+
if HAS_PE:
|
| 808 |
+
adj_k_pe = (
|
| 809 |
+
bid * stride_kb
|
| 810 |
+
+ hkid * stride_kh
|
| 811 |
+
+ k_start * stride_kn
|
| 812 |
+
+ offs_n[:, None] * stride_kn
|
| 813 |
+
+ offs_d_pe[None, :] * stride_kd
|
| 814 |
+
)
|
| 815 |
+
adj_v = (
|
| 816 |
+
bid * stride_vb
|
| 817 |
+
+ hkid * stride_vh
|
| 818 |
+
+ k_start * stride_vn
|
| 819 |
+
+ offs_n[:, None] * stride_vn
|
| 820 |
+
+ offs_d[None, :] * stride_vd
|
| 821 |
+
)
|
| 822 |
+
# load K and V: they stay in SRAM throughout the inner loop.
|
| 823 |
+
k = tl.load(K + adj_k, mask=mask_kv, other=0.0)
|
| 824 |
+
if HAS_PE:
|
| 825 |
+
k_pe = tl.load(K + adj_k_pe, mask=mask_kv, other=0.0)
|
| 826 |
+
else:
|
| 827 |
+
k_pe = None
|
| 828 |
+
v = tl.load(V + adj_v, mask=mask_kv, other=0.0)
|
| 829 |
+
# If MQA / GQA, set the K and V head offsets appropriately.
|
| 830 |
+
# hqid = hkid
|
| 831 |
+
for hqid in range(hkid * GROUP_SIZE, hkid * GROUP_SIZE + GROUP_SIZE):
|
| 832 |
+
if delta_qk >= 0:
|
| 833 |
+
start_m = start_n + start_delta
|
| 834 |
+
len_m = BLOCK_N1
|
| 835 |
+
else:
|
| 836 |
+
start_m = max(start_n + delta_qk, 0)
|
| 837 |
+
start_m = start_m // BLOCK_M1 * BLOCK_M1
|
| 838 |
+
# because we might shift the masked blocks up, we are deeper into
|
| 839 |
+
# the masked out region, so we would potentially increase the total
|
| 840 |
+
# steps with masked operation to get out of it
|
| 841 |
+
residue_m = max(start_n + delta_qk - start_m, 0)
|
| 842 |
+
len_m = BLOCK_N1 + residue_m
|
| 843 |
+
if DEBUG_TRITON:
|
| 844 |
+
print(f"residue_m = {residue_m}") # noqa: E701
|
| 845 |
+
|
| 846 |
+
# offset input and output tensor by batch and Q/K heads
|
| 847 |
+
adj_q = bid * stride_qb + hqid * stride_qh + q_start * stride_qm
|
| 848 |
+
Q_ptr = Q + adj_q
|
| 849 |
+
adj_do = bid * stride_dob + hqid * stride_doh + q_start * stride_dom
|
| 850 |
+
DO_ptr = DO + adj_do
|
| 851 |
+
adj_delta = (
|
| 852 |
+
bid * stride_deltab + hqid * stride_deltah + q_start * stride_deltam
|
| 853 |
+
)
|
| 854 |
+
M_ptr = M + adj_delta
|
| 855 |
+
Delta_ptr = Delta + adj_delta
|
| 856 |
+
|
| 857 |
+
if USE_ALIBI:
|
| 858 |
+
alibi_offset = bid * stride_az + hqid * stride_ah
|
| 859 |
+
alibi_slope = tl.load(Alibi_slopes + alibi_offset)
|
| 860 |
+
else:
|
| 861 |
+
alibi_slope = None
|
| 862 |
+
|
| 863 |
+
# batch_philox_offset is the ACTUALLY dropout offset
|
| 864 |
+
# dropout_offset is for debug purpose and will be removed later
|
| 865 |
+
batch_philox_offset = 0
|
| 866 |
+
dropout_offset = 0
|
| 867 |
+
if ENABLE_DROPOUT:
|
| 868 |
+
batch_philox_offset = (
|
| 869 |
+
philox_offset_base + bid * stride_dropoutb + hqid * stride_dropouth
|
| 870 |
+
)
|
| 871 |
+
dropout_offset = (
|
| 872 |
+
Dropout_mask + bid * stride_dropoutb + hqid * stride_dropouth
|
| 873 |
+
)
|
| 874 |
+
|
| 875 |
+
if IS_FP8:
|
| 876 |
+
descale_q = tl.load(Descale_q + bid * stride_descale_q_z + hqid)
|
| 877 |
+
descale_k = tl.load(Descale_k + bid * stride_descale_k_z + hkid)
|
| 878 |
+
descale_v = tl.load(Descale_v + bid * stride_descale_v_z + hkid)
|
| 879 |
+
descale_do = tl.load(Descale_do + bid * stride_descale_do_z + hqid)
|
| 880 |
+
else:
|
| 881 |
+
descale_q, descale_k, descale_v, descale_do = 1.0, 1.0, 1.0, 1.0
|
| 882 |
+
|
| 883 |
+
MASK_BLOCK_M1: tl.constexpr = BLOCK_M1 // BLK_SLICE_FACTOR
|
| 884 |
+
# bound the masked operation to q len so it does not have to wast cycles
|
| 885 |
+
len_m = min(len_m, seqlen_q)
|
| 886 |
+
num_steps = tl.cdiv(len_m, MASK_BLOCK_M1)
|
| 887 |
+
# when q < k, we may skip the initial masked op
|
| 888 |
+
if pid < num_blocks_skip:
|
| 889 |
+
num_steps = 0
|
| 890 |
+
|
| 891 |
+
# if start_m is negative, the current N-tile has no block on the
|
| 892 |
+
# diagonal of causal mask, so everything have no causal mask
|
| 893 |
+
if DEBUG_TRITON:
|
| 894 |
+
print(
|
| 895 |
+
f"Masked: start_n: {start_n}; start_m: {start_m}, num_steps: {num_steps}"
|
| 896 |
+
) # noqa: E701
|
| 897 |
+
dk, dk_pe, dv = _bwd_dkdv_inner(
|
| 898 |
+
dk, # output tensor
|
| 899 |
+
dk_pe, # optional output tensor
|
| 900 |
+
dv, # output tensor
|
| 901 |
+
Q_ptr,
|
| 902 |
+
k,
|
| 903 |
+
k_pe,
|
| 904 |
+
v,
|
| 905 |
+
DO_ptr,
|
| 906 |
+
M_ptr,
|
| 907 |
+
Delta_ptr,
|
| 908 |
+
sm_scale, # input tensors
|
| 909 |
+
stride_qm,
|
| 910 |
+
stride_qd, # strides for q
|
| 911 |
+
stride_dom,
|
| 912 |
+
stride_dod, # strides for o
|
| 913 |
+
stride_dropoutm,
|
| 914 |
+
stride_dropoutn, # strides for dropout
|
| 915 |
+
stride_deltam,
|
| 916 |
+
MASK_BLOCK_M1,
|
| 917 |
+
BLOCK_N1, # block dim
|
| 918 |
+
HEAD_DIM,
|
| 919 |
+
ACTUAL_HEAD_DIM, # head dim
|
| 920 |
+
PE_HEAD_DIM,
|
| 921 |
+
dropout_p,
|
| 922 |
+
philox_seed,
|
| 923 |
+
batch_philox_offset,
|
| 924 |
+
dropout_offset,
|
| 925 |
+
alibi_slope,
|
| 926 |
+
seqlen_q,
|
| 927 |
+
seqlen_k, # max sequence length for q and k
|
| 928 |
+
start_n,
|
| 929 |
+
start_m,
|
| 930 |
+
num_steps, # iteration numbers
|
| 931 |
+
descale_q,
|
| 932 |
+
descale_k,
|
| 933 |
+
descale_v,
|
| 934 |
+
descale_do,
|
| 935 |
+
MASK=True, # causal masking
|
| 936 |
+
ENABLE_DROPOUT=ENABLE_DROPOUT, # activate dropout
|
| 937 |
+
USE_ALIBI=USE_ALIBI,
|
| 938 |
+
USE_EXP2=USE_EXP2,
|
| 939 |
+
IS_FP8=IS_FP8,
|
| 940 |
+
FP8_MAX=FP8_MAX,
|
| 941 |
+
DEBUG_TRITON=DEBUG_TRITON,
|
| 942 |
+
DEBUG_TRITON_DETAIL=DEBUG_TRITON_DETAIL,
|
| 943 |
+
SLIDING_WINDOW=SLIDING_WINDOW,
|
| 944 |
+
)
|
| 945 |
+
start_m += num_steps * MASK_BLOCK_M1
|
| 946 |
+
end_m = seqlen_q
|
| 947 |
+
if SLIDING_WINDOW > 0:
|
| 948 |
+
end_m = min(start_n + BLOCK_N1 + delta_qk + SLIDING_WINDOW, seqlen_q)
|
| 949 |
+
num_steps = tl.cdiv(max(end_m - start_m, 0), BLOCK_M1)
|
| 950 |
+
end_m = start_m + num_steps * BLOCK_M1
|
| 951 |
+
|
| 952 |
+
if DEBUG_TRITON:
|
| 953 |
+
print(
|
| 954 |
+
f"start_m after Masked step: {start_m}; num_steps: {num_steps}"
|
| 955 |
+
) # noqa: E701
|
| 956 |
+
if DEBUG_TRITON:
|
| 957 |
+
print(
|
| 958 |
+
f"unMasked: start_n: {start_n}, start_m: {start_m}, end_m: {end_m}, num_steps: {num_steps}"
|
| 959 |
+
) # noqa: E701
|
| 960 |
+
if DEBUG_TRITON:
|
| 961 |
+
print("unMasked") # noqa: E701
|
| 962 |
+
dk, dk_pe, dv = _bwd_dkdv_inner(
|
| 963 |
+
dk, # output tensor
|
| 964 |
+
dk_pe, # optional output tensor
|
| 965 |
+
dv, # output tensor
|
| 966 |
+
Q_ptr,
|
| 967 |
+
k,
|
| 968 |
+
k_pe,
|
| 969 |
+
v,
|
| 970 |
+
DO_ptr,
|
| 971 |
+
M_ptr,
|
| 972 |
+
Delta_ptr,
|
| 973 |
+
sm_scale, # input tensors
|
| 974 |
+
stride_qm,
|
| 975 |
+
stride_qd, # strides for q
|
| 976 |
+
stride_dom,
|
| 977 |
+
stride_dod, # strides for o
|
| 978 |
+
stride_dropoutm,
|
| 979 |
+
stride_dropoutn, # strides for dropout
|
| 980 |
+
stride_deltam,
|
| 981 |
+
BLOCK_M1,
|
| 982 |
+
BLOCK_N1, # block dim
|
| 983 |
+
HEAD_DIM,
|
| 984 |
+
ACTUAL_HEAD_DIM, # head dim
|
| 985 |
+
PE_HEAD_DIM,
|
| 986 |
+
dropout_p,
|
| 987 |
+
philox_seed,
|
| 988 |
+
batch_philox_offset,
|
| 989 |
+
dropout_offset,
|
| 990 |
+
alibi_slope,
|
| 991 |
+
seqlen_q,
|
| 992 |
+
seqlen_k, # max sequence length for q and k
|
| 993 |
+
start_n,
|
| 994 |
+
start_m,
|
| 995 |
+
num_steps, # iteration numbers
|
| 996 |
+
descale_q,
|
| 997 |
+
descale_k,
|
| 998 |
+
descale_v,
|
| 999 |
+
descale_do,
|
| 1000 |
+
MASK=False, # causal masking
|
| 1001 |
+
ENABLE_DROPOUT=ENABLE_DROPOUT, # activate dropout
|
| 1002 |
+
USE_ALIBI=USE_ALIBI,
|
| 1003 |
+
USE_EXP2=USE_EXP2,
|
| 1004 |
+
IS_FP8=IS_FP8,
|
| 1005 |
+
FP8_MAX=FP8_MAX,
|
| 1006 |
+
DEBUG_TRITON=DEBUG_TRITON,
|
| 1007 |
+
DEBUG_TRITON_DETAIL=DEBUG_TRITON_DETAIL,
|
| 1008 |
+
SLIDING_WINDOW=SLIDING_WINDOW,
|
| 1009 |
+
)
|
| 1010 |
+
# end of GQA/MQA of dkdv
|
| 1011 |
+
# Write back dV
|
| 1012 |
+
adj_dv = bid * stride_dvb + hkid * stride_dvh + k_start * stride_dvn
|
| 1013 |
+
offs_dv = offs_n[:, None] * stride_dvn + offs_d[None, :] * stride_dvd
|
| 1014 |
+
tl.store(DV + adj_dv + offs_dv, dv, mask=mask_kv)
|
| 1015 |
+
# write back dk
|
| 1016 |
+
adj_dk = bid * stride_dkb + hkid * stride_dkh + k_start * stride_dkn
|
| 1017 |
+
offs_dk = offs_n[:, None] * stride_dkn + offs_d[None, :] * stride_dkd
|
| 1018 |
+
dk *= sm_scale
|
| 1019 |
+
tl.store(DK + adj_dk + offs_dk, dk, mask=mask_kv)
|
| 1020 |
+
if HAS_PE:
|
| 1021 |
+
offs_dk_pe = offs_n[:, None] * stride_dkn + offs_d_pe[None, :] * stride_dkd
|
| 1022 |
+
dk_pe *= sm_scale
|
| 1023 |
+
tl.store(DK + adj_dk + offs_dk_pe, dk_pe, mask=mask_kv)
|
| 1024 |
+
|
| 1025 |
+
# This part does dq
|
| 1026 |
+
start_m = pid * BLOCK_M2
|
| 1027 |
+
if start_m < seqlen_q:
|
| 1028 |
+
# seqlen_q > seqlen_k, no need to process these tile for dq
|
| 1029 |
+
if DEBUG_TRITON:
|
| 1030 |
+
print(
|
| 1031 |
+
f"end_n = start_m + BLOCK_M = {start_m} + {BLOCK_M2} = {start_m + BLOCK_M2}"
|
| 1032 |
+
) # noqa: E701
|
| 1033 |
+
if start_m + BLOCK_M2 < delta_qk:
|
| 1034 |
+
if DEBUG_TRITON:
|
| 1035 |
+
print(
|
| 1036 |
+
f"start_m + BLOCK_M2 = {start_m} + {BLOCK_M2} = {start_m + BLOCK_M2} < delta_qk of {delta_qk}"
|
| 1037 |
+
) # noqa: E701
|
| 1038 |
+
return
|
| 1039 |
+
|
| 1040 |
+
offs_m = start_m + tl.arange(0, BLOCK_M2)
|
| 1041 |
+
# Mask for loading K and V
|
| 1042 |
+
mask_q = offs_m[:, None] < seqlen_q
|
| 1043 |
+
if PADDED_HEAD:
|
| 1044 |
+
mask_d = offs_d < ACTUAL_HEAD_DIM
|
| 1045 |
+
mask_q &= mask_d[None, :]
|
| 1046 |
+
offs_q = offs_m[:, None] * stride_qm + offs_d[None, :] * stride_qd
|
| 1047 |
+
if HAS_PE:
|
| 1048 |
+
offs_q_pe = offs_m[:, None] * stride_qm + offs_d_pe[None, :] * stride_qd
|
| 1049 |
+
offs_do = offs_m[:, None] * stride_dom + offs_d[None, :] * stride_dod
|
| 1050 |
+
# NOTE: don't assume that the strides for k and v are the same!
|
| 1051 |
+
K += bid * stride_kb + hkid * stride_kh + k_start * stride_kn
|
| 1052 |
+
V += bid * stride_vb + hkid * stride_vh + k_start * stride_vn
|
| 1053 |
+
|
| 1054 |
+
# If MQA / GQA, set the K and V head offsets appropriately.
|
| 1055 |
+
for hqid in range(hkid * GROUP_SIZE, hkid * GROUP_SIZE + GROUP_SIZE):
|
| 1056 |
+
# seqlen_q < seqlen_k: delta_qk more kv tokens are added at the front
|
| 1057 |
+
# for every M-tile
|
| 1058 |
+
end_n = start_m + BLOCK_M2 - delta_qk
|
| 1059 |
+
# clamp end_n at [0, seqlen_k]
|
| 1060 |
+
end_n = max(min(end_n, seqlen_k), 0)
|
| 1061 |
+
if DEBUG_TRITON:
|
| 1062 |
+
print(f"delta_qk: {delta_qk}; end_n: {end_n}") # noqa: E701
|
| 1063 |
+
# offset input and output tensor by batch and Q/K heads
|
| 1064 |
+
adj_q = bid * stride_qb + hqid * stride_qh + q_start * stride_qm
|
| 1065 |
+
adj_do = bid * stride_dob + hqid * stride_doh + q_start * stride_dom
|
| 1066 |
+
adj_delta = (
|
| 1067 |
+
bid * stride_deltab + hqid * stride_deltah + q_start * stride_deltam
|
| 1068 |
+
)
|
| 1069 |
+
Delta_ptr = Delta + adj_delta
|
| 1070 |
+
|
| 1071 |
+
if USE_ALIBI:
|
| 1072 |
+
alibi_offset = bid * stride_az + hqid * stride_ah
|
| 1073 |
+
alibi_slope = tl.load(Alibi_slopes + alibi_offset)
|
| 1074 |
+
else:
|
| 1075 |
+
alibi_slope = None
|
| 1076 |
+
|
| 1077 |
+
# batch_philox_offset is the ACTUALLY dropout offset
|
| 1078 |
+
# dropout_offset is for debug purpose and will be removed later
|
| 1079 |
+
batch_philox_offset = 0
|
| 1080 |
+
dropout_offset = 0
|
| 1081 |
+
if ENABLE_DROPOUT:
|
| 1082 |
+
batch_philox_offset = (
|
| 1083 |
+
philox_offset_base + bid * stride_dropoutb + hqid * stride_dropouth
|
| 1084 |
+
)
|
| 1085 |
+
dropout_offset = (
|
| 1086 |
+
Dropout_mask + bid * stride_dropoutb + hqid * stride_dropouth
|
| 1087 |
+
)
|
| 1088 |
+
q = tl.load(Q + adj_q + offs_q, mask=mask_q, other=0.0)
|
| 1089 |
+
if HAS_PE:
|
| 1090 |
+
q_pe = tl.load(Q + adj_q + offs_q_pe, mask=mask_q, other=0.0)
|
| 1091 |
+
else:
|
| 1092 |
+
q_pe = None
|
| 1093 |
+
do = tl.load(DO + adj_do + offs_do, mask=mask_q, other=0.0)
|
| 1094 |
+
mask_m = offs_m < seqlen_q
|
| 1095 |
+
m = tl.load(M + adj_delta + offs_m * stride_deltam, mask=mask_m, other=0.0)
|
| 1096 |
+
m = m[:, None]
|
| 1097 |
+
delta = tl.load(Delta_ptr + offs_m * stride_deltam, mask=mask_m, other=0.0)
|
| 1098 |
+
|
| 1099 |
+
MASK_BLOCK_N2: tl.constexpr = BLOCK_N2 // BLK_SLICE_FACTOR
|
| 1100 |
+
# start can only be 0 at minimum
|
| 1101 |
+
start_n = max(end_n - BLOCK_M2, 0)
|
| 1102 |
+
num_steps = tl.cdiv(end_n - start_n, MASK_BLOCK_N2)
|
| 1103 |
+
|
| 1104 |
+
if IS_FP8:
|
| 1105 |
+
descale_q = tl.load(Descale_q + bid * stride_descale_q_z + hqid)
|
| 1106 |
+
descale_k = tl.load(Descale_k + bid * stride_descale_k_z + hkid)
|
| 1107 |
+
descale_v = tl.load(Descale_v + bid * stride_descale_v_z + hkid)
|
| 1108 |
+
descale_do = tl.load(Descale_do + bid * stride_descale_do_z + hqid)
|
| 1109 |
+
else:
|
| 1110 |
+
descale_q, descale_k, descale_v, descale_do = 1.0, 1.0, 1.0, 1.0
|
| 1111 |
+
|
| 1112 |
+
dq = tl.zeros([BLOCK_M2, HEAD_DIM], dtype=tl.float32)
|
| 1113 |
+
if HAS_PE:
|
| 1114 |
+
dq_pe = tl.zeros([BLOCK_M2, PE_HEAD_DIM], dtype=tl.float32)
|
| 1115 |
+
else:
|
| 1116 |
+
dq_pe = dq # Couldn't assign None to dq_pe because _bwd_dq_inner can't return None.
|
| 1117 |
+
dq, dq_pe, delta_recomp_masked = _bwd_dq_inner(
|
| 1118 |
+
dq, # output tensor
|
| 1119 |
+
dq_pe, # optional output tensor
|
| 1120 |
+
q,
|
| 1121 |
+
q_pe,
|
| 1122 |
+
K,
|
| 1123 |
+
V,
|
| 1124 |
+
do,
|
| 1125 |
+
m,
|
| 1126 |
+
delta,
|
| 1127 |
+
sm_scale,
|
| 1128 |
+
stride_qm,
|
| 1129 |
+
stride_qd,
|
| 1130 |
+
stride_kn,
|
| 1131 |
+
stride_kd,
|
| 1132 |
+
stride_vn,
|
| 1133 |
+
stride_vd,
|
| 1134 |
+
stride_dropoutm,
|
| 1135 |
+
stride_dropoutn,
|
| 1136 |
+
seqlen_q,
|
| 1137 |
+
seqlen_k,
|
| 1138 |
+
BLOCK_M2,
|
| 1139 |
+
MASK_BLOCK_N2,
|
| 1140 |
+
HEAD_DIM,
|
| 1141 |
+
ACTUAL_HEAD_DIM,
|
| 1142 |
+
PE_HEAD_DIM,
|
| 1143 |
+
dropout_p,
|
| 1144 |
+
philox_seed,
|
| 1145 |
+
batch_philox_offset,
|
| 1146 |
+
dropout_offset,
|
| 1147 |
+
alibi_slope,
|
| 1148 |
+
start_m,
|
| 1149 |
+
start_n,
|
| 1150 |
+
end_n,
|
| 1151 |
+
num_steps,
|
| 1152 |
+
descale_q,
|
| 1153 |
+
descale_k,
|
| 1154 |
+
descale_v,
|
| 1155 |
+
descale_do,
|
| 1156 |
+
MASK=True, #
|
| 1157 |
+
ENABLE_DROPOUT=ENABLE_DROPOUT,
|
| 1158 |
+
USE_ALIBI=USE_ALIBI,
|
| 1159 |
+
USE_EXP2=USE_EXP2,
|
| 1160 |
+
IS_FP8=IS_FP8,
|
| 1161 |
+
FP8_MAX=FP8_MAX,
|
| 1162 |
+
DEBUG_TRITON=DEBUG_TRITON,
|
| 1163 |
+
DEBUG_TRITON_DETAIL=DEBUG_TRITON_DETAIL,
|
| 1164 |
+
SLIDING_WINDOW=SLIDING_WINDOW,
|
| 1165 |
+
ENABLE_SINK=ENABLE_SINK,
|
| 1166 |
+
)
|
| 1167 |
+
end_n -= num_steps * MASK_BLOCK_N2
|
| 1168 |
+
window_start_n = 0
|
| 1169 |
+
if SLIDING_WINDOW > 0:
|
| 1170 |
+
window_start_n = max(start_m - delta_qk - SLIDING_WINDOW, 0)
|
| 1171 |
+
start_n = window_start_n // BLOCK_N2 * BLOCK_N2
|
| 1172 |
+
num_steps = tl.cdiv(max(end_n - start_n, 0), BLOCK_N2)
|
| 1173 |
+
if DEBUG_TRITON:
|
| 1174 |
+
print(
|
| 1175 |
+
f"unMasked: start_m: {start_m}, start_n: {start_n}, end_n: {end_n}, num_steps: {num_steps}"
|
| 1176 |
+
) # noqa: E701
|
| 1177 |
+
dq, dq_pe, delta_recomp_unmasked = _bwd_dq_inner(
|
| 1178 |
+
dq, # output tensor
|
| 1179 |
+
dq_pe, # optional output tensor
|
| 1180 |
+
q,
|
| 1181 |
+
q_pe,
|
| 1182 |
+
K,
|
| 1183 |
+
V,
|
| 1184 |
+
do,
|
| 1185 |
+
m,
|
| 1186 |
+
delta,
|
| 1187 |
+
sm_scale,
|
| 1188 |
+
stride_qm,
|
| 1189 |
+
stride_qd,
|
| 1190 |
+
stride_kn,
|
| 1191 |
+
stride_kd,
|
| 1192 |
+
stride_vn,
|
| 1193 |
+
stride_vd,
|
| 1194 |
+
stride_dropoutm,
|
| 1195 |
+
stride_dropoutn,
|
| 1196 |
+
seqlen_q,
|
| 1197 |
+
seqlen_k,
|
| 1198 |
+
BLOCK_M2,
|
| 1199 |
+
BLOCK_N2,
|
| 1200 |
+
HEAD_DIM,
|
| 1201 |
+
ACTUAL_HEAD_DIM,
|
| 1202 |
+
PE_HEAD_DIM,
|
| 1203 |
+
dropout_p,
|
| 1204 |
+
philox_seed,
|
| 1205 |
+
batch_philox_offset,
|
| 1206 |
+
dropout_offset,
|
| 1207 |
+
alibi_slope,
|
| 1208 |
+
start_m,
|
| 1209 |
+
start_n,
|
| 1210 |
+
end_n,
|
| 1211 |
+
num_steps,
|
| 1212 |
+
descale_q,
|
| 1213 |
+
descale_k,
|
| 1214 |
+
descale_v,
|
| 1215 |
+
descale_do,
|
| 1216 |
+
MASK=False,
|
| 1217 |
+
ENABLE_DROPOUT=ENABLE_DROPOUT,
|
| 1218 |
+
USE_ALIBI=USE_ALIBI,
|
| 1219 |
+
USE_EXP2=USE_EXP2,
|
| 1220 |
+
IS_FP8=IS_FP8,
|
| 1221 |
+
FP8_MAX=FP8_MAX,
|
| 1222 |
+
DEBUG_TRITON=DEBUG_TRITON,
|
| 1223 |
+
DEBUG_TRITON_DETAIL=DEBUG_TRITON_DETAIL,
|
| 1224 |
+
SLIDING_WINDOW=SLIDING_WINDOW,
|
| 1225 |
+
ENABLE_SINK=ENABLE_SINK,
|
| 1226 |
+
)
|
| 1227 |
+
if ENABLE_SINK:
|
| 1228 |
+
sink = tl.load(Sink + hqid).to(tl.float32)
|
| 1229 |
+
if USE_EXP2:
|
| 1230 |
+
RCP_LN2: tl.constexpr = 1.4426950408889634
|
| 1231 |
+
psink = tl.math.exp2(sink * RCP_LN2 - m * RCP_LN2)
|
| 1232 |
+
else:
|
| 1233 |
+
psink = tl.math.exp(sink - m)
|
| 1234 |
+
delta_recomp = delta_recomp_masked + delta_recomp_unmasked
|
| 1235 |
+
dsink = tl.sum(-psink * delta_recomp[:, None])
|
| 1236 |
+
tl.atomic_add(DSink + hqid, dsink, sem="relaxed")
|
| 1237 |
+
# Write back dQ.
|
| 1238 |
+
adj_dq = bid * stride_dqb + hqid * stride_dqh + q_start * stride_dqm
|
| 1239 |
+
offs_dq = offs_m[:, None] * stride_dqm + offs_d[None, :] * stride_dqd
|
| 1240 |
+
dq *= sm_scale
|
| 1241 |
+
tl.store(DQ + adj_dq + offs_dq, dq, mask=mask_q)
|
| 1242 |
+
if HAS_PE:
|
| 1243 |
+
offs_dq_pe = (
|
| 1244 |
+
offs_m[:, None] * stride_dqm + offs_d_pe[None, :] * stride_dqd
|
| 1245 |
+
)
|
| 1246 |
+
dq_pe *= sm_scale
|
| 1247 |
+
tl.store(DQ + adj_dq + offs_dq_pe, dq_pe, mask=mask_q)
|
| 1248 |
+
# end of GQA/MQA of dq
|
| 1249 |
+
|
| 1250 |
+
|
| 1251 |
+
_bwd_kernel_noncausal_repr = make_kernel_repr(
|
| 1252 |
+
"bwd_kernel_noncausal",
|
| 1253 |
+
[
|
| 1254 |
+
"BLOCK_M1",
|
| 1255 |
+
"BLOCK_N1",
|
| 1256 |
+
"BLOCK_M2",
|
| 1257 |
+
"BLOCK_N2",
|
| 1258 |
+
"BLK_SLICE_FACTOR",
|
| 1259 |
+
"HEAD_DIM",
|
| 1260 |
+
"ENABLE_DROPOUT",
|
| 1261 |
+
"IS_VARLEN",
|
| 1262 |
+
"USE_ALIBI",
|
| 1263 |
+
"USE_EXP2",
|
| 1264 |
+
"IS_FP8",
|
| 1265 |
+
"USE_INT64_STRIDES",
|
| 1266 |
+
"ENABLE_SINK",
|
| 1267 |
+
"SLIDING_WINDOW",
|
| 1268 |
+
],
|
| 1269 |
+
)
|
| 1270 |
+
|
| 1271 |
+
|
| 1272 |
+
@triton.jit(repr=_bwd_kernel_noncausal_repr)
|
| 1273 |
+
def bwd_kernel_noncausal(
|
| 1274 |
+
Q,
|
| 1275 |
+
K,
|
| 1276 |
+
V,
|
| 1277 |
+
Sink,
|
| 1278 |
+
sm_scale,
|
| 1279 |
+
DO,
|
| 1280 |
+
DQ,
|
| 1281 |
+
DK,
|
| 1282 |
+
DV,
|
| 1283 |
+
DSink,
|
| 1284 |
+
M,
|
| 1285 |
+
Delta,
|
| 1286 |
+
stride_qb_in,
|
| 1287 |
+
stride_qh_in,
|
| 1288 |
+
stride_qm_in,
|
| 1289 |
+
stride_qd_in,
|
| 1290 |
+
stride_kb_in,
|
| 1291 |
+
stride_kh_in,
|
| 1292 |
+
stride_kn_in,
|
| 1293 |
+
stride_kd_in,
|
| 1294 |
+
stride_vb_in,
|
| 1295 |
+
stride_vh_in,
|
| 1296 |
+
stride_vn_in,
|
| 1297 |
+
stride_vd_in,
|
| 1298 |
+
stride_dqb_in,
|
| 1299 |
+
stride_dqh_in,
|
| 1300 |
+
stride_dqm_in,
|
| 1301 |
+
stride_dqd_in,
|
| 1302 |
+
stride_dkb_in,
|
| 1303 |
+
stride_dkh_in,
|
| 1304 |
+
stride_dkn_in,
|
| 1305 |
+
stride_dkd_in,
|
| 1306 |
+
stride_dvb_in,
|
| 1307 |
+
stride_dvh_in,
|
| 1308 |
+
stride_dvn_in,
|
| 1309 |
+
stride_dvd_in,
|
| 1310 |
+
stride_deltab_in,
|
| 1311 |
+
stride_deltah_in,
|
| 1312 |
+
stride_deltam_in,
|
| 1313 |
+
stride_dob_in,
|
| 1314 |
+
stride_doh_in,
|
| 1315 |
+
stride_dom_in,
|
| 1316 |
+
stride_dod_in,
|
| 1317 |
+
stride_dropoutb_in,
|
| 1318 |
+
stride_dropouth_in,
|
| 1319 |
+
stride_dropoutm_in,
|
| 1320 |
+
stride_dropoutn_in,
|
| 1321 |
+
stride_descale_q_z_in,
|
| 1322 |
+
stride_descale_k_z_in,
|
| 1323 |
+
stride_descale_v_z_in,
|
| 1324 |
+
stride_descale_do_z_in,
|
| 1325 |
+
stride_az_in,
|
| 1326 |
+
stride_ah_in,
|
| 1327 |
+
HQ,
|
| 1328 |
+
HK,
|
| 1329 |
+
cu_seqlens_q,
|
| 1330 |
+
cu_seqlens_k,
|
| 1331 |
+
max_seqlen_q,
|
| 1332 |
+
max_seqlen_k,
|
| 1333 |
+
Dropout_mask,
|
| 1334 |
+
dropout_p,
|
| 1335 |
+
philox_seed,
|
| 1336 |
+
philox_offset_base_in,
|
| 1337 |
+
Alibi_slopes,
|
| 1338 |
+
Descale_q,
|
| 1339 |
+
Descale_k,
|
| 1340 |
+
Descale_v,
|
| 1341 |
+
Descale_do,
|
| 1342 |
+
BLOCK_M1: tl.constexpr, # 32
|
| 1343 |
+
BLOCK_N1: tl.constexpr, # 128
|
| 1344 |
+
BLOCK_M2: tl.constexpr, # 128
|
| 1345 |
+
BLOCK_N2: tl.constexpr, # 32
|
| 1346 |
+
BLK_SLICE_FACTOR: tl.constexpr,
|
| 1347 |
+
HEAD_DIM: tl.constexpr,
|
| 1348 |
+
ACTUAL_HEAD_DIM: tl.constexpr,
|
| 1349 |
+
PE_HEAD_DIM: tl.constexpr,
|
| 1350 |
+
ENABLE_DROPOUT: tl.constexpr,
|
| 1351 |
+
IS_VARLEN: tl.constexpr,
|
| 1352 |
+
USE_ALIBI: tl.constexpr,
|
| 1353 |
+
USE_EXP2: tl.constexpr,
|
| 1354 |
+
IS_FP8: tl.constexpr,
|
| 1355 |
+
FP8_MAX: tl.constexpr,
|
| 1356 |
+
DEBUG_TRITON: tl.constexpr,
|
| 1357 |
+
DEBUG_TRITON_DETAIL: tl.constexpr,
|
| 1358 |
+
USE_INT64_STRIDES: tl.constexpr,
|
| 1359 |
+
ENABLE_SINK: tl.constexpr,
|
| 1360 |
+
SLIDING_WINDOW: tl.constexpr,
|
| 1361 |
+
):
|
| 1362 |
+
if USE_INT64_STRIDES:
|
| 1363 |
+
stride_qb = tl.cast(stride_qb_in, tl.int64)
|
| 1364 |
+
stride_qh = tl.cast(stride_qh_in, tl.int64)
|
| 1365 |
+
stride_qm = tl.cast(stride_qm_in, tl.int64)
|
| 1366 |
+
stride_qd = tl.cast(stride_qd_in, tl.int64)
|
| 1367 |
+
stride_kb = tl.cast(stride_kb_in, tl.int64)
|
| 1368 |
+
stride_kh = tl.cast(stride_kh_in, tl.int64)
|
| 1369 |
+
stride_kn = tl.cast(stride_kn_in, tl.int64)
|
| 1370 |
+
stride_kd = tl.cast(stride_kd_in, tl.int64)
|
| 1371 |
+
stride_vb = tl.cast(stride_vb_in, tl.int64)
|
| 1372 |
+
stride_vh = tl.cast(stride_vh_in, tl.int64)
|
| 1373 |
+
stride_vn = tl.cast(stride_vn_in, tl.int64)
|
| 1374 |
+
stride_vd = tl.cast(stride_vd_in, tl.int64)
|
| 1375 |
+
stride_dqb = tl.cast(stride_dqb_in, tl.int64)
|
| 1376 |
+
stride_dqh = tl.cast(stride_dqh_in, tl.int64)
|
| 1377 |
+
stride_dqm = tl.cast(stride_dqm_in, tl.int64)
|
| 1378 |
+
stride_dqd = tl.cast(stride_dqd_in, tl.int64)
|
| 1379 |
+
stride_dkb = tl.cast(stride_dkb_in, tl.int64)
|
| 1380 |
+
stride_dkh = tl.cast(stride_dkh_in, tl.int64)
|
| 1381 |
+
stride_dkn = tl.cast(stride_dkn_in, tl.int64)
|
| 1382 |
+
stride_dkd = tl.cast(stride_dkd_in, tl.int64)
|
| 1383 |
+
stride_dvb = tl.cast(stride_dvb_in, tl.int64)
|
| 1384 |
+
stride_dvh = tl.cast(stride_dvh_in, tl.int64)
|
| 1385 |
+
stride_dvn = tl.cast(stride_dvn_in, tl.int64)
|
| 1386 |
+
stride_dvd = tl.cast(stride_dvd_in, tl.int64)
|
| 1387 |
+
stride_deltab = tl.cast(stride_deltab_in, tl.int64)
|
| 1388 |
+
stride_deltah = tl.cast(stride_deltah_in, tl.int64)
|
| 1389 |
+
stride_deltam = tl.cast(stride_deltam_in, tl.int64)
|
| 1390 |
+
stride_dob = tl.cast(stride_dob_in, tl.int64)
|
| 1391 |
+
stride_doh = tl.cast(stride_doh_in, tl.int64)
|
| 1392 |
+
stride_dom = tl.cast(stride_dom_in, tl.int64)
|
| 1393 |
+
stride_dod = tl.cast(stride_dod_in, tl.int64)
|
| 1394 |
+
philox_offset_base = tl.cast(philox_offset_base_in, tl.int64)
|
| 1395 |
+
stride_dropoutb = tl.cast(stride_dropoutb_in, tl.int64)
|
| 1396 |
+
stride_dropouth = tl.cast(stride_dropouth_in, tl.int64)
|
| 1397 |
+
stride_dropoutm = tl.cast(stride_dropoutm_in, tl.int64)
|
| 1398 |
+
stride_dropoutn = tl.cast(stride_dropoutn_in, tl.int64)
|
| 1399 |
+
if IS_FP8:
|
| 1400 |
+
stride_descale_q_z = tl.cast(stride_descale_q_z_in, tl.int64)
|
| 1401 |
+
stride_descale_k_z = tl.cast(stride_descale_k_z_in, tl.int64)
|
| 1402 |
+
stride_descale_v_z = tl.cast(stride_descale_v_z_in, tl.int64)
|
| 1403 |
+
stride_descale_do_z = tl.cast(stride_descale_do_z_in, tl.int64)
|
| 1404 |
+
stride_az = tl.cast(stride_az_in, tl.int64)
|
| 1405 |
+
stride_ah = tl.cast(stride_ah_in, tl.int64)
|
| 1406 |
+
else:
|
| 1407 |
+
stride_qb = stride_qb_in
|
| 1408 |
+
stride_qh = stride_qh_in
|
| 1409 |
+
stride_qm = stride_qm_in
|
| 1410 |
+
stride_qd = stride_qd_in
|
| 1411 |
+
stride_kb = stride_kb_in
|
| 1412 |
+
stride_kh = stride_kh_in
|
| 1413 |
+
stride_kn = stride_kn_in
|
| 1414 |
+
stride_kd = stride_kd_in
|
| 1415 |
+
stride_vb = stride_vb_in
|
| 1416 |
+
stride_vh = stride_vh_in
|
| 1417 |
+
stride_vn = stride_vn_in
|
| 1418 |
+
stride_vd = stride_vd_in
|
| 1419 |
+
stride_dqb = stride_dqb_in
|
| 1420 |
+
stride_dqh = stride_dqh_in
|
| 1421 |
+
stride_dqm = stride_dqm_in
|
| 1422 |
+
stride_dqd = stride_dqd_in
|
| 1423 |
+
stride_dkb = stride_dkb_in
|
| 1424 |
+
stride_dkh = stride_dkh_in
|
| 1425 |
+
stride_dkn = stride_dkn_in
|
| 1426 |
+
stride_dkd = stride_dkd_in
|
| 1427 |
+
stride_dvb = stride_dvb_in
|
| 1428 |
+
stride_dvh = stride_dvh_in
|
| 1429 |
+
stride_dvn = stride_dvn_in
|
| 1430 |
+
stride_dvd = stride_dvd_in
|
| 1431 |
+
stride_deltab = stride_deltab_in
|
| 1432 |
+
stride_deltah = stride_deltah_in
|
| 1433 |
+
stride_deltam = stride_deltam_in
|
| 1434 |
+
stride_dob = stride_dob_in
|
| 1435 |
+
stride_doh = stride_doh_in
|
| 1436 |
+
stride_dom = stride_dom_in
|
| 1437 |
+
stride_dod = stride_dod_in
|
| 1438 |
+
philox_offset_base = philox_offset_base_in
|
| 1439 |
+
stride_dropoutb = stride_dropoutb_in
|
| 1440 |
+
stride_dropouth = stride_dropouth_in
|
| 1441 |
+
stride_dropoutm = stride_dropoutm_in
|
| 1442 |
+
stride_dropoutn = stride_dropoutn_in
|
| 1443 |
+
stride_descale_q_z = stride_descale_q_z_in
|
| 1444 |
+
stride_descale_k_z = stride_descale_k_z_in
|
| 1445 |
+
stride_descale_v_z = stride_descale_v_z_in
|
| 1446 |
+
stride_descale_do_z = stride_descale_do_z_in
|
| 1447 |
+
stride_az = stride_az_in
|
| 1448 |
+
stride_ah = stride_ah_in
|
| 1449 |
+
|
| 1450 |
+
# program ids
|
| 1451 |
+
hkid = tl.program_id(0)
|
| 1452 |
+
pid = tl.program_id(1)
|
| 1453 |
+
bid = tl.program_id(2)
|
| 1454 |
+
if DEBUG_TRITON:
|
| 1455 |
+
print(f"\npid: {pid}, bid: {bid}, hkid: {hkid}") # noqa: E701
|
| 1456 |
+
# figure out varlen start and end
|
| 1457 |
+
q_start = 0
|
| 1458 |
+
k_start = 0
|
| 1459 |
+
seqlen_q = max_seqlen_q
|
| 1460 |
+
seqlen_k = max_seqlen_k
|
| 1461 |
+
if IS_VARLEN:
|
| 1462 |
+
# Compute actual sequence lengths
|
| 1463 |
+
q_start = tl.load(cu_seqlens_q + bid)
|
| 1464 |
+
q_end = tl.load(cu_seqlens_q + bid + 1)
|
| 1465 |
+
k_start = tl.load(cu_seqlens_k + bid)
|
| 1466 |
+
k_end = tl.load(cu_seqlens_k + bid + 1)
|
| 1467 |
+
seqlen_q = q_end - q_start
|
| 1468 |
+
seqlen_k = k_end - k_start
|
| 1469 |
+
|
| 1470 |
+
PADDED_HEAD: tl.constexpr = ACTUAL_HEAD_DIM != HEAD_DIM
|
| 1471 |
+
HAS_PE: tl.constexpr = PE_HEAD_DIM > 0
|
| 1472 |
+
offs_d = tl.arange(0, HEAD_DIM)
|
| 1473 |
+
if HAS_PE:
|
| 1474 |
+
offs_d_pe = HEAD_DIM + tl.arange(0, PE_HEAD_DIM)
|
| 1475 |
+
GROUP_SIZE: tl.constexpr = HQ // HK
|
| 1476 |
+
|
| 1477 |
+
start_n = pid * BLOCK_N1
|
| 1478 |
+
if start_n < seqlen_k:
|
| 1479 |
+
dk = tl.zeros([BLOCK_N1, HEAD_DIM], dtype=tl.float32)
|
| 1480 |
+
if HAS_PE:
|
| 1481 |
+
dk_pe = tl.zeros([BLOCK_N1, PE_HEAD_DIM], dtype=tl.float32)
|
| 1482 |
+
else:
|
| 1483 |
+
# Couldn't assign None to dk_pe because _bwd_dkdv_inner can't return None.
|
| 1484 |
+
dk_pe = dk
|
| 1485 |
+
dv = tl.zeros([BLOCK_N1, HEAD_DIM], dtype=tl.float32)
|
| 1486 |
+
|
| 1487 |
+
offs_n = start_n + tl.arange(0, BLOCK_N1)
|
| 1488 |
+
# Mask for loading K and V
|
| 1489 |
+
mask_kv = offs_n[:, None] < seqlen_k
|
| 1490 |
+
if PADDED_HEAD:
|
| 1491 |
+
mask_d = offs_d < ACTUAL_HEAD_DIM
|
| 1492 |
+
mask_kv &= mask_d[None, :]
|
| 1493 |
+
# NOTE: don't assume that the strides for k and v are the same!
|
| 1494 |
+
# K/V tensors not changed for the group
|
| 1495 |
+
adj_k = (
|
| 1496 |
+
bid * stride_kb
|
| 1497 |
+
+ hkid * stride_kh
|
| 1498 |
+
+ k_start * stride_kn
|
| 1499 |
+
+ offs_n[:, None] * stride_kn
|
| 1500 |
+
+ offs_d[None, :] * stride_kd
|
| 1501 |
+
)
|
| 1502 |
+
if HAS_PE:
|
| 1503 |
+
adj_k_pe = (
|
| 1504 |
+
bid * stride_kb
|
| 1505 |
+
+ hkid * stride_kh
|
| 1506 |
+
+ k_start * stride_kn
|
| 1507 |
+
+ offs_n[:, None] * stride_kn
|
| 1508 |
+
+ offs_d_pe[None, :] * stride_kd
|
| 1509 |
+
)
|
| 1510 |
+
adj_v = (
|
| 1511 |
+
bid * stride_vb
|
| 1512 |
+
+ hkid * stride_vh
|
| 1513 |
+
+ k_start * stride_vn
|
| 1514 |
+
+ offs_n[:, None] * stride_vn
|
| 1515 |
+
+ offs_d[None, :] * stride_vd
|
| 1516 |
+
)
|
| 1517 |
+
# load K and V: they stay in SRAM throughout the inner loop.
|
| 1518 |
+
k = tl.load(K + adj_k, mask=mask_kv, other=0.0)
|
| 1519 |
+
if HAS_PE:
|
| 1520 |
+
k_pe = tl.load(K + adj_k_pe, mask=mask_kv, other=0.0)
|
| 1521 |
+
else:
|
| 1522 |
+
k_pe = None
|
| 1523 |
+
v = tl.load(V + adj_v, mask=mask_kv, other=0.0)
|
| 1524 |
+
# If MQA / GQA, set the K and V head offsets appropriately.
|
| 1525 |
+
for hqid in range(hkid * GROUP_SIZE, hkid * GROUP_SIZE + GROUP_SIZE):
|
| 1526 |
+
# offset input and output tensor by batch and Q/K heads
|
| 1527 |
+
adj_q = bid * stride_qb + hqid * stride_qh + q_start * stride_qm
|
| 1528 |
+
Q_ptr = Q + adj_q
|
| 1529 |
+
adj_do = bid * stride_dob + hqid * stride_doh + q_start * stride_dom
|
| 1530 |
+
DO_ptr = DO + adj_do
|
| 1531 |
+
adj_delta = (
|
| 1532 |
+
bid * stride_deltab + hqid * stride_deltah + q_start * stride_deltam
|
| 1533 |
+
)
|
| 1534 |
+
M_ptr = M + adj_delta
|
| 1535 |
+
Delta_ptr = Delta + adj_delta
|
| 1536 |
+
|
| 1537 |
+
if USE_ALIBI:
|
| 1538 |
+
alibi_offset = bid * stride_az + hqid * stride_ah
|
| 1539 |
+
alibi_slope = tl.load(Alibi_slopes + alibi_offset)
|
| 1540 |
+
else:
|
| 1541 |
+
alibi_slope = None
|
| 1542 |
+
|
| 1543 |
+
# batch_philox_offset is the ACTUALLY dropout offset
|
| 1544 |
+
# dropout_offset is for debug purpose and will be removed later
|
| 1545 |
+
batch_philox_offset = 0
|
| 1546 |
+
dropout_offset = 0
|
| 1547 |
+
if ENABLE_DROPOUT:
|
| 1548 |
+
batch_philox_offset = (
|
| 1549 |
+
philox_offset_base + bid * stride_dropoutb + hqid * stride_dropouth
|
| 1550 |
+
)
|
| 1551 |
+
dropout_offset = (
|
| 1552 |
+
Dropout_mask + bid * stride_dropoutb + hqid * stride_dropouth
|
| 1553 |
+
)
|
| 1554 |
+
|
| 1555 |
+
if IS_FP8:
|
| 1556 |
+
descale_q = tl.load(Descale_q + bid * stride_descale_q_z + hqid)
|
| 1557 |
+
descale_k = tl.load(Descale_k + bid * stride_descale_k_z + hkid)
|
| 1558 |
+
descale_v = tl.load(Descale_v + bid * stride_descale_v_z + hkid)
|
| 1559 |
+
descale_do = tl.load(Descale_do + bid * stride_descale_do_z + hqid)
|
| 1560 |
+
else:
|
| 1561 |
+
descale_q, descale_k, descale_v, descale_do = 1.0, 1.0, 1.0, 1.0
|
| 1562 |
+
|
| 1563 |
+
# because there is no causal, we always start from the beginning
|
| 1564 |
+
start_m = 0
|
| 1565 |
+
num_steps = tl.cdiv(seqlen_q, BLOCK_M1)
|
| 1566 |
+
dk, dk_pe, dv = _bwd_dkdv_inner(
|
| 1567 |
+
dk, # output tensor
|
| 1568 |
+
dk_pe, # optional output tensor
|
| 1569 |
+
dv, # output tensor
|
| 1570 |
+
Q_ptr,
|
| 1571 |
+
k,
|
| 1572 |
+
k_pe,
|
| 1573 |
+
v,
|
| 1574 |
+
DO_ptr,
|
| 1575 |
+
M_ptr,
|
| 1576 |
+
Delta_ptr,
|
| 1577 |
+
sm_scale, # input tensors
|
| 1578 |
+
stride_qm,
|
| 1579 |
+
stride_qd, # strides for q
|
| 1580 |
+
stride_dom,
|
| 1581 |
+
stride_dod, # strides for o
|
| 1582 |
+
stride_dropoutm,
|
| 1583 |
+
stride_dropoutn, # strides for dropout
|
| 1584 |
+
stride_deltam,
|
| 1585 |
+
BLOCK_M1,
|
| 1586 |
+
BLOCK_N1, # block dim
|
| 1587 |
+
HEAD_DIM,
|
| 1588 |
+
ACTUAL_HEAD_DIM, # head dim
|
| 1589 |
+
PE_HEAD_DIM,
|
| 1590 |
+
dropout_p,
|
| 1591 |
+
philox_seed,
|
| 1592 |
+
batch_philox_offset,
|
| 1593 |
+
dropout_offset, #
|
| 1594 |
+
alibi_slope,
|
| 1595 |
+
seqlen_q,
|
| 1596 |
+
seqlen_k, # max sequence length for q and k
|
| 1597 |
+
start_n,
|
| 1598 |
+
start_m,
|
| 1599 |
+
num_steps, # iteration numbers
|
| 1600 |
+
descale_q,
|
| 1601 |
+
descale_k,
|
| 1602 |
+
descale_v,
|
| 1603 |
+
descale_do, # fp8 descale factors from user
|
| 1604 |
+
MASK=False, # causal masking
|
| 1605 |
+
ENABLE_DROPOUT=ENABLE_DROPOUT, # activate dropout
|
| 1606 |
+
USE_ALIBI=USE_ALIBI,
|
| 1607 |
+
USE_EXP2=USE_EXP2,
|
| 1608 |
+
IS_FP8=IS_FP8,
|
| 1609 |
+
FP8_MAX=FP8_MAX,
|
| 1610 |
+
DEBUG_TRITON=DEBUG_TRITON,
|
| 1611 |
+
DEBUG_TRITON_DETAIL=DEBUG_TRITON_DETAIL,
|
| 1612 |
+
SLIDING_WINDOW=SLIDING_WINDOW,
|
| 1613 |
+
)
|
| 1614 |
+
|
| 1615 |
+
# Write back dV
|
| 1616 |
+
adj_dv = bid * stride_dvb + hkid * stride_dvh + k_start * stride_dvn
|
| 1617 |
+
offs_dv = offs_n[:, None] * stride_dvn + offs_d[None, :] * stride_dvd
|
| 1618 |
+
tl.store(DV + adj_dv + offs_dv, dv, mask=mask_kv)
|
| 1619 |
+
# write back dk
|
| 1620 |
+
adj_dk = bid * stride_dkb + hkid * stride_dkh + k_start * stride_dkn
|
| 1621 |
+
offs_dk = offs_n[:, None] * stride_dkn + offs_d[None, :] * stride_dkd
|
| 1622 |
+
dk *= sm_scale
|
| 1623 |
+
tl.store(DK + adj_dk + offs_dk, dk, mask=mask_kv)
|
| 1624 |
+
if HAS_PE:
|
| 1625 |
+
offs_dk_pe = offs_n[:, None] * stride_dkn + offs_d_pe[None, :] * stride_dkd
|
| 1626 |
+
dk_pe *= sm_scale
|
| 1627 |
+
tl.store(DK + adj_dk + offs_dk_pe, dk_pe, mask=mask_kv)
|
| 1628 |
+
|
| 1629 |
+
# THIS PART DOES DQ
|
| 1630 |
+
start_m = pid * BLOCK_M2
|
| 1631 |
+
if start_m < seqlen_q:
|
| 1632 |
+
offs_m = start_m + tl.arange(0, BLOCK_M2)
|
| 1633 |
+
# Mask for loading K and V
|
| 1634 |
+
mask_q = offs_m[:, None] < seqlen_q
|
| 1635 |
+
if PADDED_HEAD:
|
| 1636 |
+
mask_d = offs_d < ACTUAL_HEAD_DIM
|
| 1637 |
+
mask_q &= mask_d[None, :]
|
| 1638 |
+
offs_q = offs_m[:, None] * stride_qm + offs_d[None, :] * stride_qd
|
| 1639 |
+
if HAS_PE:
|
| 1640 |
+
offs_q_pe = offs_m[:, None] * stride_qm + offs_d_pe[None, :] * stride_qd
|
| 1641 |
+
offs_do = offs_m[:, None] * stride_dom + offs_d[None, :] * stride_dod
|
| 1642 |
+
K += bid * stride_kb + hkid * stride_kh + k_start * stride_kn
|
| 1643 |
+
V += bid * stride_vb + hkid * stride_vh + k_start * stride_vn
|
| 1644 |
+
# If MQA / GQA, set the K and V head offsets appropriately.
|
| 1645 |
+
for hqid in range(hkid * GROUP_SIZE, hkid * GROUP_SIZE + GROUP_SIZE):
|
| 1646 |
+
# offset input and output tensor by batch and Q/K heads
|
| 1647 |
+
adj_q = bid * stride_qb + hqid * stride_qh + q_start * stride_qm
|
| 1648 |
+
adj_do = bid * stride_dob + hqid * stride_doh + q_start * stride_dom
|
| 1649 |
+
adj_delta = (
|
| 1650 |
+
bid * stride_deltab + hqid * stride_deltah + q_start * stride_deltam
|
| 1651 |
+
)
|
| 1652 |
+
Delta_ptr = Delta + adj_delta
|
| 1653 |
+
|
| 1654 |
+
if USE_ALIBI:
|
| 1655 |
+
alibi_offset = bid * stride_az + hqid * stride_ah
|
| 1656 |
+
alibi_slope = tl.load(Alibi_slopes + alibi_offset)
|
| 1657 |
+
else:
|
| 1658 |
+
alibi_slope = None
|
| 1659 |
+
|
| 1660 |
+
# batch_philox_offset is the ACTUALLY dropout offset
|
| 1661 |
+
# dropout_offset is for debug purpose and will be removed later
|
| 1662 |
+
batch_philox_offset = 0
|
| 1663 |
+
dropout_offset = 0
|
| 1664 |
+
if ENABLE_DROPOUT:
|
| 1665 |
+
batch_philox_offset = (
|
| 1666 |
+
philox_offset_base + bid * stride_dropoutb + hqid * stride_dropouth
|
| 1667 |
+
)
|
| 1668 |
+
dropout_offset = (
|
| 1669 |
+
Dropout_mask + bid * stride_dropoutb + hqid * stride_dropouth
|
| 1670 |
+
)
|
| 1671 |
+
|
| 1672 |
+
q = tl.load(Q + adj_q + offs_q, mask=mask_q, other=0.0)
|
| 1673 |
+
if HAS_PE:
|
| 1674 |
+
q_pe = tl.load(Q + adj_q + offs_q_pe, mask=mask_q, other=0.0)
|
| 1675 |
+
else:
|
| 1676 |
+
q_pe = None
|
| 1677 |
+
do = tl.load(DO + adj_do + offs_do, mask=mask_q, other=0.0)
|
| 1678 |
+
mask_m = offs_m < seqlen_q
|
| 1679 |
+
m = tl.load(M + adj_delta + offs_m * stride_deltam, mask=mask_m, other=0.0)
|
| 1680 |
+
m = m[:, None]
|
| 1681 |
+
delta = tl.load(Delta_ptr + offs_m * stride_deltam, mask=mask_m, other=0.0)
|
| 1682 |
+
|
| 1683 |
+
if IS_FP8:
|
| 1684 |
+
descale_q = tl.load(Descale_q + bid * stride_descale_q_z + hqid)
|
| 1685 |
+
descale_k = tl.load(Descale_k + bid * stride_descale_k_z + hkid)
|
| 1686 |
+
descale_v = tl.load(Descale_v + bid * stride_descale_v_z + hkid)
|
| 1687 |
+
descale_do = tl.load(Descale_do + bid * stride_descale_do_z + hqid)
|
| 1688 |
+
else:
|
| 1689 |
+
descale_q, descale_k, descale_v, descale_do = 1.0, 1.0, 1.0, 1.0
|
| 1690 |
+
|
| 1691 |
+
# start can only be 0 at minimum
|
| 1692 |
+
start_n = 0
|
| 1693 |
+
end_n = seqlen_k
|
| 1694 |
+
num_steps = tl.cdiv(seqlen_k, BLOCK_N2)
|
| 1695 |
+
|
| 1696 |
+
dq = tl.zeros([BLOCK_M2, HEAD_DIM], dtype=tl.float32)
|
| 1697 |
+
if HAS_PE:
|
| 1698 |
+
dq_pe = tl.zeros([BLOCK_M2, PE_HEAD_DIM], dtype=tl.float32)
|
| 1699 |
+
else:
|
| 1700 |
+
dq_pe = dq # Couldn't assign None to dq_pe because _bwd_dq_inner can't return None.
|
| 1701 |
+
dq, dq_pe, delta_recomp = _bwd_dq_inner(
|
| 1702 |
+
dq, # output tensor
|
| 1703 |
+
dq_pe, # optional output tensor
|
| 1704 |
+
q,
|
| 1705 |
+
q_pe,
|
| 1706 |
+
K,
|
| 1707 |
+
V,
|
| 1708 |
+
do,
|
| 1709 |
+
m,
|
| 1710 |
+
delta,
|
| 1711 |
+
sm_scale,
|
| 1712 |
+
stride_qm,
|
| 1713 |
+
stride_qd,
|
| 1714 |
+
stride_kn,
|
| 1715 |
+
stride_kd,
|
| 1716 |
+
stride_vn,
|
| 1717 |
+
stride_vd,
|
| 1718 |
+
stride_dropoutm,
|
| 1719 |
+
stride_dropoutn,
|
| 1720 |
+
seqlen_q,
|
| 1721 |
+
seqlen_k,
|
| 1722 |
+
BLOCK_M2,
|
| 1723 |
+
BLOCK_N2,
|
| 1724 |
+
HEAD_DIM,
|
| 1725 |
+
ACTUAL_HEAD_DIM,
|
| 1726 |
+
PE_HEAD_DIM,
|
| 1727 |
+
dropout_p,
|
| 1728 |
+
philox_seed,
|
| 1729 |
+
batch_philox_offset,
|
| 1730 |
+
dropout_offset,
|
| 1731 |
+
alibi_slope,
|
| 1732 |
+
start_m,
|
| 1733 |
+
start_n,
|
| 1734 |
+
end_n,
|
| 1735 |
+
num_steps,
|
| 1736 |
+
descale_q,
|
| 1737 |
+
descale_k,
|
| 1738 |
+
descale_v,
|
| 1739 |
+
descale_do,
|
| 1740 |
+
MASK=False,
|
| 1741 |
+
ENABLE_DROPOUT=ENABLE_DROPOUT,
|
| 1742 |
+
USE_ALIBI=USE_ALIBI,
|
| 1743 |
+
USE_EXP2=USE_EXP2,
|
| 1744 |
+
IS_FP8=IS_FP8,
|
| 1745 |
+
FP8_MAX=FP8_MAX,
|
| 1746 |
+
DEBUG_TRITON=DEBUG_TRITON,
|
| 1747 |
+
DEBUG_TRITON_DETAIL=DEBUG_TRITON_DETAIL,
|
| 1748 |
+
SLIDING_WINDOW=SLIDING_WINDOW,
|
| 1749 |
+
ENABLE_SINK=ENABLE_SINK,
|
| 1750 |
+
)
|
| 1751 |
+
if ENABLE_SINK:
|
| 1752 |
+
sink = tl.load(Sink + hqid).to(tl.float32)
|
| 1753 |
+
if USE_EXP2:
|
| 1754 |
+
RCP_LN2: tl.constexpr = 1.4426950408889634
|
| 1755 |
+
psink = tl.math.exp2(sink * RCP_LN2 - m * RCP_LN2)
|
| 1756 |
+
else:
|
| 1757 |
+
psink = tl.math.exp(sink - m)
|
| 1758 |
+
dsink = tl.sum(-psink * delta_recomp[:, None])
|
| 1759 |
+
tl.atomic_add(DSink + hqid, dsink, sem="relaxed")
|
| 1760 |
+
# Write back dQ.
|
| 1761 |
+
adj_dq = bid * stride_dqb + hqid * stride_dqh + q_start * stride_dqm
|
| 1762 |
+
offs_dq = offs_m[:, None] * stride_dqm + offs_d[None, :] * stride_dqd
|
| 1763 |
+
dq *= sm_scale
|
| 1764 |
+
tl.store(DQ + adj_dq + offs_dq, dq, mask=mask_q)
|
| 1765 |
+
if HAS_PE:
|
| 1766 |
+
offs_dq_pe = (
|
| 1767 |
+
offs_m[:, None] * stride_dqm + offs_d_pe[None, :] * stride_dqd
|
| 1768 |
+
)
|
| 1769 |
+
dq_pe *= sm_scale
|
| 1770 |
+
tl.store(DQ + adj_dq + offs_dq_pe, dq_pe, mask=mask_q)
|
| 1771 |
+
|
| 1772 |
+
|
| 1773 |
+
@functools.lru_cache(maxsize=1024)
|
| 1774 |
+
def _get_config():
|
| 1775 |
+
if not hasattr(_get_config, "_config_dict"):
|
| 1776 |
+
dev = arch_info.get_arch()
|
| 1777 |
+
_get_config._config_dict = {}
|
| 1778 |
+
fpath = f"{AITER_TRITON_CONFIGS_PATH}/{dev}-MHA-DEFAULT.json"
|
| 1779 |
+
with open(fpath, "r") as file:
|
| 1780 |
+
config = json.load(file)
|
| 1781 |
+
_get_config._config_dict = config
|
| 1782 |
+
|
| 1783 |
+
return _get_config._config_dict["bkwd_onekernel"]
|
build/torch-rocm/_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 = "aiter_flash_attn"
|
| 25 |
+
unique_id = "27f5cac"
|
| 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-rocm/aiter_flash_attn/__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-rocm/metadata.json
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"name": "aiter-flash-attn",
|
| 3 |
+
"id": "_aiter_flash_attn_rocm_27f5cac",
|
| 4 |
+
"version": 1,
|
| 5 |
+
"license": "MIT",
|
| 6 |
+
"python-depends": [],
|
| 7 |
+
"backend": {
|
| 8 |
+
"type": "rocm"
|
| 9 |
+
}
|
| 10 |
+
}
|
build/torch-rocm/mha.py
ADDED
|
@@ -0,0 +1,1000 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# SPDX-License-Identifier: MIT
|
| 2 |
+
# Copyright (C) 2024-2026, Advanced Micro Devices, Inc. All rights reserved.
|
| 3 |
+
|
| 4 |
+
from typing import Literal, Optional, Tuple, Union
|
| 5 |
+
import torch
|
| 6 |
+
import triton
|
| 7 |
+
import triton.language as tl
|
| 8 |
+
|
| 9 |
+
from .utils import types
|
| 10 |
+
from .mha_onekernel_bwd import flash_attn_onekernel_backward
|
| 11 |
+
from .mha_fused_bwd import flash_attn_fused_backward
|
| 12 |
+
from .utils.logger import AiterTritonLogger
|
| 13 |
+
from .utils.device_info import get_num_xcds
|
| 14 |
+
from ._kernels.mha import _attn_fwd, _get_config
|
| 15 |
+
from ._kernels.flash_attn_triton_amd import flash_attn_2
|
| 16 |
+
|
| 17 |
+
_LOGGER = AiterTritonLogger()
|
| 18 |
+
|
| 19 |
+
global _USE_FUSED_BWD_KERNEL
|
| 20 |
+
_USE_FUSED_BWD_KERNEL = False
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
def mha_set_use_fused_bwd_kernel(value: bool):
|
| 24 |
+
"""
|
| 25 |
+
Set whether to use fused backward kernel (with atomics) or one-kernel backward (without atomics).
|
| 26 |
+
Fused backward is faster but doesn't support positional encoding.
|
| 27 |
+
"""
|
| 28 |
+
global _USE_FUSED_BWD_KERNEL
|
| 29 |
+
_USE_FUSED_BWD_KERNEL = value
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
_MHA_IMPL: Literal["default", "dao_ai"] = "default"
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
def mha_set_impl(impl: Literal["default", "dao_ai"]):
|
| 36 |
+
"""Set MHA forward implementation: 'default' (_attn_fwd) or 'dao_ai' (flash_attn_triton_amd)."""
|
| 37 |
+
global _MHA_IMPL
|
| 38 |
+
_MHA_IMPL = impl
|
| 39 |
+
|
| 40 |
+
|
| 41 |
+
_USE_INT64_STRIDES = True
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
def mha_set_use_int64_strides(value: bool):
|
| 45 |
+
"""Use 64-bit integer strides to prevent integer overflows with very large tensors."""
|
| 46 |
+
global _USE_INT64_STRIDES
|
| 47 |
+
_USE_INT64_STRIDES = value
|
| 48 |
+
|
| 49 |
+
|
| 50 |
+
def _get_sliding_window_size(window_size: Tuple[int, int]) -> int:
|
| 51 |
+
return int(window_size[0]) if int(window_size[0]) >= 0 else 0
|
| 52 |
+
|
| 53 |
+
|
| 54 |
+
def _flash_attn_forward(
|
| 55 |
+
q: torch.Tensor,
|
| 56 |
+
k: torch.Tensor,
|
| 57 |
+
v: torch.Tensor,
|
| 58 |
+
dropout_p: float,
|
| 59 |
+
softmax_scale: float,
|
| 60 |
+
causal: bool,
|
| 61 |
+
window_size_left: int,
|
| 62 |
+
window_size_right: int,
|
| 63 |
+
bias: Optional[torch.Tensor],
|
| 64 |
+
alibi_slopes: Optional[torch.Tensor],
|
| 65 |
+
return_lse: bool, # Not used
|
| 66 |
+
return_softmax: bool,
|
| 67 |
+
max_seqlen_q: int,
|
| 68 |
+
max_seqlen_k: int,
|
| 69 |
+
cu_seqlens_q: Optional[torch.Tensor] = None,
|
| 70 |
+
cu_seqlens_k: Optional[torch.Tensor] = None,
|
| 71 |
+
descale_q: Optional[torch.Tensor] = None,
|
| 72 |
+
descale_k: Optional[torch.Tensor] = None,
|
| 73 |
+
descale_v: Optional[torch.Tensor] = None,
|
| 74 |
+
sink: Optional[torch.Tensor] = None,
|
| 75 |
+
config: Optional[dict[str, any]] = None,
|
| 76 |
+
) -> Tuple[torch.Tensor, torch.Tensor, Optional[torch.Tensor], int, int]:
|
| 77 |
+
|
| 78 |
+
if bias is not None:
|
| 79 |
+
raise ValueError("Bias is not supported yet in the Triton Backend")
|
| 80 |
+
if window_size_right != -1:
|
| 81 |
+
raise ValueError("window_size_right is not supported yet in the Triton Backend")
|
| 82 |
+
sliding_window = window_size_left if window_size_left >= 0 else 0
|
| 83 |
+
|
| 84 |
+
# Triton cannot specialize on numpy scalar types; ensure native Python int
|
| 85 |
+
max_seqlen_q = int(max_seqlen_q)
|
| 86 |
+
max_seqlen_k = int(max_seqlen_k)
|
| 87 |
+
|
| 88 |
+
# FP8
|
| 89 |
+
IS_FP8 = types._is_fp8(q)
|
| 90 |
+
FP8_MAX: tl.constexpr = torch.finfo(q.dtype).max
|
| 91 |
+
is_varlen = True if cu_seqlens_q is not None else False
|
| 92 |
+
|
| 93 |
+
if IS_FP8:
|
| 94 |
+
o = torch.zeros(
|
| 95 |
+
(q.shape[:-1] + v.shape[-1:]), dtype=torch.float32, device=q.device
|
| 96 |
+
)
|
| 97 |
+
else:
|
| 98 |
+
o = torch.zeros((q.shape[:-1] + v.shape[-1:]), dtype=q.dtype, device=q.device)
|
| 99 |
+
if is_varlen:
|
| 100 |
+
# Layout is thd.
|
| 101 |
+
# q and k are [total_tokens, num_head, head_dim_qk].
|
| 102 |
+
# v is [total_tokens, num_head, head_dim_v].
|
| 103 |
+
batch, seqlen_q, num_q_heads = (
|
| 104 |
+
len(cu_seqlens_q) - 1,
|
| 105 |
+
max_seqlen_q,
|
| 106 |
+
q.shape[1],
|
| 107 |
+
)
|
| 108 |
+
num_k_heads = k.shape[1]
|
| 109 |
+
q_strides = (0, q.stride(1), q.stride(0), q.stride(2))
|
| 110 |
+
k_strides = (0, k.stride(1), k.stride(0), k.stride(2))
|
| 111 |
+
v_strides = (0, v.stride(1), v.stride(0), v.stride(2))
|
| 112 |
+
o_strides = (0, o.stride(1), o.stride(0), o.stride(2))
|
| 113 |
+
else:
|
| 114 |
+
# Layout is bshd.
|
| 115 |
+
# q and k are [batch, seq_len, num_head, head_dim_qk].
|
| 116 |
+
# v is [batch, seq_len, num_head, head_dim_v].
|
| 117 |
+
batch, seqlen_q, num_q_heads = (int(x) for x in q.shape[:-1])
|
| 118 |
+
num_k_heads = k.shape[2]
|
| 119 |
+
q_strides = (q.stride(0), q.stride(2), q.stride(1), q.stride(3))
|
| 120 |
+
k_strides = (k.stride(0), k.stride(2), k.stride(1), k.stride(3))
|
| 121 |
+
v_strides = (v.stride(0), v.stride(2), v.stride(1), v.stride(3))
|
| 122 |
+
o_strides = (o.stride(0), o.stride(2), o.stride(1), o.stride(3))
|
| 123 |
+
|
| 124 |
+
qk_head_dim = q.shape[-1]
|
| 125 |
+
v_head_dim = v.shape[-1]
|
| 126 |
+
pe_head_dim = qk_head_dim - v_head_dim
|
| 127 |
+
# padding for head_dim. Power of 2 or 16
|
| 128 |
+
BLOCK_DMODEL_POW2 = max(triton.next_power_of_2(v_head_dim), 16)
|
| 129 |
+
BLOCK_DMODEL_PE_POW2 = (
|
| 130 |
+
0 if pe_head_dim == 0 else max(triton.next_power_of_2(pe_head_dim), 16)
|
| 131 |
+
)
|
| 132 |
+
assert (pe_head_dim == 0 and BLOCK_DMODEL_PE_POW2 == 0) or (
|
| 133 |
+
v_head_dim == BLOCK_DMODEL_POW2 and pe_head_dim == BLOCK_DMODEL_PE_POW2
|
| 134 |
+
), "Positional encoding support requires NOPE and PE head sizes to be unpadded powers of 2."
|
| 135 |
+
assert (not IS_FP8) or (
|
| 136 |
+
IS_FP8 and pe_head_dim == 0
|
| 137 |
+
), "Positional encoding doesn't support FP8."
|
| 138 |
+
|
| 139 |
+
assert (sink is None) or (
|
| 140 |
+
sink is not None and sink.dim() == 1 and sink.shape[0] == num_q_heads
|
| 141 |
+
), "Sink must be 1D and have one element per query head."
|
| 142 |
+
|
| 143 |
+
# softmax_lse [batch, num_q_heads, seqlen_q]
|
| 144 |
+
if is_varlen:
|
| 145 |
+
softmax_lse = torch.zeros(
|
| 146 |
+
(q.shape[0], num_q_heads), device=q.device, dtype=torch.float32
|
| 147 |
+
)
|
| 148 |
+
stride_lse_z, stride_lse_h, stride_lse_m = (
|
| 149 |
+
0,
|
| 150 |
+
softmax_lse.stride(1),
|
| 151 |
+
softmax_lse.stride(0),
|
| 152 |
+
)
|
| 153 |
+
else:
|
| 154 |
+
softmax_lse = torch.zeros(
|
| 155 |
+
(batch, num_q_heads, max_seqlen_q), device=q.device, dtype=torch.float32
|
| 156 |
+
)
|
| 157 |
+
stride_lse_z, stride_lse_h, stride_lse_m = softmax_lse.stride()
|
| 158 |
+
|
| 159 |
+
# exp_scores [batch, num_q_heads, seqlen_q, seqlen_k]
|
| 160 |
+
enable_dropout = dropout_p > 0.0
|
| 161 |
+
if enable_dropout:
|
| 162 |
+
philox_seed = torch.randint(0, 0xFFFFFF, (1,))[
|
| 163 |
+
0
|
| 164 |
+
].item() # No specific reason to restrict range to 0xffffff
|
| 165 |
+
philox_offset = torch.randint(0, 0xFFFFFF, (1,))[
|
| 166 |
+
0
|
| 167 |
+
].item() # Pass in an int, not Tensor
|
| 168 |
+
else:
|
| 169 |
+
philox_seed = 0
|
| 170 |
+
philox_offset = 0
|
| 171 |
+
if return_softmax or enable_dropout:
|
| 172 |
+
s_dmask = torch.zeros(
|
| 173 |
+
(batch, num_q_heads, max_seqlen_q, max_seqlen_k),
|
| 174 |
+
device=q.device,
|
| 175 |
+
dtype=torch.float32,
|
| 176 |
+
)
|
| 177 |
+
dropout_mask = torch.zeros(
|
| 178 |
+
(batch, num_q_heads, max_seqlen_q, max_seqlen_k),
|
| 179 |
+
device=q.device,
|
| 180 |
+
dtype=torch.float32,
|
| 181 |
+
)
|
| 182 |
+
else:
|
| 183 |
+
s_dmask = None
|
| 184 |
+
dropout_mask = None
|
| 185 |
+
|
| 186 |
+
if _MHA_IMPL == "dao_ai":
|
| 187 |
+
assert sink is None, "dao_ai impl does not support attention sink."
|
| 188 |
+
assert (
|
| 189 |
+
pe_head_dim == 0
|
| 190 |
+
), "dao_ai impl does not support positional encoding (pe_head_dim > 0)."
|
| 191 |
+
assert (
|
| 192 |
+
not IS_FP8
|
| 193 |
+
), "dao_ai impl does not support FP8. Use the default impl or FA3 path."
|
| 194 |
+
assert (
|
| 195 |
+
window_size_left == -1 and window_size_right == -1
|
| 196 |
+
), "dao_ai impl does not support sliding window attention."
|
| 197 |
+
if is_varlen:
|
| 198 |
+
o, softmax_lse, s_dmask, _ = flash_attn_2.varlen_fwd(
|
| 199 |
+
q,
|
| 200 |
+
k,
|
| 201 |
+
v,
|
| 202 |
+
o,
|
| 203 |
+
cu_seqlens_q,
|
| 204 |
+
cu_seqlens_k,
|
| 205 |
+
seqused_k=None,
|
| 206 |
+
leftpad_k=None,
|
| 207 |
+
block_table_=None,
|
| 208 |
+
alibi_slopes=alibi_slopes,
|
| 209 |
+
max_seqlen_q=max_seqlen_q,
|
| 210 |
+
max_seqlen_k=max_seqlen_k,
|
| 211 |
+
dropout_p=dropout_p,
|
| 212 |
+
softmax_scale=softmax_scale,
|
| 213 |
+
zero_tensors=False,
|
| 214 |
+
causal=causal,
|
| 215 |
+
window_size_left=-1,
|
| 216 |
+
window_size_right=-1,
|
| 217 |
+
softcap=0.0,
|
| 218 |
+
return_softmax=return_softmax,
|
| 219 |
+
)
|
| 220 |
+
else:
|
| 221 |
+
o, softmax_lse, s_dmask, _ = flash_attn_2.fwd(
|
| 222 |
+
q,
|
| 223 |
+
k,
|
| 224 |
+
v,
|
| 225 |
+
o,
|
| 226 |
+
alibi_slopes,
|
| 227 |
+
dropout_p,
|
| 228 |
+
softmax_scale,
|
| 229 |
+
causal,
|
| 230 |
+
window_size_left=-1,
|
| 231 |
+
window_size_right=-1,
|
| 232 |
+
softcap=0.0,
|
| 233 |
+
return_softmax=return_softmax,
|
| 234 |
+
)
|
| 235 |
+
# Verify softmax_lse shape contract:
|
| 236 |
+
# non-varlen: (batch, nheads_q, seqlen_q)
|
| 237 |
+
# varlen: (nheads_q, total_q) — transposed vs default impl
|
| 238 |
+
if is_varlen:
|
| 239 |
+
assert softmax_lse.shape == (
|
| 240 |
+
num_q_heads,
|
| 241 |
+
q.shape[0],
|
| 242 |
+
), f"dao_ai varlen softmax_lse shape {softmax_lse.shape} != expected ({num_q_heads}, {q.shape[0]})"
|
| 243 |
+
else:
|
| 244 |
+
assert (
|
| 245 |
+
softmax_lse.shape[0] == batch and softmax_lse.shape[1] == num_q_heads
|
| 246 |
+
), f"dao_ai softmax_lse shape {softmax_lse.shape} != expected (batch={batch}, nheads={num_q_heads}, ...)"
|
| 247 |
+
else:
|
| 248 |
+
if config is None:
|
| 249 |
+
config = _get_config(enable_dropout, q.dtype, has_pe=pe_head_dim > 0)
|
| 250 |
+
|
| 251 |
+
grid = lambda META: ( # noqa: E731
|
| 252 |
+
batch * num_q_heads * triton.cdiv(seqlen_q, META["BLOCK_M"]),
|
| 253 |
+
)
|
| 254 |
+
|
| 255 |
+
_attn_fwd[grid](
|
| 256 |
+
q,
|
| 257 |
+
k,
|
| 258 |
+
v,
|
| 259 |
+
descale_q,
|
| 260 |
+
descale_k,
|
| 261 |
+
descale_v,
|
| 262 |
+
o,
|
| 263 |
+
alibi_slopes,
|
| 264 |
+
s_dmask,
|
| 265 |
+
dropout_mask,
|
| 266 |
+
softmax_lse,
|
| 267 |
+
sink,
|
| 268 |
+
*q_strides,
|
| 269 |
+
*k_strides,
|
| 270 |
+
*v_strides,
|
| 271 |
+
descale_q.stride(0) if descale_q is not None else 0,
|
| 272 |
+
descale_k.stride(0) if descale_k is not None else 0,
|
| 273 |
+
descale_v.stride(0) if descale_v is not None else 0,
|
| 274 |
+
*o_strides,
|
| 275 |
+
alibi_slopes.stride(0) if alibi_slopes is not None else 0,
|
| 276 |
+
alibi_slopes.stride(1) if alibi_slopes is not None else 0,
|
| 277 |
+
s_dmask.stride(0) if s_dmask is not None else 0,
|
| 278 |
+
s_dmask.stride(1) if s_dmask is not None else 0,
|
| 279 |
+
s_dmask.stride(2) if s_dmask is not None else 0,
|
| 280 |
+
s_dmask.stride(3) if s_dmask is not None else 0,
|
| 281 |
+
stride_lse_z if softmax_lse is not None else 0,
|
| 282 |
+
stride_lse_h if softmax_lse is not None else 0,
|
| 283 |
+
stride_lse_m if softmax_lse is not None else 0,
|
| 284 |
+
softmax_scale,
|
| 285 |
+
cu_seqlens_q,
|
| 286 |
+
cu_seqlens_k,
|
| 287 |
+
dropout_p,
|
| 288 |
+
philox_seed,
|
| 289 |
+
philox_offset,
|
| 290 |
+
SEQLEN_Q=max_seqlen_q,
|
| 291 |
+
SEQLEN_K=max_seqlen_k,
|
| 292 |
+
IS_CAUSAL=causal,
|
| 293 |
+
NUM_Q_HEADS=num_q_heads,
|
| 294 |
+
NUM_K_HEADS=num_k_heads,
|
| 295 |
+
BLOCK_DMODEL=v_head_dim,
|
| 296 |
+
BLOCK_DMODEL_POW2=BLOCK_DMODEL_POW2,
|
| 297 |
+
BLOCK_DMODEL_PE=pe_head_dim,
|
| 298 |
+
RETURN_SCORES=return_softmax,
|
| 299 |
+
ENABLE_DROPOUT=enable_dropout,
|
| 300 |
+
IS_FP8=IS_FP8,
|
| 301 |
+
FP8_MAX=FP8_MAX,
|
| 302 |
+
VARLEN=is_varlen,
|
| 303 |
+
BATCH=batch,
|
| 304 |
+
NUM_XCD=get_num_xcds(),
|
| 305 |
+
USE_INT64_STRIDES=_USE_INT64_STRIDES,
|
| 306 |
+
ENABLE_SINK=sink is not None,
|
| 307 |
+
SLIDING_WINDOW=sliding_window,
|
| 308 |
+
**config,
|
| 309 |
+
)
|
| 310 |
+
|
| 311 |
+
return o, softmax_lse, s_dmask, philox_seed, philox_offset
|
| 312 |
+
|
| 313 |
+
|
| 314 |
+
class _FlashAttnFunc(torch.autograd.Function):
|
| 315 |
+
@staticmethod
|
| 316 |
+
def forward(
|
| 317 |
+
ctx,
|
| 318 |
+
q,
|
| 319 |
+
k,
|
| 320 |
+
v,
|
| 321 |
+
dropout_p,
|
| 322 |
+
softmax_scale,
|
| 323 |
+
causal,
|
| 324 |
+
window_size,
|
| 325 |
+
bias,
|
| 326 |
+
alibi_slopes,
|
| 327 |
+
deterministic,
|
| 328 |
+
return_lse,
|
| 329 |
+
return_softmax,
|
| 330 |
+
sink,
|
| 331 |
+
is_grad_enabled,
|
| 332 |
+
config=None,
|
| 333 |
+
):
|
| 334 |
+
is_grad = is_grad_enabled and any(
|
| 335 |
+
x is not None and x.requires_grad for x in [q, k, v, sink]
|
| 336 |
+
)
|
| 337 |
+
if softmax_scale is None:
|
| 338 |
+
softmax_scale = q.shape[-1] ** (-0.5)
|
| 339 |
+
head_size_og = q.size(3)
|
| 340 |
+
if head_size_og % 8 != 0:
|
| 341 |
+
q = torch.nn.functional.pad(q, [0, 8 - head_size_og % 8])
|
| 342 |
+
k = torch.nn.functional.pad(k, [0, 8 - head_size_og % 8])
|
| 343 |
+
v = torch.nn.functional.pad(v, [0, 8 - head_size_og % 8])
|
| 344 |
+
out_padded, softmax_lse, S_dmask, philox_seed, philox_offset = (
|
| 345 |
+
_flash_attn_forward(
|
| 346 |
+
q,
|
| 347 |
+
k,
|
| 348 |
+
v,
|
| 349 |
+
dropout_p,
|
| 350 |
+
softmax_scale,
|
| 351 |
+
causal=causal,
|
| 352 |
+
window_size_left=int(window_size[0]),
|
| 353 |
+
window_size_right=int(window_size[1]),
|
| 354 |
+
bias=bias,
|
| 355 |
+
alibi_slopes=alibi_slopes,
|
| 356 |
+
return_lse=return_lse,
|
| 357 |
+
return_softmax=return_softmax and dropout_p > 0,
|
| 358 |
+
max_seqlen_q=q.shape[1],
|
| 359 |
+
max_seqlen_k=k.shape[1],
|
| 360 |
+
sink=sink,
|
| 361 |
+
config=config,
|
| 362 |
+
)
|
| 363 |
+
)
|
| 364 |
+
|
| 365 |
+
if is_grad:
|
| 366 |
+
ctx.save_for_backward(q, k, v, out_padded, softmax_lse, sink)
|
| 367 |
+
ctx.philox_seed = philox_seed
|
| 368 |
+
ctx.philox_offset = philox_offset
|
| 369 |
+
ctx.dropout_p = dropout_p
|
| 370 |
+
ctx.softmax_scale = softmax_scale
|
| 371 |
+
ctx.causal = causal
|
| 372 |
+
ctx.bias = bias
|
| 373 |
+
ctx.window_size = window_size
|
| 374 |
+
ctx.alibi_slopes = alibi_slopes
|
| 375 |
+
ctx.deterministic = deterministic
|
| 376 |
+
|
| 377 |
+
out = out_padded[..., :head_size_og]
|
| 378 |
+
result = [out]
|
| 379 |
+
if return_lse:
|
| 380 |
+
result.append(softmax_lse)
|
| 381 |
+
if return_softmax:
|
| 382 |
+
result.append(S_dmask)
|
| 383 |
+
|
| 384 |
+
return result[0] if len(result) == 1 else tuple(result)
|
| 385 |
+
|
| 386 |
+
@staticmethod
|
| 387 |
+
def backward(ctx, do, *args):
|
| 388 |
+
q, k, v, out, softmax_lse, sink = ctx.saved_tensors
|
| 389 |
+
bias = ctx.bias
|
| 390 |
+
dbias = torch.empty_like(bias) if bias is not None else None
|
| 391 |
+
dq, dk, dv = torch.zeros_like(q), torch.empty_like(k), torch.empty_like(v)
|
| 392 |
+
dsink = (
|
| 393 |
+
torch.zeros_like(sink, dtype=torch.float32) if sink is not None else None
|
| 394 |
+
)
|
| 395 |
+
head_size_v_og = do.size(3)
|
| 396 |
+
do_padded = do
|
| 397 |
+
if head_size_v_og % 8 != 0:
|
| 398 |
+
do_padded = torch.nn.functional.pad(do, [0, 8 - head_size_v_og % 8])
|
| 399 |
+
sliding_window = _get_sliding_window_size(ctx.window_size)
|
| 400 |
+
|
| 401 |
+
if _MHA_IMPL == "dao_ai":
|
| 402 |
+
assert sink is None, "dao_ai impl does not support attention sink."
|
| 403 |
+
flash_attn_2.bwd(
|
| 404 |
+
do_padded,
|
| 405 |
+
q,
|
| 406 |
+
k,
|
| 407 |
+
v,
|
| 408 |
+
out,
|
| 409 |
+
softmax_lse,
|
| 410 |
+
dq,
|
| 411 |
+
dk,
|
| 412 |
+
dv,
|
| 413 |
+
ctx.alibi_slopes,
|
| 414 |
+
ctx.dropout_p,
|
| 415 |
+
ctx.softmax_scale,
|
| 416 |
+
ctx.causal,
|
| 417 |
+
window_size_left=-1,
|
| 418 |
+
window_size_right=-1,
|
| 419 |
+
softcap=0.0,
|
| 420 |
+
deterministic=ctx.deterministic,
|
| 421 |
+
)
|
| 422 |
+
else:
|
| 423 |
+
if _USE_FUSED_BWD_KERNEL:
|
| 424 |
+
if sliding_window > 0:
|
| 425 |
+
raise ValueError(
|
| 426 |
+
"Fused backward doesn't support sliding window attention. "
|
| 427 |
+
"Disable fused backward or use the one-kernel backward."
|
| 428 |
+
)
|
| 429 |
+
assert (
|
| 430 |
+
sink is None and dsink is None
|
| 431 |
+
), "Fused backward doesn't support sinks."
|
| 432 |
+
flash_attn_fused_backward(
|
| 433 |
+
do_padded,
|
| 434 |
+
q,
|
| 435 |
+
k,
|
| 436 |
+
v,
|
| 437 |
+
out,
|
| 438 |
+
softmax_lse,
|
| 439 |
+
dq,
|
| 440 |
+
dk,
|
| 441 |
+
dv,
|
| 442 |
+
dbias,
|
| 443 |
+
ctx.softmax_scale,
|
| 444 |
+
ctx.alibi_slopes,
|
| 445 |
+
ctx.causal,
|
| 446 |
+
None,
|
| 447 |
+
None,
|
| 448 |
+
max_seqlen_q=q.shape[1],
|
| 449 |
+
max_seqlen_k=k.shape[1],
|
| 450 |
+
dropout_p=ctx.dropout_p,
|
| 451 |
+
philox_seed=ctx.philox_seed,
|
| 452 |
+
philox_offset=ctx.philox_offset,
|
| 453 |
+
USE_INT64_STRIDES=_USE_INT64_STRIDES,
|
| 454 |
+
)
|
| 455 |
+
else:
|
| 456 |
+
flash_attn_onekernel_backward(
|
| 457 |
+
do_padded,
|
| 458 |
+
q,
|
| 459 |
+
k,
|
| 460 |
+
v,
|
| 461 |
+
out,
|
| 462 |
+
softmax_lse,
|
| 463 |
+
dq,
|
| 464 |
+
dk,
|
| 465 |
+
dv,
|
| 466 |
+
dbias,
|
| 467 |
+
ctx.softmax_scale,
|
| 468 |
+
ctx.alibi_slopes,
|
| 469 |
+
ctx.causal,
|
| 470 |
+
None,
|
| 471 |
+
None,
|
| 472 |
+
max_seqlen_q=q.shape[1],
|
| 473 |
+
max_seqlen_k=k.shape[1],
|
| 474 |
+
dropout_p=ctx.dropout_p,
|
| 475 |
+
philox_seed=ctx.philox_seed,
|
| 476 |
+
philox_offset=ctx.philox_offset,
|
| 477 |
+
USE_INT64_STRIDES=_USE_INT64_STRIDES,
|
| 478 |
+
sink=sink,
|
| 479 |
+
dsink=dsink,
|
| 480 |
+
sliding_window=sliding_window,
|
| 481 |
+
)
|
| 482 |
+
|
| 483 |
+
dq = dq[..., : q.shape[-1]] # We could have padded the head dimension
|
| 484 |
+
dk = dk[..., : k.shape[-1]]
|
| 485 |
+
dv = dv[..., : v.shape[-1]]
|
| 486 |
+
return (
|
| 487 |
+
dq,
|
| 488 |
+
dk,
|
| 489 |
+
dv,
|
| 490 |
+
None, # dropout_p
|
| 491 |
+
None, # softmax_scale
|
| 492 |
+
None, # causal
|
| 493 |
+
None, # window_size
|
| 494 |
+
dbias,
|
| 495 |
+
None, # alibi_slopes
|
| 496 |
+
None, # deterministic
|
| 497 |
+
None, # return_lse
|
| 498 |
+
None, # return_softmax
|
| 499 |
+
dsink,
|
| 500 |
+
None, # is_grad_enabled
|
| 501 |
+
None, # config
|
| 502 |
+
)
|
| 503 |
+
|
| 504 |
+
|
| 505 |
+
def flash_attn_func(
|
| 506 |
+
q,
|
| 507 |
+
k,
|
| 508 |
+
v,
|
| 509 |
+
dropout_p=0.0,
|
| 510 |
+
softmax_scale=None,
|
| 511 |
+
causal=False,
|
| 512 |
+
window_size=(-1, -1), # -1 means infinite context window
|
| 513 |
+
bias=None,
|
| 514 |
+
alibi_slopes=None,
|
| 515 |
+
deterministic=True,
|
| 516 |
+
return_lse=False,
|
| 517 |
+
return_attn_probs=False,
|
| 518 |
+
sink=None,
|
| 519 |
+
config: Optional[dict[str, any]] = None,
|
| 520 |
+
):
|
| 521 |
+
"""dropout_p should be set to 0.0 during evaluation
|
| 522 |
+
Supports multi-query and grouped-query attention (MQA/GQA) by passing in KV with fewer heads
|
| 523 |
+
than Q. Note that the number of heads in Q must be divisible by the number of heads in KV.
|
| 524 |
+
For example, if Q has 6 heads and K, V have 2 heads, head 0, 1, 2 of Q will attention to head
|
| 525 |
+
0 of K, V, and head 3, 4, 5 of Q will attention to head 1 of K, V.
|
| 526 |
+
|
| 527 |
+
If causal=True, the causal mask is aligned to the bottom right corner of the attention matrix.
|
| 528 |
+
For example, if seqlen_q = 2 and seqlen_k = 5, the causal mask (1 = keep, 0 = masked out) is:
|
| 529 |
+
1 1 1 1 0
|
| 530 |
+
1 1 1 1 1
|
| 531 |
+
If seqlen_q = 5 and seqlen_k = 2, the causal mask is:
|
| 532 |
+
0 0
|
| 533 |
+
0 0
|
| 534 |
+
0 0
|
| 535 |
+
1 0
|
| 536 |
+
1 1
|
| 537 |
+
If the row of the mask is all zero, the output will be zero.
|
| 538 |
+
|
| 539 |
+
If window_size != (-1, -1), implements sliding window local attention. Query at position i
|
| 540 |
+
will only attend to keys between
|
| 541 |
+
[i + seqlen_k - seqlen_q - window_size[0], i + seqlen_k - seqlen_q + window_size[1]] inclusive.
|
| 542 |
+
|
| 543 |
+
Arguments:
|
| 544 |
+
q: (batch_size, seqlen, nheads, headdim)
|
| 545 |
+
k: (batch_size, seqlen, nheads_k, headdim)
|
| 546 |
+
v: (batch_size, seqlen, nheads_k, headdim)
|
| 547 |
+
dropout_p: float. Dropout probability.
|
| 548 |
+
softmax_scale: float. The scaling of QK^T before applying softmax.
|
| 549 |
+
Default to 1 / sqrt(headdim_q).
|
| 550 |
+
causal: bool. Whether to apply causal attention mask (e.g., for auto-regressive modeling).
|
| 551 |
+
window_size: (left, right). If not (-1, -1), implements sliding window local attention.
|
| 552 |
+
bias: (seqlen_q, seqlen_k)
|
| 553 |
+
alibi_slopes: (nheads,) or (batch_size, nheads), fp32. A bias of
|
| 554 |
+
(-alibi_slope * |i + seqlen_k - seqlen_q - j|)
|
| 555 |
+
is added to the attention score of query i and key j.
|
| 556 |
+
deterministic: bool. Whether to use the deterministic implementation of the backward pass,
|
| 557 |
+
which is slightly slower and uses more memory. The forward pass is always deterministic.
|
| 558 |
+
return_attn_probs: bool. Whether to return the attention probabilities. This option is for
|
| 559 |
+
testing only. The returned probabilities are not guaranteed to be correct
|
| 560 |
+
(they might not have the right scaling).
|
| 561 |
+
sink: (nheads,), attention sink scores (one per Q head), or None
|
| 562 |
+
Return:
|
| 563 |
+
out: (batch_size, seqlen, nheads, headdim).
|
| 564 |
+
softmax_lse [optional, if return_attn_probs=True]: (batch_size, nheads, seqlen). The
|
| 565 |
+
logsumexp of each row of the matrix QK^T * scaling (e.g., log of the softmax
|
| 566 |
+
normalization factor).
|
| 567 |
+
S_dmask [optional, if return_attn_probs=True]: (batch_size, nheads, seqlen, seqlen).
|
| 568 |
+
The output of softmax (possibly with different scaling). It also encodes the dropout
|
| 569 |
+
pattern (negative means that location was dropped, nonnegative means it was kept).
|
| 570 |
+
"""
|
| 571 |
+
_LOGGER.info(
|
| 572 |
+
f"FLASH_ATTN: q={tuple(q.shape)} k={tuple(k.shape)} v={tuple(v.shape)}"
|
| 573 |
+
)
|
| 574 |
+
return _FlashAttnFunc.apply(
|
| 575 |
+
q,
|
| 576 |
+
k,
|
| 577 |
+
v,
|
| 578 |
+
dropout_p,
|
| 579 |
+
softmax_scale,
|
| 580 |
+
causal,
|
| 581 |
+
window_size,
|
| 582 |
+
bias,
|
| 583 |
+
alibi_slopes,
|
| 584 |
+
deterministic,
|
| 585 |
+
return_lse,
|
| 586 |
+
return_attn_probs,
|
| 587 |
+
sink,
|
| 588 |
+
torch.is_grad_enabled(),
|
| 589 |
+
config,
|
| 590 |
+
)
|
| 591 |
+
|
| 592 |
+
|
| 593 |
+
class _FlashAttnVarlenFunc(torch.autograd.Function):
|
| 594 |
+
@staticmethod
|
| 595 |
+
def forward(
|
| 596 |
+
ctx,
|
| 597 |
+
q,
|
| 598 |
+
k,
|
| 599 |
+
v,
|
| 600 |
+
cu_seqlens_q,
|
| 601 |
+
cu_seqlens_k,
|
| 602 |
+
max_seqlen_q,
|
| 603 |
+
max_seqlen_k,
|
| 604 |
+
dropout_p,
|
| 605 |
+
softmax_scale,
|
| 606 |
+
causal,
|
| 607 |
+
window_size,
|
| 608 |
+
bias,
|
| 609 |
+
alibi_slopes,
|
| 610 |
+
deterministic,
|
| 611 |
+
return_lse,
|
| 612 |
+
return_softmax,
|
| 613 |
+
block_table,
|
| 614 |
+
out,
|
| 615 |
+
sink,
|
| 616 |
+
is_grad_enabled,
|
| 617 |
+
config=None,
|
| 618 |
+
):
|
| 619 |
+
is_grad = is_grad_enabled and any(
|
| 620 |
+
x is not None and x.requires_grad for x in [q, k, v, sink]
|
| 621 |
+
)
|
| 622 |
+
if softmax_scale is None:
|
| 623 |
+
softmax_scale = q.shape[-1] ** (-0.5)
|
| 624 |
+
head_size_og = q.size(2)
|
| 625 |
+
if head_size_og % 8 != 0:
|
| 626 |
+
q = torch.nn.functional.pad(q, [0, 8 - head_size_og % 8])
|
| 627 |
+
k = torch.nn.functional.pad(k, [0, 8 - head_size_og % 8])
|
| 628 |
+
v = torch.nn.functional.pad(v, [0, 8 - head_size_og % 8])
|
| 629 |
+
out_padded, softmax_lse, S_dmask, philox_seed, philox_offset = (
|
| 630 |
+
_flash_attn_forward(
|
| 631 |
+
q,
|
| 632 |
+
k,
|
| 633 |
+
v,
|
| 634 |
+
dropout_p,
|
| 635 |
+
softmax_scale,
|
| 636 |
+
causal=causal,
|
| 637 |
+
window_size_left=int(window_size[0]),
|
| 638 |
+
window_size_right=int(window_size[1]),
|
| 639 |
+
bias=bias,
|
| 640 |
+
alibi_slopes=alibi_slopes,
|
| 641 |
+
return_lse=return_lse,
|
| 642 |
+
return_softmax=return_softmax and dropout_p > 0.0,
|
| 643 |
+
max_seqlen_q=max_seqlen_q,
|
| 644 |
+
max_seqlen_k=max_seqlen_k,
|
| 645 |
+
cu_seqlens_q=cu_seqlens_q,
|
| 646 |
+
cu_seqlens_k=cu_seqlens_k,
|
| 647 |
+
sink=sink,
|
| 648 |
+
config=config,
|
| 649 |
+
)
|
| 650 |
+
)
|
| 651 |
+
if is_grad:
|
| 652 |
+
ctx.save_for_backward(
|
| 653 |
+
q, k, v, out_padded, softmax_lse, cu_seqlens_q, cu_seqlens_k, sink
|
| 654 |
+
)
|
| 655 |
+
ctx.max_seqlen_q = max_seqlen_q
|
| 656 |
+
ctx.max_seqlen_k = max_seqlen_k
|
| 657 |
+
ctx.philox_seed = philox_seed
|
| 658 |
+
ctx.philox_offset = philox_offset
|
| 659 |
+
ctx.dropout_p = dropout_p
|
| 660 |
+
ctx.softmax_scale = softmax_scale
|
| 661 |
+
ctx.causal = causal
|
| 662 |
+
ctx.window_size = window_size
|
| 663 |
+
ctx.bias = bias
|
| 664 |
+
ctx.alibi_slopes = alibi_slopes
|
| 665 |
+
out = out_padded[..., :head_size_og]
|
| 666 |
+
|
| 667 |
+
result = [out]
|
| 668 |
+
if return_lse:
|
| 669 |
+
result.append(softmax_lse)
|
| 670 |
+
if return_softmax:
|
| 671 |
+
result.append(S_dmask)
|
| 672 |
+
|
| 673 |
+
return result[0] if len(result) == 1 else tuple(result)
|
| 674 |
+
|
| 675 |
+
@staticmethod
|
| 676 |
+
def backward(ctx, do, *args):
|
| 677 |
+
q, k, v, out, softmax_lse, cu_seqlens_q, cu_seqlens_k, sink = ctx.saved_tensors
|
| 678 |
+
dq, dk, dv = torch.zeros_like(q), torch.empty_like(k), torch.empty_like(v)
|
| 679 |
+
bias = ctx.bias
|
| 680 |
+
dbias = torch.empty_like(bias) if bias is not None else None
|
| 681 |
+
dsink = (
|
| 682 |
+
torch.zeros_like(sink, dtype=torch.float32) if sink is not None else None
|
| 683 |
+
)
|
| 684 |
+
head_size_og = do.size(2)
|
| 685 |
+
do_padded = do
|
| 686 |
+
if head_size_og % 8 != 0:
|
| 687 |
+
do_padded = torch.nn.functional.pad(do, [0, 8 - head_size_og % 8])
|
| 688 |
+
sliding_window = _get_sliding_window_size(ctx.window_size)
|
| 689 |
+
|
| 690 |
+
if _MHA_IMPL == "dao_ai":
|
| 691 |
+
assert sink is None, "dao_ai impl does not support attention sink."
|
| 692 |
+
flash_attn_2.varlen_bwd(
|
| 693 |
+
do_padded,
|
| 694 |
+
q,
|
| 695 |
+
k,
|
| 696 |
+
v,
|
| 697 |
+
out,
|
| 698 |
+
softmax_lse,
|
| 699 |
+
dq,
|
| 700 |
+
dk,
|
| 701 |
+
dv,
|
| 702 |
+
cu_seqlens_q,
|
| 703 |
+
cu_seqlens_k,
|
| 704 |
+
ctx.alibi_slopes,
|
| 705 |
+
max_seqlen_q=ctx.max_seqlen_q,
|
| 706 |
+
max_seqlen_k=ctx.max_seqlen_k,
|
| 707 |
+
dropout_p=ctx.dropout_p,
|
| 708 |
+
softmax_scale=ctx.softmax_scale,
|
| 709 |
+
zero_tensors=False,
|
| 710 |
+
causal=ctx.causal,
|
| 711 |
+
window_size_left=-1,
|
| 712 |
+
window_size_right=-1,
|
| 713 |
+
softcap=0.0,
|
| 714 |
+
deterministic=False,
|
| 715 |
+
)
|
| 716 |
+
else:
|
| 717 |
+
if _USE_FUSED_BWD_KERNEL:
|
| 718 |
+
if sliding_window > 0:
|
| 719 |
+
raise ValueError(
|
| 720 |
+
"Fused backward doesn't support sliding window attention. "
|
| 721 |
+
"Disable fused backward or use the one-kernel backward."
|
| 722 |
+
)
|
| 723 |
+
assert (
|
| 724 |
+
sink is None and dsink is None
|
| 725 |
+
), "Fused backward doesn't support sinks."
|
| 726 |
+
flash_attn_fused_backward(
|
| 727 |
+
do_padded,
|
| 728 |
+
q,
|
| 729 |
+
k,
|
| 730 |
+
v,
|
| 731 |
+
out,
|
| 732 |
+
softmax_lse,
|
| 733 |
+
dq,
|
| 734 |
+
dk,
|
| 735 |
+
dv,
|
| 736 |
+
dbias,
|
| 737 |
+
ctx.softmax_scale,
|
| 738 |
+
ctx.alibi_slopes,
|
| 739 |
+
ctx.causal,
|
| 740 |
+
cu_seqlens_q,
|
| 741 |
+
cu_seqlens_k,
|
| 742 |
+
max_seqlen_q=ctx.max_seqlen_q,
|
| 743 |
+
max_seqlen_k=ctx.max_seqlen_k,
|
| 744 |
+
dropout_p=ctx.dropout_p,
|
| 745 |
+
philox_seed=ctx.philox_seed,
|
| 746 |
+
philox_offset=ctx.philox_offset,
|
| 747 |
+
USE_INT64_STRIDES=_USE_INT64_STRIDES,
|
| 748 |
+
)
|
| 749 |
+
else:
|
| 750 |
+
flash_attn_onekernel_backward(
|
| 751 |
+
do_padded,
|
| 752 |
+
q,
|
| 753 |
+
k,
|
| 754 |
+
v,
|
| 755 |
+
out,
|
| 756 |
+
softmax_lse,
|
| 757 |
+
dq,
|
| 758 |
+
dk,
|
| 759 |
+
dv,
|
| 760 |
+
dbias,
|
| 761 |
+
ctx.softmax_scale,
|
| 762 |
+
ctx.alibi_slopes,
|
| 763 |
+
ctx.causal,
|
| 764 |
+
cu_seqlens_q,
|
| 765 |
+
cu_seqlens_k,
|
| 766 |
+
max_seqlen_q=ctx.max_seqlen_q,
|
| 767 |
+
max_seqlen_k=ctx.max_seqlen_k,
|
| 768 |
+
dropout_p=ctx.dropout_p,
|
| 769 |
+
philox_seed=ctx.philox_seed,
|
| 770 |
+
philox_offset=ctx.philox_offset,
|
| 771 |
+
USE_INT64_STRIDES=_USE_INT64_STRIDES,
|
| 772 |
+
sink=sink,
|
| 773 |
+
dsink=dsink,
|
| 774 |
+
sliding_window=sliding_window,
|
| 775 |
+
)
|
| 776 |
+
|
| 777 |
+
dq = dq[..., : q.shape[-1]] # We could have padded the head dimension
|
| 778 |
+
dk = dk[..., : k.shape[-1]]
|
| 779 |
+
dv = dv[..., : v.shape[-1]]
|
| 780 |
+
return (
|
| 781 |
+
dq,
|
| 782 |
+
dk,
|
| 783 |
+
dv,
|
| 784 |
+
None, # cu_seqlens_q,
|
| 785 |
+
None, # cu_seqlens_k
|
| 786 |
+
None, # max_seqlen_q
|
| 787 |
+
None, # max_seqlen_k
|
| 788 |
+
None, # dropout_p
|
| 789 |
+
None, # softmax_scale
|
| 790 |
+
None, # causal
|
| 791 |
+
None, # window_size
|
| 792 |
+
dbias,
|
| 793 |
+
None, # alibi_slopes
|
| 794 |
+
None, # deterministic
|
| 795 |
+
None, # return_lse
|
| 796 |
+
None, # return_softmax
|
| 797 |
+
None, # block_table
|
| 798 |
+
None, # out
|
| 799 |
+
dsink,
|
| 800 |
+
None, # is_grad_enabled
|
| 801 |
+
None, # config
|
| 802 |
+
)
|
| 803 |
+
|
| 804 |
+
|
| 805 |
+
def flash_attn_varlen_func(
|
| 806 |
+
q,
|
| 807 |
+
k,
|
| 808 |
+
v,
|
| 809 |
+
cu_seqlens_q,
|
| 810 |
+
cu_seqlens_k,
|
| 811 |
+
max_seqlen_q,
|
| 812 |
+
max_seqlen_k,
|
| 813 |
+
dropout_p=0.0,
|
| 814 |
+
softmax_scale=None,
|
| 815 |
+
causal=False,
|
| 816 |
+
window_size=(-1, -1), # -1 means infinite context window
|
| 817 |
+
bias=None,
|
| 818 |
+
alibi_slopes=None,
|
| 819 |
+
deterministic=False,
|
| 820 |
+
return_lse=False,
|
| 821 |
+
return_attn_probs=False,
|
| 822 |
+
block_table=None,
|
| 823 |
+
out=None,
|
| 824 |
+
sink=None,
|
| 825 |
+
config: Optional[dict[str, any]] = None,
|
| 826 |
+
):
|
| 827 |
+
"""dropout_p should be set to 0.0 during evaluation
|
| 828 |
+
Supports multi-query and grouped-query attention (MQA/GQA) by passing in K, V with fewer heads
|
| 829 |
+
than Q. Note that the number of heads in Q must be divisible by the number of heads in KV.
|
| 830 |
+
For example, if Q has 6 heads and K, V have 2 heads, head 0, 1, 2 of Q will attention to head
|
| 831 |
+
0 of K, V, and head 3, 4, 5 of Q will attention to head 1 of K, V.
|
| 832 |
+
|
| 833 |
+
If causal=True, the causal mask is aligned to the bottom right corner of the attention matrix.
|
| 834 |
+
For example, if seqlen_q = 2 and seqlen_k = 5, the causal mask (1 = keep, 0 = masked out) is:
|
| 835 |
+
1 1 1 1 0
|
| 836 |
+
1 1 1 1 1
|
| 837 |
+
If seqlen_q = 5 and seqlen_k = 2, the causal mask is:
|
| 838 |
+
0 0
|
| 839 |
+
0 0
|
| 840 |
+
0 0
|
| 841 |
+
1 0
|
| 842 |
+
1 1
|
| 843 |
+
If the row of the mask is all zero, the output will be zero.
|
| 844 |
+
|
| 845 |
+
If window_size != (-1, -1), implements sliding window local attention. Query at position i
|
| 846 |
+
will only attend to keys between
|
| 847 |
+
[i + seqlen_k - seqlen_q - window_size[0], i + seqlen_k - seqlen_q + window_size[1]] inclusive.
|
| 848 |
+
|
| 849 |
+
Arguments:
|
| 850 |
+
q: (total_q, nheads, headdim), where total_q = total number of query tokens in the batch.
|
| 851 |
+
k: (total_k, nheads_k, headdim), where total_k = total number of key tokens in the batch.
|
| 852 |
+
v: (total_k, nheads_k, headdim), where total_k = total number of key tokens in the batch.
|
| 853 |
+
cu_seqlens_q: (batch_size + 1,), dtype torch.int32. The cumulative sequence lengths
|
| 854 |
+
of the sequences in the batch, used to index into q.
|
| 855 |
+
cu_seqlens_k: (batch_size + 1,), dtype torch.int32. The cumulative sequence lengths
|
| 856 |
+
of the sequences in the batch, used to index into kv.
|
| 857 |
+
max_seqlen_q: int. Maximum query sequence length in the batch.
|
| 858 |
+
max_seqlen_k: int. Maximum key sequence length in the batch.
|
| 859 |
+
dropout_p: float. Dropout probability.
|
| 860 |
+
softmax_scale: float. The scaling of QK^T before applying softmax.
|
| 861 |
+
Default to 1 / sqrt(headdim).
|
| 862 |
+
causal: bool. Whether to apply causal attention mask (e.g., for auto-regressive modeling).
|
| 863 |
+
window_size: (left, right). If not (-1, -1), implements sliding window local attention.
|
| 864 |
+
bias: (seqlen_q, seqlen_k)
|
| 865 |
+
alibi_slopes: (nheads,) or (batch_size, nheads), fp32. A bias of
|
| 866 |
+
(-alibi_slope * |i + seqlen_k - seqlen_q - j|)
|
| 867 |
+
is added to the attention score of query i and key j.
|
| 868 |
+
deterministic: bool. Whether to use the deterministic implementation of the backward pass,
|
| 869 |
+
which is slightly slower and uses more memory. The forward pass is always deterministic.
|
| 870 |
+
return_attn_probs: bool. Whether to return the attention probabilities. This option is for
|
| 871 |
+
testing only. The returned probabilities are not guaranteed to be correct
|
| 872 |
+
(they might not have the right scaling).
|
| 873 |
+
sink: (nheads,), attention sink scores (one per Q head), or None
|
| 874 |
+
Return:
|
| 875 |
+
out: (total, nheads, headdim).
|
| 876 |
+
softmax_lse [optional, if return_attn_probs=True]: (nheads, total_q_seqlen). The
|
| 877 |
+
logsumexp of each row of the matrix QK^T * scaling (e.g., log of the softmax
|
| 878 |
+
normalization factor).
|
| 879 |
+
S_dmask [optional, if return_attn_probs=True]: (batch_size, nheads, seqlen, seqlen).
|
| 880 |
+
The output of softmax (possibly with different scaling). It also encodes the dropout
|
| 881 |
+
pattern (negative means that location was dropped, nonnegative means it was kept).
|
| 882 |
+
"""
|
| 883 |
+
|
| 884 |
+
_LOGGER.info(
|
| 885 |
+
f"FLASH_ATTN_VARLEN: q={tuple(q.shape)} k={tuple(k.shape)} v={tuple(v.shape)}"
|
| 886 |
+
)
|
| 887 |
+
return _FlashAttnVarlenFunc.apply(
|
| 888 |
+
q,
|
| 889 |
+
k,
|
| 890 |
+
v,
|
| 891 |
+
cu_seqlens_q,
|
| 892 |
+
cu_seqlens_k,
|
| 893 |
+
max_seqlen_q,
|
| 894 |
+
max_seqlen_k,
|
| 895 |
+
dropout_p,
|
| 896 |
+
softmax_scale,
|
| 897 |
+
causal,
|
| 898 |
+
window_size,
|
| 899 |
+
bias,
|
| 900 |
+
alibi_slopes,
|
| 901 |
+
deterministic,
|
| 902 |
+
return_lse,
|
| 903 |
+
return_attn_probs,
|
| 904 |
+
block_table,
|
| 905 |
+
out,
|
| 906 |
+
sink,
|
| 907 |
+
torch.is_grad_enabled(),
|
| 908 |
+
config,
|
| 909 |
+
)
|
| 910 |
+
|
| 911 |
+
|
| 912 |
+
def flash_attn_with_kvcache(
|
| 913 |
+
q: torch.Tensor,
|
| 914 |
+
k_cache: torch.Tensor,
|
| 915 |
+
v_cache: torch.Tensor,
|
| 916 |
+
k: Optional[torch.Tensor] = None,
|
| 917 |
+
v: Optional[torch.Tensor] = None,
|
| 918 |
+
cache_seqlens: Optional[Union[torch.Tensor, int]] = None,
|
| 919 |
+
softmax_scale: Optional[float] = None,
|
| 920 |
+
causal: bool = True,
|
| 921 |
+
window_size: tuple[int, int] = (-1, -1),
|
| 922 |
+
softcap: float = 0.0,
|
| 923 |
+
num_splits: int = 0,
|
| 924 |
+
rotary_cos: Optional[torch.Tensor] = None,
|
| 925 |
+
rotary_sin: Optional[torch.Tensor] = None,
|
| 926 |
+
cache_batch_idx: Optional[torch.Tensor] = None,
|
| 927 |
+
cache_leftpad: Optional[torch.Tensor] = None,
|
| 928 |
+
block_table: Optional[torch.Tensor] = None,
|
| 929 |
+
alibi_slopes: Optional[torch.Tensor] = None,
|
| 930 |
+
rotary_interleaved: bool = True,
|
| 931 |
+
return_softmax_lse: bool = False,
|
| 932 |
+
):
|
| 933 |
+
"""
|
| 934 |
+
This mirrors the public flash_attn v2 interface for KV cache using the AMD Triton backend.
|
| 935 |
+
|
| 936 |
+
Args:
|
| 937 |
+
q: (batch, seqlen_q, nheads_q, headdim)
|
| 938 |
+
k_cache / v_cache: Either contiguous (batch, seqlen_cache, nheads_k, headdim) or paged
|
| 939 |
+
(num_blocks, page_block_size, nheads_k, headdim) when block_table provided.
|
| 940 |
+
k, v: Optional incremental tokens to append in-place (appended logically after existing cache).
|
| 941 |
+
cache_seqlens: int or (batch,) current valid lengths per batch entry.
|
| 942 |
+
softmax_scale: Optional override; defaults to 1/sqrt(headdim).
|
| 943 |
+
causal: Apply causal masking.
|
| 944 |
+
window_size: (left, right) local attention window; (-1,-1) = full.
|
| 945 |
+
softcap: (float) currently must be 0.0 (backend limitation).
|
| 946 |
+
num_splits: 0 or 1 only (backend limitation >1).
|
| 947 |
+
rotary_cos/rotary_sin: Optional rotary embeddings (applied if provided) - interleaving flag unused here.
|
| 948 |
+
cache_batch_idx/cache_leftpad: Optional indexing / left padding metadata.
|
| 949 |
+
block_table: Optional paging table mapping logical blocks for paged KV cache.
|
| 950 |
+
alibi_slopes: (nheads,) or (batch,nheads) bias slopes (currently ignored if provided - placeholder).
|
| 951 |
+
rotary_interleaved: Flag kept for parity (currently forwarded as True constant to backend which ignores it).
|
| 952 |
+
return_softmax_lse: If True returns (out, lse) else out.
|
| 953 |
+
|
| 954 |
+
Returns:
|
| 955 |
+
out (and optionally softmax_lse): (batch, seqlen_q, nheads_q, headdim)
|
| 956 |
+
"""
|
| 957 |
+
# Feature guards / normalization
|
| 958 |
+
if softcap != 0.0:
|
| 959 |
+
raise NotImplementedError(
|
| 960 |
+
"softcap != 0 not supported in v2 KV cache backend yet"
|
| 961 |
+
)
|
| 962 |
+
if num_splits not in (0, 1):
|
| 963 |
+
raise NotImplementedError(
|
| 964 |
+
"num_splits > 1 not supported in v2 KV cache backend yet"
|
| 965 |
+
)
|
| 966 |
+
|
| 967 |
+
if softmax_scale is None:
|
| 968 |
+
softmax_scale = q.shape[-1] ** (-0.5)
|
| 969 |
+
|
| 970 |
+
if cache_seqlens is not None and isinstance(cache_seqlens, int):
|
| 971 |
+
cache_seqlens = torch.full(
|
| 972 |
+
(k_cache.shape[0],), cache_seqlens, dtype=torch.int32, device=k_cache.device
|
| 973 |
+
)
|
| 974 |
+
|
| 975 |
+
# Contiguity (align last dim contiguous requirement similar to v3 path assumptions)
|
| 976 |
+
assert q.stride(-1) == 1 and k_cache.stride(-1) == 1 and v_cache.stride(-1) == 1
|
| 977 |
+
|
| 978 |
+
out, softmax_lse = flash_attn_2.fwd_kvcache(
|
| 979 |
+
q,
|
| 980 |
+
k_cache,
|
| 981 |
+
v_cache,
|
| 982 |
+
k,
|
| 983 |
+
v,
|
| 984 |
+
cache_seqlens,
|
| 985 |
+
rotary_cos,
|
| 986 |
+
rotary_sin,
|
| 987 |
+
cache_batch_idx,
|
| 988 |
+
cache_leftpad,
|
| 989 |
+
block_table,
|
| 990 |
+
alibi_slopes,
|
| 991 |
+
None, # out tensor
|
| 992 |
+
softmax_scale,
|
| 993 |
+
causal,
|
| 994 |
+
int(window_size[0]),
|
| 995 |
+
int(window_size[1]),
|
| 996 |
+
0.0, # softcap (guarded)
|
| 997 |
+
rotary_interleaved,
|
| 998 |
+
num_splits,
|
| 999 |
+
)
|
| 1000 |
+
return (out, softmax_lse) if return_softmax_lse else out
|
build/torch-rocm/mha_fused_bwd.py
ADDED
|
@@ -0,0 +1,317 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# SPDX-License-Identifier: MIT
|
| 2 |
+
# Copyright (C) 2024-2026, Advanced Micro Devices, Inc. All rights reserved.
|
| 3 |
+
|
| 4 |
+
from typing import Optional, Dict
|
| 5 |
+
import torch
|
| 6 |
+
import triton
|
| 7 |
+
|
| 8 |
+
from .utils.types import _is_fp8
|
| 9 |
+
from .utils.logger import AiterTritonLogger
|
| 10 |
+
from ._kernels.mha_fused_bwd import (
|
| 11 |
+
_bwd_preprocess,
|
| 12 |
+
_bwd_kernel_dkdvdq_causal,
|
| 13 |
+
_bwd_kernel_dkdvdq_noncausal,
|
| 14 |
+
_get_config,
|
| 15 |
+
)
|
| 16 |
+
from .utils.device_info import get_num_xcds
|
| 17 |
+
|
| 18 |
+
_LOGGER = AiterTritonLogger()
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
def flash_attn_fused_backward(
|
| 22 |
+
do: torch.Tensor,
|
| 23 |
+
q: torch.Tensor,
|
| 24 |
+
k: torch.Tensor,
|
| 25 |
+
v: torch.Tensor,
|
| 26 |
+
o: torch.Tensor,
|
| 27 |
+
softmax_lse: torch.Tensor,
|
| 28 |
+
dq: torch.Tensor,
|
| 29 |
+
dk: torch.Tensor,
|
| 30 |
+
dv: torch.Tensor,
|
| 31 |
+
dbias: torch.Tensor,
|
| 32 |
+
sm_scale: float,
|
| 33 |
+
alibi_slopes: Optional[torch.Tensor],
|
| 34 |
+
causal: bool,
|
| 35 |
+
cu_seqlens_q: Optional[torch.Tensor],
|
| 36 |
+
cu_seqlens_k: Optional[torch.Tensor],
|
| 37 |
+
max_seqlen_q: int,
|
| 38 |
+
max_seqlen_k: int,
|
| 39 |
+
dropout_p: float,
|
| 40 |
+
philox_seed: Optional[int] = 0,
|
| 41 |
+
philox_offset: Optional[int] = 0,
|
| 42 |
+
descale_q: Optional[torch.Tensor] = None,
|
| 43 |
+
descale_k: Optional[torch.Tensor] = None,
|
| 44 |
+
descale_v: Optional[torch.Tensor] = None,
|
| 45 |
+
descale_do: Optional[torch.Tensor] = None,
|
| 46 |
+
USE_INT64_STRIDES: Optional[bool] = False,
|
| 47 |
+
config: Optional[Dict[str, any]] = None,
|
| 48 |
+
):
|
| 49 |
+
"""
|
| 50 |
+
Flash Attention fused backward pass computing dQ, dK, dV in a single kernel using atomics.
|
| 51 |
+
Supports variable-length sequences, GQA, FP8 quantization, and dropout.
|
| 52 |
+
|
| 53 |
+
Args:
|
| 54 |
+
do (torch.Tensor): Output gradient. Shape (batch, seqlen_q, num_q_heads, head_dim)
|
| 55 |
+
or (total_tokens, num_q_heads, head_dim) for varlen.
|
| 56 |
+
q (torch.Tensor): Query tensor from forward pass with same shape as do.
|
| 57 |
+
k (torch.Tensor): Key tensor with shape (batch, seqlen_k, num_k_heads, head_dim)
|
| 58 |
+
or (total_tokens_k, num_k_heads, head_dim) for varlen.
|
| 59 |
+
v (torch.Tensor): Value tensor with same shape as k.
|
| 60 |
+
o (torch.Tensor): Output from forward pass with same shape as q.
|
| 61 |
+
softmax_lse (torch.Tensor): Log-sum-exp from forward pass with shape
|
| 62 |
+
(batch, num_q_heads, seqlen_q) or (total_tokens, num_q_heads) for varlen.
|
| 63 |
+
dq (torch.Tensor): Pre-allocated query gradient with same shape as q.
|
| 64 |
+
dk (torch.Tensor): Pre-allocated key gradient with same shape as k.
|
| 65 |
+
dv (torch.Tensor): Pre-allocated value gradient with same shape as v.
|
| 66 |
+
dbias (torch.Tensor): Bias gradient (not supported, must be None).
|
| 67 |
+
sm_scale (float): Softmax scale, typically 1/sqrt(head_dim).
|
| 68 |
+
alibi_slopes (Optional[torch.Tensor]): ALiBi position bias slopes.
|
| 69 |
+
causal (bool): Apply causal masking.
|
| 70 |
+
cu_seqlens_q (Optional[torch.Tensor]): Cumulative sequence lengths for query with shape
|
| 71 |
+
(batch + 1,). Enables variable-length mode.
|
| 72 |
+
cu_seqlens_k (Optional[torch.Tensor]): Cumulative sequence lengths for key with shape
|
| 73 |
+
(batch + 1,).
|
| 74 |
+
max_seqlen_q (int): Maximum query sequence length in batch.
|
| 75 |
+
max_seqlen_k (int): Maximum key sequence length in batch.
|
| 76 |
+
dropout_p (float): Dropout probability. 0.0 disables dropout.
|
| 77 |
+
philox_seed (Optional[int]): Random seed for dropout.
|
| 78 |
+
philox_offset (Optional[int]): Random offset for dropout.
|
| 79 |
+
descale_q (Optional[torch.Tensor]): FP8 descaling factor for q.
|
| 80 |
+
descale_k (Optional[torch.Tensor]): FP8 descaling factor for k.
|
| 81 |
+
descale_v (Optional[torch.Tensor]): FP8 descaling factor for v.
|
| 82 |
+
descale_do (Optional[torch.Tensor]): FP8 descaling factor for do.
|
| 83 |
+
USE_INT64_STRIDES (Optional[bool]): Use 64-bit stride indexing for large tensors.
|
| 84 |
+
config (Optional[Dict[str, any]]): Kernel tuning parameters (preprocess_kernel,
|
| 85 |
+
dkdvdq_kernel_N64, dkdvdq_kernel_N128).
|
| 86 |
+
|
| 87 |
+
Returns:
|
| 88 |
+
torch.Tensor: Delta tensor (element-wise product of do and o) with shape matching softmax_lse.
|
| 89 |
+
"""
|
| 90 |
+
_LOGGER.info(
|
| 91 |
+
f"FLASH_ATTN_FUSED_BKWD: do={tuple(do.shape)} q={tuple(q.shape)} k={tuple(k.shape)} v={tuple(v.shape)} "
|
| 92 |
+
+ f"dq={tuple(dq.shape)} dk={tuple(dk.shape)} dv={tuple(dv.shape)}"
|
| 93 |
+
)
|
| 94 |
+
if dbias is not None:
|
| 95 |
+
raise ValueError("Bias is not supported yet in the Triton Backend")
|
| 96 |
+
if q.shape[-1] == k.shape[-1] and k.shape[-1] > v.shape[-1]:
|
| 97 |
+
raise ValueError(
|
| 98 |
+
"'Fused' backward doesn't support Positional Encoding (PE). Please use 'one kernel' backward implementation for PE."
|
| 99 |
+
)
|
| 100 |
+
|
| 101 |
+
IS_FP8 = _is_fp8(q)
|
| 102 |
+
if IS_FP8:
|
| 103 |
+
FP8_MAX = torch.finfo(q.dtype).max
|
| 104 |
+
descale_strides = (
|
| 105 |
+
descale_q.stride(0),
|
| 106 |
+
descale_k.stride(0),
|
| 107 |
+
descale_v.stride(0),
|
| 108 |
+
descale_do.stride(0),
|
| 109 |
+
)
|
| 110 |
+
else:
|
| 111 |
+
FP8_MAX = None
|
| 112 |
+
stride_descale_q_z = stride_descale_k_z = stride_descale_v_z = (
|
| 113 |
+
stride_descale_do_z
|
| 114 |
+
) = None
|
| 115 |
+
descale_strides = (
|
| 116 |
+
stride_descale_q_z,
|
| 117 |
+
stride_descale_k_z,
|
| 118 |
+
stride_descale_v_z,
|
| 119 |
+
stride_descale_do_z,
|
| 120 |
+
)
|
| 121 |
+
|
| 122 |
+
IS_VARLEN = True if cu_seqlens_q is not None else False
|
| 123 |
+
|
| 124 |
+
# get strides and shape
|
| 125 |
+
if IS_VARLEN:
|
| 126 |
+
# Layout for q,k,v is thd ie [total tokens, num_head, head_dim]
|
| 127 |
+
batch, seqlen_q, num_q_heads, head_sz = (
|
| 128 |
+
len(cu_seqlens_q) - 1,
|
| 129 |
+
max_seqlen_q,
|
| 130 |
+
q.shape[1],
|
| 131 |
+
q.shape[2],
|
| 132 |
+
)
|
| 133 |
+
_, num_k_heads = max_seqlen_k, k.shape[1]
|
| 134 |
+
q_strides = (0, q.stride(1), q.stride(0), q.stride(2))
|
| 135 |
+
q_strides = (0, q.stride(1), q.stride(0), q.stride(2))
|
| 136 |
+
k_strides = (0, k.stride(1), k.stride(0), k.stride(2))
|
| 137 |
+
v_strides = (0, v.stride(1), v.stride(0), v.stride(2))
|
| 138 |
+
o_strides = (0, o.stride(1), o.stride(0), o.stride(2))
|
| 139 |
+
dq_strides = (0, dq.stride(1), dq.stride(0), dq.stride(2))
|
| 140 |
+
dk_strides = (0, dk.stride(1), dk.stride(0), dk.stride(2))
|
| 141 |
+
do_strides = (0, do.stride(1), do.stride(0), do.stride(2))
|
| 142 |
+
else:
|
| 143 |
+
# Layout for q,k,v is bshd ie [batch, seq_len, num_head, head_dim]
|
| 144 |
+
batch, seqlen_q, num_q_heads, head_sz = q.shape
|
| 145 |
+
_, num_k_heads = k.shape[1], k.shape[2]
|
| 146 |
+
q_strides = (q.stride(0), q.stride(2), q.stride(1), q.stride(3))
|
| 147 |
+
k_strides = (k.stride(0), k.stride(2), k.stride(1), k.stride(3))
|
| 148 |
+
v_strides = (v.stride(0), v.stride(2), v.stride(1), v.stride(3))
|
| 149 |
+
o_strides = (o.stride(0), o.stride(2), o.stride(1), o.stride(3))
|
| 150 |
+
dq_strides = (dq.stride(0), dq.stride(2), dq.stride(1), dq.stride(3))
|
| 151 |
+
dk_strides = (dk.stride(0), dk.stride(2), dk.stride(1), dk.stride(3))
|
| 152 |
+
do_strides = (do.stride(0), do.stride(2), do.stride(1), do.stride(3))
|
| 153 |
+
|
| 154 |
+
# BLOCK_D_MODEL, BLOCK_D_MODEL_POW2
|
| 155 |
+
# padding for head_dim. Power of 2 or 16
|
| 156 |
+
BLOCK_D_MODEL_POW2 = triton.next_power_of_2(head_sz)
|
| 157 |
+
BLOCK_D_MODEL_POW2 = max(BLOCK_D_MODEL_POW2, 16)
|
| 158 |
+
|
| 159 |
+
# init delta
|
| 160 |
+
delta = torch.zeros_like(softmax_lse)
|
| 161 |
+
if IS_VARLEN:
|
| 162 |
+
# [total_tokens, num_q_heads, seqlen_q]
|
| 163 |
+
delta_strides = (0, delta.stride(1), delta.stride(0))
|
| 164 |
+
else:
|
| 165 |
+
# [batch, num_q_heads, seqlen_q]
|
| 166 |
+
delta_strides = delta.stride()
|
| 167 |
+
|
| 168 |
+
# preprocess
|
| 169 |
+
# compute D(delta) = rowsum(dO*O). Note, multiplication is element-wise.
|
| 170 |
+
if config is None:
|
| 171 |
+
config = _get_config()
|
| 172 |
+
|
| 173 |
+
pre_grid = (
|
| 174 |
+
triton.cdiv(max_seqlen_q, config["preprocess_kernel"]["PRE_BLOCK"]),
|
| 175 |
+
batch,
|
| 176 |
+
num_q_heads,
|
| 177 |
+
)
|
| 178 |
+
|
| 179 |
+
_bwd_preprocess[pre_grid](
|
| 180 |
+
o,
|
| 181 |
+
do,
|
| 182 |
+
delta,
|
| 183 |
+
*o_strides,
|
| 184 |
+
*do_strides,
|
| 185 |
+
*delta_strides,
|
| 186 |
+
descale_strides[3],
|
| 187 |
+
cu_seqlens_q,
|
| 188 |
+
max_seqlen_q,
|
| 189 |
+
descale_do,
|
| 190 |
+
BLOCK_M=config["preprocess_kernel"]["PRE_BLOCK"],
|
| 191 |
+
BLOCK_D_MODEL=head_sz,
|
| 192 |
+
BLOCK_D_MODEL_POW2=BLOCK_D_MODEL_POW2,
|
| 193 |
+
IS_VARLEN=IS_VARLEN,
|
| 194 |
+
IS_FP8=IS_FP8,
|
| 195 |
+
)
|
| 196 |
+
# dropout_mask
|
| 197 |
+
use_dropout = dropout_p > 0.0
|
| 198 |
+
if use_dropout:
|
| 199 |
+
dropout_mask = torch.zeros(
|
| 200 |
+
(batch, num_q_heads, max_seqlen_q, max_seqlen_k),
|
| 201 |
+
device=q.device,
|
| 202 |
+
dtype=torch.float32,
|
| 203 |
+
)
|
| 204 |
+
dropout_strides = dropout_mask.stride()
|
| 205 |
+
else:
|
| 206 |
+
dropout_mask = None
|
| 207 |
+
dropout_strides = (0, 0, 0, 0)
|
| 208 |
+
|
| 209 |
+
# Fuses dk,dv and dq computations into one kernel using atomics
|
| 210 |
+
if BLOCK_D_MODEL_POW2 > 160 or q.dtype == torch.float32:
|
| 211 |
+
config_dkdvdq = config["dkdvdq_kernel_N64"]
|
| 212 |
+
else:
|
| 213 |
+
config_dkdvdq = config["dkdvdq_kernel_N128"]
|
| 214 |
+
|
| 215 |
+
num_k_pids = (max_seqlen_k + config_dkdvdq["BLOCK_N"] - 1) // config_dkdvdq[
|
| 216 |
+
"BLOCK_N"
|
| 217 |
+
]
|
| 218 |
+
# NUM_SMS = torch.cuda.get_device_properties("cuda").multi_processor_count
|
| 219 |
+
if causal:
|
| 220 |
+
grid_dkdvdq = (batch * num_q_heads * num_k_pids,)
|
| 221 |
+
|
| 222 |
+
_bwd_kernel_dkdvdq_causal[grid_dkdvdq](
|
| 223 |
+
q,
|
| 224 |
+
k,
|
| 225 |
+
v,
|
| 226 |
+
sm_scale,
|
| 227 |
+
do,
|
| 228 |
+
dk,
|
| 229 |
+
dv,
|
| 230 |
+
dq,
|
| 231 |
+
softmax_lse,
|
| 232 |
+
delta,
|
| 233 |
+
*q_strides,
|
| 234 |
+
*k_strides,
|
| 235 |
+
*v_strides,
|
| 236 |
+
*dk_strides,
|
| 237 |
+
*dq_strides,
|
| 238 |
+
*delta_strides,
|
| 239 |
+
*do_strides,
|
| 240 |
+
*dropout_strides,
|
| 241 |
+
*descale_strides,
|
| 242 |
+
cu_seqlens_q,
|
| 243 |
+
cu_seqlens_k,
|
| 244 |
+
max_seqlen_q,
|
| 245 |
+
max_seqlen_k,
|
| 246 |
+
dropout_mask,
|
| 247 |
+
dropout_p,
|
| 248 |
+
philox_seed,
|
| 249 |
+
philox_offset,
|
| 250 |
+
descale_q,
|
| 251 |
+
descale_k,
|
| 252 |
+
descale_v,
|
| 253 |
+
descale_do,
|
| 254 |
+
NUM_Q_HEADS=num_q_heads,
|
| 255 |
+
NUM_K_HEADS=num_k_heads,
|
| 256 |
+
BATCH=batch,
|
| 257 |
+
NUM_K_PIDS=num_k_pids,
|
| 258 |
+
BLOCK_D_MODEL=head_sz,
|
| 259 |
+
BLOCK_D_MODEL_POW2=BLOCK_D_MODEL_POW2,
|
| 260 |
+
ENABLE_DROPOUT=use_dropout,
|
| 261 |
+
IS_VARLEN=IS_VARLEN,
|
| 262 |
+
IS_FP8=IS_FP8,
|
| 263 |
+
FP8_MAX=FP8_MAX,
|
| 264 |
+
USE_INT64_STRIDES=USE_INT64_STRIDES,
|
| 265 |
+
NUM_XCD=get_num_xcds(),
|
| 266 |
+
**config_dkdvdq,
|
| 267 |
+
)
|
| 268 |
+
else:
|
| 269 |
+
# in non causal inner loop over grouped q heads
|
| 270 |
+
grid_dkdvdq = (batch * num_k_heads * num_k_pids,)
|
| 271 |
+
_bwd_kernel_dkdvdq_noncausal[grid_dkdvdq](
|
| 272 |
+
q,
|
| 273 |
+
k,
|
| 274 |
+
v,
|
| 275 |
+
sm_scale,
|
| 276 |
+
do,
|
| 277 |
+
dk,
|
| 278 |
+
dv,
|
| 279 |
+
dq,
|
| 280 |
+
softmax_lse,
|
| 281 |
+
delta,
|
| 282 |
+
*q_strides,
|
| 283 |
+
*k_strides,
|
| 284 |
+
*v_strides,
|
| 285 |
+
*dk_strides,
|
| 286 |
+
*dq_strides,
|
| 287 |
+
*delta_strides,
|
| 288 |
+
*do_strides,
|
| 289 |
+
*dropout_strides,
|
| 290 |
+
*descale_strides,
|
| 291 |
+
cu_seqlens_q,
|
| 292 |
+
cu_seqlens_k,
|
| 293 |
+
max_seqlen_q,
|
| 294 |
+
max_seqlen_k,
|
| 295 |
+
dropout_mask,
|
| 296 |
+
dropout_p,
|
| 297 |
+
philox_seed,
|
| 298 |
+
philox_offset,
|
| 299 |
+
descale_q,
|
| 300 |
+
descale_k,
|
| 301 |
+
descale_v,
|
| 302 |
+
descale_do,
|
| 303 |
+
NUM_Q_HEADS=num_q_heads,
|
| 304 |
+
NUM_K_HEADS=num_k_heads,
|
| 305 |
+
BATCH=batch,
|
| 306 |
+
NUM_K_PIDS=num_k_pids,
|
| 307 |
+
BLOCK_D_MODEL=head_sz,
|
| 308 |
+
BLOCK_D_MODEL_POW2=BLOCK_D_MODEL_POW2,
|
| 309 |
+
ENABLE_DROPOUT=use_dropout,
|
| 310 |
+
IS_VARLEN=IS_VARLEN,
|
| 311 |
+
IS_FP8=IS_FP8,
|
| 312 |
+
FP8_MAX=FP8_MAX,
|
| 313 |
+
USE_INT64_STRIDES=USE_INT64_STRIDES,
|
| 314 |
+
**config_dkdvdq,
|
| 315 |
+
)
|
| 316 |
+
|
| 317 |
+
return delta
|
build/torch-rocm/mha_onekernel_bwd.py
ADDED
|
@@ -0,0 +1,380 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# SPDX-License-Identifier: MIT
|
| 2 |
+
# Copyright (C) 2024-2026, Advanced Micro Devices, Inc. All rights reserved.
|
| 3 |
+
|
| 4 |
+
from typing import Optional, Dict
|
| 5 |
+
import torch
|
| 6 |
+
import triton # type: ignore
|
| 7 |
+
import triton.language as tl # type: ignore
|
| 8 |
+
from .utils.types import _is_fp8
|
| 9 |
+
from .utils.logger import AiterTritonLogger
|
| 10 |
+
|
| 11 |
+
from ._kernels.mha_onekernel_bwd import (
|
| 12 |
+
_bwd_preprocess,
|
| 13 |
+
bwd_kernel_causal,
|
| 14 |
+
bwd_kernel_noncausal,
|
| 15 |
+
_get_config,
|
| 16 |
+
)
|
| 17 |
+
|
| 18 |
+
_LOGGER = AiterTritonLogger()
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
# NOTE: triton fails to import tl.constexprs so create them here for the file
|
| 22 |
+
DROPOUT_USE_PYTORCH = False
|
| 23 |
+
DROPOUT_DUMP = False
|
| 24 |
+
|
| 25 |
+
tl_DROPOUT_USE_PYTORCH: tl.constexpr = triton.language.constexpr(DROPOUT_USE_PYTORCH)
|
| 26 |
+
tl_DROPOUT_DUMP: tl.constexpr = triton.language.constexpr(DROPOUT_DUMP)
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
def flash_attn_onekernel_backward(
|
| 30 |
+
do: torch.Tensor,
|
| 31 |
+
q: torch.Tensor,
|
| 32 |
+
k: torch.Tensor,
|
| 33 |
+
v: torch.Tensor,
|
| 34 |
+
o: torch.Tensor,
|
| 35 |
+
softmax_lse: torch.Tensor,
|
| 36 |
+
dq: torch.Tensor,
|
| 37 |
+
dk: torch.Tensor,
|
| 38 |
+
dv: torch.Tensor,
|
| 39 |
+
dbias: torch.Tensor,
|
| 40 |
+
sm_scale: float,
|
| 41 |
+
alibi_slopes: Optional[torch.Tensor],
|
| 42 |
+
causal: bool,
|
| 43 |
+
cu_seqlens_q: Optional[torch.Tensor],
|
| 44 |
+
cu_seqlens_k: Optional[torch.Tensor],
|
| 45 |
+
max_seqlen_q: int,
|
| 46 |
+
max_seqlen_k: int,
|
| 47 |
+
dropout_p: float,
|
| 48 |
+
philox_seed: Optional[int] = 0,
|
| 49 |
+
philox_offset: Optional[int] = 0,
|
| 50 |
+
descale_q: Optional[torch.Tensor] = None,
|
| 51 |
+
descale_k: Optional[torch.Tensor] = None,
|
| 52 |
+
descale_v: Optional[torch.Tensor] = None,
|
| 53 |
+
descale_do: Optional[torch.Tensor] = None,
|
| 54 |
+
USE_INT64_STRIDES: Optional[bool] = False,
|
| 55 |
+
sink: Optional[torch.Tensor] = None,
|
| 56 |
+
dsink: Optional[torch.Tensor] = None,
|
| 57 |
+
config: Optional[Dict[str, any]] = None,
|
| 58 |
+
sliding_window: int = 0,
|
| 59 |
+
):
|
| 60 |
+
"""
|
| 61 |
+
Flash Attention one-kernel backward pass with positional encoding support.
|
| 62 |
+
Computes dQ, dK, dV in separate passes without atomics. Supports Q/K head dimensions
|
| 63 |
+
larger than V for positional encoding.
|
| 64 |
+
|
| 65 |
+
Args:
|
| 66 |
+
do (torch.Tensor): Output gradient. Shape (batch, seqlen_q, num_q_heads, v_head_dim)
|
| 67 |
+
or (total_tokens, num_q_heads, v_head_dim) for varlen.
|
| 68 |
+
q (torch.Tensor): Query tensor with shape (batch, seqlen_q, num_q_heads, qk_head_dim).
|
| 69 |
+
qk_head_dim may be larger than v_head_dim for positional encoding.
|
| 70 |
+
k (torch.Tensor): Key tensor with shape (batch, seqlen_k, num_k_heads, qk_head_dim).
|
| 71 |
+
v (torch.Tensor): Value tensor with shape (batch, seqlen_k, num_k_heads, v_head_dim).
|
| 72 |
+
o (torch.Tensor): Output from forward pass with same shape as do.
|
| 73 |
+
softmax_lse (torch.Tensor): Log-sum-exp from forward pass with shape
|
| 74 |
+
(batch, num_q_heads, seqlen_q) or (total_tokens, num_q_heads) for varlen.
|
| 75 |
+
dq (torch.Tensor): Pre-allocated query gradient with same shape as q.
|
| 76 |
+
dk (torch.Tensor): Pre-allocated key gradient with same shape as k.
|
| 77 |
+
dv (torch.Tensor): Pre-allocated value gradient with same shape as v.
|
| 78 |
+
dbias (torch.Tensor): Bias gradient (not supported, must be None).
|
| 79 |
+
sm_scale (float): Softmax scale, typically 1/sqrt(head_dim).
|
| 80 |
+
alibi_slopes (Optional[torch.Tensor]): ALiBi position bias slopes with shape (num_q_heads,).
|
| 81 |
+
causal (bool): Apply causal masking.
|
| 82 |
+
cu_seqlens_q (Optional[torch.Tensor]): Cumulative sequence lengths for query with shape
|
| 83 |
+
(batch + 1,). Enables variable-length mode.
|
| 84 |
+
cu_seqlens_k (Optional[torch.Tensor]): Cumulative sequence lengths for key with shape
|
| 85 |
+
(batch + 1,).
|
| 86 |
+
max_seqlen_q (int): Maximum query sequence length in batch.
|
| 87 |
+
max_seqlen_k (int): Maximum key sequence length in batch.
|
| 88 |
+
dropout_p (float): Dropout probability. 0.0 disables dropout.
|
| 89 |
+
philox_seed (Optional[int]): Random seed for dropout.
|
| 90 |
+
philox_offset (Optional[int]): Random offset for dropout.
|
| 91 |
+
descale_q (Optional[torch.Tensor]): FP8 descaling factor for q.
|
| 92 |
+
descale_k (Optional[torch.Tensor]): FP8 descaling factor for k.
|
| 93 |
+
descale_v (Optional[torch.Tensor]): FP8 descaling factor for v.
|
| 94 |
+
descale_do (Optional[torch.Tensor]): FP8 descaling factor for do.
|
| 95 |
+
USE_INT64_STRIDES (Optional[bool]): Use 64-bit stride indexing for large tensors.
|
| 96 |
+
sink (Optional[torch.Tensor]): Attention sink scores (one per Q head). Shape (num_q_heads,).
|
| 97 |
+
dsink (Optional[torch.Tensor]): Pre-allocated sink gradient with same shape as sink.
|
| 98 |
+
config (Optional[Dict[str, any]]): Kernel tuning parameters (preprocess_kernel,
|
| 99 |
+
onekernel, onekernel_pe).
|
| 100 |
+
|
| 101 |
+
Returns:
|
| 102 |
+
torch.Tensor: Delta tensor (element-wise product of do and o) with shape matching softmax_lse.
|
| 103 |
+
"""
|
| 104 |
+
_LOGGER.info(
|
| 105 |
+
f"FLASH_ATTN_ONEKERNEL_BKWD: do={tuple(do.shape)} q={tuple(q.shape)} k={tuple(k.shape)} v={tuple(v.shape)} "
|
| 106 |
+
+ f"dq={tuple(dq.shape)} dk={tuple(dk.shape)} dv={tuple(dv.shape)}"
|
| 107 |
+
)
|
| 108 |
+
if dbias is not None:
|
| 109 |
+
raise ValueError("Bias is not supported yet in the Triton Backend")
|
| 110 |
+
|
| 111 |
+
use_alibi, (stride_az, stride_ah) = (
|
| 112 |
+
(True, alibi_slopes.stride()) if alibi_slopes is not None else (False, (0, 0))
|
| 113 |
+
)
|
| 114 |
+
|
| 115 |
+
IS_FP8 = _is_fp8(q)
|
| 116 |
+
if IS_FP8:
|
| 117 |
+
FP8_MAX = torch.finfo(q.dtype).max
|
| 118 |
+
descale_strides = (
|
| 119 |
+
descale_q.stride(0),
|
| 120 |
+
descale_k.stride(0),
|
| 121 |
+
descale_v.stride(0),
|
| 122 |
+
descale_do.stride(0),
|
| 123 |
+
)
|
| 124 |
+
else:
|
| 125 |
+
FP8_MAX = None
|
| 126 |
+
stride_descale_q_z = stride_descale_k_z = stride_descale_v_z = (
|
| 127 |
+
stride_descale_do_z
|
| 128 |
+
) = None
|
| 129 |
+
descale_strides = (
|
| 130 |
+
stride_descale_q_z,
|
| 131 |
+
stride_descale_k_z,
|
| 132 |
+
stride_descale_v_z,
|
| 133 |
+
stride_descale_do_z,
|
| 134 |
+
)
|
| 135 |
+
|
| 136 |
+
IS_VARLEN = True if cu_seqlens_q is not None else False
|
| 137 |
+
|
| 138 |
+
# get strides and shape
|
| 139 |
+
if IS_VARLEN:
|
| 140 |
+
# Layout is thd.
|
| 141 |
+
# q and k are [total_tokens, num_head, head_dim_qk].
|
| 142 |
+
# v is [total_tokens, num_head, head_dim_v].
|
| 143 |
+
batch, seqlen_q, num_q_heads = (
|
| 144 |
+
len(cu_seqlens_q) - 1,
|
| 145 |
+
max_seqlen_q,
|
| 146 |
+
q.shape[1],
|
| 147 |
+
)
|
| 148 |
+
_, num_k_heads = max_seqlen_k, k.shape[1]
|
| 149 |
+
q_strides = (0, q.stride(1), q.stride(0), q.stride(2))
|
| 150 |
+
q_strides = (0, q.stride(1), q.stride(0), q.stride(2))
|
| 151 |
+
k_strides = (0, k.stride(1), k.stride(0), k.stride(2))
|
| 152 |
+
v_strides = (0, v.stride(1), v.stride(0), v.stride(2))
|
| 153 |
+
o_strides = (0, o.stride(1), o.stride(0), o.stride(2))
|
| 154 |
+
dq_strides = (0, dq.stride(1), dq.stride(0), dq.stride(2))
|
| 155 |
+
dk_strides = (0, dk.stride(1), dk.stride(0), dk.stride(2))
|
| 156 |
+
dv_strides = (0, dv.stride(1), dv.stride(0), dv.stride(2))
|
| 157 |
+
do_strides = (0, do.stride(1), do.stride(0), do.stride(2))
|
| 158 |
+
else:
|
| 159 |
+
# Layout is bshd.
|
| 160 |
+
# q and k are [batch, seq_len, num_head, head_dim_qk].
|
| 161 |
+
# v is [batch, seq_len, num_head, head_dim_v]
|
| 162 |
+
batch, seqlen_q, num_q_heads = q.shape[:-1]
|
| 163 |
+
_, num_k_heads = k.shape[1], k.shape[2]
|
| 164 |
+
q_strides = (q.stride(0), q.stride(2), q.stride(1), q.stride(3))
|
| 165 |
+
k_strides = (k.stride(0), k.stride(2), k.stride(1), k.stride(3))
|
| 166 |
+
v_strides = (v.stride(0), v.stride(2), v.stride(1), v.stride(3))
|
| 167 |
+
o_strides = (o.stride(0), o.stride(2), o.stride(1), o.stride(3))
|
| 168 |
+
dq_strides = (dq.stride(0), dq.stride(2), dq.stride(1), dq.stride(3))
|
| 169 |
+
dk_strides = (dk.stride(0), dk.stride(2), dk.stride(1), dk.stride(3))
|
| 170 |
+
dv_strides = (dv.stride(0), dv.stride(2), dv.stride(1), dv.stride(3))
|
| 171 |
+
do_strides = (do.stride(0), do.stride(2), do.stride(1), do.stride(3))
|
| 172 |
+
|
| 173 |
+
qk_head_dim = q.shape[-1]
|
| 174 |
+
v_head_dim = v.shape[-1]
|
| 175 |
+
pe_head_dim = qk_head_dim - v_head_dim
|
| 176 |
+
# BLOCK_D_MODEL, BLOCK_D_MODEL_POW2
|
| 177 |
+
# padding for head_dim. Power of 2 or 16
|
| 178 |
+
BLOCK_D_MODEL_POW2 = max(triton.next_power_of_2(v_head_dim), 16)
|
| 179 |
+
BLOCK_D_MODEL_PE_POW2 = (
|
| 180 |
+
0 if pe_head_dim == 0 else max(triton.next_power_of_2(pe_head_dim), 16)
|
| 181 |
+
)
|
| 182 |
+
assert (pe_head_dim == 0 and BLOCK_D_MODEL_PE_POW2 == 0) or (
|
| 183 |
+
v_head_dim == BLOCK_D_MODEL_POW2 and pe_head_dim == BLOCK_D_MODEL_PE_POW2
|
| 184 |
+
), "Positional encoding support requires NOPE and PE head sizes to be unpadded powers of 2."
|
| 185 |
+
assert (not IS_FP8) or (
|
| 186 |
+
IS_FP8 and pe_head_dim == 0
|
| 187 |
+
), "Positional encoding doesn't support FP8."
|
| 188 |
+
|
| 189 |
+
assert (sink is None) or (
|
| 190 |
+
sink is not None and sink.dim() == 1 and sink.shape[0] == num_q_heads
|
| 191 |
+
), "Sink must be 1D and have one element per query head."
|
| 192 |
+
assert (dsink is None) or (
|
| 193 |
+
dsink is not None and dsink.dim() == 1 and dsink.shape[0] == num_q_heads
|
| 194 |
+
), "Sink gradient must be 1D and have one element per query head."
|
| 195 |
+
assert (sink is None) == (
|
| 196 |
+
dsink is None
|
| 197 |
+
), "Sink and its gradient must be both present or absent."
|
| 198 |
+
|
| 199 |
+
# Configs
|
| 200 |
+
if config is None:
|
| 201 |
+
config = _get_config()
|
| 202 |
+
|
| 203 |
+
# init delta
|
| 204 |
+
delta = torch.zeros_like(softmax_lse)
|
| 205 |
+
if IS_VARLEN:
|
| 206 |
+
# [total_tokens, num_q_heads, seqlen_q]
|
| 207 |
+
delta_strides = (0, delta.stride(1), delta.stride(0))
|
| 208 |
+
else:
|
| 209 |
+
# [batch, num_q_heads, seqlen_q]
|
| 210 |
+
delta_strides = delta.stride()
|
| 211 |
+
|
| 212 |
+
# preprocess
|
| 213 |
+
# compute D(delta) = rowsum(dO*O). Note, multiplication is element-wise.
|
| 214 |
+
pre_grid = (
|
| 215 |
+
triton.cdiv(max_seqlen_q, config["preprocess_kernel"]["PRE_BLOCK"]),
|
| 216 |
+
batch,
|
| 217 |
+
num_q_heads,
|
| 218 |
+
)
|
| 219 |
+
_bwd_preprocess[pre_grid](
|
| 220 |
+
o,
|
| 221 |
+
do,
|
| 222 |
+
delta,
|
| 223 |
+
*o_strides,
|
| 224 |
+
*do_strides,
|
| 225 |
+
*delta_strides,
|
| 226 |
+
descale_strides[3],
|
| 227 |
+
cu_seqlens_q,
|
| 228 |
+
max_seqlen_q,
|
| 229 |
+
descale_do,
|
| 230 |
+
BLOCK_M=config["preprocess_kernel"]["PRE_BLOCK"],
|
| 231 |
+
BLOCK_D_MODEL=v_head_dim,
|
| 232 |
+
BLOCK_D_MODEL_POW2=BLOCK_D_MODEL_POW2,
|
| 233 |
+
IS_VARLEN=IS_VARLEN,
|
| 234 |
+
IS_FP8=IS_FP8,
|
| 235 |
+
)
|
| 236 |
+
|
| 237 |
+
# dropout_mask
|
| 238 |
+
use_dropout = dropout_p > 0.0
|
| 239 |
+
if use_dropout:
|
| 240 |
+
dropout_mask = torch.zeros(
|
| 241 |
+
(batch, num_q_heads, max_seqlen_q, max_seqlen_k),
|
| 242 |
+
device=q.device,
|
| 243 |
+
dtype=torch.float32,
|
| 244 |
+
)
|
| 245 |
+
dropout_strides = dropout_mask.stride()
|
| 246 |
+
else:
|
| 247 |
+
dropout_mask = None
|
| 248 |
+
dropout_strides = (0, 0, 0, 0)
|
| 249 |
+
|
| 250 |
+
seqlen = max(max_seqlen_q, max_seqlen_k)
|
| 251 |
+
|
| 252 |
+
# "onekernel_pe" is for Positional Encoding (PE) causal case, it's going to be
|
| 253 |
+
# used if present. Otherwise, fallback to default "onekernel" config.
|
| 254 |
+
config_onekernel = (
|
| 255 |
+
config["onekernel_pe"]
|
| 256 |
+
if (pe_head_dim > 0 and causal and "onekernel_pe" in config)
|
| 257 |
+
else config["onekernel"]
|
| 258 |
+
)
|
| 259 |
+
grid = (
|
| 260 |
+
num_k_heads,
|
| 261 |
+
triton.cdiv(seqlen, config_onekernel["BLOCK_N1"]),
|
| 262 |
+
batch,
|
| 263 |
+
)
|
| 264 |
+
|
| 265 |
+
if causal:
|
| 266 |
+
bwd_kernel_causal[grid](
|
| 267 |
+
q,
|
| 268 |
+
k,
|
| 269 |
+
v,
|
| 270 |
+
sink,
|
| 271 |
+
sm_scale,
|
| 272 |
+
do,
|
| 273 |
+
dq,
|
| 274 |
+
dk,
|
| 275 |
+
dv,
|
| 276 |
+
dsink,
|
| 277 |
+
softmax_lse,
|
| 278 |
+
delta,
|
| 279 |
+
*q_strides,
|
| 280 |
+
*k_strides,
|
| 281 |
+
*v_strides,
|
| 282 |
+
*dq_strides,
|
| 283 |
+
*dk_strides,
|
| 284 |
+
*dv_strides,
|
| 285 |
+
*delta_strides,
|
| 286 |
+
*do_strides,
|
| 287 |
+
*dropout_strides,
|
| 288 |
+
*descale_strides,
|
| 289 |
+
stride_az,
|
| 290 |
+
stride_ah,
|
| 291 |
+
num_q_heads,
|
| 292 |
+
num_k_heads,
|
| 293 |
+
cu_seqlens_q,
|
| 294 |
+
cu_seqlens_k,
|
| 295 |
+
max_seqlen_q,
|
| 296 |
+
max_seqlen_k,
|
| 297 |
+
dropout_mask,
|
| 298 |
+
dropout_p,
|
| 299 |
+
philox_seed,
|
| 300 |
+
philox_offset,
|
| 301 |
+
alibi_slopes,
|
| 302 |
+
descale_q,
|
| 303 |
+
descale_k,
|
| 304 |
+
descale_v,
|
| 305 |
+
descale_do,
|
| 306 |
+
HEAD_DIM=BLOCK_D_MODEL_POW2,
|
| 307 |
+
ACTUAL_HEAD_DIM=v_head_dim,
|
| 308 |
+
PE_HEAD_DIM=pe_head_dim,
|
| 309 |
+
ENABLE_DROPOUT=use_dropout,
|
| 310 |
+
IS_VARLEN=IS_VARLEN,
|
| 311 |
+
USE_ALIBI=use_alibi,
|
| 312 |
+
USE_EXP2=True,
|
| 313 |
+
IS_FP8=IS_FP8,
|
| 314 |
+
FP8_MAX=FP8_MAX,
|
| 315 |
+
DEBUG_TRITON=False,
|
| 316 |
+
DEBUG_TRITON_DETAIL=False,
|
| 317 |
+
USE_INT64_STRIDES=USE_INT64_STRIDES,
|
| 318 |
+
ENABLE_SINK=sink is not None,
|
| 319 |
+
SLIDING_WINDOW=sliding_window,
|
| 320 |
+
**config_onekernel,
|
| 321 |
+
)
|
| 322 |
+
else:
|
| 323 |
+
bwd_kernel_noncausal[grid](
|
| 324 |
+
q,
|
| 325 |
+
k,
|
| 326 |
+
v,
|
| 327 |
+
sink,
|
| 328 |
+
sm_scale,
|
| 329 |
+
do,
|
| 330 |
+
dq,
|
| 331 |
+
dk,
|
| 332 |
+
dv,
|
| 333 |
+
dsink,
|
| 334 |
+
softmax_lse,
|
| 335 |
+
delta,
|
| 336 |
+
*q_strides,
|
| 337 |
+
*k_strides,
|
| 338 |
+
*v_strides,
|
| 339 |
+
*dq_strides,
|
| 340 |
+
*dk_strides,
|
| 341 |
+
*dv_strides,
|
| 342 |
+
*delta_strides,
|
| 343 |
+
*do_strides,
|
| 344 |
+
*dropout_strides,
|
| 345 |
+
*descale_strides,
|
| 346 |
+
stride_az,
|
| 347 |
+
stride_ah,
|
| 348 |
+
num_q_heads,
|
| 349 |
+
num_k_heads,
|
| 350 |
+
cu_seqlens_q,
|
| 351 |
+
cu_seqlens_k,
|
| 352 |
+
max_seqlen_q,
|
| 353 |
+
max_seqlen_k,
|
| 354 |
+
dropout_mask,
|
| 355 |
+
dropout_p,
|
| 356 |
+
philox_seed,
|
| 357 |
+
philox_offset,
|
| 358 |
+
alibi_slopes,
|
| 359 |
+
descale_q,
|
| 360 |
+
descale_k,
|
| 361 |
+
descale_v,
|
| 362 |
+
descale_do,
|
| 363 |
+
HEAD_DIM=BLOCK_D_MODEL_POW2,
|
| 364 |
+
ACTUAL_HEAD_DIM=v_head_dim,
|
| 365 |
+
PE_HEAD_DIM=pe_head_dim,
|
| 366 |
+
ENABLE_DROPOUT=use_dropout,
|
| 367 |
+
IS_VARLEN=IS_VARLEN,
|
| 368 |
+
USE_ALIBI=use_alibi,
|
| 369 |
+
USE_EXP2=True,
|
| 370 |
+
IS_FP8=IS_FP8,
|
| 371 |
+
FP8_MAX=FP8_MAX,
|
| 372 |
+
DEBUG_TRITON=False,
|
| 373 |
+
DEBUG_TRITON_DETAIL=False,
|
| 374 |
+
USE_INT64_STRIDES=USE_INT64_STRIDES,
|
| 375 |
+
ENABLE_SINK=sink is not None,
|
| 376 |
+
SLIDING_WINDOW=sliding_window,
|
| 377 |
+
**config_onekernel,
|
| 378 |
+
)
|
| 379 |
+
|
| 380 |
+
return delta
|
build/torch-rocm/utils/__init__.py
ADDED
|
File without changes
|
build/torch-rocm/utils/_triton/__init__.py
ADDED
|
File without changes
|
build/torch-rocm/utils/_triton/arch_info.py
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import triton
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
_CACHED_ARCH = None
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
def get_arch():
|
| 8 |
+
# Lazy-detect on first call so the module is importable in environments without an
|
| 9 |
+
# active GPU driver (e.g. the kernel-builder Nix build sandbox running
|
| 10 |
+
# `get_local_kernel` for layout checks).
|
| 11 |
+
global _CACHED_ARCH
|
| 12 |
+
if _CACHED_ARCH is None:
|
| 13 |
+
try:
|
| 14 |
+
_CACHED_ARCH = triton.runtime.driver.active.get_current_target().arch
|
| 15 |
+
except RuntimeError:
|
| 16 |
+
try:
|
| 17 |
+
from jax._src.lib import gpu_triton as triton_kernel_call_lib
|
| 18 |
+
|
| 19 |
+
_CACHED_ARCH = triton_kernel_call_lib.get_arch_details("0").split(":")[0]
|
| 20 |
+
except ImportError:
|
| 21 |
+
_CACHED_ARCH = ""
|
| 22 |
+
return _CACHED_ARCH
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
def is_gluon_avail():
|
| 26 |
+
return get_arch() in ("gfx950", "gfx1250")
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
def is_fp4_avail():
|
| 30 |
+
return get_arch() in ("gfx950", "gfx1250")
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
def is_fp8_avail():
|
| 34 |
+
return get_arch() in ("gfx942", "gfx950", "gfx1250", "gfx1200", "gfx1201")
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
def is_mx_scale_preshuffling_avail():
|
| 38 |
+
return get_arch() in ("gfx950", "gfx1250")
|
| 39 |
+
|
| 40 |
+
|
| 41 |
+
def is_tdm_avail():
|
| 42 |
+
return get_arch() in ("gfx1250",)
|
build/torch-rocm/utils/_triton/kernel_repr.py
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
def _sanitize_constexpr_value(value):
|
| 2 |
+
if value is None:
|
| 3 |
+
return "NONE"
|
| 4 |
+
if isinstance(value, bool):
|
| 5 |
+
return str(int(value))
|
| 6 |
+
if isinstance(value, int):
|
| 7 |
+
return str(value)
|
| 8 |
+
if isinstance(value, float):
|
| 9 |
+
if value.is_integer():
|
| 10 |
+
return str(int(value))
|
| 11 |
+
return str(value)
|
| 12 |
+
|
| 13 |
+
# for lists, tuples, sets - recursively join each
|
| 14 |
+
if isinstance(value, (list, tuple, set)):
|
| 15 |
+
items = sorted(value, key=str) if isinstance(value, set) else value
|
| 16 |
+
sanitized_items = [_sanitize_constexpr_value(item) for item in items]
|
| 17 |
+
joined = "_".join(sanitized_items)
|
| 18 |
+
return joined if joined else "NONE"
|
| 19 |
+
|
| 20 |
+
if isinstance(value, str):
|
| 21 |
+
cleaned_value = "".join(ch if ch.isalnum() else "_" for ch in value).strip("_")
|
| 22 |
+
return cleaned_value.upper() if cleaned_value else "NONE"
|
| 23 |
+
|
| 24 |
+
cleaned_value = "".join(ch if ch.isalnum() else "_" for ch in str(value)).strip("_")
|
| 25 |
+
return cleaned_value.upper() if cleaned_value else "NONE"
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
def make_kernel_repr(base_name, config_keys, name_key=None):
|
| 29 |
+
# When name_key is set, the base name is taken from the matching constexpr
|
| 30 |
+
# kwarg at call time (falling back to base_name if missing/empty). Lets a
|
| 31 |
+
# single shared kernel produce caller-specific names in compiled artifacts.
|
| 32 |
+
def _repr(specialization):
|
| 33 |
+
constants = specialization.constants
|
| 34 |
+
|
| 35 |
+
name = base_name
|
| 36 |
+
if name_key is not None:
|
| 37 |
+
override = constants.get(name_key, None)
|
| 38 |
+
if override:
|
| 39 |
+
cleaned = "".join(
|
| 40 |
+
ch if ch.isalnum() or ch == "_" else "_" for ch in str(override)
|
| 41 |
+
)
|
| 42 |
+
if cleaned:
|
| 43 |
+
name = cleaned
|
| 44 |
+
|
| 45 |
+
name_parts = []
|
| 46 |
+
for key in config_keys:
|
| 47 |
+
value = constants.get(key, None)
|
| 48 |
+
symbol = _sanitize_constexpr_value(value)
|
| 49 |
+
name_parts.append(f"{key}_{symbol}")
|
| 50 |
+
|
| 51 |
+
if not name_parts:
|
| 52 |
+
return name
|
| 53 |
+
|
| 54 |
+
suffix = "_".join(name_parts)
|
| 55 |
+
return f"{name}_{suffix}"
|
| 56 |
+
|
| 57 |
+
return _repr
|
build/torch-rocm/utils/_triton/mha_kernel_utils.py
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# SPDX-License-Identifier: MIT
|
| 2 |
+
# Copyright (C) 2024-2025, Advanced Micro Devices, Inc. All rights reserved.
|
| 3 |
+
|
| 4 |
+
import triton
|
| 5 |
+
import triton.language as tl
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
@triton.jit
|
| 9 |
+
def _compute_fp8_scaling_factors(x, fp8_max: tl.constexpr):
|
| 10 |
+
# compute fp8 scaling and descaling factor for a block
|
| 11 |
+
x_amax = tl.max(tl.abs(x)) # NOTE: abs deals with negative values
|
| 12 |
+
x_amax = tl.where(x_amax <= 1e-9, 1e-9, x_amax)
|
| 13 |
+
scale_x = fp8_max / x_amax
|
| 14 |
+
descale_x = x_amax / fp8_max
|
| 15 |
+
return scale_x, descale_x
|
build/torch-rocm/utils/_triton/pid_preprocessing.py
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# SPDX-License-Identifier: MIT
|
| 2 |
+
|
| 3 |
+
# Copyright (C) 2024-2026, Advanced Micro Devices, Inc. All rights reserved.
|
| 4 |
+
|
| 5 |
+
import triton
|
| 6 |
+
import triton.language as tl
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
@triton.jit
|
| 10 |
+
def remap_xcd_chunked(
|
| 11 |
+
pid, GRID_MN, NUM_XCDS: tl.constexpr = 8, CHUNK_SIZE: tl.constexpr = 2
|
| 12 |
+
):
|
| 13 |
+
# Compute current XCD and local PID
|
| 14 |
+
xcd = pid % NUM_XCDS
|
| 15 |
+
# distribute the modulo pids in round robin
|
| 16 |
+
if pid > (GRID_MN // (NUM_XCDS * CHUNK_SIZE)) * (NUM_XCDS * CHUNK_SIZE):
|
| 17 |
+
return pid
|
| 18 |
+
local_pid = pid // NUM_XCDS
|
| 19 |
+
# Calculate chunk index and position within chunk
|
| 20 |
+
chunk_idx = local_pid // CHUNK_SIZE
|
| 21 |
+
pos_in_chunk = local_pid % CHUNK_SIZE
|
| 22 |
+
# Calculate new PID
|
| 23 |
+
new_pid = chunk_idx * NUM_XCDS * CHUNK_SIZE + xcd * CHUNK_SIZE + pos_in_chunk
|
| 24 |
+
return new_pid
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
@triton.jit
|
| 28 |
+
def remap_xcd(pid, GRID_MN, NUM_XCDS: tl.constexpr = 8):
|
| 29 |
+
## pid remapping on xcds
|
| 30 |
+
# Number of pids per XCD in the new arrangement
|
| 31 |
+
pids_per_xcd = (GRID_MN + NUM_XCDS - 1) // NUM_XCDS
|
| 32 |
+
# When GRID_MN cannot divide NUM_XCDS, some xcds will have
|
| 33 |
+
# pids_per_xcd pids, the other will have pids_per_xcd - 1 pids.
|
| 34 |
+
# We calculate the number of xcds that have pids_per_xcd pids as
|
| 35 |
+
# tall_xcds
|
| 36 |
+
tall_xcds = GRID_MN % NUM_XCDS
|
| 37 |
+
tall_xcds = NUM_XCDS if tall_xcds == 0 else tall_xcds
|
| 38 |
+
# Compute current XCD and local pid within the XCD
|
| 39 |
+
xcd = pid % NUM_XCDS
|
| 40 |
+
local_pid = pid // NUM_XCDS
|
| 41 |
+
# Calculate new pid based on the new grouping
|
| 42 |
+
# Note that we need to consider the following two cases:
|
| 43 |
+
# 1. the current pid is on a tall xcd
|
| 44 |
+
# 2. the current pid is on a short xcd
|
| 45 |
+
if xcd < tall_xcds:
|
| 46 |
+
pid = xcd * pids_per_xcd + local_pid
|
| 47 |
+
else:
|
| 48 |
+
pid = (
|
| 49 |
+
tall_xcds * pids_per_xcd
|
| 50 |
+
+ (xcd - tall_xcds) * (pids_per_xcd - 1)
|
| 51 |
+
+ local_pid
|
| 52 |
+
)
|
| 53 |
+
|
| 54 |
+
return pid
|
| 55 |
+
|
| 56 |
+
|
| 57 |
+
@triton.jit
|
| 58 |
+
def pid_grid(pid: int, num_pid_m: int, num_pid_n: int, GROUP_SIZE_M: tl.constexpr = 1):
|
| 59 |
+
"""
|
| 60 |
+
Maps 1D pid to 2D grid coords (pid_m, pid_n).
|
| 61 |
+
|
| 62 |
+
Args:
|
| 63 |
+
- pid: 1D pid
|
| 64 |
+
- num_pid_m: grid m size
|
| 65 |
+
- num_pid_n: grid n size
|
| 66 |
+
- GROUP_SIZE_M: tl.constexpr: default is 1
|
| 67 |
+
"""
|
| 68 |
+
if GROUP_SIZE_M == 1:
|
| 69 |
+
pid_m = pid // num_pid_n
|
| 70 |
+
pid_n = pid % num_pid_n
|
| 71 |
+
else:
|
| 72 |
+
num_pid_in_group = GROUP_SIZE_M * num_pid_n
|
| 73 |
+
group_id = pid // num_pid_in_group
|
| 74 |
+
first_pid_m = group_id * GROUP_SIZE_M
|
| 75 |
+
group_size_m = min(num_pid_m - first_pid_m, GROUP_SIZE_M)
|
| 76 |
+
tl.assume(group_size_m >= 0)
|
| 77 |
+
pid_m = first_pid_m + (pid % group_size_m)
|
| 78 |
+
pid_n = (pid % num_pid_in_group) // group_size_m
|
| 79 |
+
|
| 80 |
+
return pid_m, pid_n
|
| 81 |
+
|
| 82 |
+
|
| 83 |
+
@triton.jit
|
| 84 |
+
def pid_grid_3d(pid: int, num_pid_m: int, num_pid_n: int, num_pid_k):
|
| 85 |
+
"""
|
| 86 |
+
Maps 1D pid to 3D grid coords (pid_m, pid_n, pid_k).
|
| 87 |
+
Args:
|
| 88 |
+
- pid: 1D pid
|
| 89 |
+
- num_pid_m: grid m size
|
| 90 |
+
- num_pid_n: grid n size
|
| 91 |
+
- num_pid_k: grid k size
|
| 92 |
+
|
| 93 |
+
Returns:
|
| 94 |
+
- pid_m, pid_n, pid_k: 3D grid coordinates
|
| 95 |
+
"""
|
| 96 |
+
pid_m = pid % num_pid_m
|
| 97 |
+
pid_n = (pid // num_pid_m) % num_pid_n
|
| 98 |
+
pid_k = pid // (num_pid_m * num_pid_n) % num_pid_k
|
| 99 |
+
|
| 100 |
+
return pid_m, pid_n, pid_k
|
build/torch-rocm/utils/core.py
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
|
| 3 |
+
this_dir = os.path.dirname(os.path.abspath(__file__))
|
| 4 |
+
AITER_TRITON_OPS_PATH = os.path.abspath(f"{this_dir}/../")
|
| 5 |
+
AITER_TRITON_CONFIGS_PATH = os.path.abspath(f"{this_dir}/../configs")
|
build/torch-rocm/utils/device_info.py
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
def get_num_sms():
|
| 5 |
+
# Returns the Compute Unit count of the current device
|
| 6 |
+
current_device_index = torch.cuda.current_device()
|
| 7 |
+
current_device = torch.cuda.get_device_properties(current_device_index)
|
| 8 |
+
num_sms = current_device.multi_processor_count
|
| 9 |
+
return num_sms
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
def get_num_xcds():
|
| 13 |
+
# Currently, you can't query this programmatically. For gfx942/gfx950 it's 8, so we hardcode that here.
|
| 14 |
+
return 8
|
build/torch-rocm/utils/logger.py
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import logging
|
| 3 |
+
|
| 4 |
+
|
| 5 |
+
# AITER Triton Logger which is singleton object around python logging.
|
| 6 |
+
# Note: Python logging is also a singleton object, but we want to read the
|
| 7 |
+
# env var AITER_LOG_LEVEL once at the beginning. Another alternative is to do
|
| 8 |
+
# this in __init__.py. In fact, that's how CK logger is setup. We can look at
|
| 9 |
+
# switching to that at some point
|
| 10 |
+
#
|
| 11 |
+
# AITER_LOG_LEVEL follows python logging levels
|
| 12 |
+
# DEBUG
|
| 13 |
+
# INFO
|
| 14 |
+
# WARNING
|
| 15 |
+
# ERROR
|
| 16 |
+
# CRITICAL
|
| 17 |
+
#
|
| 18 |
+
class AiterTritonLogger(object):
|
| 19 |
+
_instance = None
|
| 20 |
+
|
| 21 |
+
def __new__(cls):
|
| 22 |
+
if cls._instance is None:
|
| 23 |
+
cls._instance = super(AiterTritonLogger, cls).__new__(cls)
|
| 24 |
+
log_level_str = os.getenv("AITER_TRITON_LOG_LEVEL", "WARNING").upper()
|
| 25 |
+
numeric_level = getattr(logging, log_level_str, logging.WARNING)
|
| 26 |
+
cls._instance._logger = logging.getLogger("AITER_TRITON")
|
| 27 |
+
cls._instance._logger.setLevel(numeric_level)
|
| 28 |
+
|
| 29 |
+
return cls._instance
|
| 30 |
+
|
| 31 |
+
def get_logger(self):
|
| 32 |
+
return self._logger
|
| 33 |
+
|
| 34 |
+
def debug(self, msg):
|
| 35 |
+
self._logger.debug(msg)
|
| 36 |
+
|
| 37 |
+
def info(self, msg):
|
| 38 |
+
self._logger.info(msg)
|
| 39 |
+
|
| 40 |
+
def warning(self, msg):
|
| 41 |
+
self._logger.warning(msg)
|
| 42 |
+
|
| 43 |
+
def error(self, msg):
|
| 44 |
+
self._logger.error(msg)
|
| 45 |
+
|
| 46 |
+
def critical(self, msg):
|
| 47 |
+
self._logger.critical(msg)
|
build/torch-rocm/utils/types.py
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
import triton.language as tl
|
| 3 |
+
from ._triton import arch_info
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
def get_dtype_max(dtype):
|
| 7 |
+
if torch.is_floating_point(torch.tensor([], dtype=dtype)):
|
| 8 |
+
return torch.finfo(dtype).max
|
| 9 |
+
else:
|
| 10 |
+
return torch.iinfo(dtype).max
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
def get_fp8_dtypes():
|
| 14 |
+
if arch_info.get_arch() in ("gfx950", "gfx1250", "gfx1200", "gfx1201"):
|
| 15 |
+
e5m2_dtype = torch.float8_e5m2
|
| 16 |
+
e4m3_dtype = torch.float8_e4m3fn
|
| 17 |
+
else:
|
| 18 |
+
e5m2_dtype = torch.float8_e5m2fnuz
|
| 19 |
+
e4m3_dtype = torch.float8_e4m3fnuz
|
| 20 |
+
|
| 21 |
+
return e5m2_dtype, e4m3_dtype
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
def get_fp8_e4m3_dtype():
|
| 25 |
+
if arch_info.get_arch() in ("gfx950", "gfx1250", "gfx1200", "gfx1201"):
|
| 26 |
+
e4m3_dtype = torch.float8_e4m3fn
|
| 27 |
+
else:
|
| 28 |
+
e4m3_dtype = torch.float8_e4m3fnuz
|
| 29 |
+
|
| 30 |
+
return e4m3_dtype
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
e5m2_dtype, e4m3_dtype = get_fp8_dtypes()
|
| 34 |
+
str_to_torch_dtype = {
|
| 35 |
+
"float64": torch.float64,
|
| 36 |
+
"float32": torch.float32,
|
| 37 |
+
"fp32": torch.float32,
|
| 38 |
+
"bfloat16": torch.bfloat16,
|
| 39 |
+
"bf16": torch.bfloat16,
|
| 40 |
+
"float16": torch.float16,
|
| 41 |
+
"fp16": torch.float16,
|
| 42 |
+
"float8_e5m2": e5m2_dtype,
|
| 43 |
+
"float8_e4m3fn": e4m3_dtype,
|
| 44 |
+
"e5m2fnuz": e5m2_dtype,
|
| 45 |
+
"e4m3fnuz": e4m3_dtype,
|
| 46 |
+
"fp8e4m3": e4m3_dtype,
|
| 47 |
+
"fp8e5m2": e5m2_dtype,
|
| 48 |
+
"int64": torch.int64,
|
| 49 |
+
"int32": torch.int32,
|
| 50 |
+
"int16": torch.int16,
|
| 51 |
+
"uint8": torch.uint8,
|
| 52 |
+
"int8": torch.int8,
|
| 53 |
+
"mxfp4_e2m1": torch.uint8, # OCP MXFP4 packs two 4-bits into 8-bit
|
| 54 |
+
}
|
| 55 |
+
|
| 56 |
+
torch_to_triton_dtype = {
|
| 57 |
+
torch.float64: tl.float64,
|
| 58 |
+
torch.float32: tl.float32,
|
| 59 |
+
torch.float16: tl.float16,
|
| 60 |
+
torch.bfloat16: tl.bfloat16,
|
| 61 |
+
torch.float8_e4m3fn: tl.float8e4nv,
|
| 62 |
+
torch.float8_e4m3fnuz: tl.float8e4b8,
|
| 63 |
+
torch.float8_e5m2: tl.float8e5,
|
| 64 |
+
torch.float8_e5m2fnuz: tl.float8e5b16,
|
| 65 |
+
torch.int64: tl.int64,
|
| 66 |
+
torch.int32: tl.int32,
|
| 67 |
+
torch.int16: tl.int16,
|
| 68 |
+
torch.int8: tl.int8,
|
| 69 |
+
torch.uint8: tl.uint8,
|
| 70 |
+
}
|
| 71 |
+
|
| 72 |
+
|
| 73 |
+
def _is_fp8(x):
|
| 74 |
+
if x.dtype in {
|
| 75 |
+
torch.float8_e4m3fnuz,
|
| 76 |
+
torch.float8_e4m3fn,
|
| 77 |
+
torch.float8_e5m2,
|
| 78 |
+
torch.float8_e5m2fnuz,
|
| 79 |
+
}:
|
| 80 |
+
if arch_info.is_fp8_avail():
|
| 81 |
+
return True
|
| 82 |
+
else:
|
| 83 |
+
raise RuntimeError("This device does not support fp8")
|
| 84 |
+
else:
|
| 85 |
+
return False
|