← 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 v0 = _mm512_loadu_si512((const __m512i*)&d_y[i]);
203 __m512i v1 = _mm512_loadu_si512((const __m512i*)&d_y[i + 32]);
204 _mm512_storeu_si512((__m512i*)&d_a[i], v0);
205 _mm512_storeu_si512((__m512i*)&d_a[i + 32], v1);
206 }
207#endif
208 for (; i < n; ++i) {
209 d_a[i] = d_y[i];
210 }
211 }
212
213 /* Copy to d_b if not in-place */
214 i = 0;
215 if (d_b && d_b != d_y) {
216#if defined(__AVX512F__)
217 for (; i + 32 <= n; i += 32) {
218 __m512i v0 = _mm512_loadu_si512((const __m512i*)&d_y[i]);
219 __m512i v1 = _mm512_loadu_si512((const __m512i*)&d_y[i + 32]);
220 _mm512_storeu_si512((__m512i*)&d_b[i], v0);
221 _mm512_storeu_si512((__m512i*)&d_b[i + 32], v1);
222 }
223#endif
224 for (; i < n; ++i) {
225 d_b[i] = d_y[i];
226 }
227 }
228}
229
230/* =============================================================================
231 * 2D tensor version: add_forward_2d_bf16
232 * For [T, D] shaped tensors (common in transformers)
233 * ============================================================================= */
234
235void add_forward_2d_bf16(const uint16_t *a,
236 const uint16_t *b,
237 uint16_t *y,
238 int tokens,
239 int dim,
240 int aligned_dim)
241{
242 if (!a || !b || !y || tokens <= 0 || dim <= 0) {
243 return;
244 }
245
246 for (int t = 0; t < tokens; ++t) {
247 const uint16_t *a_row = a + (size_t)t * aligned_dim;
248 const uint16_t *b_row = b + (size_t)t * aligned_dim;
249 uint16_t *y_row = y + (size_t)t * aligned_dim;
250
251 int d = 0;
252
253#if defined(__AVX512F__)
254 for (; d + 16 <= dim; d += 16) {
255 __m512 av = bf16_loadu_cvt_fp32(&a_row[d]);
256 __m512 bv = bf16_loadu_cvt_fp32(&b_row[d]);
257 __m512 yv = _mm512_add_ps(av, bv);
258 fp32_cvt_storeu_bf16(&y_row[d], yv);
259 }
260#endif
261
262 for (; d < dim; ++d) {
263 float af = bf16_to_float(a_row[d]);
264 float bf = bf16_to_float(b_row[d]);
265 y_row[d] = float_to_bf16(af + bf);
266 }
267 }
268}
269
270/* =============================================================================
271 * FP32 versions (for reference/testing)
272 * ============================================================================= */
273
274/**
275 * Element-wise add: y = a + b
276 * @test test_add.py::TestAddForward::test_add_forward_f32
277 * @test test_add.py::TestAddForward::test_add_inplace_f32
278 * @test test_multi_layer_parity.py::TestMultiLayerParity::test_residual_add
279 *
280 * Element-wise addition of two vectors.
281 *
282 * After changes: make test
283 */
284void add_forward_f32(const float *a,
285 const float *b,
286 float *y,
287 size_t n)
288{
289 if (!a || !b || !y || n == 0) {
290 return;
291 }
292
293 size_t i = 0;
294
295#if defined(__AVX512F__)
296 for (; i + 16 <= n; i += 16) {
297 __m512 av = _mm512_loadu_ps(&a[i]);
298 __m512 bv = _mm512_loadu_ps(&b[i]);
299 __m512 yv = _mm512_add_ps(av, bv);
300 _mm512_storeu_ps(&y[i], yv);
301 }
302#endif
303
304#if defined(__AVX2__)
305 for (; i + 8 <= n; i += 8) {
306 __m256 av = _mm256_loadu_ps(&a[i]);
307 __m256 bv = _mm256_loadu_ps(&b[i]);
308 __m256 yv = _mm256_add_ps(av, bv);
309 _mm256_storeu_ps(&y[i], yv);
310 }
311#endif
312
313 for (; i < n; ++i) {
314 y[i] = a[i] + b[i];
315 }
316}
317
318void add_inplace_f32(float *a,
319 const float *b,
320 size_t n)
321{
322 if (!a || !b || n == 0) {
323 return;
324 }
325
326 size_t i = 0;
327
328#if defined(__AVX512F__)
329 for (; i + 16 <= n; i += 16) {
330 __m512 av = _mm512_loadu_ps(&a[i]);
331 __m512 bv = _mm512_loadu_ps(&b[i]);
332 __m512 yv = _mm512_add_ps(av, bv);
333 _mm512_storeu_ps(&a[i], yv);
334 }
335#endif
336
337#if defined(__AVX2__)
338 for (; i + 8 <= n; i += 8) {
339 __m256 av = _mm256_loadu_ps(&a[i]);
340 __m256 bv = _mm256_loadu_ps(&b[i]);
341 __m256 yv = _mm256_add_ps(av, bv);
342 _mm256_storeu_ps(&a[i], yv);
343 }
344#endif
345
346 for (; i < n; ++i) {
347 a[i] = a[i] + b[i];
348 }
349}
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