← Back to C-Kernel-Engine Docs Doxygen Source Documentation
 
Loading...
Searching...
No Matches
mlp_kernels_bf16.c File Reference

Optimized BF16 MLP Kernels. More...

#include <stddef.h>
#include <stdint.h>
#include <math.h>
#include "bf16_utils.h"
#include "ckernel_engine.h"

Go to the source code of this file.

Functions

static float gelu_derivative_scalar (float x)
 
static float gelu_scalar (float x)
 
void gemm_bf16_fp32out (const uint16_t *A, const uint16_t *B, const float *bias, float *C, int M, int N, int K)
 
void mlp_token_parallel_bf16 (const uint16_t *input, const uint16_t *W_fc1, const uint16_t *b_fc1, const uint16_t *W_fc2, const uint16_t *b_fc2, float *fc1_output, float *output, int T, int aligned_dim, int num_threads, float *scratch_bias1_f, float *scratch_bias2_f, uint16_t *scratch_fc1_bf16)
 
void mlp_token_parallel_bf16_backward_mixed (const uint16_t *input, const uint16_t *W_fc1, const uint16_t *b_fc1, const uint16_t *W_fc2, const uint16_t *d_output, float *d_input, float *d_W_fc1, float *d_b_fc1, float *d_W_fc2, float *d_b_fc2, int T, int aligned_dim, int num_threads, float *scratch_fc1_pre, uint16_t *scratch_fc1_act_bf16, float *scratch_d_fc1)
 
void mlp_token_parallel_bf16_fp32act (const uint16_t *input, const uint16_t *W_fc1, const uint16_t *b_fc1, const uint16_t *W_fc2, const uint16_t *b_fc2, float *fc1_output, float *output, int T, int aligned_dim, int num_threads, float *scratch_input_f, float *scratch_bias1_f, float *scratch_bias2_f, uint16_t *scratch_fc1_bf16)
 

Detailed Description

Optimized BF16 MLP Kernels.

CK-ENGINE KERNEL RULES:

  1. NO malloc/free - memory via bump allocator, pointers passed in
  2. NO OpenMP - parallelization at orchestrator/codegen layer
  3. API must define: inputs, outputs, workspace, and memory layouts
  4. Pure computation - deterministic, no side effects

After changes: make test && make llamacpp-parity-full

Uses direct BF16 GEMM instead of converting to FP32. Layout: input[T,D] -> fc1[T,4D] -> GELU -> fc2[T,D]

All functions use caller-provided scratch buffers (no internal malloc).

Definition in file mlp_kernels_bf16.c.

Function Documentation

◆ gelu_derivative_scalar()

static float gelu_derivative_scalar ( float  x)
inlinestatic

Definition at line 53 of file mlp_kernels_bf16.c.

54{
55 const float c = 0.7978845608f; /* sqrt(2/pi) */
56 const float k = 0.044715f;
57 const float x2 = x * x;
58 const float x3 = x2 * x;
59 const float g = c * (x + k * x3);
60 const float tanh_g = tanhf(g);
61 const float sech2_g = 1.0f - tanh_g * tanh_g;
62 const float g_prime = c * (1.0f + 3.0f * k * x2);
63 return 0.5f * (1.0f + tanh_g) + 0.5f * x * sech2_g * g_prime;
64}

Referenced by mlp_token_parallel_bf16_backward_mixed().

◆ gelu_scalar()

static float gelu_scalar ( float  x)
inlinestatic

Definition at line 45 of file mlp_kernels_bf16.c.

46{
47 const float c = 0.7978845608f; /* sqrt(2/pi) */
48 const float k = 0.044715f;
49 float x3 = x * x * x;
50 return 0.5f * x * (1.0f + tanhf(c * (x + k * x3)));
51}

Referenced by mlp_token_parallel_bf16(), mlp_token_parallel_bf16_backward_mixed(), and mlp_token_parallel_bf16_fp32act().

◆ gemm_bf16_fp32out()

void gemm_bf16_fp32out ( const uint16_t *  A,
const uint16_t *  B,
const float *  bias,
float *  C,
int  M,
int  N,
int  K 
)
extern

Definition at line 453 of file gemm_kernels_bf16.c.

458{
459 if (!A || !B || !C || M <= 0 || N <= 0 || K <= 0) {
460 return;
461 }
462
463#if HAVE_NATIVE_BF16
464#if HAVE_AMX_BF16
465 const char *amx_env = getenv("CK_BF16_AMX");
466 if (amx_env && amx_env[0] == '1' &&
467 (M % 16) == 0 && (N % 16) == 0 && (K % 32) == 0 &&
468 M >= 16 && N >= 16 && K >= 32 && ck_amx_request_xtile_data()) {
469 gemm_bf16_fp32out_amx(A, B, bias, C, M, N, K);
470 return;
471 }
472#endif
473
474 #pragma omp parallel for schedule(dynamic)
475 for (int i = 0; i < M; ++i) {
476 const uint16_t *a_row = A + (size_t)i * K;
477 int j = 0;
478
479 for (; j + 4 <= N; j += 4) {
480 const uint16_t *b0 = B + (size_t)(j + 0) * K;
481 const uint16_t *b1 = B + (size_t)(j + 1) * K;
482 const uint16_t *b2 = B + (size_t)(j + 2) * K;
483 const uint16_t *b3 = B + (size_t)(j + 3) * K;
484 __m512 acc0 = _mm512_setzero_ps();
485 __m512 acc1 = _mm512_setzero_ps();
486 __m512 acc2 = _mm512_setzero_ps();
487 __m512 acc3 = _mm512_setzero_ps();
488
489 int k = 0;
490 for (; k <= K - 32; k += 32) {
491 const __m512bh a_vec = load_bf16x32(a_row + k);
492 acc0 = _mm512_dpbf16_ps(acc0, a_vec, load_bf16x32(b0 + k));
493 acc1 = _mm512_dpbf16_ps(acc1, a_vec, load_bf16x32(b1 + k));
494 acc2 = _mm512_dpbf16_ps(acc2, a_vec, load_bf16x32(b2 + k));
495 acc3 = _mm512_dpbf16_ps(acc3, a_vec, load_bf16x32(b3 + k));
496 }
497
498 float s0 = _mm512_reduce_add_ps(acc0);
499 float s1 = _mm512_reduce_add_ps(acc1);
500 float s2 = _mm512_reduce_add_ps(acc2);
501 float s3 = _mm512_reduce_add_ps(acc3);
502 for (; k < K; ++k) {
503 const float a = bf16_to_float(a_row[k]);
504 s0 += a * bf16_to_float(b0[k]);
505 s1 += a * bf16_to_float(b1[k]);
506 s2 += a * bf16_to_float(b2[k]);
507 s3 += a * bf16_to_float(b3[k]);
508 }
509 if (bias) {
510 s0 += bias[j + 0];
511 s1 += bias[j + 1];
512 s2 += bias[j + 2];
513 s3 += bias[j + 3];
514 }
515 C[(size_t)i * N + (j + 0)] = s0;
516 C[(size_t)i * N + (j + 1)] = s1;
517 C[(size_t)i * N + (j + 2)] = s2;
518 C[(size_t)i * N + (j + 3)] = s3;
519 }
520
521 for (; j < N; ++j) {
522 const uint16_t *b_row = B + (size_t)j * K;
523 __m512 sum_vec = _mm512_setzero_ps();
524
525 int k = 0;
526 for (; k <= K - 32; k += 32) {
527 const __m512bh a_vec = load_bf16x32(a_row + k);
528 const __m512bh b_vec = load_bf16x32(b_row + k);
529 sum_vec = _mm512_dpbf16_ps(sum_vec, a_vec, b_vec);
530 }
531
532 float sum = _mm512_reduce_add_ps(sum_vec);
533 for (; k < K; ++k) {
534 sum += bf16_to_float(a_row[k]) * bf16_to_float(b_row[k]);
535 }
536 if (bias) {
537 sum += bias[j];
538 }
539 C[(size_t)i * N + j] = sum;
540 }
541 }
542#elif defined(__AVX512F__)
543 #pragma omp parallel for schedule(dynamic)
544 for (int i = 0; i < M; ++i) {
545 const uint16_t *a_row = A + (size_t)i * K;
546
547 for (int j = 0; j < N; ++j) {
548 const uint16_t *b_row = B + (size_t)j * K;
549
550 __m512 sum_vec = _mm512_setzero_ps();
551
552 int k = 0;
553 for (; k <= K - 16; k += 16) {
554 __m256i a_bf16 = _mm256_loadu_si256((const __m256i *)(a_row + k));
555 __m256i b_bf16 = _mm256_loadu_si256((const __m256i *)(b_row + k));
556 sum_vec = bf16_dot16(a_bf16, b_bf16, sum_vec);
557 }
558
559 float sum = _mm512_reduce_add_ps(sum_vec);
560
561 for (; k < K; ++k) {
562 sum += bf16_to_float(a_row[k]) * bf16_to_float(b_row[k]);
563 }
564
565 if (bias) {
566 sum += bias[j];
567 }
568
569 C[(size_t)i * N + j] = sum;
570 }
571 }
572#else
573 for (int i = 0; i < M; ++i) {
574 for (int j = 0; j < N; ++j) {
575 float sum = bias ? bias[j] : 0.0f;
576 for (int k = 0; k < K; ++k) {
577 sum += bf16_to_float(A[(size_t)i * K + k]) *
578 bf16_to_float(B[(size_t)j * K + k]);
579 }
580 C[(size_t)i * N + j] = sum;
581 }
582 }
583#endif
584}
static float bf16_to_float(uint16_t v)
Definition bf16_utils.h:38
#define C(color)
Definition show_config.c:39

References bf16_to_float(), and C.

Referenced by mlp_token_parallel_bf16(), and mlp_token_parallel_bf16_fp32act().

◆ mlp_token_parallel_bf16()

void mlp_token_parallel_bf16 ( const uint16_t *  input,
const uint16_t *  W_fc1,
const uint16_t *  b_fc1,
const uint16_t *  W_fc2,
const uint16_t *  b_fc2,
float *  fc1_output,
float *  output,
int  T,
int  aligned_dim,
int  num_threads,
float *  scratch_bias1_f,
float *  scratch_bias2_f,
uint16_t *  scratch_fc1_bf16 
)

Optimized MLP Forward (BF16 weights, FP32 activations)

Caller-provided scratch buffers: scratch_bias1_f: [4*D] floats scratch_bias2_f: [D] floats scratch_fc1_bf16: [T * 4*D] uint16_t (BF16)

Definition at line 104 of file mlp_kernels_bf16.c.

117{
118 if (!input || !W_fc1 || !b_fc1 || !W_fc2 || !b_fc2 || !fc1_output || !output) return;
119 if (!scratch_bias1_f || !scratch_bias2_f || !scratch_fc1_bf16) return;
120
121 (void)num_threads;
122 const int D = aligned_dim;
123 const int fourD = 4 * D;
124
125 /* Convert biases to FP32 */
126 for (int i = 0; i < fourD; ++i) {
127 scratch_bias1_f[i] = bf16_to_float(b_fc1[i]);
128 }
129 for (int i = 0; i < D; ++i) {
130 scratch_bias2_f[i] = bf16_to_float(b_fc2[i]);
131 }
132
133 /* FC1: [T, D] x [4D, D].T -> [T, 4D] */
134 gemm_bf16_fp32out(input, W_fc1, scratch_bias1_f, fc1_output, T, fourD, D);
135
136 /* GELU activation */
137#if defined(__AVX512F__)
138 #pragma omp parallel for
139 for (int t = 0; t < T; ++t) {
140 float *row = fc1_output + (size_t)t * fourD;
141 int j = 0;
142 for (; j <= fourD - 16; j += 16) {
143 __m512 x = _mm512_loadu_ps(row + j);
144 _mm512_storeu_ps(row + j, gelu_avx512(x));
145 }
146 for (; j < fourD; ++j) {
147 row[j] = gelu_scalar(row[j]);
148 }
149 }
150#else
151 for (int t = 0; t < T; ++t) {
152 for (int j = 0; j < fourD; ++j) {
153 fc1_output[t * fourD + j] = gelu_scalar(fc1_output[t * fourD + j]);
154 }
155 }
156#endif
157
158 /* Convert FP32 activations to BF16 */
159#if defined(__AVX512F__)
160 #pragma omp parallel for
161 for (int t = 0; t < T; ++t) {
162 float *src = fc1_output + (size_t)t * fourD;
163 uint16_t *dst = scratch_fc1_bf16 + (size_t)t * fourD;
164 int j = 0;
165 for (; j <= fourD - 16; j += 16) {
166 __m512 fp32 = _mm512_loadu_ps(src + j);
167 __m512i as_int = _mm512_castps_si512(fp32);
168 __m512i lsb = _mm512_srli_epi32(as_int, 16);
169 lsb = _mm512_and_si512(lsb, _mm512_set1_epi32(1));
170 __m512i rounding = _mm512_add_epi32(_mm512_set1_epi32(0x7FFF), lsb);
171 __m512i rounded = _mm512_add_epi32(as_int, rounding);
172 __m512i shifted = _mm512_srli_epi32(rounded, 16);
173 __m256i bf16 = _mm512_cvtepi32_epi16(shifted);
174 _mm256_storeu_si256((__m256i *)(dst + j), bf16);
175 }
176 for (; j < fourD; ++j) {
177 dst[j] = float_to_bf16(src[j]);
178 }
179 }
180#else
181 for (size_t i = 0; i < (size_t)T * fourD; ++i) {
182 scratch_fc1_bf16[i] = float_to_bf16(fc1_output[i]);
183 }
184#endif
185
186 /* FC2: BF16 GEMM with FP32 output */
187 gemm_bf16_fp32out(scratch_fc1_bf16, W_fc2, scratch_bias2_f, output, T, D, fourD);
188}
static uint16_t float_to_bf16(float f)
Definition bf16_utils.h:90
static float gelu_scalar(float x)
void gemm_bf16_fp32out(const uint16_t *A, const uint16_t *B, const float *bias, float *C, int M, int N, int K)

References bf16_to_float(), float_to_bf16(), gelu_scalar(), and gemm_bf16_fp32out().

◆ mlp_token_parallel_bf16_backward_mixed()

void mlp_token_parallel_bf16_backward_mixed ( const uint16_t *  input,
const uint16_t *  W_fc1,
const uint16_t *  b_fc1,
const uint16_t *  W_fc2,
const uint16_t *  d_output,
float *  d_input,
float *  d_W_fc1,
float *  d_b_fc1,
float *  d_W_fc2,
float *  d_b_fc2,
int  T,
int  aligned_dim,
int  num_threads,
float *  scratch_fc1_pre,
uint16_t *  scratch_fc1_act_bf16,
float *  scratch_d_fc1 
)

BF16 MLP backward with FP32 gradient accumulation.

Forward contract matched here: z1 = input_bf16 @ W_fc1_bf16.T + b_fc1_bf16 h = GELU(z1) hq = round_to_bf16(h) y = hq @ W_fc2_bf16.T + b_fc2_bf16

Gradients are accumulated and written as FP32. The BF16 activation cast is treated like PyTorch's mixed-precision cast: gradient flows through to h, while d_W_fc2 uses the rounded hq values that FC2 actually consumed.

Definition at line 268 of file mlp_kernels_bf16.c.

284{
285 if (!input || !W_fc1 || !b_fc1 || !W_fc2 || !d_output) return;
286 if (!scratch_fc1_pre || !scratch_fc1_act_bf16 || !scratch_d_fc1) return;
287 if (T <= 0 || aligned_dim <= 0) return;
288
289 (void)num_threads;
290 const int D = aligned_dim;
291 const int fourD = 4 * D;
292
293 /* Recompute FC1 pre-activation and the rounded activation consumed by FC2. */
294 for (int t = 0; t < T; ++t) {
295 for (int j = 0; j < fourD; ++j) {
296 float sum = bf16_to_float(b_fc1[j]);
297 for (int i = 0; i < D; ++i) {
298 const float x = bf16_to_float(input[(size_t)t * (size_t)D + (size_t)i]);
299 const float w = bf16_to_float(W_fc1[(size_t)j * (size_t)D + (size_t)i]);
300 sum += x * w;
301 }
302 scratch_fc1_pre[(size_t)t * (size_t)fourD + (size_t)j] = sum;
303 scratch_fc1_act_bf16[(size_t)t * (size_t)fourD + (size_t)j] = float_to_bf16(gelu_scalar(sum));
304 }
305 }
306
307 if (d_input) {
308 for (int t = 0; t < T; ++t) {
309 for (int i = 0; i < D; ++i) {
310 d_input[(size_t)t * (size_t)D + (size_t)i] = 0.0f;
311 }
312 }
313 }
314 if (d_W_fc1) {
315 for (size_t i = 0; i < (size_t)fourD * (size_t)D; ++i) d_W_fc1[i] = 0.0f;
316 }
317 if (d_b_fc1) {
318 for (int j = 0; j < fourD; ++j) d_b_fc1[j] = 0.0f;
319 }
320 if (d_W_fc2) {
321 for (size_t i = 0; i < (size_t)D * (size_t)fourD; ++i) d_W_fc2[i] = 0.0f;
322 }
323 if (d_b_fc2) {
324 for (int o = 0; o < D; ++o) d_b_fc2[o] = 0.0f;
325 }
326
327 /* d_W_fc2, d_b_fc2, and d_h = d_output @ W_fc2. */
328 for (int t = 0; t < T; ++t) {
329 for (int j = 0; j < fourD; ++j) {
330 float dh = 0.0f;
331 const float hq = bf16_to_float(scratch_fc1_act_bf16[(size_t)t * (size_t)fourD + (size_t)j]);
332 for (int o = 0; o < D; ++o) {
333 const float dy = bf16_to_float(d_output[(size_t)t * (size_t)D + (size_t)o]);
334 const float w2 = bf16_to_float(W_fc2[(size_t)o * (size_t)fourD + (size_t)j]);
335 dh += dy * w2;
336 if (d_W_fc2) {
337 d_W_fc2[(size_t)o * (size_t)fourD + (size_t)j] += dy * hq;
338 }
339 }
340 const float z = scratch_fc1_pre[(size_t)t * (size_t)fourD + (size_t)j];
341 scratch_d_fc1[(size_t)t * (size_t)fourD + (size_t)j] = dh * gelu_derivative_scalar(z);
342 }
343 if (d_b_fc2) {
344 for (int o = 0; o < D; ++o) {
345 d_b_fc2[o] += bf16_to_float(d_output[(size_t)t * (size_t)D + (size_t)o]);
346 }
347 }
348 }
349
350 /* Backprop through FC1. */
351 for (int t = 0; t < T; ++t) {
352 for (int j = 0; j < fourD; ++j) {
353 const float dz = scratch_d_fc1[(size_t)t * (size_t)fourD + (size_t)j];
354 if (d_b_fc1) d_b_fc1[j] += dz;
355 for (int i = 0; i < D; ++i) {
356 const float x = bf16_to_float(input[(size_t)t * (size_t)D + (size_t)i]);
357 const float w1 = bf16_to_float(W_fc1[(size_t)j * (size_t)D + (size_t)i]);
358 if (d_W_fc1) d_W_fc1[(size_t)j * (size_t)D + (size_t)i] += dz * x;
359 if (d_input) d_input[(size_t)t * (size_t)D + (size_t)i] += dz * w1;
360 }
361 }
362 }
363}
static float gelu_derivative_scalar(float x)

References bf16_to_float(), float_to_bf16(), gelu_derivative_scalar(), and gelu_scalar().

◆ mlp_token_parallel_bf16_fp32act()

void mlp_token_parallel_bf16_fp32act ( const uint16_t *  input,
const uint16_t *  W_fc1,
const uint16_t *  b_fc1,
const uint16_t *  W_fc2,
const uint16_t *  b_fc2,
float *  fc1_output,
float *  output,
int  T,
int  aligned_dim,
int  num_threads,
float *  scratch_input_f,
float *  scratch_bias1_f,
float *  scratch_bias2_f,
uint16_t *  scratch_fc1_bf16 
)

Alternative: Fully FP32 activations throughout

Caller-provided scratch buffers: scratch_input_f: [T * D] floats scratch_bias1_f: [4*D] floats scratch_bias2_f: [D] floats scratch_fc1_bf16: [T * 4*D] uint16_t (BF16)

Definition at line 199 of file mlp_kernels_bf16.c.

213{
214 if (!input || !W_fc1 || !b_fc1 || !W_fc2 || !b_fc2 || !fc1_output || !output) return;
215 if (!scratch_input_f || !scratch_bias1_f || !scratch_bias2_f || !scratch_fc1_bf16) return;
216
217 (void)num_threads;
218 const int D = aligned_dim;
219 const int fourD = 4 * D;
220
221 /* Convert input and biases to FP32 */
222 bf16_tensor_to_float(input, scratch_input_f, (size_t)T * D);
223 bf16_tensor_to_float(b_fc1, scratch_bias1_f, fourD);
224 bf16_tensor_to_float(b_fc2, scratch_bias2_f, D);
225
226 /* FC1 */
227 gemm_bf16_fp32out(input, W_fc1, scratch_bias1_f, fc1_output, T, fourD, D);
228
229 /* GELU */
230#if defined(__AVX512F__)
231 #pragma omp parallel for
232 for (int t = 0; t < T; ++t) {
233 float *row = fc1_output + (size_t)t * fourD;
234 int j = 0;
235 for (; j <= fourD - 16; j += 16) {
236 __m512 x = _mm512_loadu_ps(row + j);
237 _mm512_storeu_ps(row + j, gelu_avx512(x));
238 }
239 for (; j < fourD; ++j) {
240 row[j] = gelu_scalar(row[j]);
241 }
242 }
243#else
244 for (size_t i = 0; i < (size_t)T * fourD; ++i) {
245 fc1_output[i] = gelu_scalar(fc1_output[i]);
246 }
247#endif
248
249 /* Convert fc1_output to BF16 for FC2 */
250 float_tensor_to_bf16(fc1_output, scratch_fc1_bf16, (size_t)T * fourD);
251 gemm_bf16_fp32out(scratch_fc1_bf16, W_fc2, scratch_bias2_f, output, T, D, fourD);
252}
static void float_tensor_to_bf16(const float *src, uint16_t *dst, size_t count)
Definition bf16_utils.h:271
static void bf16_tensor_to_float(const uint16_t *src, float *dst, size_t count)
Definition bf16_utils.h:250

References bf16_tensor_to_float(), float_tensor_to_bf16(), gelu_scalar(), and gemm_bf16_fp32out().