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

BF16 optimizer kernels for training. More...

#include <math.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "bf16_utils.h"

Go to the source code of this file.

Functions

void adamw_update_bf16 (const uint16_t *grad, uint16_t *weight, float *m, float *v, size_t numel, float lr, float beta1, float beta2, float eps, float weight_decay, int step)
 AdamW optimizer update (bf16 weights/gradients, fp32 optimizer state)
 
void adamw_update_f32 (const float *grad, float *weight, float *m, float *v, size_t numel, float lr, float beta1, float beta2, float eps, float weight_decay, int step)
 
void gradient_accumulate_bf16 (uint16_t *dst, const uint16_t *src, size_t numel)
 Accumulate gradients: dst += src (bf16)
 
void gradient_accumulate_f32 (float *dst, const float *src, size_t numel)
 
float gradient_clip_norm_bf16 (uint16_t *grad, size_t numel, float max_norm)
 Clip gradient norm (bf16)
 
void gradient_scale_bf16 (uint16_t *grad, size_t numel, float scale)
 Scale gradients: grad *= scale (bf16)
 
void gradient_scale_f32 (float *grad, size_t numel, float scale)
 
void sgd_momentum_update_bf16 (const uint16_t *grad, uint16_t *weight, float *velocity, size_t numel, float lr, float momentum, float weight_decay)
 SGD with momentum (bf16 weights/gradients)
 
void sgd_momentum_update_f32 (const float *grad, float *weight, float *velocity, size_t numel, float lr, float momentum, float weight_decay)
 SGD with momentum optimizer update (fp32 version)
 
void zero_gradients_bf16 (uint16_t *grad, size_t numel)
 Zero out gradient buffer (bf16)
 

Detailed Description

BF16 optimizer kernels for training.

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

Note: Optimizer state (m, v) is always kept in fp32 for numerical stability. Only weights and gradients are in bf16.

Definition in file optimizer_kernels_bf16.c.

Function Documentation

◆ adamw_update_bf16()

void adamw_update_bf16 ( const uint16_t *  grad,
uint16_t *  weight,
float *  m,
float *  v,
size_t  numel,
float  lr,
float  beta1,
float  beta2,
float  eps,
float  weight_decay,
int  step 
)

AdamW optimizer update (bf16 weights/gradients, fp32 optimizer state)

Weights and gradients are in bf16 for memory efficiency. Momentum (m) and variance (v) are in fp32 for numerical stability.

Parameters
gradGradient tensor (bf16) [numel]
weightWeight tensor to update (bf16, in-place) [numel]
mFirst moment buffer (fp32, in-place) [numel]
vSecond moment buffer (fp32, in-place) [numel]
numelNumber of elements
lrLearning rate
beta1First moment decay (typically 0.9)
beta2Second moment decay (typically 0.999)
epsNumerical stability constant (typically 1e-8)
weight_decayWeight decay coefficient
stepCurrent step number (1-indexed)

Definition at line 57 of file optimizer_kernels_bf16.c.

69{
70 if (!grad || !weight || !m || !v || numel == 0) {
71 return;
72 }
73
74 // Bias correction terms
75 float bias_correction1 = 1.0f - powf(beta1, (float)step);
76 float bias_correction2 = 1.0f - powf(beta2, (float)step);
77 float one_minus_beta1 = 1.0f - beta1;
78 float one_minus_beta2 = 1.0f - beta2;
79
80#if defined(__AVX512F__)
81 // Vectorized path: process 16 elements at a time
82 __m512 v_beta1 = _mm512_set1_ps(beta1);
83 __m512 v_beta2 = _mm512_set1_ps(beta2);
84 __m512 v_one_minus_beta1 = _mm512_set1_ps(one_minus_beta1);
85 __m512 v_one_minus_beta2 = _mm512_set1_ps(one_minus_beta2);
86 __m512 v_lr = _mm512_set1_ps(lr);
87 __m512 v_eps = _mm512_set1_ps(eps);
88 __m512 v_weight_decay = _mm512_set1_ps(weight_decay);
89 __m512 v_bc1_inv = _mm512_set1_ps(1.0f / bias_correction1);
90 __m512 v_bc2_inv = _mm512_set1_ps(1.0f / bias_correction2);
91
92 size_t i = 0;
93 for (; i + 16 <= numel; i += 16) {
94 // Load bf16 gradient and weight, convert to fp32
95 __m512 g = bf16_loadu_cvt_fp32(&grad[i]);
96 __m512 w = bf16_loadu_cvt_fp32(&weight[i]);
97
98 // Load fp32 optimizer state
99 __m512 m_val = _mm512_loadu_ps(&m[i]);
100 __m512 v_val = _mm512_loadu_ps(&v[i]);
101
102 // Update m: m = beta1 * m + (1 - beta1) * g
103 m_val = _mm512_fmadd_ps(v_beta1, m_val, _mm512_mul_ps(v_one_minus_beta1, g));
104
105 // Update v: v = beta2 * v + (1 - beta2) * g^2
106 __m512 g_sq = _mm512_mul_ps(g, g);
107 v_val = _mm512_fmadd_ps(v_beta2, v_val, _mm512_mul_ps(v_one_minus_beta2, g_sq));
108
109 // Bias-corrected estimates
110 __m512 m_hat = _mm512_mul_ps(m_val, v_bc1_inv);
111 __m512 v_hat = _mm512_mul_ps(v_val, v_bc2_inv);
112
113 // Update weight: w = w - lr * (m_hat / (sqrt(v_hat) + eps) + weight_decay * w)
114 __m512 denom = _mm512_add_ps(_mm512_sqrt_ps(v_hat), v_eps);
115 __m512 update = _mm512_div_ps(m_hat, denom);
116 update = _mm512_fmadd_ps(v_weight_decay, w, update);
117 w = _mm512_fnmadd_ps(v_lr, update, w);
118
119 // Store updated weight as bf16
120 fp32_cvt_storeu_bf16(&weight[i], w);
121
122 // Store updated optimizer state (stays fp32)
123 _mm512_storeu_ps(&m[i], m_val);
124 _mm512_storeu_ps(&v[i], v_val);
125 }
126
127 // Scalar tail
128 for (; i < numel; ++i) {
129 float g = bf16_to_float(grad[i]);
130 float w = bf16_to_float(weight[i]);
131
132 m[i] = beta1 * m[i] + one_minus_beta1 * g;
133 v[i] = beta2 * v[i] + one_minus_beta2 * g * g;
134
135 float m_hat = m[i] / bias_correction1;
136 float v_hat = v[i] / bias_correction2;
137
138 w = w - lr * (m_hat / (sqrtf(v_hat) + eps) + weight_decay * w);
139 weight[i] = float_to_bf16(w);
140 }
141#else
142 // Scalar path
143 for (size_t i = 0; i < numel; ++i) {
144 float g = bf16_to_float(grad[i]);
145 float w = bf16_to_float(weight[i]);
146
147 m[i] = beta1 * m[i] + one_minus_beta1 * g;
148 v[i] = beta2 * v[i] + one_minus_beta2 * g * g;
149
150 float m_hat = m[i] / bias_correction1;
151 float v_hat = v[i] / bias_correction2;
152
153 w = w - lr * (m_hat / (sqrtf(v_hat) + eps) + weight_decay * w);
154 weight[i] = float_to_bf16(w);
155 }
156#endif
157}
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

References bf16_to_float(), and float_to_bf16().

◆ adamw_update_f32()

void adamw_update_f32 ( const float *  grad,
float *  weight,
float *  m,
float *  v,
size_t  numel,
float  lr,
float  beta1,
float  beta2,
float  eps,
float  weight_decay,
int  step 
)
extern

Definition at line 592 of file optimizer_kernels.c.

604{
605 if (!grad || !weight || !m || !v || numel == 0) {
606 return;
607 }
608
609 ck_threadpool_t *pool = ck_threadpool_global();
610 int nth = pool ? ck_threadpool_n_threads(pool) : 1;
611 if (!pool || nth <= 1 || nth > CK_OPT_PAR_MAX_THREADS || numel < CK_OPT_PAR_MIN_NUMEL) {
612 adamw_update_f32_impl(grad, weight, m, v, numel, lr, beta1, beta2, eps, weight_decay, step);
613 return;
614 }
615 int active_nth = ck_opt_pick_active_threads(nth, numel, CK_OPT_PAR_MIN_NUMEL);
616 if (active_nth <= 1) {
617 adamw_update_f32_impl(grad, weight, m, v, numel, lr, beta1, beta2, eps, weight_decay, step);
618 return;
619 }
620
621 ck_adamw_parallel_args_t args = {
622 .grad = grad,
623 .weight = weight,
624 .m = m,
625 .v = v,
626 .numel = numel,
627 .lr = lr,
628 .beta1 = beta1,
629 .beta2 = beta2,
630 .eps = eps,
631 .weight_decay = weight_decay,
632 .step = step,
633 };
634 ck_threadpool_dispatch_n(pool, active_nth, ck_adamw_parallel_work, &args);
635}
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 void adamw_update_f32_impl(const float *grad, float *weight, float *m, float *v, size_t numel, float lr, float beta1, float beta2, float eps, float weight_decay, int step)
AdamW optimizer update (fp32 version)
static int ck_opt_pick_active_threads(int nth, size_t work_items, size_t min_chunk)
#define CK_OPT_PAR_MIN_NUMEL
static void ck_adamw_parallel_work(int ith, int nth, void *argp)
#define CK_OPT_PAR_MAX_THREADS

◆ gradient_accumulate_bf16()

void gradient_accumulate_bf16 ( uint16_t *  dst,
const uint16_t *  src,
size_t  numel 
)

Accumulate gradients: dst += src (bf16)

Definition at line 229 of file optimizer_kernels_bf16.c.

230{
231 if (!dst || !src || numel == 0) {
232 return;
233 }
234
235#if defined(__AVX512F__)
236 size_t i = 0;
237 for (; i + 16 <= numel; i += 16) {
238 __m512 d = bf16_loadu_cvt_fp32(&dst[i]);
239 __m512 s = bf16_loadu_cvt_fp32(&src[i]);
240 fp32_cvt_storeu_bf16(&dst[i], _mm512_add_ps(d, s));
241 }
242 for (; i < numel; ++i) {
243 float d = bf16_to_float(dst[i]);
244 float s = bf16_to_float(src[i]);
245 dst[i] = float_to_bf16(d + s);
246 }
247#else
248 for (size_t i = 0; i < numel; ++i) {
249 float d = bf16_to_float(dst[i]);
250 float s = bf16_to_float(src[i]);
251 dst[i] = float_to_bf16(d + s);
252 }
253#endif
254}

References bf16_to_float(), and float_to_bf16().

◆ gradient_accumulate_f32()

void gradient_accumulate_f32 ( float *  dst,
const float *  src,
size_t  numel 
)
extern

Definition at line 925 of file optimizer_kernels.c.

926{
927 if (!dst || !src || numel == 0) {
928 return;
929 }
930
931 ck_threadpool_t *pool = ck_threadpool_global();
932 int nth = pool ? ck_threadpool_n_threads(pool) : 1;
933 if (!pool || nth <= 1 || nth > CK_OPT_PAR_MAX_THREADS || numel < CK_OPT_PAR_MIN_NUMEL) {
934 gradient_accumulate_f32_impl(dst, src, numel);
935 return;
936 }
937 int active_nth = ck_opt_pick_active_threads(nth, numel, CK_OPT_PAR_MIN_NUMEL);
938 if (active_nth <= 1) {
939 gradient_accumulate_f32_impl(dst, src, numel);
940 return;
941 }
942
943 ck_accum_parallel_args_t args = {
944 .dst = dst,
945 .src = src,
946 .numel = numel,
947 };
948 ck_threadpool_dispatch_n(pool, active_nth, ck_accum_parallel_work, &args);
949}
static void gradient_accumulate_f32_impl(float *dst, const float *src, size_t numel)
Accumulate gradients: dst += src (fp32)
static void ck_accum_parallel_work(int ith, int nth, void *argp)

◆ gradient_clip_norm_bf16()

float gradient_clip_norm_bf16 ( uint16_t *  grad,
size_t  numel,
float  max_norm 
)

Clip gradient norm (bf16)

Returns
The original L2 norm before clipping

Definition at line 291 of file optimizer_kernels_bf16.c.

292{
293 if (!grad || numel == 0 || max_norm <= 0.0f) {
294 return 0.0f;
295 }
296
297 // Compute L2 norm in fp32 for accuracy
298 double sum_sq = 0.0;
299#if defined(__AVX512F__)
300 __m512 acc = _mm512_setzero_ps();
301 size_t i = 0;
302 for (; i + 16 <= numel; i += 16) {
303 __m512 g = bf16_loadu_cvt_fp32(&grad[i]);
304 acc = _mm512_fmadd_ps(g, g, acc);
305 }
306 sum_sq = _mm512_reduce_add_ps(acc);
307 for (; i < numel; ++i) {
308 float g = bf16_to_float(grad[i]);
309 sum_sq += (double)g * (double)g;
310 }
311#else
312 for (size_t i = 0; i < numel; ++i) {
313 float g = bf16_to_float(grad[i]);
314 sum_sq += (double)g * (double)g;
315 }
316#endif
317
318 float norm = sqrtf((float)sum_sq);
319
320 if (norm > max_norm) {
321 float scale = max_norm / norm;
322 gradient_scale_bf16(grad, numel, scale);
323 }
324
325 return norm;
326}
void gradient_scale_bf16(uint16_t *grad, size_t numel, float scale)
Scale gradients: grad *= scale (bf16)

References bf16_to_float(), and gradient_scale_bf16().

◆ gradient_scale_bf16()

void gradient_scale_bf16 ( uint16_t *  grad,
size_t  numel,
float  scale 
)

Scale gradients: grad *= scale (bf16)

Definition at line 260 of file optimizer_kernels_bf16.c.

261{
262 if (!grad || numel == 0) {
263 return;
264 }
265
266#if defined(__AVX512F__)
267 __m512 v_scale = _mm512_set1_ps(scale);
268 size_t i = 0;
269 for (; i + 16 <= numel; i += 16) {
270 __m512 g = bf16_loadu_cvt_fp32(&grad[i]);
271 fp32_cvt_storeu_bf16(&grad[i], _mm512_mul_ps(g, v_scale));
272 }
273 for (; i < numel; ++i) {
274 float g = bf16_to_float(grad[i]);
275 grad[i] = float_to_bf16(g * scale);
276 }
277#else
278 for (size_t i = 0; i < numel; ++i) {
279 float g = bf16_to_float(grad[i]);
280 grad[i] = float_to_bf16(g * scale);
281 }
282#endif
283}

References bf16_to_float(), and float_to_bf16().

Referenced by gradient_clip_norm_bf16().

◆ gradient_scale_f32()

void gradient_scale_f32 ( float *  grad,
size_t  numel,
float  scale 
)
extern

Definition at line 1073 of file optimizer_kernels.c.

1074{
1075 if (!grad || numel == 0) {
1076 return;
1077 }
1078
1079 ck_threadpool_t *pool = ck_threadpool_global();
1080 int nth = pool ? ck_threadpool_n_threads(pool) : 1;
1081 if (!pool || nth <= 1 || nth > CK_OPT_PAR_MAX_THREADS || numel < CK_OPT_PAR_MIN_NUMEL) {
1082 gradient_scale_f32_impl(grad, numel, scale);
1083 return;
1084 }
1085 int active_nth = ck_opt_pick_active_threads(nth, numel, CK_OPT_PAR_MIN_NUMEL);
1086 if (active_nth <= 1) {
1087 gradient_scale_f32_impl(grad, numel, scale);
1088 return;
1089 }
1090
1091 ck_scale_parallel_args_t args = {
1092 .grad = grad,
1093 .numel = numel,
1094 .scale = scale,
1095 };
1096 ck_threadpool_dispatch_n(pool, active_nth, ck_scale_parallel_work, &args);
1097}
static void ck_scale_parallel_work(int ith, int nth, void *argp)
static void gradient_scale_f32_impl(float *grad, size_t numel, float scale)
Scale gradients by a constant: grad *= scale (fp32)

Referenced by gradient_clip_norm_f32().

◆ sgd_momentum_update_bf16()

void sgd_momentum_update_bf16 ( const uint16_t *  grad,
uint16_t *  weight,
float *  velocity,
size_t  numel,
float  lr,
float  momentum,
float  weight_decay 
)

SGD with momentum (bf16 weights/gradients)

Definition at line 163 of file optimizer_kernels_bf16.c.

171{
172 if (!grad || !weight || !velocity || numel == 0) {
173 return;
174 }
175
176#if defined(__AVX512F__)
177 __m512 v_lr = _mm512_set1_ps(lr);
178 __m512 v_momentum = _mm512_set1_ps(momentum);
179 __m512 v_weight_decay = _mm512_set1_ps(weight_decay);
180
181 size_t i = 0;
182 for (; i + 16 <= numel; i += 16) {
183 __m512 g = bf16_loadu_cvt_fp32(&grad[i]);
184 __m512 w = bf16_loadu_cvt_fp32(&weight[i]);
185 __m512 vel = _mm512_loadu_ps(&velocity[i]);
186
187 vel = _mm512_fmadd_ps(v_momentum, vel, g);
188 __m512 update = _mm512_fmadd_ps(v_weight_decay, w, vel);
189 w = _mm512_fnmadd_ps(v_lr, update, w);
190
191 fp32_cvt_storeu_bf16(&weight[i], w);
192 _mm512_storeu_ps(&velocity[i], vel);
193 }
194
195 for (; i < numel; ++i) {
196 float g = bf16_to_float(grad[i]);
197 float w = bf16_to_float(weight[i]);
198 velocity[i] = momentum * velocity[i] + g;
199 w = w - lr * (velocity[i] + weight_decay * w);
200 weight[i] = float_to_bf16(w);
201 }
202#else
203 for (size_t i = 0; i < numel; ++i) {
204 float g = bf16_to_float(grad[i]);
205 float w = bf16_to_float(weight[i]);
206 velocity[i] = momentum * velocity[i] + g;
207 w = w - lr * (velocity[i] + weight_decay * w);
208 weight[i] = float_to_bf16(w);
209 }
210#endif
211}

References bf16_to_float(), and float_to_bf16().

◆ sgd_momentum_update_f32()

void sgd_momentum_update_f32 ( const float *  grad,
float *  weight,
float *  velocity,
size_t  numel,
float  lr,
float  momentum,
float  weight_decay 
)
extern

SGD with momentum optimizer update (fp32 version)

v_t = momentum * v_{t-1} + g_t w_t = w_{t-1} - lr * (v_t + weight_decay * w_{t-1})

Parameters
gradGradient tensor (fp32) [numel]
weightWeight tensor to update (fp32, in-place) [numel]
velocityVelocity buffer (fp32, in-place) [numel]
numelNumber of elements
lrLearning rate
momentumMomentum coefficient (typically 0.9)
weight_decayWeight decay coefficient

Definition at line 753 of file optimizer_kernels.c.

761{
762 if (!grad || !weight || !velocity || numel == 0) {
763 return;
764 }
765
766#if defined(__AVX512F__)
767 // AVX-512 path: process 16 floats at a time
768 __m512 v_lr = _mm512_set1_ps(lr);
769 __m512 v_momentum = _mm512_set1_ps(momentum);
770 __m512 v_weight_decay = _mm512_set1_ps(weight_decay);
771
772 size_t i = 0;
773 for (; i + 16 <= numel; i += 16) {
774 __m512 g = _mm512_loadu_ps(&grad[i]);
775 __m512 w = _mm512_loadu_ps(&weight[i]);
776 __m512 vel = _mm512_loadu_ps(&velocity[i]);
777
778 vel = _mm512_fmadd_ps(v_momentum, vel, g);
779 __m512 update = _mm512_fmadd_ps(v_weight_decay, w, vel);
780 w = _mm512_fnmadd_ps(v_lr, update, w);
781
782 _mm512_storeu_ps(&weight[i], w);
783 _mm512_storeu_ps(&velocity[i], vel);
784 }
785
786 for (; i < numel; ++i) {
787 velocity[i] = momentum * velocity[i] + grad[i];
788 weight[i] = weight[i] - lr * (velocity[i] + weight_decay * weight[i]);
789 }
790
791#elif defined(__AVX__)
792 // AVX path: process 8 floats at a time
793 __m256 v_lr = _mm256_set1_ps(lr);
794 __m256 v_momentum = _mm256_set1_ps(momentum);
795 __m256 v_weight_decay = _mm256_set1_ps(weight_decay);
796
797 size_t i = 0;
798 for (; i + 8 <= numel; i += 8) {
799 __m256 g = _mm256_loadu_ps(&grad[i]);
800 __m256 w = _mm256_loadu_ps(&weight[i]);
801 __m256 vel = _mm256_loadu_ps(&velocity[i]);
802
803 // v = momentum * v + g
804 vel = _mm256_add_ps(_mm256_mul_ps(v_momentum, vel), g);
805
806 // w = w - lr * (v + weight_decay * w)
807 __m256 update = _mm256_add_ps(vel, _mm256_mul_ps(v_weight_decay, w));
808 w = _mm256_sub_ps(w, _mm256_mul_ps(v_lr, update));
809
810 _mm256_storeu_ps(&weight[i], w);
811 _mm256_storeu_ps(&velocity[i], vel);
812 }
813
814 for (; i < numel; ++i) {
815 velocity[i] = momentum * velocity[i] + grad[i];
816 weight[i] = weight[i] - lr * (velocity[i] + weight_decay * weight[i]);
817 }
818
819#elif defined(__SSE2__)
820 // SSE2 path: process 4 floats at a time
821 __m128 v_lr = _mm_set1_ps(lr);
822 __m128 v_momentum = _mm_set1_ps(momentum);
823 __m128 v_weight_decay = _mm_set1_ps(weight_decay);
824
825 size_t i = 0;
826 for (; i + 4 <= numel; i += 4) {
827 __m128 g = _mm_loadu_ps(&grad[i]);
828 __m128 w = _mm_loadu_ps(&weight[i]);
829 __m128 vel = _mm_loadu_ps(&velocity[i]);
830
831 vel = _mm_add_ps(_mm_mul_ps(v_momentum, vel), g);
832 __m128 update = _mm_add_ps(vel, _mm_mul_ps(v_weight_decay, w));
833 w = _mm_sub_ps(w, _mm_mul_ps(v_lr, update));
834
835 _mm_storeu_ps(&weight[i], w);
836 _mm_storeu_ps(&velocity[i], vel);
837 }
838
839 for (; i < numel; ++i) {
840 velocity[i] = momentum * velocity[i] + grad[i];
841 weight[i] = weight[i] - lr * (velocity[i] + weight_decay * weight[i]);
842 }
843
844#else
845 // Scalar path
846 for (size_t i = 0; i < numel; ++i) {
847 velocity[i] = momentum * velocity[i] + grad[i];
848 weight[i] = weight[i] - lr * (velocity[i] + weight_decay * weight[i]);
849 }
850#endif
851}

◆ zero_gradients_bf16()

void zero_gradients_bf16 ( uint16_t *  grad,
size_t  numel 
)

Zero out gradient buffer (bf16)

Definition at line 217 of file optimizer_kernels_bf16.c.

218{
219 if (!grad || numel == 0) {
220 return;
221 }
222 memset(grad, 0, numel * sizeof(uint16_t));
223}