315 lines
13 KiB
Metal
315 lines
13 KiB
Metal
// Kimi Delta Attention kernels, adapted from the kimi-k3 branch.
|
|
|
|
struct glm53_kda_args {
|
|
uint n_heads;
|
|
uint n_rows;
|
|
float lower_bound;
|
|
float norm_eps;
|
|
};
|
|
|
|
/*
|
|
* One threadgroup owns one (sequence, head). Four simdgroups update four
|
|
* value rows concurrently; every lane owns four adjacent key columns.
|
|
*/
|
|
kernel void kernel_glm53_kda_decode(
|
|
constant glm53_kda_args &args,
|
|
device const float *q_in,
|
|
device const float *k_in,
|
|
device const float *v_in,
|
|
device const float *raw_gate,
|
|
device const float *raw_beta,
|
|
device const float *output_gate,
|
|
device const float *q_conv,
|
|
device const float *k_conv,
|
|
device const float *v_conv,
|
|
device const float *a_log,
|
|
device const float *dt_bias,
|
|
device const float *output_norm,
|
|
device float *conv_state,
|
|
device float *state,
|
|
device float *out,
|
|
threadgroup float *scratch [[threadgroup(0)]],
|
|
uint2 tgpig [[threadgroup_position_in_grid]],
|
|
ushort tid [[thread_index_in_threadgroup]],
|
|
ushort lane [[thread_index_in_simdgroup]],
|
|
ushort sg [[simdgroup_index_in_threadgroup]]) {
|
|
constexpr uint D = 128u;
|
|
constexpr uint HISTORY = 3u;
|
|
const uint row = tgpig.x;
|
|
const uint head = tgpig.y;
|
|
if (row >= args.n_rows || head >= args.n_heads) return;
|
|
|
|
threadgroup float *sq = scratch;
|
|
threadgroup float *sk = sq + D;
|
|
threadgroup float *sd = sk + D;
|
|
threadgroup float *sv = sd + D;
|
|
threadgroup float *so = sv + D;
|
|
threadgroup float *reduce_q = so + D;
|
|
threadgroup float *reduce_k = reduce_q + 4u;
|
|
threadgroup float *reduce_o = reduce_k + 4u;
|
|
threadgroup float *beta_shared = reduce_o + 4u;
|
|
|
|
const uint projection = args.n_heads * D;
|
|
const uint channel = head * D + tid;
|
|
const ulong input_base = (ulong)row * projection + head * D;
|
|
const ulong conv_row_stride = 3ul * HISTORY * projection;
|
|
|
|
if (tid < D) {
|
|
float q_acc = 0.0f;
|
|
float k_acc = 0.0f;
|
|
float v_acc = 0.0f;
|
|
device float *q_state = conv_state +
|
|
(ulong)row * conv_row_stride;
|
|
device float *k_state = q_state + HISTORY * projection;
|
|
device float *v_state = k_state + HISTORY * projection;
|
|
for (uint w = 0; w < HISTORY; w++) {
|
|
q_acc = fma(q_state[(ulong)w * projection + channel],
|
|
q_conv[(ulong)channel * 4u + w], q_acc);
|
|
k_acc = fma(k_state[(ulong)w * projection + channel],
|
|
k_conv[(ulong)channel * 4u + w], k_acc);
|
|
v_acc = fma(v_state[(ulong)w * projection + channel],
|
|
v_conv[(ulong)channel * 4u + w], v_acc);
|
|
}
|
|
const float q_new = q_in[input_base + tid];
|
|
const float k_new = k_in[input_base + tid];
|
|
const float v_new = v_in[input_base + tid];
|
|
q_acc = fma(q_new, q_conv[(ulong)channel * 4u + 3u], q_acc);
|
|
k_acc = fma(k_new, k_conv[(ulong)channel * 4u + 3u], k_acc);
|
|
v_acc = fma(v_new, v_conv[(ulong)channel * 4u + 3u], v_acc);
|
|
|
|
q_state[channel] = q_state[projection + channel];
|
|
q_state[projection + channel] = q_state[2ul * projection + channel];
|
|
q_state[2ul * projection + channel] = q_new;
|
|
k_state[channel] = k_state[projection + channel];
|
|
k_state[projection + channel] = k_state[2ul * projection + channel];
|
|
k_state[2ul * projection + channel] = k_new;
|
|
v_state[channel] = v_state[projection + channel];
|
|
v_state[projection + channel] = v_state[2ul * projection + channel];
|
|
v_state[2ul * projection + channel] = v_new;
|
|
|
|
sq[tid] = q_acc / (1.0f + exp(-q_acc));
|
|
sk[tid] = k_acc / (1.0f + exp(-k_acc));
|
|
sv[tid] = v_acc / (1.0f + exp(-v_acc));
|
|
const float gate = raw_gate[input_base + tid] + dt_bias[channel];
|
|
sd[tid] = exp(args.lower_bound *
|
|
(1.0f / (1.0f + exp(-exp(a_log[head]) * gate))));
|
|
}
|
|
if (tid == 0u) {
|
|
beta_shared[0] =
|
|
1.0f / (1.0f + exp(-raw_beta[(ulong)row * args.n_heads + head]));
|
|
}
|
|
threadgroup_barrier(mem_flags::mem_threadgroup |
|
|
mem_flags::mem_device);
|
|
|
|
float q_sumsq = sq[tid] * sq[tid];
|
|
float k_sumsq = sk[tid] * sk[tid];
|
|
q_sumsq = simd_sum(q_sumsq);
|
|
k_sumsq = simd_sum(k_sumsq);
|
|
if (lane == 0u) {
|
|
reduce_q[sg] = q_sumsq;
|
|
reduce_k[sg] = k_sumsq;
|
|
}
|
|
threadgroup_barrier(mem_flags::mem_threadgroup);
|
|
float q_total = lane < 4u ? reduce_q[lane] : 0.0f;
|
|
float k_total = lane < 4u ? reduce_k[lane] : 0.0f;
|
|
q_total = simd_sum(q_total);
|
|
k_total = simd_sum(k_total);
|
|
const float q_scale = rsqrt(q_total + 1.0e-6f) * 0x1.6a09e6p-4f;
|
|
const float k_scale = rsqrt(k_total + 1.0e-6f);
|
|
if (tid < D) {
|
|
sq[tid] *= q_scale;
|
|
sk[tid] *= k_scale;
|
|
}
|
|
threadgroup_barrier(mem_flags::mem_threadgroup);
|
|
|
|
const uint k0 = lane * 4u;
|
|
const float4 q4 = *((threadgroup float4 *)(sq + k0));
|
|
const float4 k4 = *((threadgroup float4 *)(sk + k0));
|
|
const float4 decay4 = *((threadgroup float4 *)(sd + k0));
|
|
const ulong state_head =
|
|
((ulong)row * args.n_heads + head) * D * D;
|
|
|
|
for (uint value = sg; value < D; value += 4u) {
|
|
device float4 *hptr =
|
|
(device float4 *)(state + state_head + (ulong)value * D + k0);
|
|
float4 h = *hptr * decay4;
|
|
float hk = dot(h, k4);
|
|
hk = simd_sum(hk);
|
|
const float delta_v = (sv[value] - hk) * beta_shared[0];
|
|
h = fma(k4, float4(delta_v), h);
|
|
*hptr = h;
|
|
float hq = simd_sum(dot(h, q4));
|
|
if (lane == 0u) so[value] = hq;
|
|
}
|
|
threadgroup_barrier(mem_flags::mem_threadgroup |
|
|
mem_flags::mem_device);
|
|
|
|
float o_sumsq = so[tid] * so[tid];
|
|
o_sumsq = simd_sum(o_sumsq);
|
|
if (lane == 0u) reduce_o[sg] = o_sumsq;
|
|
threadgroup_barrier(mem_flags::mem_threadgroup);
|
|
float o_total = lane < 4u ? reduce_o[lane] : 0.0f;
|
|
o_total = simd_sum(o_total);
|
|
const float o_scale = rsqrt(o_total / (float)D + args.norm_eps);
|
|
if (tid < D) {
|
|
const ulong index = input_base + tid;
|
|
const float gate =
|
|
1.0f / (1.0f + exp(-output_gate[index]));
|
|
out[index] = so[tid] * o_scale * output_norm[tid] * gate;
|
|
}
|
|
}
|
|
|
|
kernel void kernel_glm53_kda_prefill_prepare(
|
|
constant glm53_kda_args &args,
|
|
device float *q,
|
|
device float *k,
|
|
device float *v,
|
|
device float *raw_gate,
|
|
device const float *q_conv,
|
|
device const float *k_conv,
|
|
device const float *v_conv,
|
|
device const float *a_log,
|
|
device const float *dt_bias,
|
|
device float *conv_state,
|
|
threadgroup float *scratch [[threadgroup(0)]],
|
|
uint head [[threadgroup_position_in_grid]],
|
|
ushort tid [[thread_index_in_threadgroup]],
|
|
ushort lane [[thread_index_in_simdgroup]],
|
|
ushort sg [[simdgroup_index_in_threadgroup]]) {
|
|
constexpr uint D = 128u;
|
|
constexpr uint HISTORY = 3u;
|
|
if (head >= args.n_heads) return;
|
|
threadgroup float *sq = scratch;
|
|
threadgroup float *sk = sq + D;
|
|
threadgroup float *reduce_q = sk + D;
|
|
threadgroup float *reduce_k = reduce_q + 4u;
|
|
const uint projection = args.n_heads * D;
|
|
const uint channel = head * D + tid;
|
|
device float *q_state = conv_state;
|
|
device float *k_state = q_state + HISTORY * projection;
|
|
device float *v_state = k_state + HISTORY * projection;
|
|
|
|
for (uint token = 0; token < args.n_rows; token++) {
|
|
const ulong index = (ulong)token * projection + channel;
|
|
float q_acc = 0.0f;
|
|
float k_acc = 0.0f;
|
|
float v_acc = 0.0f;
|
|
for (uint w = 0; w < HISTORY; w++) {
|
|
q_acc = fma(q_state[(ulong)w * projection + channel],
|
|
q_conv[(ulong)channel * 4u + w], q_acc);
|
|
k_acc = fma(k_state[(ulong)w * projection + channel],
|
|
k_conv[(ulong)channel * 4u + w], k_acc);
|
|
v_acc = fma(v_state[(ulong)w * projection + channel],
|
|
v_conv[(ulong)channel * 4u + w], v_acc);
|
|
}
|
|
const float q_new = q[index];
|
|
const float k_new = k[index];
|
|
const float v_new = v[index];
|
|
q_acc = fma(q_new, q_conv[(ulong)channel * 4u + 3u], q_acc);
|
|
k_acc = fma(k_new, k_conv[(ulong)channel * 4u + 3u], k_acc);
|
|
v_acc = fma(v_new, v_conv[(ulong)channel * 4u + 3u], v_acc);
|
|
q_state[channel] = q_state[projection + channel];
|
|
q_state[projection + channel] = q_state[2ul * projection + channel];
|
|
q_state[2ul * projection + channel] = q_new;
|
|
k_state[channel] = k_state[projection + channel];
|
|
k_state[projection + channel] = k_state[2ul * projection + channel];
|
|
k_state[2ul * projection + channel] = k_new;
|
|
v_state[channel] = v_state[projection + channel];
|
|
v_state[projection + channel] = v_state[2ul * projection + channel];
|
|
v_state[2ul * projection + channel] = v_new;
|
|
|
|
sq[tid] = q_acc / (1.0f + exp(-q_acc));
|
|
sk[tid] = k_acc / (1.0f + exp(-k_acc));
|
|
v[index] = v_acc / (1.0f + exp(-v_acc));
|
|
const float gate = raw_gate[index] + dt_bias[channel];
|
|
raw_gate[index] = exp(args.lower_bound *
|
|
(1.0f / (1.0f + exp(-exp(a_log[head]) * gate))));
|
|
threadgroup_barrier(mem_flags::mem_threadgroup |
|
|
mem_flags::mem_device);
|
|
|
|
float q_sumsq = simd_sum(sq[tid] * sq[tid]);
|
|
float k_sumsq = simd_sum(sk[tid] * sk[tid]);
|
|
if (lane == 0u) {
|
|
reduce_q[sg] = q_sumsq;
|
|
reduce_k[sg] = k_sumsq;
|
|
}
|
|
threadgroup_barrier(mem_flags::mem_threadgroup);
|
|
float q_total = lane < 4u ? reduce_q[lane] : 0.0f;
|
|
float k_total = lane < 4u ? reduce_k[lane] : 0.0f;
|
|
q_total = simd_sum(q_total);
|
|
k_total = simd_sum(k_total);
|
|
q[index] = sq[tid] * rsqrt(q_total + 1.0e-6f) *
|
|
0x1.6a09e6p-4f;
|
|
k[index] = sk[tid] * rsqrt(k_total + 1.0e-6f);
|
|
threadgroup_barrier(mem_flags::mem_threadgroup |
|
|
mem_flags::mem_device);
|
|
}
|
|
}
|
|
|
|
kernel void kernel_glm53_kda_prefill_recurrence(
|
|
constant glm53_kda_args &args,
|
|
device const float *q,
|
|
device const float *k,
|
|
device const float *v,
|
|
device const float *decay,
|
|
device const float *raw_beta,
|
|
device float *state,
|
|
device float *out,
|
|
uint2 tgpig [[threadgroup_position_in_grid]],
|
|
ushort lane [[thread_index_in_simdgroup]],
|
|
ushort sg [[simdgroup_index_in_threadgroup]]) {
|
|
constexpr uint D = 128u;
|
|
const uint head = tgpig.x;
|
|
const uint value = tgpig.y * 4u + sg;
|
|
if (head >= args.n_heads || value >= D) return;
|
|
const uint projection = args.n_heads * D;
|
|
const uint k0 = lane * 4u;
|
|
device float4 *state_ptr = (device float4 *)(
|
|
state + ((ulong)head * D + value) * D + k0);
|
|
float4 h = *state_ptr;
|
|
|
|
for (uint token = 0; token < args.n_rows; token++) {
|
|
const ulong base = (ulong)token * projection + head * D;
|
|
const float4 q4 = *((device const float4 *)(q + base + k0));
|
|
const float4 k4 = *((device const float4 *)(k + base + k0));
|
|
const float4 decay4 =
|
|
*((device const float4 *)(decay + base + k0));
|
|
h *= decay4;
|
|
const float hk = simd_sum(dot(h, k4));
|
|
const float beta = 1.0f /
|
|
(1.0f + exp(-raw_beta[(ulong)token * args.n_heads + head]));
|
|
const float delta_v = (v[base + value] - hk) * beta;
|
|
h = fma(k4, float4(delta_v), h);
|
|
const float result = simd_sum(dot(h, q4));
|
|
if (lane == 0u) out[base + value] = result;
|
|
}
|
|
*state_ptr = h;
|
|
}
|
|
|
|
kernel void kernel_glm53_kda_prefill_output(
|
|
constant glm53_kda_args &args,
|
|
device float *out,
|
|
device const float *output_gate,
|
|
device const float *output_norm,
|
|
threadgroup float *partial [[threadgroup(0)]],
|
|
uint2 tgpig [[threadgroup_position_in_grid]],
|
|
ushort tid [[thread_index_in_threadgroup]],
|
|
ushort lane [[thread_index_in_simdgroup]],
|
|
ushort sg [[simdgroup_index_in_threadgroup]]) {
|
|
constexpr uint D = 128u;
|
|
const uint token = tgpig.x;
|
|
const uint head = tgpig.y;
|
|
if (token >= args.n_rows || head >= args.n_heads) return;
|
|
const uint projection = args.n_heads * D;
|
|
const ulong base = (ulong)token * projection + head * D;
|
|
const float raw = out[base + tid];
|
|
float sumsq = simd_sum(raw * raw);
|
|
if (lane == 0u) partial[sg] = sumsq;
|
|
threadgroup_barrier(mem_flags::mem_threadgroup);
|
|
float total = lane < 4u ? partial[lane] : 0.0f;
|
|
total = simd_sum(total);
|
|
const float scale = rsqrt(total / (float)D + args.norm_eps);
|
|
out[base + tid] = raw * scale * output_norm[tid] /
|
|
(1.0f + exp(-output_gate[base + tid]));
|
|
}
|