← Back to C-Kernel-Engine Docs Doxygen Source Documentation
 
Loading...
Searching...
No Matches
hybrid_attention_kernels.c
Go to the documentation of this file.
1#include "ckernel_engine.h"
2#include "bf16_utils.h"
3#include "ck_threadpool.h"
4
5#include <dlfcn.h>
6#include <limits.h>
7#include <math.h>
8#include <pthread.h>
9#include <stdint.h>
10#include <stdio.h>
11#include <stdlib.h>
12#include <string.h>
13
14typedef float (*ck_hybrid_libm_f32_fn)(float);
16static void *ck_hybrid_libm_handle = NULL;
17static pthread_once_t ck_hybrid_libm_once = PTHREAD_ONCE_INIT;
18
19static void ck_bind_hybrid_llama_libm(void) {
20 ck_hybrid_libm_handle = dlopen("libm.so.6", RTLD_NOW | RTLD_LOCAL);
24 }
26 fprintf(stderr,
27 "HARD KERNEL CONTRACT FAULT: llama.cpp attention gate "
28 "requires expf from libm.so.6\n");
29 abort();
30 }
31}
32
33#if defined(__GNUC__) || defined(__clang__)
34__attribute__((noinline))
35#endif
36static float hybrid_sigmoid(float x) {
38 return 1.0f / (1.0f + ck_hybrid_llama_expf(-x));
39}
40
41void split_q_gate_forward(const float *packed_qg,
42 float *q,
43 float *gate,
44 int rows,
45 int q_dim,
46 int gate_dim,
47 int group_dim) {
48 const int packed_dim = q_dim + gate_dim;
49 if (!packed_qg || !q || !gate || rows <= 0 || q_dim <= 0 || gate_dim <= 0) {
50 return;
51 }
52 if (group_dim <= 0) {
53 group_dim = q_dim;
54 }
55 const int q_groups = q_dim / group_dim;
56 const int gate_group_dim = (q_groups > 0 && gate_dim % q_groups == 0) ? (gate_dim / q_groups) : gate_dim;
57 for (int row = 0; row < rows; ++row) {
58 const float *src = packed_qg + (size_t) row * (size_t) packed_dim;
59 float *q_dst = q + (size_t) row * (size_t) q_dim;
60 float *gate_dst = gate + (size_t) row * (size_t) gate_dim;
61 if (q_groups > 0 && q_groups * group_dim == q_dim && q_groups * gate_group_dim == gate_dim) {
62 for (int group = 0; group < q_groups; ++group) {
63 const size_t src_group_off = (size_t) group * (size_t) (group_dim + gate_group_dim);
64 memcpy(
65 q_dst + (size_t) group * (size_t) group_dim,
66 src + src_group_off,
67 (size_t) group_dim * sizeof(float));
68 memcpy(
69 gate_dst + (size_t) group * (size_t) gate_group_dim,
70 src + src_group_off + (size_t) group_dim,
71 (size_t) gate_group_dim * sizeof(float));
72 }
73 } else {
74 memcpy(q_dst, src, (size_t) q_dim * sizeof(float));
75 memcpy(gate_dst, src + q_dim, (size_t) gate_dim * sizeof(float));
76 }
77 }
78}
79
80void split_q_gate_backward(const float *d_q,
81 const float *d_gate,
82 float *d_packed_qg,
83 int rows,
84 int q_dim,
85 int gate_dim,
86 int group_dim) {
87 const int packed_dim = q_dim + gate_dim;
88 if (!d_q || !d_gate || !d_packed_qg || rows <= 0 || q_dim <= 0 || gate_dim <= 0) {
89 return;
90 }
91 if (group_dim <= 0) {
92 group_dim = q_dim;
93 }
94 const int q_groups = q_dim / group_dim;
95 const int gate_group_dim = (q_groups > 0 && gate_dim % q_groups == 0) ? (gate_dim / q_groups) : gate_dim;
96 for (int row = 0; row < rows; ++row) {
97 const float *dq_src = d_q + (size_t) row * (size_t) q_dim;
98 const float *dg_src = d_gate + (size_t) row * (size_t) gate_dim;
99 float *dst = d_packed_qg + (size_t) row * (size_t) packed_dim;
100 if (q_groups > 0 && q_groups * group_dim == q_dim && q_groups * gate_group_dim == gate_dim) {
101 for (int group = 0; group < q_groups; ++group) {
102 const size_t dst_group_off = (size_t) group * (size_t) (group_dim + gate_group_dim);
103 memcpy(
104 dst + dst_group_off,
105 dq_src + (size_t) group * (size_t) group_dim,
106 (size_t) group_dim * sizeof(float));
107 memcpy(
108 dst + dst_group_off + (size_t) group_dim,
109 dg_src + (size_t) group * (size_t) gate_group_dim,
110 (size_t) gate_group_dim * sizeof(float));
111 }
112 } else {
113 memcpy(dst, dq_src, (size_t) q_dim * sizeof(float));
114 memcpy(dst + q_dim, dg_src, (size_t) gate_dim * sizeof(float));
115 }
116 }
117}
118
119typedef struct {
120 const float *x;
121 const float *gate;
122 float *out;
123 int dim;
124} ck_attn_gate_sigmoid_mul_args_t;
125
127 int end,
128 void *opaque) {
129 const ck_attn_gate_sigmoid_mul_args_t *args =
130 (const ck_attn_gate_sigmoid_mul_args_t *)opaque;
131 for (int row = begin; row < end; ++row) {
132 const float *x_row =
133 args->x + (size_t)row * (size_t)args->dim;
134 const float *gate_row =
135 args->gate + (size_t)row * (size_t)args->dim;
136 float *out_row =
137 args->out + (size_t)row * (size_t)args->dim;
138 const int dim = args->dim;
139 for (int col = 0; col < dim; ++col) {
140 out_row[col] = x_row[col] * hybrid_sigmoid(gate_row[col]);
141 }
142 }
143}
144
145static int hybrid_ranges_overlap(const float *lhs,
146 const float *rhs,
147 size_t elements) {
148 const uintptr_t lhs_begin = (uintptr_t)lhs;
149 const uintptr_t rhs_begin = (uintptr_t)rhs;
150 const size_t bytes = elements * sizeof(float);
151 return lhs_begin < rhs_begin + bytes && rhs_begin < lhs_begin + bytes;
152}
153
155 const float *gate,
156 float *out,
157 int rows,
158 int num_heads,
159 int state_dim) {
160 if (!x || !gate || !out || rows <= 0 || num_heads <= 0 ||
161 state_dim <= 0 || num_heads > INT_MAX / state_dim) {
162 return;
163 }
164 const int dim = num_heads * state_dim;
165 const size_t elements = (size_t)rows * (size_t)dim;
166 ck_attn_gate_sigmoid_mul_args_t args = {x, gate, out, dim};
167 const size_t minimum_elements_per_thread = 32768u;
168
169 const int partial_x_overlap = out != x &&
170 hybrid_ranges_overlap(out, x, elements);
171 const int gate_overlap = hybrid_ranges_overlap(out, gate, elements);
172 if (partial_x_overlap || gate_overlap ||
173 elements < 2u * minimum_elements_per_thread) {
174 attn_gate_sigmoid_mul_row_range(0, rows, &args);
175 return;
176 }
177
178 ck_threadpool_t *pool = ck_threadpool_global();
179 int active = pool ? ck_threadpool_n_threads(pool) : 1;
180 size_t useful_threads =
181 (elements + minimum_elements_per_thread - 1u) /
182 minimum_elements_per_thread;
183 if ((size_t)active > useful_threads) {
184 active = (int)useful_threads;
185 }
186 if (active > rows) {
187 active = rows;
188 }
189
190 if (active > 1 && ck_threadpool_thread_id(pool) <= 0) {
192 pool, active, 0, rows, 4,
194 } else {
195 attn_gate_sigmoid_mul_row_range(0, rows, &args);
196 }
197}
198
200 const float *gate,
201 float *out,
202 int rows,
203 int num_heads,
204 int state_dim) {
205 if (!x || !gate || !out || rows <= 0 || num_heads <= 0 || state_dim <= 0) {
206 return;
207 }
208 const int dim = num_heads * state_dim;
209 for (int row = 0; row < rows; ++row) {
210 const float *x_row = x + (size_t)row * (size_t)dim;
211 const float *gate_row = gate + (size_t)row * (size_t)dim;
212 float *out_row = out + (size_t)row * (size_t)dim;
213 for (int col = 0; col < dim; col += 16) {
214 const int width = dim - col < 16 ? dim - col : 16;
215 float sigmoid[16];
217 gate_row + col, sigmoid, 1, width);
218 for (int lane = 0; lane < width; ++lane) {
219 const float x_bf16 = bf16_to_float(float_to_bf16(x_row[col + lane]));
220 const float sigmoid_bf16 = bf16_to_float(float_to_bf16(sigmoid[lane]));
221 out_row[col + lane] = bf16_to_float(float_to_bf16(
222 x_bf16 * sigmoid_bf16));
223 }
224 }
225 }
226}
227
229 const float *gate,
230 float *out,
231 int rows,
232 int num_heads,
233 int state_dim) {
234 const int dim = num_heads * state_dim;
235 for (int row = 0; row < rows; ++row) {
236 const float *x_row = x + (size_t) row * (size_t) dim;
237 const float *gate_row = gate + (size_t) row * (size_t) num_heads;
238 float *out_row = out + (size_t) row * (size_t) dim;
239 for (int head = 0; head < num_heads; ++head) {
240 const float value = gate_row[head];
241 const float scale = value > 20.0f
242 ? value
243 : log1pf(expf(value));
244 const size_t base = (size_t) head * (size_t) state_dim;
245 for (int col = 0; col < state_dim; ++col) {
246 out_row[base + (size_t) col] =
247 x_row[base + (size_t) col] * scale;
248 }
249 }
250 }
251}
252
253void attn_gate_sigmoid_mul_backward(const float *d_out,
254 const float *x,
255 const float *gate,
256 float *d_x,
257 float *d_gate,
258 int rows,
259 int num_heads,
260 int state_dim) {
261 const int dim = num_heads * state_dim;
262 for (int row = 0; row < rows; ++row) {
263 const float *d_out_row = d_out + (size_t) row * (size_t) dim;
264 const float *x_row = x + (size_t) row * (size_t) dim;
265 const float *gate_row = gate + (size_t) row * (size_t) dim;
266 float *d_x_row = d_x + (size_t) row * (size_t) dim;
267 float *d_gate_row = d_gate + (size_t) row * (size_t) dim;
268 for (int col = 0; col < dim; ++col) {
269 const float sig = hybrid_sigmoid(gate_row[col]);
270 d_x_row[col] = d_out_row[col] * sig;
271 d_gate_row[col] = d_out_row[col] * x_row[col] * sig * (1.0f - sig);
272 }
273 }
274}
static uint16_t float_to_bf16(float f)
Definition bf16_utils.h:90
static float bf16_to_float(uint16_t v)
Definition bf16_utils.h:38
Persistent pthread thread pool for CK-Engine inference.
void ck_threadpool_parallel_for_n(ck_threadpool_t *pool, int active_threads, int begin, int end, int grain_size, ck_range_fn_t fn, void *args)
ck_threadpool_t * ck_threadpool_global(void)
int ck_threadpool_thread_id(const ck_threadpool_t *pool)
int ck_threadpool_n_threads(const ck_threadpool_t *pool)
void recurrent_sigmoid_forward_pytorch_bf16_input_fp32_output(const float *x, float *out, int rows, int dim)
static void ck_bind_hybrid_llama_libm(void)
void split_q_gate_backward(const float *d_q, const float *d_gate, float *d_packed_qg, int rows, int q_dim, int gate_dim, int group_dim)
void attn_gate_sigmoid_mul_pytorch_bf16_storage(const float *x, const float *gate, float *out, int rows, int num_heads, int state_dim)
void attn_gate_sigmoid_mul_forward(const float *x, const float *gate, float *out, int rows, int num_heads, int state_dim)
void attn_gate_softplus_mul_forward(const float *x, const float *gate, float *out, int rows, int num_heads, int state_dim)
static void * ck_hybrid_libm_handle
static pthread_once_t ck_hybrid_libm_once
static int hybrid_ranges_overlap(const float *lhs, const float *rhs, size_t elements)
static ck_hybrid_libm_f32_fn ck_hybrid_llama_expf
static void attn_gate_sigmoid_mul_row_range(int begin, int end, void *opaque)
float(* ck_hybrid_libm_f32_fn)(float)
void attn_gate_sigmoid_mul_backward(const float *d_out, const float *x, const float *gate, float *d_x, float *d_gate, int rows, int num_heads, int state_dim)
static float hybrid_sigmoid(float x)
void split_q_gate_forward(const float *packed_qg, float *q, float *gate, int rows, int q_dim, int gate_dim, int group_dim)
__attribute__((visibility("default"))) CKTokenizer *ck_tokenizer_create(CKTokenizerType type)
uint32_t end
Definition utf8.c:215