← Back to C-Kernel-Engine Docs Doxygen Source Documentation
 
Loading...
Searching...
No Matches
add_kernels_bf16.c
Go to the documentation of this file.
1/**
2 * @file add_kernels_bf16.c
3 * @brief Element-wise addition kernels for BF16 tensors
4 *
5 * CK-ENGINE KERNEL RULES:
6 * =======================
7 * 1. NO malloc/free - memory via bump allocator, pointers passed in
8 * 2. NO OpenMP - parallelization at orchestrator/codegen layer
9 * 3. API must define: inputs, outputs, workspace, and memory layouts
10 * 4. Pure computation - deterministic, no side effects
11 *
12 * After changes: make test && make llamacpp-parity-full
13 *
14 * Used for residual connections in transformer models:
15 * residual = x + sublayer_output
16 *
17 * Supports:
18 * - Forward: y = a + b
19 * - Forward with scale: y = a + alpha * b
20 * - Backward: d_a = d_y, d_b = d_y (gradient flows through unchanged)
21 * - In-place: a += b
22 */
23
24#include "bf16_utils.h"
25#include "ckernel_engine.h"
26
27#include <stdint.h>
28#include <stddef.h>
29
30#if defined(__AVX512F__) || defined(__AVX2__) || defined(__AVX__)
31#include <immintrin.h>
32#endif
33
34/* =============================================================================
35 * Forward: y = a + b
36 * ============================================================================= */
37
38void add_forward_bf16(const uint16_t *a,
39 const uint16_t *b,
40 uint16_t *y,
41 size_t n)
42{
43 if (!a || !b || !y || n == 0) {
44 return;
45 }
46
47 size_t i = 0;
48
49#if defined(__AVX512F__)
50 /* AVX-512: Process 16 bf16 elements at a time */
51 for (; i + 16 <= n; i += 16) {
52 __m512 av = bf16_loadu_cvt_fp32(&a[i]);
53 __m512 bv = bf16_loadu_cvt_fp32(&b[i]);
54 __m512 yv = _mm512_add_ps(av, bv);
55 fp32_cvt_storeu_bf16(&y[i], yv);
56 }
57#endif
58
59 /* Scalar fallback */
60 for (; i < n; ++i) {
61 float af = bf16_to_float(a[i]);
62 float bf = bf16_to_float(b[i]);
63 y[i] = float_to_bf16(af + bf);
64 }
65}
66
68 const float *b,
69 float *out,
70 int tokens,
71 int aligned_embed_dim)
72{
73 const size_t count = (size_t)tokens * (size_t)aligned_embed_dim;
74 for (size_t i = 0; i < count; ++i) {
75 const float av = bf16_to_float(float_to_bf16(a[i]));
76 const float bv = bf16_to_float(float_to_bf16(b[i]));
77 out[i] = bf16_to_float(float_to_bf16(av + bv));
78 }
79}
80
81/* =============================================================================
82 * Forward with scale: y = a + alpha * b
83 * Useful for gradient accumulation or weighted residuals
84 * ============================================================================= */
85
86void add_scaled_forward_bf16(const uint16_t *a,
87 const uint16_t *b,
88 uint16_t *y,
89 float alpha,
90 size_t n)
91{
92 if (!a || !b || !y || n == 0) {
93 return;
94 }
95
96 size_t i = 0;
97
98#if defined(__AVX512F__)
99 __m512 alpha_v = _mm512_set1_ps(alpha);
100 for (; i + 16 <= n; i += 16) {
101 __m512 av = bf16_loadu_cvt_fp32(&a[i]);
102 __m512 bv = bf16_loadu_cvt_fp32(&b[i]);
103 __m512 yv = _mm512_fmadd_ps(bv, alpha_v, av); /* a + alpha * b */
104 fp32_cvt_storeu_bf16(&y[i], yv);
105 }
106#endif
107
108 for (; i < n; ++i) {
109 float af = bf16_to_float(a[i]);
110 float bf = bf16_to_float(b[i]);
111 y[i] = float_to_bf16(af + alpha * bf);
112 }
113}
114
115/* =============================================================================
116 * In-place: a += b
117 * ============================================================================= */
118
119void add_inplace_bf16(uint16_t *a,
120 const uint16_t *b,
121 size_t n)
122{
123 if (!a || !b || n == 0) {
124 return;
125 }
126
127 size_t i = 0;
128
129#if defined(__AVX512F__)
130 for (; i + 16 <= n; i += 16) {
131 __m512 av = bf16_loadu_cvt_fp32(&a[i]);
132 __m512 bv = bf16_loadu_cvt_fp32(&b[i]);
133 __m512 yv = _mm512_add_ps(av, bv);
134 fp32_cvt_storeu_bf16(&a[i], yv);
135 }
136#endif
137
138 for (; i < n; ++i) {
139 float af = bf16_to_float(a[i]);
140 float bf = bf16_to_float(b[i]);
141 a[i] = float_to_bf16(af + bf);
142 }
143}
144
145/* =============================================================================
146 * In-place scaled: a += alpha * b
147 * ============================================================================= */
148
149void add_scaled_inplace_bf16(uint16_t *a,
150 const uint16_t *b,
151 float alpha,
152 size_t n)
153{
154 if (!a || !b || n == 0) {
155 return;
156 }
157
158 size_t i = 0;
159
160#if defined(__AVX512F__)
161 __m512 alpha_v = _mm512_set1_ps(alpha);
162 for (; i + 16 <= n; i += 16) {
163 __m512 av = bf16_loadu_cvt_fp32(&a[i]);
164 __m512 bv = bf16_loadu_cvt_fp32(&b[i]);
165 __m512 yv = _mm512_fmadd_ps(bv, alpha_v, av);
166 fp32_cvt_storeu_bf16(&a[i], yv);
167 }
168#endif
169
170 for (; i < n; ++i) {
171 float af = bf16_to_float(a[i]);
172 float bf = bf16_to_float(b[i]);
173 a[i] = float_to_bf16(af + alpha * bf);
174 }
175}
176
177/* =============================================================================
178 * Backward: d_a = d_y, d_b = d_y
179 *
180 * For y = a + b, gradients pass through unchanged:
181 * dy/da = 1, dy/db = 1
182 *
183 * This is a simple copy operation, but we provide it for API consistency.
184 * If d_a == d_y or d_b == d_y (in-place), no copy needed.
185 * ============================================================================= */
186
187void add_backward_bf16(const uint16_t *d_y,
188 uint16_t *d_a,
189 uint16_t *d_b,
190 size_t n)
191{
192 if (!d_y || n == 0) {
193 return;
194 }
195
196 size_t i = 0;
197
198 /* Copy to d_a if not in-place */
199 if (d_a && d_a != d_y) {
200#if defined(__AVX512F__)
201 for (; i + 32 <= n; i += 32) {
202 __m512i values = _mm512_loadu_si512((const __m512i*)&d_y[i]);
203 _mm512_storeu_si512((__m512i*)&d_a[i], values);
204 }
205#endif
206 for (; i < n; ++i) {
207 d_a[i] = d_y[i];
208 }
209 }
210
211 /* Copy to d_b if not in-place */
212 i = 0;
213 if (d_b && d_b != d_y) {
214#if defined(__AVX512F__)
215 for (; i + 32 <= n; i += 32) {
216 __m512i values = _mm512_loadu_si512((const __m512i*)&d_y[i]);
217 _mm512_storeu_si512((__m512i*)&d_b[i], values);
218 }
219#endif
220 for (; i < n; ++i) {
221 d_b[i] = d_y[i];
222 }
223 }
224}
225
226/* =============================================================================
227 * 2D tensor version: add_forward_2d_bf16
228 * For [T, D] shaped tensors (common in transformers)
229 * ============================================================================= */
230
231void add_forward_2d_bf16(const uint16_t *a,
232 const uint16_t *b,
233 uint16_t *y,
234 int tokens,
235 int dim,
236 int aligned_dim)
237{
238 if (!a || !b || !y || tokens <= 0 || dim <= 0) {
239 return;
240 }
241
242 for (int t = 0; t < tokens; ++t) {
243 const uint16_t *a_row = a + (size_t)t * aligned_dim;
244 const uint16_t *b_row = b + (size_t)t * aligned_dim;
245 uint16_t *y_row = y + (size_t)t * aligned_dim;
246
247 int d = 0;
248
249#if defined(__AVX512F__)
250 for (; d + 16 <= dim; d += 16) {
251 __m512 av = bf16_loadu_cvt_fp32(&a_row[d]);
252 __m512 bv = bf16_loadu_cvt_fp32(&b_row[d]);
253 __m512 yv = _mm512_add_ps(av, bv);
254 fp32_cvt_storeu_bf16(&y_row[d], yv);
255 }
256#endif
257
258 for (; d < dim; ++d) {
259 float af = bf16_to_float(a_row[d]);
260 float bf = bf16_to_float(b_row[d]);
261 y_row[d] = float_to_bf16(af + bf);
262 }
263 }
264}
265
266/* =============================================================================
267 * FP32 versions (for reference/testing)
268 * ============================================================================= */
269
270/**
271 * Element-wise add: y = a + b
272 * @test test_add.py::TestAddForward::test_add_forward_f32
273 * @test test_add.py::TestAddForward::test_add_inplace_f32
274 * @test test_multi_layer_parity.py::TestMultiLayerParity::test_residual_add
275 *
276 * Element-wise addition of two vectors.
277 *
278 * After changes: make test
279 */
280void add_forward_f32(const float *a,
281 const float *b,
282 float *y,
283 size_t n)
284{
285 if (!a || !b || !y || n == 0) {
286 return;
287 }
288
289 size_t i = 0;
290
291#if defined(__AVX512F__)
292 for (; i + 16 <= n; i += 16) {
293 __m512 av = _mm512_loadu_ps(&a[i]);
294 __m512 bv = _mm512_loadu_ps(&b[i]);
295 __m512 yv = _mm512_add_ps(av, bv);
296 _mm512_storeu_ps(&y[i], yv);
297 }
298#endif
299
300#if defined(__AVX2__)
301 for (; i + 8 <= n; i += 8) {
302 __m256 av = _mm256_loadu_ps(&a[i]);
303 __m256 bv = _mm256_loadu_ps(&b[i]);
304 __m256 yv = _mm256_add_ps(av, bv);
305 _mm256_storeu_ps(&y[i], yv);
306 }
307#endif
308
309 for (; i < n; ++i) {
310 y[i] = a[i] + b[i];
311 }
312}
313
314void add_inplace_f32(float *a,
315 const float *b,
316 size_t n)
317{
318 if (!a || !b || n == 0) {
319 return;
320 }
321
322 size_t i = 0;
323
324#if defined(__AVX512F__)
325 for (; i + 16 <= n; i += 16) {
326 __m512 av = _mm512_loadu_ps(&a[i]);
327 __m512 bv = _mm512_loadu_ps(&b[i]);
328 __m512 yv = _mm512_add_ps(av, bv);
329 _mm512_storeu_ps(&a[i], yv);
330 }
331#endif
332
333#if defined(__AVX2__)
334 for (; i + 8 <= n; i += 8) {
335 __m256 av = _mm256_loadu_ps(&a[i]);
336 __m256 bv = _mm256_loadu_ps(&b[i]);
337 __m256 yv = _mm256_add_ps(av, bv);
338 _mm256_storeu_ps(&a[i], yv);
339 }
340#endif
341
342 for (; i < n; ++i) {
343 a[i] = a[i] + b[i];
344 }
345}
void ck_residual_add_token_major_bf16_storage(const float *a, const float *b, float *out, int tokens, int aligned_embed_dim)
void add_scaled_forward_bf16(const uint16_t *a, const uint16_t *b, uint16_t *y, float alpha, size_t n)
void add_inplace_bf16(uint16_t *a, const uint16_t *b, size_t n)
void add_forward_f32(const float *a, const float *b, float *y, size_t n)
void add_inplace_f32(float *a, const float *b, size_t n)
void add_forward_bf16(const uint16_t *a, const uint16_t *b, uint16_t *y, size_t n)
void add_forward_2d_bf16(const uint16_t *a, const uint16_t *b, uint16_t *y, int tokens, int dim, int aligned_dim)
void add_backward_bf16(const uint16_t *d_y, uint16_t *d_a, uint16_t *d_b, size_t n)
void add_scaled_inplace_bf16(uint16_t *a, const uint16_t *b, float alpha, size_t n)
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
const uint8_t size_t count
Definition true_bpe.h:209