← Back to C-Kernel-Engine Docs Doxygen Source Documentation
 
Loading...
Searching...
No Matches
attention_full_token_major.c
Go to the documentation of this file.
2
3#include <float.h>
4#include <math.h>
5#include <stdint.h>
6
7static int checked_geometry(size_t tokens, size_t heads, size_t head_dim,
8 size_t *width, size_t *elements)
9{
10 if (!tokens || !heads || !head_dim || heads > SIZE_MAX / head_dim)
11 return 0;
12 *width = heads * head_dim;
13 if (tokens > SIZE_MAX / *width)
14 return 0;
15 *elements = tokens * *width;
16 return 1;
17}
18
19static double score(const float *query, const float *key, size_t head_dim,
20 double scale)
21{
22 double sum = 0.0;
23 for (size_t d = 0; d < head_dim; ++d) {
24 volatile double product = (double)query[d] * (double)key[d];
25 sum += product;
26 }
27 return sum * scale;
28}
29
31 const float *query, size_t query_elements,
32 const float *key, size_t key_elements,
33 const float *value, size_t value_elements,
34 float *output, size_t output_elements,
35 float *scratch, size_t scratch_bytes,
36 size_t tokens, size_t heads, size_t head_dim)
37{
38 size_t width, elements;
39 if (!query || !key || !value || !output || !scratch ||
40 !checked_geometry(tokens, heads, head_dim, &width, &elements) ||
41 query_elements < elements || key_elements < elements ||
42 value_elements < elements || output_elements < elements ||
43 tokens > scratch_bytes / sizeof(float))
44 return -1;
45
46 /* A rejected input never publishes a partially updated output. */
47 for (size_t i = 0; i < elements; ++i)
48 if (!isfinite(query[i]) || !isfinite(key[i]) || !isfinite(value[i]))
49 return -1;
50
51 const double scale = 1.0 / sqrt((double)head_dim);
52 for (size_t t = 0; t < tokens; ++t)
53 for (size_t h = 0; h < heads; ++h)
54 for (size_t k = 0; k < tokens; ++k) {
55 const double s = score(query + t * width + h * head_dim,
56 key + k * width + h * head_dim,
57 head_dim, scale);
58 if (!isfinite(s) || fabs(s) > FLT_MAX)
59 return -1;
60 }
61
62 for (size_t t = 0; t < tokens; ++t) {
63 for (size_t h = 0; h < heads; ++h) {
64 const float *q = query + t * width + h * head_dim;
65 double maximum = -INFINITY;
66 for (size_t k = 0; k < tokens; ++k) {
67 const float *kv = key + k * width + h * head_dim;
68 const double s = score(q, kv, head_dim, scale);
69 scratch[k] = (float)s;
70 /* Softmax consumes rounded FP32 scores. Its maximum must be
71 * drawn from those same values: using the unrounded dot can
72 * make even a one-token softmax overflow or underflow. At least
73 * one exponent is then exactly exp(0), and all are in [0, 1]. */
74 if ((double)scratch[k] > maximum) maximum = scratch[k];
75 }
76 double denominator = 0.0;
77 for (size_t k = 0; k < tokens; ++k) {
78 scratch[k] = (float)exp((double)scratch[k] - maximum);
79 denominator += scratch[k];
80 }
81 for (size_t d = 0; d < head_dim; ++d) {
82 double weighted = 0.0;
83 for (size_t k = 0; k < tokens; ++k) {
84 volatile double product = (double)scratch[k] *
85 (double)value[k * width + h * head_dim + d];
86 weighted += product;
87 }
88 output[t * width + h * head_dim + d] = (float)(weighted / denominator);
89 }
90 }
91 }
92 return 0;
93}
static int checked_geometry(size_t tokens, size_t heads, size_t head_dim, size_t *width, size_t *elements)
int attention_full_token_major_f32_checked(const float *query, size_t query_elements, const float *key, size_t key_elements, const float *value, size_t value_elements, float *output, size_t output_elements, float *scratch, size_t scratch_bytes, size_t tokens, size_t heads, size_t head_dim)
static int product(size_t a, size_t b, size_t *out)
int32_t float * score
Definition tokenizer.h:328