← Back to C-Kernel-Engine Docs Doxygen Source Documentation
 
Loading...
Searching...
No Matches
deepseek_kernels.c
Go to the documentation of this file.
1/**
2 * @file deepseek_kernels.c
3 * @brief Scalar reference kernels for DeepSeek-style research ops.
4 *
5 * These kernels intentionally prioritize explicit math contracts over speed.
6 * They are used to pin PyTorch parity before adding SIMD/threaded variants.
7 */
8
9#include <math.h>
10#include <float.h>
11#include <limits.h>
12#include <stddef.h>
13#include <stdint.h>
14#include <stdlib.h>
15#include <string.h>
16
17#include "bf16_utils.h"
18#include "ck_threadpool.h"
19
20static inline size_t ds_mhc_idx(int t, int s, int d, int n_streams, int dim)
21{
22 return ((size_t)t * (size_t)n_streams + (size_t)s) * (size_t)dim + (size_t)d;
23}
24
25static inline size_t ds_mix_idx(int t, int out_s, int in_s, int n_streams)
26{
27 return ((size_t)t * (size_t)n_streams + (size_t)out_s) * (size_t)n_streams + (size_t)in_s;
28}
29
30void deepseek_mhc_mix_f32(const float *streams,
31 const float *mix,
32 float *out,
33 int tokens,
34 int n_streams,
35 int dim)
36{
37 if (!streams || !mix || !out || tokens <= 0 || n_streams <= 0 || dim <= 0) return;
38
39 for (int t = 0; t < tokens; ++t) {
40 for (int os = 0; os < n_streams; ++os) {
41 for (int d = 0; d < dim; ++d) {
42 float acc = 0.0f;
43 for (int is = 0; is < n_streams; ++is) {
44 acc += mix[ds_mix_idx(t, os, is, n_streams)] *
45 streams[ds_mhc_idx(t, is, d, n_streams, dim)];
46 }
47 out[ds_mhc_idx(t, os, d, n_streams, dim)] = acc;
48 }
49 }
50 }
51}
52
53void deepseek_mhc_mix_backward_f32(const float *d_out,
54 const float *streams,
55 const float *mix,
56 float *d_streams,
57 float *d_mix,
58 int tokens,
59 int n_streams,
60 int dim)
61{
62 if (!d_out || !streams || !mix || !d_streams || !d_mix ||
63 tokens <= 0 || n_streams <= 0 || dim <= 0) return;
64
65 const size_t stream_count = (size_t)tokens * (size_t)n_streams * (size_t)dim;
66 const size_t mix_count = (size_t)tokens * (size_t)n_streams * (size_t)n_streams;
67 for (size_t i = 0; i < stream_count; ++i) d_streams[i] = 0.0f;
68 for (size_t i = 0; i < mix_count; ++i) d_mix[i] = 0.0f;
69
70 for (int t = 0; t < tokens; ++t) {
71 for (int os = 0; os < n_streams; ++os) {
72 for (int is = 0; is < n_streams; ++is) {
73 float d_mix_acc = 0.0f;
74 const float m = mix[ds_mix_idx(t, os, is, n_streams)];
75 for (int d = 0; d < dim; ++d) {
76 const float go = d_out[ds_mhc_idx(t, os, d, n_streams, dim)];
77 d_streams[ds_mhc_idx(t, is, d, n_streams, dim)] += m * go;
78 d_mix_acc += go * streams[ds_mhc_idx(t, is, d, n_streams, dim)];
79 }
80 d_mix[ds_mix_idx(t, os, is, n_streams)] = d_mix_acc;
81 }
82 }
83 }
84}
85
86void deepseek_dsa_topk_softmax_f32(const float *scores,
87 int *indices,
88 float *weights,
89 int tokens,
90 int heads,
91 int key_count,
92 int top_k)
93{
94 if (!scores || !indices || !weights || tokens <= 0 || heads <= 0 ||
95 key_count <= 0 || top_k <= 0) return;
96
97 if (top_k > key_count) top_k = key_count;
98
99 for (int t = 0; t < tokens; ++t) {
100 for (int h = 0; h < heads; ++h) {
101 const float *row = scores + ((size_t)t * (size_t)heads + (size_t)h) * (size_t)key_count;
102 int *idx = indices + ((size_t)t * (size_t)heads + (size_t)h) * (size_t)top_k;
103 float *w = weights + ((size_t)t * (size_t)heads + (size_t)h) * (size_t)top_k;
104
105 for (int k = 0; k < top_k; ++k) {
106 idx[k] = -1;
107 w[k] = -FLT_MAX;
108 }
109
110 for (int j = 0; j < key_count; ++j) {
111 const float v = row[j];
112 int pos = -1;
113 for (int k = 0; k < top_k; ++k) {
114 if (idx[k] < 0 || v > w[k] || (v == w[k] && j < idx[k])) {
115 pos = k;
116 break;
117 }
118 }
119 if (pos >= 0) {
120 for (int k = top_k - 1; k > pos; --k) {
121 idx[k] = idx[k - 1];
122 w[k] = w[k - 1];
123 }
124 idx[pos] = j;
125 w[pos] = v;
126 }
127 }
128
129 float max_v = w[0];
130 for (int k = 1; k < top_k; ++k) if (w[k] > max_v) max_v = w[k];
131 float sum = 0.0f;
132 for (int k = 0; k < top_k; ++k) {
133 w[k] = expf(w[k] - max_v);
134 sum += w[k];
135 }
136 if (sum > 0.0f) {
137 const float inv = 1.0f / sum;
138 for (int k = 0; k < top_k; ++k) w[k] *= inv;
139 }
140 }
141 }
142}
143
144
145extern void topk_softmax_backward_f32(const int *indices,
146 const float *weights,
147 const float *d_weights,
148 float *d_scores,
149 int num_tokens,
150 int n_experts_or_keys,
151 int k);
152
154 const float *weights,
155 const float *d_weights,
156 float *d_scores,
157 int tokens,
158 int heads,
159 int key_count,
160 int top_k)
161{
162 if (!indices || !weights || !d_weights || !d_scores ||
163 tokens <= 0 || heads <= 0 || key_count <= 0 || top_k <= 0) return;
164
166 weights,
167 d_weights,
168 d_scores,
169 tokens * heads,
170 key_count,
171 top_k);
172}
173
174
175static inline size_t ds_mla_tok_idx(int t, int d, int dim)
176{
177 return (size_t)t * (size_t)dim + (size_t)d;
178}
179
180static inline size_t ds_mla_thd_idx(int t, int h, int d, int heads, int dim)
181{
182 return ((size_t)t * (size_t)heads + (size_t)h) * (size_t)dim + (size_t)d;
183}
184
185void deepseek_mla_kv_decompress_f32(const float *compressed_kv,
186 const float *kv_b_proj,
187 float *k_nope,
188 float *value,
189 int tokens,
190 int heads,
191 int kv_lora_rank,
192 int qk_nope_dim,
193 int v_dim)
194{
195 if (!compressed_kv || !kv_b_proj || !k_nope || !value ||
196 tokens <= 0 || heads <= 0 || kv_lora_rank <= 0 || qk_nope_dim <= 0 || v_dim <= 0) {
197 return;
198 }
199
200 const int out_per_head = qk_nope_dim + v_dim;
201 for (int t = 0; t < tokens; ++t) {
202 for (int h = 0; h < heads; ++h) {
203 for (int d = 0; d < qk_nope_dim; ++d) {
204 const int out_col = h * out_per_head + d;
205 float acc = 0.0f;
206 for (int r = 0; r < kv_lora_rank; ++r) {
207 acc += kv_b_proj[(size_t)out_col * (size_t)kv_lora_rank + (size_t)r] *
208 compressed_kv[ds_mla_tok_idx(t, r, kv_lora_rank)];
209 }
210 k_nope[ds_mla_thd_idx(t, h, d, heads, qk_nope_dim)] = acc;
211 }
212 for (int d = 0; d < v_dim; ++d) {
213 const int out_col = h * out_per_head + qk_nope_dim + d;
214 float acc = 0.0f;
215 for (int r = 0; r < kv_lora_rank; ++r) {
216 acc += kv_b_proj[(size_t)out_col * (size_t)kv_lora_rank + (size_t)r] *
217 compressed_kv[ds_mla_tok_idx(t, r, kv_lora_rank)];
218 }
219 value[ds_mla_thd_idx(t, h, d, heads, v_dim)] = acc;
220 }
221 }
222 }
223}
224
225void deepseek_mla_kv_decompress_bf16_token_range(const float *compressed_kv,
226 const uint16_t *kv_b_proj,
227 float *k_nope,
228 float *value,
229 int tokens,
230 int heads,
231 int kv_lora_rank,
232 int qk_nope_dim,
233 int v_dim,
234 int token_begin,
235 int token_end)
236{
237 if (!compressed_kv || !kv_b_proj || !k_nope || !value ||
238 tokens <= 0 || heads <= 0 || kv_lora_rank <= 0 || qk_nope_dim <= 0 || v_dim <= 0 ||
239 token_begin < 0 || token_begin >= token_end || token_end > tokens) {
240 return;
241 }
242
243 const int out_per_head = qk_nope_dim + v_dim;
244 for (int t = token_begin; t < token_end; ++t) {
245 for (int h = 0; h < heads; ++h) {
246 for (int d = 0; d < qk_nope_dim; ++d) {
247 const int out_col = h * out_per_head + d;
248 float acc = 0.0f;
249 for (int r = 0; r < kv_lora_rank; ++r) {
250 acc += bf16_to_float(kv_b_proj[(size_t)out_col * (size_t)kv_lora_rank + (size_t)r]) *
251 compressed_kv[ds_mla_tok_idx(t, r, kv_lora_rank)];
252 }
253 k_nope[ds_mla_thd_idx(t, h, d, heads, qk_nope_dim)] =
255 }
256 for (int d = 0; d < v_dim; ++d) {
257 const int out_col = h * out_per_head + qk_nope_dim + d;
258 float acc = 0.0f;
259 for (int r = 0; r < kv_lora_rank; ++r) {
260 acc += bf16_to_float(kv_b_proj[(size_t)out_col * (size_t)kv_lora_rank + (size_t)r]) *
261 compressed_kv[ds_mla_tok_idx(t, r, kv_lora_rank)];
262 }
263 value[ds_mla_thd_idx(t, h, d, heads, v_dim)] =
265 }
266 }
267 }
268}
269
270void deepseek_mla_kv_decompress_bf16(const float *compressed_kv,
271 const uint16_t *kv_b_proj,
272 float *k_nope,
273 float *value,
274 int tokens,
275 int heads,
276 int kv_lora_rank,
277 int qk_nope_dim,
278 int v_dim)
279{
280 if (tokens <= 0) return;
282 compressed_kv, kv_b_proj, k_nope, value, tokens, heads,
283 kv_lora_rank, qk_nope_dim, v_dim, 0, tokens);
284}
285
286typedef struct {
287 const float *compressed_kv;
288 const uint16_t *kv_b_proj;
289 float *k_nope;
290 float *value;
291 int tokens;
292 int heads;
293 int kv_lora_rank;
294 int qk_nope_dim;
295 int v_dim;
296} ds_mla_kv_decompress_bf16_args_t;
297
298static void ds_mla_kv_decompress_bf16_rows(int begin, int end, void *opaque)
299{
300 const ds_mla_kv_decompress_bf16_args_t *args =
301 (const ds_mla_kv_decompress_bf16_args_t *)opaque;
303 args->compressed_kv, args->kv_b_proj, args->k_nope, args->value,
304 args->tokens, args->heads, args->kv_lora_rank, args->qk_nope_dim,
305 args->v_dim, begin, end);
306}
307
309 const float *compressed_kv,
310 const uint16_t *kv_b_proj,
311 float *k_nope,
312 float *value,
313 int tokens,
314 int heads,
315 int kv_lora_rank,
316 int qk_nope_dim,
317 int v_dim)
318{
319 ck_threadpool_t *pool = ck_threadpool_global();
320 const char *disabled = getenv("CK_DISABLE_MLA_PARALLEL_PREFILL");
321 if ((disabled && disabled[0] && strcmp(disabled, "0") != 0) ||
322 !pool || ck_threadpool_n_threads(pool) <= 1 || tokens < 2) {
324 compressed_kv, kv_b_proj, k_nope, value, tokens, heads,
325 kv_lora_rank, qk_nope_dim, v_dim);
326 return;
327 }
328 ds_mla_kv_decompress_bf16_args_t args = {
329 .compressed_kv = compressed_kv,
330 .kv_b_proj = kv_b_proj,
331 .k_nope = k_nope,
332 .value = value,
333 .tokens = tokens,
334 .heads = heads,
335 .kv_lora_rank = kv_lora_rank,
336 .qk_nope_dim = qk_nope_dim,
337 .v_dim = v_dim,
338 };
339 int active = ck_threadpool_n_threads(pool);
340 if (active > tokens) active = tokens;
341 int grain = tokens / (active * 4);
342 if (grain < 1) grain = 1;
344 pool, active, 0, tokens, grain, ds_mla_kv_decompress_bf16_rows, &args);
345}
346
347/* Model-agnostic implementation of the DeepSeek-V3 interleaved MLA rotary
348 * layout. Kimi and Instella reuse this exact tensor transform; the ds_ prefix
349 * records its origin, not a model-selection restriction. */
350static void ds_mla_apply_kimi_rope(const float *src,
351 float *dst,
352 const float *cos_row,
353 const float *sin_row,
354 int dim)
355{
356 const int half = dim / 2;
357 for (int i = 0; i < half; ++i) {
358 const float x_first = src[2 * i];
359 const float x_second = src[2 * i + 1];
360 const float c = cos_row[i];
361 const float s = sin_row[i];
362 dst[i] = x_first * c - x_second * s;
363 dst[half + i] = x_second * c + x_first * s;
364 }
365}
366
368 const float *q_pe,
369 const float *k_nope,
370 const float *k_pe,
371 const float *cos,
372 const float *sin,
373 float *query,
374 float *key,
375 int tokens,
376 int heads,
377 int qk_nope_dim,
378 int qk_rope_dim)
379{
380 if (!q_nope || !q_pe || !k_nope || !k_pe || !cos || !sin || !query || !key ||
381 tokens <= 0 || heads <= 0 || qk_nope_dim <= 0 || qk_rope_dim <= 0 || (qk_rope_dim % 2) != 0) {
382 return;
383 }
384
385 const int q_head_dim = qk_nope_dim + qk_rope_dim;
386 for (int t = 0; t < tokens; ++t) {
387 const float *cos_row = cos + (size_t)t * (size_t)(qk_rope_dim / 2);
388 const float *sin_row = sin + (size_t)t * (size_t)(qk_rope_dim / 2);
389 for (int h = 0; h < heads; ++h) {
390 float *q_out = query + ds_mla_thd_idx(t, h, 0, heads, q_head_dim);
391 float *k_out = key + ds_mla_thd_idx(t, h, 0, heads, q_head_dim);
392 const float *qn = q_nope + ds_mla_thd_idx(t, h, 0, heads, qk_nope_dim);
393 const float *qp = q_pe + ds_mla_thd_idx(t, h, 0, heads, qk_rope_dim);
394 const float *kn = k_nope + ds_mla_thd_idx(t, h, 0, heads, qk_nope_dim);
395 const float *kp = k_pe + ds_mla_tok_idx(t, 0, qk_rope_dim);
396 for (int d = 0; d < qk_nope_dim; ++d) {
397 q_out[d] = qn[d];
398 k_out[d] = kn[d];
399 }
400 ds_mla_apply_kimi_rope(qp, q_out + qk_nope_dim, cos_row, sin_row, qk_rope_dim);
401 ds_mla_apply_kimi_rope(kp, k_out + qk_nope_dim, cos_row, sin_row, qk_rope_dim);
402 }
403 }
404}
405
407 const float *k_nope,
408 const float *kv_a_packed,
409 const float *cos,
410 const float *sin,
411 float *query,
412 float *key,
413 int tokens,
414 int heads,
415 int kv_lora_rank,
416 int qk_nope_dim,
417 int qk_rope_dim)
418{
419 if (!q_packed || !k_nope || !kv_a_packed || !cos || !sin || !query || !key ||
420 tokens <= 0 || heads <= 0 || kv_lora_rank <= 0 || qk_nope_dim <= 0 ||
421 qk_rope_dim <= 0 || qk_rope_dim > 256 || (qk_rope_dim % 2) != 0) {
422 return;
423 }
424
425 const int q_head_dim = qk_nope_dim + qk_rope_dim;
426 const int kv_a_dim = kv_lora_rank + qk_rope_dim;
427 const int half = qk_rope_dim / 2;
428 if (qk_rope_dim > 256) {
429 return;
430 }
431 for (int t = 0; t < tokens; ++t) {
432 const float *cos_row = cos + (size_t)t * (size_t)half;
433 const float *sin_row = sin + (size_t)t * (size_t)half;
434 const float *kp = kv_a_packed + (size_t)t * (size_t)kv_a_dim + (size_t)kv_lora_rank;
435 for (int h = 0; h < heads; ++h) {
436 const float *q_in = q_packed + ds_mla_thd_idx(t, h, 0, heads, q_head_dim);
437 const float *kn = k_nope + ds_mla_thd_idx(t, h, 0, heads, qk_nope_dim);
438 float *q_out = query + ds_mla_thd_idx(t, h, 0, heads, q_head_dim);
439 float *k_out = key + ds_mla_thd_idx(t, h, 0, heads, q_head_dim);
440
441 for (int d = 0; d < qk_nope_dim; ++d) {
442 q_out[d] = q_in[d];
443 k_out[d] = kn[d];
444 }
445
446 const float *qp = q_in + qk_nope_dim;
447 float q_pe_tmp[256];
448 if (qk_rope_dim > (int)(sizeof(q_pe_tmp) / sizeof(q_pe_tmp[0]))) {
449 return;
450 }
451 for (int i = 0; i < qk_rope_dim; ++i) q_pe_tmp[i] = qp[i];
452 for (int i = 0; i < half; ++i) {
453 const float q_first = q_pe_tmp[2 * i];
454 const float q_second = q_pe_tmp[2 * i + 1];
455 const float k_first = kp[2 * i];
456 const float k_second = kp[2 * i + 1];
457 const float c = cos_row[i];
458 const float ss = sin_row[i];
459 q_out[qk_nope_dim + i] = q_first * c - q_second * ss;
460 q_out[qk_nope_dim + half + i] = q_second * c + q_first * ss;
461 k_out[qk_nope_dim + i] = k_first * c - k_second * ss;
462 k_out[qk_nope_dim + half + i] = k_second * c + k_first * ss;
463 }
464 }
465 }
466}
467
468static inline float ds_mla_bf16_round(float value)
469{
470 return bf16_to_float(float_to_bf16(value));
471}
472
474 const float *q_packed,
475 const float *k_nope,
476 const float *kv_a_packed,
477 const float *cos,
478 const float *sin,
479 float *query,
480 float *key,
481 int tokens,
482 int heads,
483 int kv_lora_rank,
484 int qk_nope_dim,
485 int qk_rope_dim)
486{
487 if (!q_packed || !k_nope || !kv_a_packed || !cos || !sin || !query || !key ||
488 tokens <= 0 || heads <= 0 || kv_lora_rank <= 0 || qk_nope_dim <= 0 ||
489 qk_rope_dim <= 0 || (qk_rope_dim % 2) != 0) {
490 return;
491 }
492
493 const int q_head_dim = qk_nope_dim + qk_rope_dim;
494 const int kv_a_dim = kv_lora_rank + qk_rope_dim;
495 const int half = qk_rope_dim / 2;
496 for (int t = 0; t < tokens; ++t) {
497 const float *cos_row = cos + (size_t)t * (size_t)half;
498 const float *sin_row = sin + (size_t)t * (size_t)half;
499 const float *kp = kv_a_packed + (size_t)t * (size_t)kv_a_dim + (size_t)kv_lora_rank;
500 for (int h = 0; h < heads; ++h) {
501 const float *q_in = q_packed + ds_mla_thd_idx(t, h, 0, heads, q_head_dim);
502 const float *kn = k_nope + ds_mla_thd_idx(t, h, 0, heads, qk_nope_dim);
503 float *q_out = query + ds_mla_thd_idx(t, h, 0, heads, q_head_dim);
504 float *k_out = key + ds_mla_thd_idx(t, h, 0, heads, q_head_dim);
505
506 for (int d = 0; d < qk_nope_dim; ++d) {
507 q_out[d] = ds_mla_bf16_round(q_in[d]);
508 k_out[d] = ds_mla_bf16_round(kn[d]);
509 }
510
511 float q_pe_tmp[256];
512 for (int i = 0; i < qk_rope_dim; ++i) {
513 q_pe_tmp[i] = ds_mla_bf16_round(q_in[qk_nope_dim + i]);
514 }
515 for (int i = 0; i < half; ++i) {
516 const float q_first = q_pe_tmp[2 * i];
517 const float q_second = q_pe_tmp[2 * i + 1];
518 const float k_first = ds_mla_bf16_round(kp[2 * i]);
519 const float k_second = ds_mla_bf16_round(kp[2 * i + 1]);
520 const float c = ds_mla_bf16_round(cos_row[i]);
521 const float s = ds_mla_bf16_round(sin_row[i]);
522
523 const float q_first_cos = ds_mla_bf16_round(q_first * c);
524 const float q_second_sin = ds_mla_bf16_round(q_second * s);
525 const float q_second_cos = ds_mla_bf16_round(q_second * c);
526 const float q_first_sin = ds_mla_bf16_round(q_first * s);
527 const float k_first_cos = ds_mla_bf16_round(k_first * c);
528 const float k_second_sin = ds_mla_bf16_round(k_second * s);
529 const float k_second_cos = ds_mla_bf16_round(k_second * c);
530 const float k_first_sin = ds_mla_bf16_round(k_first * s);
531
532 q_out[qk_nope_dim + i] = ds_mla_bf16_round(q_first_cos - q_second_sin);
533 q_out[qk_nope_dim + half + i] = ds_mla_bf16_round(q_second_cos + q_first_sin);
534 k_out[qk_nope_dim + i] = ds_mla_bf16_round(k_first_cos - k_second_sin);
535 k_out[qk_nope_dim + half + i] = ds_mla_bf16_round(k_second_cos + k_first_sin);
536 }
537 }
538 }
539}
540
541static inline size_t ds_qkv_idx(int token, int head, int d, int heads, int dim)
542{
543 return ((size_t)token * (size_t)heads + (size_t)head) * (size_t)dim + (size_t)d;
544}
545
546static void ds_softmax(float *x, int n)
547{
548 if (n <= 0) return;
549 float max_v = x[0];
550 for (int i = 1; i < n; ++i) if (x[i] > max_v) max_v = x[i];
551 float sum = 0.0f;
552 for (int i = 0; i < n; ++i) {
553 x[i] = expf(x[i] - max_v);
554 sum += x[i];
555 }
556 if (sum > 0.0f) {
557 const float inv = 1.0f / sum;
558 for (int i = 0; i < n; ++i) x[i] *= inv;
559 }
560}
561
562void deepseek_csa_attention_f32(const float *q,
563 const float *k,
564 const float *v,
565 const int *indices,
566 float *out,
567 float *attn,
568 int query_tokens,
569 int key_tokens,
570 int heads,
571 int dim,
572 int top_k,
573 float scale)
574{
575 if (!q || !k || !v || !indices || !out || query_tokens <= 0 || key_tokens <= 0 ||
576 heads <= 0 || dim <= 0 || top_k <= 0) return;
577
578 for (int tq = 0; tq < query_tokens; ++tq) {
579 for (int h = 0; h < heads; ++h) {
580 float local_scores[top_k];
581 int valid = 0;
582 for (int j = 0; j < top_k; ++j) {
583 const int tk = indices[((size_t)tq * (size_t)heads + (size_t)h) * (size_t)top_k + (size_t)j];
584 if (tk < 0 || tk >= key_tokens) {
585 local_scores[j] = -FLT_MAX;
586 continue;
587 }
588 float dot = 0.0f;
589 for (int d = 0; d < dim; ++d) {
590 dot += q[ds_qkv_idx(tq, h, d, heads, dim)] * k[ds_qkv_idx(tk, h, d, heads, dim)];
591 }
592 local_scores[j] = dot * scale;
593 valid++;
594 }
595
596 float *out_row = out + ds_qkv_idx(tq, h, 0, heads, dim);
597 for (int d = 0; d < dim; ++d) out_row[d] = 0.0f;
598 if (valid == 0) {
599 if (attn) {
600 for (int j = 0; j < top_k; ++j) {
601 attn[((size_t)tq * (size_t)heads + (size_t)h) * (size_t)top_k + (size_t)j] = 0.0f;
602 }
603 }
604 continue;
605 }
606
607 ds_softmax(local_scores, top_k);
608 for (int j = 0; j < top_k; ++j) {
609 const int tk = indices[((size_t)tq * (size_t)heads + (size_t)h) * (size_t)top_k + (size_t)j];
610 const float a = (tk >= 0 && tk < key_tokens) ? local_scores[j] : 0.0f;
611 if (attn) attn[((size_t)tq * (size_t)heads + (size_t)h) * (size_t)top_k + (size_t)j] = a;
612 if (a == 0.0f) continue;
613 for (int d = 0; d < dim; ++d) {
614 out_row[d] += a * v[ds_qkv_idx(tk, h, d, heads, dim)];
615 }
616 }
617 }
618 }
619}
620
622 const float *q,
623 const float *k,
624 const float *v,
625 const int *indices,
626 const float *attn,
627 float *d_q,
628 float *d_k,
629 float *d_v,
630 int query_tokens,
631 int key_tokens,
632 int heads,
633 int dim,
634 int top_k,
635 float scale)
636{
637 if (!d_out || !q || !k || !v || !indices || !attn || !d_q || !d_k || !d_v ||
638 query_tokens <= 0 || key_tokens <= 0 || heads <= 0 || dim <= 0 || top_k <= 0) return;
639
640 const size_t q_count = (size_t)query_tokens * (size_t)heads * (size_t)dim;
641 const size_t kv_count = (size_t)key_tokens * (size_t)heads * (size_t)dim;
642 for (size_t i = 0; i < q_count; ++i) d_q[i] = 0.0f;
643 for (size_t i = 0; i < kv_count; ++i) {
644 d_k[i] = 0.0f;
645 d_v[i] = 0.0f;
646 }
647
648 for (int tq = 0; tq < query_tokens; ++tq) {
649 for (int h = 0; h < heads; ++h) {
650 float d_attn[top_k];
651 float attn_dot = 0.0f;
652 for (int j = 0; j < top_k; ++j) {
653 const int tk = indices[((size_t)tq * (size_t)heads + (size_t)h) * (size_t)top_k + (size_t)j];
654 float da = 0.0f;
655 if (tk >= 0 && tk < key_tokens) {
656 const float a = attn[((size_t)tq * (size_t)heads + (size_t)h) * (size_t)top_k + (size_t)j];
657 for (int d = 0; d < dim; ++d) {
658 const float go = d_out[ds_qkv_idx(tq, h, d, heads, dim)];
659 da += go * v[ds_qkv_idx(tk, h, d, heads, dim)];
660 d_v[ds_qkv_idx(tk, h, d, heads, dim)] += a * go;
661 }
662 attn_dot += a * da;
663 }
664 d_attn[j] = da;
665 }
666
667 for (int j = 0; j < top_k; ++j) {
668 const int tk = indices[((size_t)tq * (size_t)heads + (size_t)h) * (size_t)top_k + (size_t)j];
669 if (tk < 0 || tk >= key_tokens) continue;
670 const float a = attn[((size_t)tq * (size_t)heads + (size_t)h) * (size_t)top_k + (size_t)j];
671 const float d_score = a * (d_attn[j] - attn_dot);
672 for (int d = 0; d < dim; ++d) {
673 const float qv = q[ds_qkv_idx(tq, h, d, heads, dim)];
674 const float kv = k[ds_qkv_idx(tk, h, d, heads, dim)];
675 d_q[ds_qkv_idx(tq, h, d, heads, dim)] += scale * d_score * kv;
676 d_k[ds_qkv_idx(tk, h, d, heads, dim)] += scale * d_score * qv;
677 }
678 }
679 }
680 }
681}
682
684 const float *k,
685 const float *v,
686 const int *indices,
687 float *out,
688 float *attn,
689 int query_tokens,
690 int key_tokens,
691 int heads,
692 int dim,
693 int top_k,
694 float scale,
695 int mode)
696{
697 if (mode != 0) {
698 deepseek_csa_attention_f32(q, k, v, indices, out, attn,
699 query_tokens, key_tokens, heads, dim, top_k, scale);
700 return;
701 }
702
703 int dense_indices[query_tokens * heads * key_tokens];
704 for (int tq = 0; tq < query_tokens; ++tq) {
705 for (int h = 0; h < heads; ++h) {
706 for (int tk = 0; tk < key_tokens; ++tk) {
707 dense_indices[((size_t)tq * (size_t)heads + (size_t)h) * (size_t)key_tokens + (size_t)tk] = tk;
708 }
709 }
710 }
711 deepseek_csa_attention_f32(q, k, v, dense_indices, out, attn,
712 query_tokens, key_tokens, heads, dim, key_tokens, scale);
713}
714
715
716static void ds_mla_attention_f32_query_range(const float *q,
717 const float *k,
718 const float *v,
719 float *output,
720 int num_heads,
721 int num_kv_heads,
722 int num_tokens,
723 int qk_head_dim,
724 int v_head_dim,
725 float scale,
726 float *scores,
727 int query_begin,
728 int query_end,
729 int query_step)
730{
731 for (int query = query_begin; query < query_end; query += query_step) {
732 const int t = query / num_heads;
733 const int h = query - t * num_heads;
734 const int kv_h = (int)((long long)h * (long long)num_kv_heads / (long long)num_heads);
735 const float *q_vec = q + ds_mla_thd_idx(t, h, 0, num_heads, qk_head_dim);
736
737 float max_score = -FLT_MAX;
738 for (int j = 0; j <= t; ++j) {
739 const float *k_vec = k + ds_mla_thd_idx(j, kv_h, 0, num_kv_heads, qk_head_dim);
740 float dot = 0.0f;
741 for (int d = 0; d < qk_head_dim; ++d) {
742 dot += q_vec[d] * k_vec[d];
743 }
744 const float score = dot * scale;
745 scores[j] = score;
746 if (score > max_score) max_score = score;
747 }
748
749 float sum = 0.0f;
750 for (int j = 0; j <= t; ++j) {
751 const float e = expf(scores[j] - max_score);
752 scores[j] = e;
753 sum += e;
754 }
755 const float inv_sum = sum > 0.0f ? (1.0f / sum) : 0.0f;
756 float *out = output + ((size_t)t * (size_t)num_heads + (size_t)h) * (size_t)v_head_dim;
757 for (int d = 0; d < v_head_dim; ++d) out[d] = 0.0f;
758 for (int j = 0; j <= t; ++j) {
759 const float weight = scores[j] * inv_sum;
760 const float *v_vec = v + ds_mla_thd_idx(j, kv_h, 0, num_kv_heads, v_head_dim);
761 for (int d = 0; d < v_head_dim; ++d) {
762 out[d] += weight * v_vec[d];
763 }
764 }
765 }
766}
767
769 const float *k,
770 const float *v,
771 float *output,
772 int num_heads,
773 int num_kv_heads,
774 int num_tokens,
775 int qk_head_dim,
776 int v_head_dim,
777 float scale,
778 float *scores,
779 size_t scores_bytes)
780{
781 if (!q || !k || !v || !output || num_heads <= 0 || num_kv_heads <= 0 ||
782 num_tokens <= 0 || qk_head_dim <= 0 || v_head_dim <= 0 ||
783 num_tokens > INT_MAX / num_heads ||
784 !isfinite(scale) || scale <= 0.0f) {
785 return;
786 }
787 if ((size_t)num_tokens > SIZE_MAX / sizeof(float) || !scores ||
788 scores_bytes < (size_t)num_tokens * sizeof(float)) {
789 return;
790 }
791
793 q, k, v, output, num_heads, num_kv_heads, num_tokens,
794 qk_head_dim, v_head_dim, scale, scores,
795 0, num_tokens * num_heads, 1);
796}
797
798typedef struct {
799 const float *q;
800 const float *k;
801 const float *v;
802 float *output;
803 int num_heads;
804 int num_kv_heads;
805 int num_tokens;
806 int qk_head_dim;
807 int v_head_dim;
808 float scale;
809 float *scores;
810 _Alignas(CK_CACHE_LINE) atomic_int next_token;
811} ds_mla_attention_f32_args_t;
812
813static void ds_mla_attention_f32_work(int ith, int nth, void *opaque)
814{
815 ds_mla_attention_f32_args_t *args =
816 (ds_mla_attention_f32_args_t *)opaque;
817 float *thread_scores = args->scores + (size_t)ith * (size_t)args->num_tokens;
818 (void)nth;
819 /* One token row contains every query head and costs O(t). Dynamic claims
820 * keep asymmetric cores busy without sharing score scratch. Query rows are
821 * independent, and every row retains its original reduction order. */
822 for (;;) {
823 const int token = atomic_fetch_add_explicit(
824 &args->next_token,
825 1,
826 memory_order_relaxed);
827 if (token >= args->num_tokens) break;
829 args->q, args->k, args->v, args->output,
830 args->num_heads, args->num_kv_heads, args->num_tokens,
831 args->qk_head_dim, args->v_head_dim, args->scale,
832 thread_scores,
833 token * args->num_heads,
834 (token + 1) * args->num_heads,
835 1);
836 }
837}
838
840 const float *q,
841 const float *k,
842 const float *v,
843 float *output,
844 int num_heads,
845 int num_kv_heads,
846 int num_tokens,
847 int qk_head_dim,
848 int v_head_dim,
849 float scale,
850 float *scores,
851 size_t scores_bytes)
852{
853 ck_threadpool_t *pool = ck_threadpool_global();
854 const char *disabled = getenv("CK_DISABLE_MLA_PARALLEL_PREFILL");
855 int active = pool ? ck_threadpool_n_threads(pool) : 1;
856 if (active > num_heads) active = num_heads;
857 const size_t score_row_bytes =
858 num_tokens > 0 ? (size_t)num_tokens * sizeof(float) : 0;
859 if ((disabled && disabled[0] && strcmp(disabled, "0") != 0) ||
860 !pool || active <= 1 || num_tokens <= 0 || num_heads <= 0 ||
861 num_tokens > INT_MAX / num_heads ||
862 score_row_bytes == 0 || (size_t)active > SIZE_MAX / score_row_bytes ||
863 scores_bytes < (size_t)active * score_row_bytes) {
865 q, k, v, output, num_heads, num_kv_heads, num_tokens,
866 qk_head_dim, v_head_dim, scale, scores, scores_bytes);
867 return;
868 }
869 ds_mla_attention_f32_args_t args = {
870 .q = q, .k = k, .v = v, .output = output,
871 .num_heads = num_heads, .num_kv_heads = num_kv_heads,
872 .num_tokens = num_tokens, .qk_head_dim = qk_head_dim,
873 .v_head_dim = v_head_dim, .scale = scale, .scores = scores,
874 };
875 atomic_init(&args.next_token, 0);
877}
878
879void deepseek_mla_attention_f32(const float *q,
880 const float *k,
881 const float *v,
882 float *output,
883 int num_heads,
884 int num_kv_heads,
885 int num_tokens,
886 int qk_head_dim,
887 int v_head_dim)
888{
889 if (num_tokens <= 0 || (size_t)num_tokens > SIZE_MAX / sizeof(float)) return;
890 const size_t scores_bytes = (size_t)num_tokens * sizeof(float);
891 float *scores = (float *)malloc(scores_bytes);
892 if (!scores) return;
894 q, k, v, output, num_heads, num_kv_heads, num_tokens,
895 qk_head_dim, v_head_dim, 1.0f / sqrtf((float)qk_head_dim),
896 scores, scores_bytes);
897 free(scores);
898}
899
901 float *v_cache,
902 const float *k,
903 const float *v,
904 int num_tokens,
905 int num_kv_heads,
906 int qk_head_dim,
907 int v_head_dim,
908 int max_seq_len,
909 int cache_stride)
910{
911 if (!k_cache || !v_cache || !k || !v || num_tokens <= 0 ||
912 num_kv_heads <= 0 || qk_head_dim <= 0 || v_head_dim <= 0 ||
913 max_seq_len <= 0 || cache_stride <= 0) {
914 return;
915 }
916 if (qk_head_dim > cache_stride || v_head_dim > cache_stride) {
917 return;
918 }
919 if (num_tokens > max_seq_len) {
920 num_tokens = max_seq_len;
921 }
922
923 for (int t = 0; t < num_tokens; ++t) {
924 for (int h = 0; h < num_kv_heads; ++h) {
925 const float *k_src = k + ((size_t)t * (size_t)num_kv_heads + (size_t)h) * (size_t)qk_head_dim;
926 const float *v_src = v + ((size_t)t * (size_t)num_kv_heads + (size_t)h) * (size_t)v_head_dim;
927 float *k_dst = k_cache + ((size_t)h * (size_t)max_seq_len + (size_t)t) * (size_t)cache_stride;
928 float *v_dst = v_cache + ((size_t)h * (size_t)max_seq_len + (size_t)t) * (size_t)cache_stride;
929 for (int d = 0; d < qk_head_dim; ++d) k_dst[d] = k_src[d];
930 for (int d = qk_head_dim; d < cache_stride; ++d) k_dst[d] = 0.0f;
931 for (int d = 0; d < v_head_dim; ++d) v_dst[d] = v_src[d];
932 for (int d = v_head_dim; d < cache_stride; ++d) v_dst[d] = 0.0f;
933 }
934 }
935}
936
938 float *v_cache,
939 const float *k,
940 const float *v,
941 int pos,
942 int num_kv_heads,
943 int qk_head_dim,
944 int v_head_dim,
945 int max_seq_len,
946 int cache_stride)
947{
948 if (!k_cache || !v_cache || !k || !v || pos < 0 ||
949 num_kv_heads <= 0 || qk_head_dim <= 0 || v_head_dim <= 0 ||
950 max_seq_len <= 0 || cache_stride <= 0) {
951 return;
952 }
953 if (pos >= max_seq_len || qk_head_dim > cache_stride || v_head_dim > cache_stride) {
954 return;
955 }
956
957 for (int h = 0; h < num_kv_heads; ++h) {
958 const float *k_src = k + ((size_t)h * (size_t)qk_head_dim);
959 const float *v_src = v + ((size_t)h * (size_t)v_head_dim);
960 float *k_dst = k_cache + ((size_t)h * (size_t)max_seq_len + (size_t)pos) * (size_t)cache_stride;
961 float *v_dst = v_cache + ((size_t)h * (size_t)max_seq_len + (size_t)pos) * (size_t)cache_stride;
962
963 for (int d = 0; d < qk_head_dim; ++d) {
964 k_dst[d] = k_src[d];
965 }
966 for (int d = qk_head_dim; d < cache_stride; ++d) {
967 k_dst[d] = 0.0f;
968 }
969 for (int d = 0; d < v_head_dim; ++d) {
970 v_dst[d] = v_src[d];
971 }
972 for (int d = v_head_dim; d < cache_stride; ++d) {
973 v_dst[d] = 0.0f;
974 }
975 }
976}
977
979 const float *k_cache,
980 const float *v_cache,
981 float *output,
982 int num_heads,
983 int num_kv_heads,
984 int cache_len,
985 int qk_head_dim,
986 int v_head_dim,
987 int max_seq_len,
988 int cache_stride,
989 float scale,
990 float *scores,
991 size_t scores_bytes)
992{
993 if (!q || !k_cache || !v_cache || !output || num_heads <= 0 ||
994 num_kv_heads <= 0 || cache_len <= 0 || qk_head_dim <= 0 ||
995 v_head_dim <= 0 || max_seq_len <= 0 || cache_stride <= 0 ||
996 !isfinite(scale) || scale <= 0.0f) {
997 return;
998 }
999 if (qk_head_dim > cache_stride || v_head_dim > cache_stride) {
1000 return;
1001 }
1002 if ((size_t)cache_len > SIZE_MAX / sizeof(float) || !scores ||
1003 scores_bytes < (size_t)cache_len * sizeof(float)) {
1004 return;
1005 }
1006
1007 for (int h = 0; h < num_heads; ++h) {
1008 const int kv_h = (int)((long long)h * (long long)num_kv_heads / (long long)num_heads);
1009 const float *q_vec = q + (size_t)h * (size_t)qk_head_dim;
1010
1011 float max_score = -FLT_MAX;
1012 for (int j = 0; j < cache_len; ++j) {
1013 const float *k_vec = k_cache + ((size_t)kv_h * (size_t)max_seq_len + (size_t)j) * (size_t)cache_stride;
1014 float dot = 0.0f;
1015 for (int d = 0; d < qk_head_dim; ++d) {
1016 dot += q_vec[d] * k_vec[d];
1017 }
1018 const float score = dot * scale;
1019 scores[j] = score;
1020 if (score > max_score) max_score = score;
1021 }
1022
1023 float sum = 0.0f;
1024 for (int j = 0; j < cache_len; ++j) {
1025 const float e = expf(scores[j] - max_score);
1026 scores[j] = e;
1027 sum += e;
1028 }
1029
1030 const float inv_sum = sum > 0.0f ? (1.0f / sum) : 0.0f;
1031 float *out = output + (size_t)h * (size_t)v_head_dim;
1032 for (int d = 0; d < v_head_dim; ++d) out[d] = 0.0f;
1033 for (int j = 0; j < cache_len; ++j) {
1034 const float w = scores[j] * inv_sum;
1035 const float *v_vec = v_cache + ((size_t)kv_h * (size_t)max_seq_len + (size_t)j) * (size_t)cache_stride;
1036 for (int d = 0; d < v_head_dim; ++d) {
1037 out[d] += w * v_vec[d];
1038 }
1039 }
1040 }
1041
1042}
1043
1045 const float *k_cache,
1046 const float *v_cache,
1047 float *output,
1048 int num_heads,
1049 int num_kv_heads,
1050 int cache_len,
1051 int qk_head_dim,
1052 int v_head_dim,
1053 int max_seq_len,
1054 int cache_stride)
1055{
1056 if (cache_len <= 0 || (size_t)cache_len > SIZE_MAX / sizeof(float)) return;
1057 const size_t scores_bytes = (size_t)cache_len * sizeof(float);
1058 float *scores = (float *)malloc(scores_bytes);
1059 if (!scores) return;
1061 q, k_cache, v_cache, output, num_heads, num_kv_heads, cache_len,
1062 qk_head_dim, v_head_dim, max_seq_len, cache_stride,
1063 1.0f / sqrtf((float)qk_head_dim), scores, scores_bytes);
1064 free(scores);
1065}
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)
#define CK_CACHE_LINE
void ck_threadpool_dispatch_n(ck_threadpool_t *pool, int active_threads, ck_work_fn_t fn, void *args)
ck_threadpool_t * ck_threadpool_global(void)
int ck_threadpool_n_threads(const ck_threadpool_t *pool)
static size_t ds_mix_idx(int t, int out_s, int in_s, int n_streams)
void deepseek_mla_partial_rope_concat_f32(const float *q_nope, const float *q_pe, const float *k_nope, const float *k_pe, const float *cos, const float *sin, float *query, float *key, int tokens, int heads, int qk_nope_dim, int qk_rope_dim)
static void ds_mla_attention_f32_work(int ith, int nth, void *opaque)
void deepseek_mla_attention_f32_workspace(const float *q, const float *k, const float *v, float *output, int num_heads, int num_kv_heads, int num_tokens, int qk_head_dim, int v_head_dim, float scale, float *scores, size_t scores_bytes)
static size_t ds_mhc_idx(int t, int s, int d, int n_streams, int dim)
void deepseek_csa_attention_f32(const float *q, const float *k, const float *v, const int *indices, float *out, float *attn, int query_tokens, int key_tokens, int heads, int dim, int top_k, float scale)
void topk_softmax_backward_f32(const int *indices, const float *weights, const float *d_weights, float *d_scores, int num_tokens, int n_experts_or_keys, int k)
Backward for hard top-k followed by softmax over selected values.
void deepseek_mla_attention_decode_f32_workspace(const float *q, const float *k_cache, const float *v_cache, float *output, int num_heads, int num_kv_heads, int cache_len, int qk_head_dim, int v_head_dim, int max_seq_len, int cache_stride, float scale, float *scores, size_t scores_bytes)
void deepseek_mhc_mix_f32(const float *streams, const float *mix, float *out, int tokens, int n_streams, int dim)
void deepseek_mla_kv_decompress_bf16(const float *compressed_kv, const uint16_t *kv_b_proj, float *k_nope, float *value, int tokens, int heads, int kv_lora_rank, int qk_nope_dim, int v_dim)
void deepseek_mla_kv_decompress_f32(const float *compressed_kv, const float *kv_b_proj, float *k_nope, float *value, int tokens, int heads, int kv_lora_rank, int qk_nope_dim, int v_dim)
static float ds_mla_bf16_round(float value)
void deepseek_mla_kv_decompress_bf16_token_range(const float *compressed_kv, const uint16_t *kv_b_proj, float *k_nope, float *value, int tokens, int heads, int kv_lora_rank, int qk_nope_dim, int v_dim, int token_begin, int token_end)
static size_t ds_qkv_idx(int token, int head, int d, int heads, int dim)
static size_t ds_mla_thd_idx(int t, int h, int d, int heads, int dim)
static void ds_mla_attention_f32_query_range(const float *q, const float *k, const float *v, float *output, int num_heads, int num_kv_heads, int num_tokens, int qk_head_dim, int v_head_dim, float scale, float *scores, int query_begin, int query_end, int query_step)
void deepseek_mla_attention_f32_parallel_dispatch(const float *q, const float *k, const float *v, float *output, int num_heads, int num_kv_heads, int num_tokens, int qk_head_dim, int v_head_dim, float scale, float *scores, size_t scores_bytes)
static void ds_mla_kv_decompress_bf16_rows(int begin, int end, void *opaque)
void deepseek_csa_attention_backward_f32(const float *d_out, const float *q, const float *k, const float *v, const int *indices, const float *attn, float *d_q, float *d_k, float *d_v, int query_tokens, int key_tokens, int heads, int dim, int top_k, float scale)
void deepseek_mla_kv_cache_batch_store_f32(float *k_cache, float *v_cache, const float *k, const float *v, int num_tokens, int num_kv_heads, int qk_head_dim, int v_head_dim, int max_seq_len, int cache_stride)
static void ds_softmax(float *x, int n)
static size_t ds_mla_tok_idx(int t, int d, int dim)
void deepseek_mla_attention_f32(const float *q, const float *k, const float *v, float *output, int num_heads, int num_kv_heads, int num_tokens, int qk_head_dim, int v_head_dim)
void deepseek_mhc_mix_backward_f32(const float *d_out, const float *streams, const float *mix, float *d_streams, float *d_mix, int tokens, int n_streams, int dim)
void deepseek_dsa_topk_softmax_f32(const float *scores, int *indices, float *weights, int tokens, int heads, int key_count, int top_k)
void deepseek_dsa_topk_softmax_backward_f32(const int *indices, const float *weights, const float *d_weights, float *d_scores, int tokens, int heads, int key_count, int top_k)
void deepseek_mla_kv_decompress_bf16_parallel_dispatch(const float *compressed_kv, const uint16_t *kv_b_proj, float *k_nope, float *value, int tokens, int heads, int kv_lora_rank, int qk_nope_dim, int v_dim)
void deepseek_mla_partial_rope_concat_packed_f32(const float *q_packed, const float *k_nope, const float *kv_a_packed, const float *cos, const float *sin, float *query, float *key, int tokens, int heads, int kv_lora_rank, int qk_nope_dim, int qk_rope_dim)
void deepseek_mla_attention_decode_f32(const float *q, const float *k_cache, const float *v_cache, float *output, int num_heads, int num_kv_heads, int cache_len, int qk_head_dim, int v_head_dim, int max_seq_len, int cache_stride)
void deepseek_hybrid_attention_f32(const float *q, const float *k, const float *v, const int *indices, float *out, float *attn, int query_tokens, int key_tokens, int heads, int dim, int top_k, float scale, int mode)
void deepseek_mla_kv_cache_store_f32(float *k_cache, float *v_cache, const float *k, const float *v, int pos, int num_kv_heads, int qk_head_dim, int v_head_dim, int max_seq_len, int cache_stride)
void deepseek_mla_partial_rope_concat_packed_bf16_storage(const float *q_packed, const float *k_nope, const float *kv_a_packed, const float *cos, const float *sin, float *query, float *key, int tokens, int heads, int kv_lora_rank, int qk_nope_dim, int qk_rope_dim)
static void ds_mla_apply_kimi_rope(const float *src, float *dst, const float *cos_row, const float *sin_row, int dim)
const char * token
Definition tokenizer.h:307
int32_t float * score
Definition tokenizer.h:328
uint32_t end
Definition utf8.c:215