#pragma once #include #include #include "attn_common.h" #include "attn_warp_utils.cuh" constexpr int PDC_CHUNK = 64; // Scalar paged decode (fallback for sm < 80, no tensor cores). // Reads K/V from flat pool via req_to_token indexing. template __global__ void paged_attn_decode_split_kv_kernel(PagedAttentionParams p) { int batch = blockIdx.x / p.kv_head; int kv_head = blockIdx.x % p.kv_head; int split = blockIdx.z; int group_size = blockDim.y; int q_head = kv_head * group_size + threadIdx.y; int lane = threadIdx.x; int hd_per_thread = p.head_dim / 32; const int seq_len = p.kv_indptr[batch + 1] - p.kv_indptr[batch]; const int64_t req_idx = p.req_pool_indices[batch]; float q_reg[8]; int q_off = batch * p.q_stride_l + q_head * p.q_stride_h + lane * hd_per_thread * p.q_stride_d; #pragma unroll for (int i = 0; i < hd_per_thread; i++) q_reg[i] = __bfloat162float(p.q[q_off + i * p.q_stride_d]); float m = -FLT_MAX, d = 0.0f, acc_reg[8] = {0.0f}; extern __shared__ __align__(16) bf16 k_smem[]; int chunks_total = (seq_len + PDC_CHUNK - 1) / PDC_CHUNK; int chunks_per_split = (chunks_total + p.num_splits - 1) / p.num_splits; int ch_begin = split * chunks_per_split; int ch_end = min(chunks_total, ch_begin + chunks_per_split); const int mask_base = batch * p.mask_b_stride; const int64_t pool_stride = (int64_t)p.kv_head * p.head_dim; const int64_t head_off = (int64_t)kv_head * p.head_dim; const int64_t rtt_stride = (int64_t)p.max_context_len; for (int ci = ch_begin; ci < ch_end; ci++) { int chunk_start = ci * PDC_CHUNK; int this_chunk = min(PDC_CHUNK, seq_len - chunk_start); int total = this_chunk * p.head_dim; for (int i = threadIdx.y * 32 + lane; i < total; i += blockDim.x * blockDim.y) { int s = i / p.head_dim; int d_dim = i % p.head_dim; int pos = chunk_start + s; int64_t slot = p.req_to_token[req_idx * rtt_stride + pos]; if (slot >= 0) { int64_t off = slot * pool_stride + head_off + d_dim; k_smem[i] = p.k_cache[off]; } else { k_smem[i] = __float2bfloat16(0.0f); } } __syncthreads(); for (int s = 0; s < this_chunk; s++) { float partial = 0.0f; #pragma unroll for (int i = 0; i < hd_per_thread; i++) partial += q_reg[i] * __bfloat162float( k_smem[s * p.head_dim + lane * hd_per_thread + i]); partial = warp_reduce_sum(partial) * p.scale; int kv_idx = chunk_start + s; bool masked = false; if constexpr (HasMask) { if (!p.mask[mask_base + kv_idx]) masked = true; } // Decode: the query is the last token, so its valid range [0, // seq_len) IS the causal range. IsCausal is accepted for dispatch // uniformity but must not apply causal_offset masking here. if (masked) partial = -FLT_MAX; float new_m = fmaxf(m, partial); float alpha = __expf(m - new_m); float beta = __expf(partial - new_m); d = d * alpha + beta; int pos = chunk_start + s; int64_t slot = p.req_to_token[req_idx * rtt_stride + pos]; if (masked) { #pragma unroll for (int i = 0; i < hd_per_thread; i++) acc_reg[i] = fmaf(acc_reg[i], alpha, 0.0f); } else if (slot >= 0) { int64_t v_base = slot * pool_stride + head_off; #pragma unroll for (int i = 0; i < hd_per_thread; i++) acc_reg[i] = fmaf(acc_reg[i], alpha, __bfloat162float(p.v_cache[v_base + lane * hd_per_thread + i]) * beta); } else { #pragma unroll for (int i = 0; i < hd_per_thread; i++) acc_reg[i] = fmaf(acc_reg[i], alpha, 0.0f); } m = new_m; } __syncthreads(); } size_t bh = (size_t)batch * p.q_head + q_head; size_t slot = bh * MAX_SPLITS + split; int d0 = lane * hd_per_thread; #pragma unroll for (int i = 0; i < hd_per_thread; i++) p.o_part[slot * p.head_dim + (d0 + i)] = acc_reg[i]; if (lane == 0) { p.ml_part[slot * 2] = m; p.ml_part[slot * 2 + 1] = d; } } __global__ void paged_attn_decode_combine_kernel(PagedAttentionParams p) { int bh = blockIdx.x; int d = threadIdx.x; if (d >= p.head_dim) return; int batch = bh / p.q_head; int q_head = bh % p.q_head; size_t split_base = (size_t)bh * MAX_SPLITS; const float* mlp = p.ml_part + split_base * 2; const float* op = p.o_part + split_base * p.head_dim; float m = -FLT_MAX, l = 0.0f, acc = 0.0f; for (int s = 0; s < p.num_splits; s++) { float mi = mlp[s * 2]; if (mi <= -FLT_MAX) continue; float li = mlp[s * 2 + 1]; float nm = fmaxf(m, mi); float corr = __expf(m - nm); float e = __expf(mi - nm); acc = fmaf(acc, corr, op[s * p.head_dim + d] * e); l = fmaf(l, corr, li * e); m = nm; } float inv = (l > 1e-20f) ? (1.0f / l) : 0.0f; int o_off = batch * p.q_stride_l + q_head * p.q_stride_h + d * p.q_stride_d; p.o[o_off] = __float2bfloat16(acc * inv); }