Flash attention with online softmax. One program per (batch, head, query-block).
(
Q_ptr, K_ptr, V_ptr, O_ptr,
stride_qz, stride_qh, stride_qm, stride_qk,
stride_kz, stride_kh, stride_kn, stride_kk,
stride_vz, stride_vh, stride_vn, stride_vk,
stride_oz, stride_oh, stride_om, stride_ok,
Z, H, M_size, N_size,
D: tl.constexpr,
sm_scale,
IS_CAUSAL: tl.constexpr,
BLOCK_M: tl.constexpr,
BLOCK_N: tl.constexpr,
)
| 21 | |
| 22 | @triton.jit |
| 23 | def flash_attention_kernel( |
| 24 | Q_ptr, K_ptr, V_ptr, O_ptr, |
| 25 | stride_qz, stride_qh, stride_qm, stride_qk, |
| 26 | stride_kz, stride_kh, stride_kn, stride_kk, |
| 27 | stride_vz, stride_vh, stride_vn, stride_vk, |
| 28 | stride_oz, stride_oh, stride_om, stride_ok, |
| 29 | Z, H, M_size, N_size, |
| 30 | D: tl.constexpr, |
| 31 | sm_scale, |
| 32 | IS_CAUSAL: tl.constexpr, |
| 33 | BLOCK_M: tl.constexpr, |
| 34 | BLOCK_N: tl.constexpr, |
| 35 | ): |
| 36 | """Flash attention with online softmax. One program per (batch, head, query-block).""" |
| 37 | pid_z = tl.program_id(2) # batch |
| 38 | pid_h = tl.program_id(1) # head |
| 39 | pid_m = tl.program_id(0) # query block |
| 40 | |
| 41 | # Offsets into the batch and head |
| 42 | qkv_offset_z = pid_z * stride_qz |
| 43 | qkv_offset_h = pid_h * stride_qh |
| 44 | |
| 45 | k_offset_z = pid_z * stride_kz |
| 46 | k_offset_h = pid_h * stride_kh |
| 47 | |
| 48 | v_offset_z = pid_z * stride_vz |
| 49 | v_offset_h = pid_h * stride_vh |
| 50 | |
| 51 | o_offset_z = pid_z * stride_oz |
| 52 | o_offset_h = pid_h * stride_oh |
| 53 | |
| 54 | # Query block offsets |
| 55 | offs_m = pid_m * BLOCK_M + tl.arange(0, BLOCK_M) |
| 56 | offs_d = tl.arange(0, D) |
| 57 | |
| 58 | # Load Q block [BLOCK_M, D] |
| 59 | q_ptrs = Q_ptr + qkv_offset_z + qkv_offset_h + offs_m[:, None] * stride_qm + offs_d[None, :] * stride_qk |
| 60 | q_mask = offs_m[:, None] < M_size |
| 61 | q = tl.load(q_ptrs, mask=q_mask, other=0.0) |
| 62 | |
| 63 | # Initialize running max and sum for online softmax |
| 64 | m_i = tl.full((BLOCK_M,), float("-inf"), dtype=tl.float32) |
| 65 | l_i = tl.zeros((BLOCK_M,), dtype=tl.float32) |
| 66 | acc = tl.zeros((BLOCK_M, D), dtype=tl.float32) |
| 67 | |
| 68 | # Determine the range of KV blocks to iterate over |
| 69 | if IS_CAUSAL: |
| 70 | kv_end = tl.minimum(N_size, (pid_m + 1) * BLOCK_M) |
| 71 | else: |
| 72 | kv_end = N_size |
| 73 | |
| 74 | # Iterate over KV blocks |
| 75 | for start_n in range(0, kv_end, BLOCK_N): |
| 76 | offs_n = start_n + tl.arange(0, BLOCK_N) |
| 77 | |
| 78 | # Load K block [BLOCK_N, D] |
| 79 | k_ptrs = K_ptr + k_offset_z + k_offset_h + offs_n[:, None] * stride_kn + offs_d[None, :] * stride_kk |
| 80 | k_mask = offs_n[:, None] < N_size |
nothing calls this directly
no outgoing calls
no test coverage detected