775 lines
29 KiB
Common Lisp
775 lines
29 KiB
Common Lisp
#ifdef MNN_SUPPORT_FP16
|
|
#pragma OPENCL EXTENSION cl_khr_fp16 : enable
|
|
#endif
|
|
|
|
// Kernel 1: Depthwise Conv1D + SiLU
|
|
// Each work-item processes one (batch*channel, seq_pos) element.
|
|
// Input: qkv [B, D, L], conv_state [B, D, conv_state_size], conv_weight [D, 1, K]
|
|
// Output: conv_out [B, D, L]
|
|
// conv_state is read but NOT updated here (updated by separate kernel).
|
|
__kernel void linear_attn_conv_silu(
|
|
__private const int global_dim0,
|
|
__global const FLOAT* qkv,
|
|
__global const FLOAT* conv_state,
|
|
__global const FLOAT* conv_weight,
|
|
__global FLOAT* conv_out,
|
|
__private const int batch,
|
|
__private const int conv_dim,
|
|
__private const int seq_len,
|
|
__private const int kernel_size,
|
|
__private const int conv_state_size)
|
|
{
|
|
const int gid = get_global_id(0);
|
|
if (gid >= global_dim0) return;
|
|
|
|
const int L = seq_len;
|
|
const int D = conv_dim;
|
|
const int K = kernel_size;
|
|
const int css = conv_state_size;
|
|
|
|
// Decompose: gid -> (batch_chan, seq_pos)
|
|
const int l = gid % L;
|
|
const int bd = gid / L;
|
|
const int b = bd / D;
|
|
const int d = bd % D;
|
|
|
|
// Compute valid convolution for position l
|
|
// Padded input = [conv_state[b,d,:], qkv[b,d,:]]
|
|
// padded[pos]: if pos < css -> conv_state[b*D*css + d*css + pos]
|
|
// else -> qkv[b*D*L + d*L + (pos - css)]
|
|
float sum = 0.0f;
|
|
for (int k = 0; k < K; ++k) {
|
|
int pos = l + k;
|
|
float input_val;
|
|
if (pos < css) {
|
|
input_val = (float)conv_state[b * D * css + d * css + pos];
|
|
} else {
|
|
input_val = (float)qkv[b * D * L + d * L + (pos - css)];
|
|
}
|
|
sum += input_val * (float)conv_weight[d * K + k];
|
|
}
|
|
|
|
// SiLU activation: x * sigmoid(x)
|
|
float sigmoid_val = 1.0f / (1.0f + exp(-sum));
|
|
conv_out[b * D * L + d * L + l] = (FLOAT)(sum * sigmoid_val);
|
|
}
|
|
|
|
// Kernel 2: Update conv state with last (K-1) elements of padded input
|
|
// new_state[i] = padded[L + i], where padded = cat(old_state[css], qkv[L])
|
|
// Must execute AFTER linear_attn_conv_silu (which reads conv_state).
|
|
__kernel void linear_attn_conv_state_update(
|
|
__private const int global_dim0,
|
|
__global const FLOAT* qkv,
|
|
__global FLOAT* conv_state,
|
|
__private const int batch,
|
|
__private const int conv_dim,
|
|
__private const int seq_len,
|
|
__private const int conv_state_size)
|
|
{
|
|
const int gid = get_global_id(0);
|
|
if (gid >= global_dim0) return;
|
|
|
|
const int L = seq_len;
|
|
const int D = conv_dim;
|
|
const int css = conv_state_size;
|
|
|
|
const int i = gid % css;
|
|
const int bd = gid / css;
|
|
const int b = bd / D;
|
|
const int d = bd % D;
|
|
|
|
// new_state[i] = padded[L + i]
|
|
// padded = cat(old_state[css], qkv[L])
|
|
// position (L + i) in padded: if (L+i) < css -> old_state, else -> qkv[(L+i) - css]
|
|
int pos = L + i;
|
|
FLOAT val;
|
|
if (pos < css) {
|
|
val = conv_state[b * D * css + d * css + pos];
|
|
} else {
|
|
val = qkv[b * D * L + d * L + (pos - css)];
|
|
}
|
|
// Safe: we write to index i, read from index (L+i) where L >= 1, so (L+i) > i always
|
|
conv_state[b * D * css + d * css + i] = val;
|
|
}
|
|
|
|
// Kernel 3: Gated Delta Rule (Steps 2-5 fused)
|
|
// Each work-item processes one (batch, head) pair across all timesteps.
|
|
// Uses float32 exclusively for numerical stability of the recurrence.
|
|
__kernel void linear_attn_gated_delta_rule(
|
|
__global const FLOAT* conv_out,
|
|
__global const FLOAT* gate,
|
|
__global const FLOAT* beta_in,
|
|
__global FLOAT* recurrent_state,
|
|
__global FLOAT* attn_out,
|
|
__private const int batch,
|
|
__private const int conv_dim,
|
|
__private const int seq_len,
|
|
__private const int num_k_heads,
|
|
__private const int num_v_heads,
|
|
__private const int d_k,
|
|
__private const int d_v,
|
|
__private const int key_dim,
|
|
__private const int val_dim,
|
|
__private const int gqa_factor,
|
|
__private const float q_scale)
|
|
{
|
|
const int dv4 = (d_v + 3) / 4;
|
|
const int total = dv4 * num_v_heads * batch;
|
|
const int gid = get_global_id(1);
|
|
if (gid >= total) return;
|
|
const int x = (gid % dv4) << 2;
|
|
const int bh = gid / dv4;
|
|
const int h = bh % num_v_heads;
|
|
const int b = bh / num_v_heads;
|
|
const int k_head = h / gqa_factor; // GQA: corresponding K-head
|
|
__local float4 __sum[LOCAL_SIZE];
|
|
__local float4 rec_local[K_SIZE];
|
|
__local float key_local[K_SIZE];
|
|
const int lid = get_local_id(0);
|
|
|
|
// State pointer: recurrent_state layout [B, H, d_k, d_v]
|
|
const int state_offset = (b * num_v_heads + h) * K_SIZE * d_v + x;
|
|
#ifdef DECODE_PHASE
|
|
const int conv_base = b * conv_dim;
|
|
float g_t = (float)(gate[b * num_v_heads + h]);
|
|
float4 beta_t = (float4)(beta_in[b * num_v_heads + h]);
|
|
float4 decay_val = (float4)(exp(g_t));
|
|
const int out_offset = b * num_v_heads * d_v + h * d_v;
|
|
float4 s = (float4)(0);
|
|
for (int i = lid; i < K_SIZE; i+=LOCAL_SIZE) {
|
|
float4 rec = convert_float4(vload4(0, recurrent_state + state_offset + i * d_v))* decay_val;
|
|
float key = (float)(conv_out[conv_base + key_dim + k_head * K_SIZE + i]);
|
|
s += rec * (float4)(key);
|
|
rec_local[i] = rec;
|
|
key_local[i] = key;
|
|
}
|
|
__sum[lid] = s;
|
|
barrier(CLK_LOCAL_MEM_FENCE);
|
|
for(int i = LOCAL_SIZE/2; i > 0; i /= 2){
|
|
if (lid < i)
|
|
__sum[lid] += __sum[lid + i];
|
|
barrier(CLK_LOCAL_MEM_FENCE);
|
|
}
|
|
s = __sum[0];
|
|
float4 v_data = convert_float4(vload4(0, conv_out + conv_base + 2 * key_dim + h * d_v + x));
|
|
float4 delta = beta_t * (v_data - s);
|
|
s = (float4)(0);
|
|
for (int i = lid; i < K_SIZE; i+=LOCAL_SIZE) {
|
|
float4 recurrent_state_data = rec_local[i] + (float4)(key_local[i]) * delta;
|
|
s += recurrent_state_data * (float4)(conv_out[conv_base + (k_head * K_SIZE + i)]) * (float4)(q_scale);
|
|
vstore4(CONVERT_FLOAT4(recurrent_state_data), 0, recurrent_state + state_offset + i * d_v);
|
|
}
|
|
__sum[lid] = s;
|
|
barrier(CLK_LOCAL_MEM_FENCE);
|
|
for(int i = LOCAL_SIZE/2; i > 0; i /= 2){
|
|
if (lid < i)
|
|
__sum[lid] += __sum[lid + i];
|
|
barrier(CLK_LOCAL_MEM_FENCE);
|
|
}
|
|
s = __sum[0];
|
|
if(lid == 0){
|
|
vstore4(CONVERT_FLOAT4(s), 0, attn_out + out_offset + x);
|
|
}
|
|
#else
|
|
for (int i = lid; i < K_SIZE; i+=LOCAL_SIZE) {
|
|
rec_local[i] = convert_float4(vload4(0, recurrent_state + state_offset + i * d_v));
|
|
}
|
|
for (int t = 0; t < seq_len; ++t) {
|
|
const int conv_base = b * seq_len * conv_dim;
|
|
float g_t = (float)(gate[b * seq_len * num_v_heads + t * num_v_heads + h]);
|
|
float4 beta_t = (float4)(beta_in[b * seq_len * num_v_heads + t * num_v_heads + h]);
|
|
float4 decay_val = (float4)(exp(g_t));
|
|
const int out_offset = (b * seq_len + t) * num_v_heads * d_v + h * d_v;
|
|
float4 s = (float4)(0);
|
|
for (int i = lid; i < K_SIZE; i+=LOCAL_SIZE) {
|
|
float4 rec = rec_local[i] * decay_val;
|
|
float key = (float)(conv_out[conv_base + (key_dim + k_head * K_SIZE + i) * seq_len + t]);
|
|
s += rec * (float4)(key);
|
|
key_local[i] = key;
|
|
}
|
|
__sum[lid] = s;
|
|
barrier(CLK_LOCAL_MEM_FENCE);
|
|
for(int i = LOCAL_SIZE/2; i > 0; i /= 2){
|
|
if (lid < i)
|
|
__sum[lid] += __sum[lid + i];
|
|
barrier(CLK_LOCAL_MEM_FENCE);
|
|
}
|
|
s = __sum[0];
|
|
float4 v_data;
|
|
v_data.x = (float)(conv_out[conv_base + (2 * key_dim + h * d_v + x) * seq_len + t]);
|
|
v_data.y = (float)(conv_out[conv_base + (2 * key_dim + h * d_v + x + 1) * seq_len + t]);
|
|
v_data.z = (float)(conv_out[conv_base + (2 * key_dim + h * d_v + x + 2) * seq_len + t]);
|
|
v_data.w = (float)(conv_out[conv_base + (2 * key_dim + h * d_v + x + 3) * seq_len + t]);
|
|
float4 delta = beta_t * (v_data - s);
|
|
s = (float4)(0);
|
|
for (int i = lid; i < K_SIZE; i+=LOCAL_SIZE) {
|
|
float4 recurrent_state_data = rec_local[i] * decay_val + (float4)(key_local[i]) * delta;
|
|
s += recurrent_state_data * (float4)(conv_out[conv_base + (k_head * K_SIZE + i) * seq_len + t]) * (float4)(q_scale);
|
|
rec_local[i] = recurrent_state_data;
|
|
}
|
|
__sum[lid] = s;
|
|
barrier(CLK_LOCAL_MEM_FENCE);
|
|
for(int i = LOCAL_SIZE/2; i > 0; i /= 2){
|
|
if (lid < i)
|
|
__sum[lid] += __sum[lid + i];
|
|
barrier(CLK_LOCAL_MEM_FENCE);
|
|
}
|
|
s = __sum[0];
|
|
if(lid == 0){
|
|
vstore4(CONVERT_FLOAT4(s), 0, attn_out + out_offset + x);
|
|
}
|
|
}
|
|
for (int i = lid; i < K_SIZE; i+=LOCAL_SIZE) {
|
|
vstore4(CONVERT_FLOAT4(rec_local[i]), 0, recurrent_state + state_offset + i * d_v);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
__kernel void l2_norm(__global const FLOAT* input,
|
|
__global FLOAT* output,
|
|
__private const int conv_dim,
|
|
__private const int head_k_dim,
|
|
__private const int gqa_factor,
|
|
__private const int key_dim,
|
|
__private const int seq_len)
|
|
{
|
|
#ifdef USE_VEC
|
|
const int hl = get_global_id(1);
|
|
const int bk = get_global_id(2);
|
|
const int seq_len4 = (seq_len + 3) / 4;
|
|
const int h = hl / seq_len4;
|
|
const int sq = hl % seq_len4;
|
|
const int b = bk / 2;
|
|
const int k = bk % 2;
|
|
const int lid = get_local_id(0);
|
|
const int k_head = h / gqa_factor; // GQA: corresponding K-head
|
|
const int input_offset = (b * conv_dim + k * key_dim + k_head * head_k_dim) * seq_len + (sq << 2);
|
|
__local float4 __sum[K_SIZE];
|
|
float4 sum = 0.0f;
|
|
for(int i = lid; i < K_SIZE; i += K_SIZE){
|
|
float4 in = convert_float4(vload4(0, input + input_offset + i * seq_len));
|
|
sum += in * in;
|
|
}
|
|
__sum[lid] = sum;
|
|
barrier(CLK_LOCAL_MEM_FENCE);
|
|
for(int i = K_SIZE/2; i > 0; i /= 2){
|
|
if (lid < i)
|
|
__sum[lid] += __sum[lid + i];
|
|
barrier(CLK_LOCAL_MEM_FENCE);
|
|
}
|
|
sum = __sum[0];
|
|
int remain = seq_len - (sq << 2);
|
|
float4 invNorm = (float4)1.0f / sqrt(sum + (float4)1e-6f);
|
|
if(remain == 1){
|
|
for(int i = lid; i < K_SIZE; i += K_SIZE){
|
|
float4 out = convert_float4(vload4(0, input + input_offset + i * seq_len)) * invNorm;
|
|
output[input_offset + i * seq_len] = (FLOAT)out.s0;
|
|
}
|
|
}else if(remain == 2){
|
|
for(int i = lid; i < K_SIZE; i += K_SIZE){
|
|
float4 out = convert_float4(vload4(0, input + input_offset + i * seq_len)) * invNorm;
|
|
vstore2(CONVERT_FLOAT2(out.s01), 0, output + input_offset + i * seq_len);
|
|
}
|
|
}else if(remain == 3){
|
|
for(int i = lid; i < K_SIZE; i += K_SIZE){
|
|
float4 out = convert_float4(vload4(0, input + input_offset + i * seq_len)) * invNorm;
|
|
vstore3(CONVERT_FLOAT3(out.s012), 0, output + input_offset + i * seq_len);
|
|
}
|
|
}else{
|
|
for(int i = lid; i < K_SIZE; i += K_SIZE){
|
|
float4 out = convert_float4(vload4(0, input + input_offset + i * seq_len)) * invNorm;
|
|
vstore4(CONVERT_FLOAT4(out), 0, output + input_offset + i * seq_len);
|
|
}
|
|
}
|
|
#else
|
|
const int h = get_global_id(1);
|
|
const int bk = get_global_id(2);
|
|
const int b = bk / 2;
|
|
const int k = bk % 2;
|
|
const int lid = get_local_id(0);
|
|
const int k_head = h / gqa_factor; // GQA: corresponding K-head
|
|
const int input_offset = b * conv_dim + k * key_dim + k_head * head_k_dim;
|
|
__local float __sum[K_SIZE];
|
|
float sum = 0.0f;
|
|
for(int i = lid; i < K_SIZE; i += K_SIZE){
|
|
float in = (float)(input[input_offset + i]);
|
|
sum += in * in;
|
|
}
|
|
__sum[lid] = sum;
|
|
barrier(CLK_LOCAL_MEM_FENCE);
|
|
for(int i = K_SIZE/2; i > 0; i /= 2){
|
|
if (lid < i)
|
|
__sum[lid] += __sum[lid + i];
|
|
barrier(CLK_LOCAL_MEM_FENCE);
|
|
}
|
|
sum = __sum[0];
|
|
float invNorm = 1.0f / sqrt(sum + 1e-6f);
|
|
for(int i = lid; i < K_SIZE; i += K_SIZE){
|
|
float out = (float)(input[input_offset + i]) * invNorm;
|
|
output[input_offset + i] = (FLOAT)out;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#ifdef CHUNK_PREFILL
|
|
|
|
// ======================== Chunked Prefill Kernels ========================
|
|
// Implements torch_chunk_gated_delta_rule from modeling_qwen3_5.py
|
|
// Decomposes sequential recurrence into chunk-parallel operations.
|
|
// CHUNK_SIZE must be defined at compile time (e.g. -DCHUNK_SIZE=64).
|
|
|
|
// Kernel C1: Cumulative sum of gate values within each chunk
|
|
// GWS: {num_v_heads, num_chunks, batch}
|
|
__kernel void chunk_g_cumsum(
|
|
__global const FLOAT* gate, // [B, L, H]
|
|
__global float* g_cumsum, // [B, H, num_chunks, CHUNK_SIZE] (float32 for precision)
|
|
__private const int num_v_heads,
|
|
__private const int seq_len,
|
|
__private const int num_chunks)
|
|
{
|
|
const int h = get_global_id(0);
|
|
const int c = get_global_id(1);
|
|
const int b = get_global_id(2);
|
|
if (h >= num_v_heads || c >= num_chunks) return;
|
|
|
|
const int H = num_v_heads;
|
|
const int L = seq_len;
|
|
const int C = CHUNK_SIZE;
|
|
|
|
float cumsum = 0.0f;
|
|
int out_base = ((b * H + h) * num_chunks + c) * C;
|
|
for (int p = 0; p < C; ++p) {
|
|
int l = c * C + p;
|
|
float g_val = (l < L) ? (float)gate[b * L * H + l * H + h] : 0.0f;
|
|
cumsum += g_val;
|
|
g_cumsum[out_base + p] = cumsum;
|
|
}
|
|
}
|
|
|
|
// Kernel C2: Build Neumann-corrected intra-chunk attention matrix
|
|
// attn = I + neumann_correction(-(k_beta @ k^T) * decay_mask)
|
|
// GWS: {CHUNK_SIZE, num_v_heads * num_chunks, batch}, LWS: {CHUNK_SIZE, 1, 1}
|
|
|
|
__kernel void chunk_build_neumann_attn_step0(
|
|
__global const FLOAT* conv_out, // [B, D, L]
|
|
__global const FLOAT* beta_in, // [B, L, H]
|
|
__global const float* g_cumsum, // [B, H, num_chunks, C] (float32)
|
|
__global float* attn_matrix, // [B, H, num_chunks, C, C] (float32)
|
|
__private const int batch,
|
|
__private const int conv_dim,
|
|
__private const int seq_len,
|
|
__private const int num_v_heads,
|
|
__private const int head_k_dim,
|
|
__private const int key_dim,
|
|
__private const int gqa_factor,
|
|
__private const int num_chunks)
|
|
{
|
|
const int rc = get_global_id(0);
|
|
const int hc = get_global_id(1);
|
|
const int b = get_global_id(2);
|
|
if (hc >= num_v_heads * num_chunks || b >= batch || rc >= CHUNK_SIZE * CHUNK_SIZE) return;
|
|
|
|
const int col = rc % CHUNK_SIZE;
|
|
const int row = rc / CHUNK_SIZE;
|
|
const int h = hc / num_chunks;
|
|
const int c = hc % num_chunks;
|
|
|
|
|
|
const int C = CHUNK_SIZE;
|
|
const int dk = head_k_dim;
|
|
const int D = conv_dim;
|
|
const int L = seq_len;
|
|
const int H = num_v_heads;
|
|
const int k_head = h / gqa_factor;
|
|
|
|
const int g_base = ((b * H + h) * num_chunks + c) * C;
|
|
const int attn_out_base = ((b * H + h) * num_chunks + c) * C * C;
|
|
const int conv_base = b * D * L;
|
|
|
|
// Phase 1: Compute strictly-lower-triangular attn
|
|
// attn[i, j] = -(k[i]*beta[i]) dot k[j] * exp(g[i]-g[j])
|
|
float val = 0.0f;
|
|
if (col < row) {
|
|
const int l_i = c * C + row;
|
|
const int l_j = c * C + col;
|
|
if (l_i < L && l_j < L) {
|
|
const float g_i = (float)g_cumsum[g_base + row];
|
|
const float beta_i = (float)beta_in[b * L * H + l_i * H + h];
|
|
const float g_j = (float)g_cumsum[g_base + col];
|
|
const float decay = exp(g_i - g_j);
|
|
float dot = 0.0f;
|
|
for (int d = 0; d < dk; ++d) {
|
|
float k_i_d = (float)conv_out[conv_base + (key_dim + k_head * dk + d) * L + l_i];
|
|
float k_j_d = (float)conv_out[conv_base + (key_dim + k_head * dk + d) * L + l_j];
|
|
dot += k_i_d * k_j_d;
|
|
}
|
|
val = -(beta_i * dot) * decay;
|
|
}
|
|
}
|
|
attn_matrix[attn_out_base + row * C + col] = val;
|
|
}
|
|
|
|
__kernel void chunk_build_neumann_attn_step1(
|
|
__global float* attn_matrix, // [B, H, num_chunks, C, C] (float32)
|
|
__private const int batch,
|
|
__private const int conv_dim,
|
|
__private const int seq_len,
|
|
__private const int num_v_heads,
|
|
__private const int head_k_dim,
|
|
__private const int key_dim,
|
|
__private const int gqa_factor,
|
|
__private const int num_chunks)
|
|
{
|
|
const int col = get_global_id(0);
|
|
const int hc = get_global_id(1);
|
|
const int b = get_global_id(2);
|
|
if (hc >= num_v_heads * num_chunks || b >= batch || col >= CHUNK_SIZE) return;
|
|
|
|
const int lid = get_local_id(0);
|
|
const int h = hc / num_chunks;
|
|
const int c = hc % num_chunks;
|
|
|
|
|
|
const int C = CHUNK_SIZE;
|
|
const int dk = head_k_dim;
|
|
const int D = conv_dim;
|
|
const int L = seq_len;
|
|
const int H = num_v_heads;
|
|
const int k_head = h / gqa_factor;
|
|
|
|
const int attn_out_base = ((b * H + h) * num_chunks + c) * C * C;
|
|
|
|
// Phase 2: Neumann series correction (row by row)
|
|
for (int r = 1; r < C; ++r) {
|
|
float orig = attn_matrix[attn_out_base + r * C + lid];
|
|
float correction = 0.0f;
|
|
if (lid < r) {
|
|
for (int k = 0; k < r; ++k) {
|
|
correction += attn_matrix[attn_out_base + r * C + k] * attn_matrix[attn_out_base + k * C + lid];
|
|
}
|
|
}
|
|
if (lid < r) {
|
|
attn_matrix[attn_out_base + r * C + lid] = orig + correction;
|
|
}
|
|
barrier(CLK_LOCAL_MEM_FENCE);
|
|
}
|
|
|
|
// Phase 3: Add identity and write to global (float32)
|
|
attn_matrix[attn_out_base + lid * C + lid] = attn_matrix[attn_out_base + lid * C + lid] + 1.0f;
|
|
}
|
|
|
|
// Kernel C3: v_corrected = attn_matrix @ (V * beta)
|
|
// GWS: {UP_DIV(dv, 4), CHUNK_SIZE * num_chunks, B * H}
|
|
__kernel void chunk_correct_v(
|
|
__global const float* attn_matrix, // [B, H, num_chunks, C, C] (float32)
|
|
__global const FLOAT* conv_out, // [B, D, L]
|
|
__global const FLOAT* beta_in, // [B, L, H]
|
|
__global const float* g_cumsum, // [B, H, num_chunks, C] (float32)
|
|
__global float* v_corrected, // [B, H, num_chunks, C, dv] (float32)
|
|
__global float* k_cumdecay, // [B, H, num_chunks, C, dk] (float32)
|
|
__private const int global_dim0,
|
|
__private const int global_dim1,
|
|
__private const int global_dim2,
|
|
__private const int conv_dim,
|
|
__private const int seq_len,
|
|
__private const int num_v_heads,
|
|
__private const int head_k_dim,
|
|
__private const int head_v_dim,
|
|
__private const int key_dim,
|
|
__private const int gqa_factor,
|
|
__private const int num_chunks)
|
|
{
|
|
const int x = get_global_id(0);
|
|
const int y = get_global_id(1);
|
|
const int z = get_global_id(2);
|
|
if (x >= global_dim0 || y >= global_dim1 || z >= global_dim2) return;
|
|
|
|
const int j = x << 2;
|
|
const int c = y / CHUNK_SIZE;
|
|
const int row = y % CHUNK_SIZE;
|
|
const int b = z / num_v_heads;
|
|
const int h = z % num_v_heads;
|
|
|
|
const int C = CHUNK_SIZE;
|
|
const int dk = head_k_dim;
|
|
const int dv = head_v_dim;
|
|
const int D = conv_dim;
|
|
const int L = seq_len;
|
|
const int H = num_v_heads;
|
|
const int k_head = h / gqa_factor;
|
|
|
|
const int attn_base = ((b * H + h) * num_chunks + c) * C * C + row * C;
|
|
const int g_base = ((b * H + h) * num_chunks + c) * C;
|
|
const int conv_base = b * D * L;
|
|
|
|
float4 result0 = (float4)(0.0f);
|
|
float4 result1 = (float4)(0.0f);
|
|
int l_p = c * C;
|
|
for (int p = 0; p < C && l_p < L; ++p, ++l_p) {
|
|
float a = attn_matrix[attn_base + p];
|
|
float beta_p = (float)beta_in[b * L * H + l_p * H + h];
|
|
float ab = a * beta_p;
|
|
float g_p = g_cumsum[g_base + p];
|
|
float coeff = ab * exp(g_p);
|
|
float4 v_p;
|
|
v_p.x = (float)conv_out[conv_base + (2 * key_dim + h * dv + j) * L + l_p];
|
|
v_p.y = (float)conv_out[conv_base + (2 * key_dim + h * dv + j + 1) * L + l_p];
|
|
v_p.z = (float)conv_out[conv_base + (2 * key_dim + h * dv + j + 2) * L + l_p];
|
|
v_p.w = (float)conv_out[conv_base + (2 * key_dim + h * dv + j + 3) * L + l_p];
|
|
result0 += ab * v_p;
|
|
float4 k_p;
|
|
k_p.x = (float)conv_out[conv_base + (key_dim + k_head * dk + j) * L + l_p];
|
|
k_p.y = (float)conv_out[conv_base + (key_dim + k_head * dk + j + 1) * L + l_p];
|
|
k_p.z = (float)conv_out[conv_base + (key_dim + k_head * dk + j + 2) * L + l_p];
|
|
k_p.w = (float)conv_out[conv_base + (key_dim + k_head * dk + j + 3) * L + l_p];
|
|
result1 += coeff * k_p;
|
|
}
|
|
|
|
int out_idx = ((b * H + h) * num_chunks + c) * C * dv + row * dv + j;
|
|
vstore4((float4)(result0), 0, v_corrected + out_idx);
|
|
vstore4((float4)(result1), 0, k_cumdecay + out_idx);
|
|
}
|
|
|
|
// Kernel C5: Intra-chunk QK attention with decay (lower triangular including diagonal)
|
|
// qk[i,j] = (q[i] dot k[j]) * q_scale * exp(g[i]-g[j]) for j <= i
|
|
// GWS: {CHUNK_SIZE, CHUNK_SIZE * num_chunks, B * H}
|
|
__kernel void chunk_qk_attn(
|
|
__global const FLOAT* conv_out, // [B, D, L]
|
|
__global const float* g_cumsum, // [B, H, num_chunks, C] (float32)
|
|
__global float* qk_attn_out, // [B, H, num_chunks, C, C] (float32, reuses attn_matrix buf)
|
|
__private const int global_dim0,
|
|
__private const int global_dim1,
|
|
__private const int global_dim2,
|
|
__private const int conv_dim,
|
|
__private const int seq_len,
|
|
__private const int num_v_heads,
|
|
__private const int head_k_dim,
|
|
__private const int key_dim,
|
|
__private const int gqa_factor,
|
|
__private const int num_chunks,
|
|
__private const float q_scale)
|
|
{
|
|
const int col = get_global_id(0);
|
|
const int y = get_global_id(1);
|
|
const int z = get_global_id(2);
|
|
if (col >= global_dim0 || y >= global_dim1 || z >= global_dim2) return;
|
|
|
|
const int c = y / CHUNK_SIZE;
|
|
const int row = y % CHUNK_SIZE;
|
|
const int b = z / num_v_heads;
|
|
const int h = z % num_v_heads;
|
|
|
|
const int C = CHUNK_SIZE;
|
|
const int dk = head_k_dim;
|
|
const int D = conv_dim;
|
|
const int L = seq_len;
|
|
const int H = num_v_heads;
|
|
const int k_head = h / gqa_factor;
|
|
|
|
float val = 0.0f;
|
|
if (col <= row) {
|
|
const int l_row = c * C + row;
|
|
const int l_col = c * C + col;
|
|
if (l_row < L && l_col < L) {
|
|
const int g_base = ((b * H + h) * num_chunks + c) * C;
|
|
const float decay = exp(g_cumsum[g_base + row] - g_cumsum[g_base + col]);
|
|
const int conv_base = b * D * L;
|
|
float dot = 0.0f;
|
|
for (int d = 0; d < dk; ++d) {
|
|
float q_d = (float)conv_out[conv_base + (k_head * dk + d) * L + l_row];
|
|
float k_d = (float)conv_out[conv_base + (key_dim + k_head * dk + d) * L + l_col];
|
|
dot += q_d * k_d;
|
|
}
|
|
val = dot * q_scale * decay;
|
|
}
|
|
}
|
|
|
|
int out_idx = ((b * H + h) * num_chunks + c) * C * C + row * C + col;
|
|
qk_attn_out[out_idx] = val;
|
|
}
|
|
|
|
// Kernel C6: v_new = v_corrected - k_cumdecay @ state (per chunk)
|
|
// GWS: {UP_DIV(dv, 4), CHUNK_SIZE, B * H}
|
|
__kernel void chunk_compute_vnew(
|
|
__global const float* v_corrected, // [B, H, num_chunks, C, dv] (float32)
|
|
__global const float* k_cumdecay, // [B, H, num_chunks, C, dk] (float32)
|
|
__global const FLOAT* recurrent_state,// [B, H, dk, dv]
|
|
__global float* v_new, // [B, H, C, dv] (float32)
|
|
__private const int global_dim0,
|
|
__private const int global_dim1,
|
|
__private const int global_dim2,
|
|
__private const int head_k_dim,
|
|
__private const int head_v_dim,
|
|
__private const int num_v_heads,
|
|
__private const int num_chunks,
|
|
__private const int chunk_idx)
|
|
{
|
|
const int x = get_global_id(0);
|
|
const int p = get_global_id(1);
|
|
const int z = get_global_id(2);
|
|
if (x >= global_dim0 || p >= global_dim1 || z >= global_dim2) return;
|
|
|
|
const int j = x << 2;
|
|
const int b = z / num_v_heads;
|
|
const int h = z % num_v_heads;
|
|
|
|
const int C = CHUNK_SIZE;
|
|
const int dk = head_k_dim;
|
|
const int dv = head_v_dim;
|
|
const int H = num_v_heads;
|
|
const int c = chunk_idx;
|
|
|
|
const int kc_base = ((b * H + h) * num_chunks + c) * C * dk + p * dk;
|
|
const int state_base = (b * H + h) * dk * dv;
|
|
|
|
float4 v_prime = (float4)(0.0f);
|
|
for (int d = 0; d < dk; ++d) {
|
|
float kc_d = k_cumdecay[kc_base + d];
|
|
float4 s = convert_float4(vload4(0, recurrent_state + state_base + d * dv + j));
|
|
v_prime += kc_d * s;
|
|
}
|
|
|
|
int vc_idx = ((b * H + h) * num_chunks + c) * C * dv + p * dv + j;
|
|
float4 vc = vload4(0, v_corrected + vc_idx);
|
|
float4 vn = vc - v_prime;
|
|
|
|
int vn_idx = (b * H + h) * C * dv + p * dv + j;
|
|
vstore4((float4)(vn), 0, v_new + vn_idx);
|
|
}
|
|
|
|
// Kernel C7: Compute output and update recurrent state (per chunk)
|
|
// GWS: {dk * UP_DIV(dv, 4), H, batch}
|
|
__kernel void chunk_output_state_update(
|
|
__global const FLOAT* conv_out, // [B, D, L]
|
|
__global const float* qk_attn_matrix, // [B, H, num_chunks, C, C] (float32)
|
|
__global const float* v_new, // [B, H, C, dv] (float32)
|
|
__global const float* g_cumsum, // [B, H, num_chunks, C] (float32)
|
|
__global FLOAT* recurrent_state, // [B, H, dk, dv]
|
|
__global FLOAT* attn_out, // [B, L, H*dv]
|
|
__private const int global_dim0,
|
|
__private const int global_dim1,
|
|
__private const int global_dim2,
|
|
__private const int conv_dim,
|
|
__private const int seq_len,
|
|
__private const int num_v_heads,
|
|
__private const int head_k_dim,
|
|
__private const int head_v_dim,
|
|
__private const int key_dim,
|
|
__private const int gqa_factor,
|
|
__private const int num_chunks,
|
|
__private const int chunk_idx,
|
|
__private const float q_scale)
|
|
{
|
|
const int x = get_global_id(0);
|
|
const int h = get_global_id(1);
|
|
const int b = get_global_id(2);
|
|
if (x >= global_dim0 || h >= global_dim1 || b >= global_dim2) return;
|
|
|
|
const int dk = head_k_dim;
|
|
const int dv = head_v_dim;
|
|
const int dv4 = (dv + 3) / 4;
|
|
const int j = (x % dv4) << 2;
|
|
const int d = x / dv4;
|
|
const int C = CHUNK_SIZE;
|
|
const int D = conv_dim;
|
|
const int L = seq_len;
|
|
const int H = num_v_heads;
|
|
const int c = chunk_idx;
|
|
const int k_head = h / gqa_factor;
|
|
|
|
const int state_base = (b * H + h) * dk * dv;
|
|
const int g_base = ((b * H + h) * num_chunks + c) * C;
|
|
const int qk_base = ((b * H + h) * num_chunks + c) * C * C;
|
|
const int vn_base = (b * H + h) * C * dv;
|
|
const int conv_base = b * D * L;
|
|
|
|
int last_p = min(C, L - c * C);
|
|
if (last_p <= 0) return;
|
|
|
|
// Phase 2: Update recurrent state
|
|
// state[d,j] = state[d,j]*exp(g_last) + sum_p k[p,d]*exp(g_last-g[p])*v_new[p,j]
|
|
float g_last = g_cumsum[g_base + last_p - 1];
|
|
float decay_state = exp(g_last);
|
|
|
|
float4 s = convert_float4(vload4(0, recurrent_state + state_base + d * dv + j)) * (float4)(decay_state);
|
|
for (int p = 0; p < last_p; ++p) {
|
|
int l_p = c * C + p;
|
|
float g_p = g_cumsum[g_base + p];
|
|
float k_d = (float)conv_out[conv_base + (key_dim + k_head * dk + d) * L + l_p] * exp(g_last - g_p);
|
|
float4 vn = vload4(0, v_new + vn_base + p * dv + j);
|
|
s += k_d * vn;
|
|
}
|
|
vstore4(CONVERT_FLOAT4(s), 0, recurrent_state + state_base + d * dv + j);
|
|
}
|
|
|
|
__kernel void chunk_output(
|
|
__global const FLOAT* conv_out, // [B, D, L]
|
|
__global const float* qk_attn_matrix, // [B, H, num_chunks, C, C] (float32)
|
|
__global const float* v_new, // [B, H, C, dv] (float32)
|
|
__global const float* g_cumsum, // [B, H, num_chunks, C] (float32)
|
|
__global const FLOAT* recurrent_state, // [B, H, dk, dv]
|
|
__global FLOAT* attn_out, // [B, L, H*dv]
|
|
__private const int global_dim0,
|
|
__private const int global_dim1,
|
|
__private const int global_dim2,
|
|
__private const int conv_dim,
|
|
__private const int seq_len,
|
|
__private const int num_v_heads,
|
|
__private const int dk,
|
|
__private const int dv,
|
|
__private const int key_dim,
|
|
__private const int gqa_factor,
|
|
__private const int num_chunks,
|
|
__private const int chunk_idx,
|
|
__private const float q_scale)
|
|
{
|
|
const int x = get_global_id(0);
|
|
const int h = get_global_id(1);
|
|
const int b = get_global_id(2);
|
|
if (x >= global_dim0 || h >= global_dim1 || b >= global_dim2) return;
|
|
|
|
const int dv4 = (dv + 3) / 4;
|
|
const int j = (x % dv4) << 2;
|
|
const int p = x / dv4;
|
|
const int C = CHUNK_SIZE;
|
|
const int D = conv_dim;
|
|
const int L = seq_len;
|
|
const int H = num_v_heads;
|
|
const int c = chunk_idx;
|
|
const int k_head = h / gqa_factor;
|
|
|
|
const int state_base = (b * H + h) * dk * dv;
|
|
const int g_base = ((b * H + h) * num_chunks + c) * C;
|
|
const int qk_base = ((b * H + h) * num_chunks + c) * C * C;
|
|
const int vn_base = (b * H + h) * C * dv;
|
|
const int conv_base = b * D * L;
|
|
|
|
int last_p = min(C, L - c * C);
|
|
if (last_p <= 0 || p >= last_p) return;
|
|
|
|
// Phase 1: Compute output for each position p
|
|
int l_p = c * C + p;
|
|
float g_p = g_cumsum[g_base + p];
|
|
float decay_q = exp(g_p) * q_scale;
|
|
|
|
// Cross-chunk: q * scale * exp(g) @ state
|
|
float4 inter = (float4)(0.0f);
|
|
for (int d = 0; d < dk; d++) {
|
|
float q_d = (float)conv_out[conv_base + (k_head * dk + d) * L + l_p] * decay_q;
|
|
float4 s = convert_float4(vload4(0, recurrent_state + state_base + d * dv + j));
|
|
inter += q_d * s;
|
|
}
|
|
|
|
// Intra-chunk: qk_attn @ v_new
|
|
float4 intra = (float4)(0.0f);
|
|
for (int r = 0; r <= p; ++r) {
|
|
float qk = qk_attn_matrix[qk_base + p * C + r];
|
|
float4 vn = vload4(0, v_new + vn_base + r * dv + j);
|
|
intra += qk * vn;
|
|
}
|
|
|
|
const int out_offset = (b * L + l_p) * H * dv + h * dv + j;
|
|
vstore4(CONVERT_FLOAT4(inter + intra), 0, attn_out + out_offset);
|
|
}
|
|
|
|
#endif // CHUNK_PREFILL |