kernels: attention sinks + sliding window in decode attention
decode_attention_bf16_kernel gains an optional per-head sink logit and a sliding-window kv_start. The sink joins the softmax max+denominator but contributes no value (rebase max to include it, add exp(sink-m) to the denom, scale O accordingly); window>0 restricts keys to the last `window`. New launch_decode_attention_sink_bf16 + decode_attention_sink() wrapper; the existing launch_decode_attention_bf16 passes nullptr/0 so qwen3's decode path is byte-for-byte unchanged. Builds green on dash5. First piece of the gpt-oss performance path (GPU sink-attention). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -216,7 +216,9 @@ __global__ void decode_attention_bf16_kernel(
|
||||
__nv_bfloat16* __restrict__ O,
|
||||
int num_q_heads, int num_kv_heads,
|
||||
int kv_len, int head_dim,
|
||||
float scale
|
||||
float scale,
|
||||
const float* __restrict__ sinks, // [num_q_heads] or nullptr (gpt-oss)
|
||||
int window // >0: only attend last `window` keys; 0: full
|
||||
) {
|
||||
int bh = blockIdx.x;
|
||||
int batch_idx = bh / num_q_heads;
|
||||
@@ -228,6 +230,10 @@ __global__ void decode_attention_bf16_kernel(
|
||||
|
||||
int tid = threadIdx.x;
|
||||
|
||||
// Sliding window: the query is at position kv_len-1 (decode appends it), so it
|
||||
// attends to keys [kv_start, kv_len). kv_start=0 for full attention.
|
||||
int kv_start = (window > 0 && kv_len > window) ? (kv_len - window) : 0;
|
||||
|
||||
// Pointers to this batch/head's data
|
||||
// Q: [batch, num_q_heads, 1, head_dim]
|
||||
const __nv_bfloat16* Q_ptr = Q + ((long long)batch_idx * num_q_heads + q_head) * head_dim;
|
||||
@@ -251,7 +257,7 @@ __global__ void decode_attention_bf16_kernel(
|
||||
local_O[d] = 0.0f;
|
||||
}
|
||||
|
||||
for (int pos = tid; pos < kv_len; pos += DECODE_THREADS) {
|
||||
for (int pos = kv_start + tid; pos < kv_len; pos += DECODE_THREADS) {
|
||||
// Compute dot(Q, K[pos]) * scale
|
||||
const __nv_bfloat16* K_pos = K_base + pos * head_dim;
|
||||
float dot = 0.0f;
|
||||
@@ -336,8 +342,20 @@ __global__ void decode_attention_bf16_kernel(
|
||||
__syncthreads();
|
||||
global_sum = smem_sum[0];
|
||||
|
||||
// gpt-oss attention sink: a per-head learned logit that joins the softmax
|
||||
// denominator but contributes no value. Rebase max to include it, then add
|
||||
// exp(sink - m_new) to the denominator. O was accumulated relative to
|
||||
// global_max, so it picks up a factor exp(global_max - m_new).
|
||||
float o_scale = 1.0f;
|
||||
if (sinks != nullptr) {
|
||||
float sink = sinks[q_head];
|
||||
float m_new = fmaxf(global_max, sink);
|
||||
o_scale = expf(global_max - m_new);
|
||||
global_sum = global_sum * o_scale + expf(sink - m_new);
|
||||
}
|
||||
|
||||
// Step 4: Reduce O across block (dimension by dimension using shared mem)
|
||||
float inv_sum = (global_sum > 0.0f) ? (1.0f / global_sum) : 0.0f;
|
||||
float inv_sum = (global_sum > 0.0f) ? (o_scale / global_sum) : 0.0f;
|
||||
|
||||
// Process head_dim in chunks: each iteration reduces one dimension
|
||||
// Use shared memory accumulator: each warp contributes via warp reduction + atomic
|
||||
|
||||
Reference in New Issue
Block a user