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

GELU activation kernels with SIMD (SSE/AVX/AVX512) More...

#include <math.h>
#include <stddef.h>
#include <pthread.h>
#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>
#include "bf16_utils.h"
#include "ckernel_quant.h"

Go to the source code of this file.

Macros

#define _GNU_SOURCE
 

Typedefs

typedef void(* ck_gelu_ggml_cpu_init_fn) (void)
 
typedef float(* ck_gelu_ggml_fp16_to_fp32_fn) (ck_half)
 
typedef ck_half(* ck_gelu_ggml_fp32_to_fp16_fn) (float)
 
typedef float(* ck_gelu_math_f32_fn) (float)
 
typedef double(* ck_gelu_math_f64_fn) (double)
 

Functions

static void ck_gelu_ggml_runtime_init (void)
 
static void ck_gelu_ggml_table_init (void)
 
static void ck_gelu_reference_math_init (void)
 
static ck_gelu_math_f64_fn ck_gelu_system_erf (void)
 
static ck_gelu_math_f32_fn ck_gelu_system_tanhf (void)
 
static float ck_gelu_tanh_f32 (float x)
 
static float ck_gelu_tanh_ggml_reference_f32 (float x)
 
static void ck_gelu_try_bind_runtime (void *handle)
 
void gelu_backward_exact (const float *input, const float *d_output, float *d_input, size_t n)
 
void gelu_backward_fast (const float *input, const float *d_output, float *d_input, size_t n)
 
void gelu_backward_scalar (const float *input, const float *d_output, float *d_input, size_t n)
 
void gelu_erf_bf16_storage (float *data, size_t n)
 
void gelu_erf_fp64_f32_inplace (float *data, size_t n)
 
void gelu_exact_inplace (float *data, size_t n)
 
void gelu_fast_inplace (float *data, size_t n)
 
void gelu_ggml_inplace (float *data, size_t n)
 
void gelu_ggml_native_inplace (float *data, size_t n)
 
void gelu_pytorch_erf_f32_inplace (float *data, size_t n)
 
void gelu_pytorch_erf_sleef_bf16_storage (float *data, size_t n)
 
void gelu_pytorch_tanh_bf16_storage (float *data, size_t n)
 

Variables

static pthread_once_t ck_gelu_ggml_runtime_once = PTHREAD_ONCE_INIT
 
static ck_half ck_gelu_ggml_table_f16 [1u<< 16]
 
static pthread_once_t ck_gelu_ggml_table_once = PTHREAD_ONCE_INIT
 
static ck_gelu_math_f64_fn ck_gelu_reference_erf = NULL
 
static pthread_once_t ck_gelu_reference_math_once = PTHREAD_ONCE_INIT
 
static ck_gelu_math_f32_fn ck_gelu_reference_tanhf = NULL
 
static ck_gelu_ggml_fp16_to_fp32_fn ck_gelu_runtime_fp16_to_fp32 = NULL
 
static ck_gelu_ggml_fp32_to_fp16_fn ck_gelu_runtime_fp32_to_fp16 = NULL
 
static void * ck_gelu_runtime_handle = NULL
 
static int ck_gelu_runtime_ready = 0
 
static const ck_halfck_gelu_runtime_table_f16 = NULL
 

Detailed Description

GELU activation kernels with SIMD (SSE/AVX/AVX512)

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

GELU: y = x * 0.5 * (1 + erf(x / sqrt(2))) Fast approx: y = x * sigmoid(1.702 * x)

Definition in file gelu_kernels.c.

Macro Definition Documentation

◆ _GNU_SOURCE

#define _GNU_SOURCE

Definition at line 19 of file gelu_kernels.c.

Typedef Documentation

◆ ck_gelu_ggml_cpu_init_fn

typedef void(* ck_gelu_ggml_cpu_init_fn) (void)

Definition at line 48 of file gelu_kernels.c.

◆ ck_gelu_ggml_fp16_to_fp32_fn

typedef float(* ck_gelu_ggml_fp16_to_fp32_fn) (ck_half)

Definition at line 50 of file gelu_kernels.c.

◆ ck_gelu_ggml_fp32_to_fp16_fn

typedef ck_half(* ck_gelu_ggml_fp32_to_fp16_fn) (float)

Definition at line 49 of file gelu_kernels.c.

◆ ck_gelu_math_f32_fn

typedef float(* ck_gelu_math_f32_fn) (float)

Definition at line 51 of file gelu_kernels.c.

◆ ck_gelu_math_f64_fn

typedef double(* ck_gelu_math_f64_fn) (double)

Definition at line 52 of file gelu_kernels.c.

Function Documentation

◆ ck_gelu_ggml_runtime_init()

static void ck_gelu_ggml_runtime_init ( void  )
static

Definition at line 119 of file gelu_kernels.c.

119 {
122 return;
123 }
124
125 ck_gelu_runtime_handle = dlopen("llama.cpp/build/bin/libggml-cpu.so", RTLD_LAZY | RTLD_LOCAL);
128 }
129}
#define RTLD_DEFAULT
static void * ck_gelu_runtime_handle
static int ck_gelu_runtime_ready
static void ck_gelu_try_bind_runtime(void *handle)

References ck_gelu_runtime_handle, ck_gelu_runtime_ready, ck_gelu_try_bind_runtime(), and RTLD_DEFAULT.

Referenced by gelu_ggml_inplace().

◆ ck_gelu_ggml_table_init()

static void ck_gelu_ggml_table_init ( void  )
static

Definition at line 131 of file gelu_kernels.c.

131 {
132 for (uint32_t i = 0; i < (1u << 16); ++i) {
133 const ck_half x_fp16 = (ck_half) i;
134 const float x = ggml_fp16_to_fp32(x_fp16);
135 const float y = ck_gelu_tanh_ggml_reference_f32(x);
137 }
138}
#define ggml_fp32_to_fp16
uint16_t ck_half
#define ggml_fp16_to_fp32
static ck_half ck_gelu_ggml_table_f16[1u<< 16]
static float ck_gelu_tanh_ggml_reference_f32(float x)

References ck_gelu_ggml_table_f16, ck_gelu_tanh_ggml_reference_f32(), ggml_fp16_to_fp32, and ggml_fp32_to_fp16.

Referenced by gelu_ggml_inplace(), and gelu_ggml_native_inplace().

◆ ck_gelu_reference_math_init()

static void ck_gelu_reference_math_init ( void  )
static

Definition at line 63 of file gelu_kernels.c.

63 {
64#if defined(__linux__)
65 void *handle = dlopen("libm.so.6", RTLD_NOW | RTLD_LOCAL);
66 if (handle) {
67 ck_gelu_reference_tanhf = (ck_gelu_math_f32_fn) dlsym(handle, "tanhf");
68 ck_gelu_reference_erf = (ck_gelu_math_f64_fn) dlsym(handle, "erf");
69 }
70#endif
71}
double(* ck_gelu_math_f64_fn)(double)
float(* ck_gelu_math_f32_fn)(float)
static ck_gelu_math_f32_fn ck_gelu_reference_tanhf
static ck_gelu_math_f64_fn ck_gelu_reference_erf

References ck_gelu_reference_erf, and ck_gelu_reference_tanhf.

Referenced by ck_gelu_system_erf(), and ck_gelu_system_tanhf().

◆ ck_gelu_system_erf()

static ck_gelu_math_f64_fn ck_gelu_system_erf ( void  )
static

Definition at line 78 of file gelu_kernels.c.

78 {
81}
static void ck_gelu_reference_math_init(void)
static pthread_once_t ck_gelu_reference_math_once

References ck_gelu_reference_erf, ck_gelu_reference_math_init(), and ck_gelu_reference_math_once.

Referenced by gelu_erf_bf16_storage(), and gelu_erf_fp64_f32_inplace().

◆ ck_gelu_system_tanhf()

static ck_gelu_math_f32_fn ck_gelu_system_tanhf ( void  )
static

◆ ck_gelu_tanh_f32()

static float ck_gelu_tanh_f32 ( float  x)
inlinestatic

Definition at line 36 of file gelu_kernels.c.

36 {
37 const float sqrt_2_over_pi = 0.7978845608f;
38 const float coeff = 0.044715f;
39 const float x3 = x * x * x;
40 const float inner = sqrt_2_over_pi * (x + coeff * x3);
41 return 0.5f * x * (1.0f + tanhf(inner));
42}

Referenced by gelu_exact_inplace().

◆ ck_gelu_tanh_ggml_reference_f32()

static float ck_gelu_tanh_ggml_reference_f32 ( float  x)
static

Definition at line 86 of file gelu_kernels.c.

86 {
87 const float gelu_coef_a = 0.044715f;
88 const float sqrt_2_over_pi = 0.79788456080286535588f;
89 ck_gelu_math_f32_fn reference_tanhf = ck_gelu_system_tanhf();
90 const float inner = sqrt_2_over_pi * x * (1.0f + gelu_coef_a * x * x);
91 const float tanh_value = reference_tanhf ? reference_tanhf(inner) : tanhf(inner);
92 return 0.5f * x * (1.0f + tanh_value);
93}
static ck_gelu_math_f32_fn ck_gelu_system_tanhf(void)

References ck_gelu_system_tanhf().

Referenced by ck_gelu_ggml_table_init().

◆ ck_gelu_try_bind_runtime()

static void ck_gelu_try_bind_runtime ( void *  handle)
static

Definition at line 98 of file gelu_kernels.c.

98 {
99 ck_gelu_ggml_cpu_init_fn cpu_init_fn =
100 (ck_gelu_ggml_cpu_init_fn) dlsym(handle, "ggml_cpu_init");
101 ck_gelu_ggml_fp32_to_fp16_fn fp32_to_fp16_fn =
102 (ck_gelu_ggml_fp32_to_fp16_fn) dlsym(handle, "ggml_fp32_to_fp16");
103 ck_gelu_ggml_fp16_to_fp32_fn fp16_to_fp32_fn =
104 (ck_gelu_ggml_fp16_to_fp32_fn) dlsym(handle, "ggml_fp16_to_fp32");
105 const ck_half *table =
106 (const ck_half *) dlsym(handle, "ggml_table_gelu_f16");
107
108 if (!cpu_init_fn || !fp32_to_fp16_fn || !fp16_to_fp32_fn || !table) {
109 return;
110 }
111
112 cpu_init_fn();
114 ck_gelu_runtime_fp32_to_fp16 = fp32_to_fp16_fn;
115 ck_gelu_runtime_fp16_to_fp32 = fp16_to_fp32_fn;
117}
float(* ck_gelu_ggml_fp16_to_fp32_fn)(ck_half)
static const ck_half * ck_gelu_runtime_table_f16
ck_half(* ck_gelu_ggml_fp32_to_fp16_fn)(float)
static ck_gelu_ggml_fp32_to_fp16_fn ck_gelu_runtime_fp32_to_fp16
static ck_gelu_ggml_fp16_to_fp32_fn ck_gelu_runtime_fp16_to_fp32
void(* ck_gelu_ggml_cpu_init_fn)(void)

References ck_gelu_runtime_fp16_to_fp32, ck_gelu_runtime_fp32_to_fp16, ck_gelu_runtime_ready, and ck_gelu_runtime_table_f16.

Referenced by ck_gelu_ggml_runtime_init().

◆ gelu_backward_exact()

void gelu_backward_exact ( const float *  input,
const float *  d_output,
float *  d_input,
size_t  n 
)

Definition at line 368 of file gelu_kernels.c.

372{
373 const float sqrt_2_over_pi = 0.7978845608f;
374 const float coeff = 0.044715f;
375
376#if defined(__AVX512F__)
377 const __m512 sqrt_2_pi_vec = _mm512_set1_ps(sqrt_2_over_pi);
378 const __m512 coeff_vec = _mm512_set1_ps(coeff);
379 const __m512 coeff3_vec = _mm512_set1_ps(3.0f * coeff);
380 const __m512 half_vec = _mm512_set1_ps(0.5f);
381 const __m512 one_vec = _mm512_set1_ps(1.0f);
382
383 size_t i = 0;
384 for (; i + 16 <= n; i += 16) {
385 __m512 x = _mm512_loadu_ps(&input[i]);
386 __m512 dy = _mm512_loadu_ps(&d_output[i]);
387
388 __m512 x2 = _mm512_mul_ps(x, x);
389 __m512 x3 = _mm512_mul_ps(x2, x);
390
391 // g = sqrt(2/pi) * (x + 0.044715 * x^3)
392 __m512 g = _mm512_fmadd_ps(coeff_vec, x3, x);
393 g = _mm512_mul_ps(sqrt_2_pi_vec, g);
394
395 __m512 tanh_g = tanh512_fast(g);
396
397 // g' = sqrt(2/pi) * (1 + 3 * 0.044715 * x^2)
398 __m512 g_prime = _mm512_fmadd_ps(coeff3_vec, x2, one_vec);
399 g_prime = _mm512_mul_ps(sqrt_2_pi_vec, g_prime);
400
401 // sech^2(g) = 1 - tanh^2(g)
402 __m512 sech2_g = _mm512_fnmadd_ps(tanh_g, tanh_g, one_vec);
403
404 // gelu_derivative = 0.5 * (1 + tanh_g) + 0.5 * x * sech2_g * g_prime
405 __m512 term1 = _mm512_mul_ps(half_vec, _mm512_add_ps(one_vec, tanh_g));
406 __m512 term2 = _mm512_mul_ps(half_vec, _mm512_mul_ps(x, _mm512_mul_ps(sech2_g, g_prime)));
407 __m512 gelu_deriv = _mm512_add_ps(term1, term2);
408
409 __m512 result = _mm512_mul_ps(dy, gelu_deriv);
410 _mm512_storeu_ps(&d_input[i], result);
411 }
412 // Handle remaining elements
413 for (; i < n; ++i) {
414 float x = input[i];
415 float x3 = x * x * x;
416 float g = sqrt_2_over_pi * (x + coeff * x3);
417 float tanh_g = tanhf(g);
418 float x2 = x * x;
419 float g_prime = sqrt_2_over_pi * (1.0f + 3.0f * coeff * x2);
420 float sech2_g = 1.0f - tanh_g * tanh_g;
421 float gelu_derivative = 0.5f * (1.0f + tanh_g) + 0.5f * x * sech2_g * g_prime;
422 d_input[i] = d_output[i] * gelu_derivative;
423 }
424
425#elif defined(__AVX2__)
426 const __m256 sqrt_2_pi_vec = _mm256_set1_ps(sqrt_2_over_pi);
427 const __m256 coeff_vec = _mm256_set1_ps(coeff);
428 const __m256 coeff3_vec = _mm256_set1_ps(3.0f * coeff);
429 const __m256 half_vec = _mm256_set1_ps(0.5f);
430 const __m256 one_vec = _mm256_set1_ps(1.0f);
431
432 size_t i = 0;
433 for (; i + 8 <= n; i += 8) {
434 __m256 x = _mm256_loadu_ps(&input[i]);
435 __m256 dy = _mm256_loadu_ps(&d_output[i]);
436
437 __m256 x2 = _mm256_mul_ps(x, x);
438 __m256 x3 = _mm256_mul_ps(x2, x);
439
440 // g = sqrt(2/pi) * (x + 0.044715 * x^3)
441 __m256 g = _mm256_fmadd_ps(coeff_vec, x3, x);
442 g = _mm256_mul_ps(sqrt_2_pi_vec, g);
443
444 __m256 tanh_g = tanh256_fast(g);
445
446 // g' = sqrt(2/pi) * (1 + 3 * 0.044715 * x^2)
447 __m256 g_prime = _mm256_fmadd_ps(coeff3_vec, x2, one_vec);
448 g_prime = _mm256_mul_ps(sqrt_2_pi_vec, g_prime);
449
450 // sech^2(g) = 1 - tanh^2(g)
451 __m256 sech2_g = _mm256_fnmadd_ps(tanh_g, tanh_g, one_vec);
452
453 // gelu_derivative = 0.5 * (1 + tanh_g) + 0.5 * x * sech2_g * g_prime
454 __m256 term1 = _mm256_mul_ps(half_vec, _mm256_add_ps(one_vec, tanh_g));
455 __m256 term2 = _mm256_mul_ps(half_vec, _mm256_mul_ps(x, _mm256_mul_ps(sech2_g, g_prime)));
456 __m256 gelu_deriv = _mm256_add_ps(term1, term2);
457
458 __m256 result = _mm256_mul_ps(dy, gelu_deriv);
459 _mm256_storeu_ps(&d_input[i], result);
460 }
461 // Handle remaining elements
462 for (; i < n; ++i) {
463 float x = input[i];
464 float x3 = x * x * x;
465 float g = sqrt_2_over_pi * (x + coeff * x3);
466 float tanh_g = tanhf(g);
467 float x2 = x * x;
468 float g_prime = sqrt_2_over_pi * (1.0f + 3.0f * coeff * x2);
469 float sech2_g = 1.0f - tanh_g * tanh_g;
470 float gelu_derivative = 0.5f * (1.0f + tanh_g) + 0.5f * x * sech2_g * g_prime;
471 d_input[i] = d_output[i] * gelu_derivative;
472 }
473
474#elif defined(__AVX__)
475 // AVX1: Vectorize arithmetic, use scalar tanh
476 const __m256 sqrt_2_pi_vec = _mm256_set1_ps(sqrt_2_over_pi);
477 const __m256 coeff_vec = _mm256_set1_ps(coeff);
478 const __m256 coeff3_vec = _mm256_set1_ps(3.0f * coeff);
479 const __m256 half_vec = _mm256_set1_ps(0.5f);
480 const __m256 one_vec = _mm256_set1_ps(1.0f);
481
482 size_t i = 0;
483 float g_arr[8] __attribute__((aligned(32)));
484 float tanh_arr[8] __attribute__((aligned(32)));
485
486 for (; i + 8 <= n; i += 8) {
487 __m256 x = _mm256_loadu_ps(&input[i]);
488 __m256 dy = _mm256_loadu_ps(&d_output[i]);
489
490 __m256 x2 = _mm256_mul_ps(x, x);
491 __m256 x3 = _mm256_mul_ps(x2, x);
492
493 // g = sqrt(2/pi) * (x + 0.044715 * x^3)
494 __m256 coeff_x3 = _mm256_mul_ps(coeff_vec, x3);
495 __m256 g = _mm256_mul_ps(sqrt_2_pi_vec, _mm256_add_ps(x, coeff_x3));
496
497 // Compute tanh scalarly
498 _mm256_store_ps(g_arr, g);
499 for (int j = 0; j < 8; ++j) {
500 tanh_arr[j] = tanhf(g_arr[j]);
501 }
502 __m256 tanh_g = _mm256_load_ps(tanh_arr);
503
504 // g' = sqrt(2/pi) * (1 + 3 * 0.044715 * x^2)
505 __m256 coeff3_x2 = _mm256_mul_ps(coeff3_vec, x2);
506 __m256 g_prime = _mm256_mul_ps(sqrt_2_pi_vec, _mm256_add_ps(one_vec, coeff3_x2));
507
508 // sech^2(g) = 1 - tanh^2(g)
509 __m256 tanh_g_sq = _mm256_mul_ps(tanh_g, tanh_g);
510 __m256 sech2_g = _mm256_sub_ps(one_vec, tanh_g_sq);
511
512 // gelu_derivative = 0.5 * (1 + tanh_g) + 0.5 * x * sech2_g * g_prime
513 __m256 term1 = _mm256_mul_ps(half_vec, _mm256_add_ps(one_vec, tanh_g));
514 __m256 term2 = _mm256_mul_ps(half_vec, _mm256_mul_ps(x, _mm256_mul_ps(sech2_g, g_prime)));
515 __m256 gelu_deriv = _mm256_add_ps(term1, term2);
516
517 __m256 result = _mm256_mul_ps(dy, gelu_deriv);
518 _mm256_storeu_ps(&d_input[i], result);
519 }
520 // Handle remaining elements
521 for (; i < n; ++i) {
522 float x = input[i];
523 float x3 = x * x * x;
524 float g = sqrt_2_over_pi * (x + coeff * x3);
525 float tanh_g = tanhf(g);
526 float x2 = x * x;
527 float g_prime = sqrt_2_over_pi * (1.0f + 3.0f * coeff * x2);
528 float sech2_g = 1.0f - tanh_g * tanh_g;
529 float gelu_derivative = 0.5f * (1.0f + tanh_g) + 0.5f * x * sech2_g * g_prime;
530 d_input[i] = d_output[i] * gelu_derivative;
531 }
532
533#else
534 // Scalar fallback
535 for (size_t i = 0; i < n; ++i) {
536 float x = input[i];
537
538 float x3 = x * x * x;
539 float g = sqrt_2_over_pi * (x + coeff * x3);
540 float tanh_g = tanhf(g);
541
542 float x2 = x * x;
543 float g_prime = sqrt_2_over_pi * (1.0f + 3.0f * coeff * x2);
544
545 float sech2_g = 1.0f - tanh_g * tanh_g;
546 float gelu_derivative =
547 0.5f * (1.0f + tanh_g) + 0.5f * x * sech2_g * g_prime;
548
549 d_input[i] = d_output[i] * gelu_derivative;
550 }
551#endif
552}
__attribute__((visibility("default"))) CKTokenizer *ck_tokenizer_create(CKTokenizerType type)

References __attribute__().

◆ gelu_backward_fast()

void gelu_backward_fast ( const float *  input,
const float *  d_output,
float *  d_input,
size_t  n 
)

Definition at line 818 of file gelu_kernels.c.

822{
823 const float beta = 1.702f;
824
825#if defined(__AVX512F__)
826 const __m512 beta_vec = _mm512_set1_ps(beta);
827 const __m512 one_vec = _mm512_set1_ps(1.0f);
828 const __m512 neg_beta_vec = _mm512_set1_ps(-beta);
829
830 size_t i = 0;
831 for (; i + 16 <= n; i += 16) {
832 __m512 x = _mm512_loadu_ps(&input[i]);
833 __m512 dy = _mm512_loadu_ps(&d_output[i]);
834
835 // s = sigmoid(beta * x) = 1 / (1 + exp(-beta * x))
836 __m512 neg_beta_x = _mm512_mul_ps(neg_beta_vec, x);
837 __m512 exp_neg = exp512_fast(neg_beta_x);
838 __m512 s = _mm512_div_ps(one_vec, _mm512_add_ps(one_vec, exp_neg));
839
840 // gelu_derivative = s * (1 + x * (1 - s) * beta)
841 __m512 one_minus_s = _mm512_sub_ps(one_vec, s);
842 __m512 inner = _mm512_fmadd_ps(_mm512_mul_ps(x, one_minus_s), beta_vec, one_vec);
843 __m512 gelu_deriv = _mm512_mul_ps(s, inner);
844
845 __m512 result = _mm512_mul_ps(dy, gelu_deriv);
846 _mm512_storeu_ps(&d_input[i], result);
847 }
848 // Handle remaining elements
849 for (; i < n; ++i) {
850 float x = input[i];
851 float s = 1.0f / (1.0f + expf(-beta * x));
852 float gelu_derivative = s * (1.0f + x * (1.0f - s) * beta);
853 d_input[i] = d_output[i] * gelu_derivative;
854 }
855
856#elif defined(__AVX2__)
857 const __m256 beta_vec = _mm256_set1_ps(beta);
858 const __m256 one_vec = _mm256_set1_ps(1.0f);
859 const __m256 neg_beta_vec = _mm256_set1_ps(-beta);
860
861 size_t i = 0;
862 for (; i + 8 <= n; i += 8) {
863 __m256 x = _mm256_loadu_ps(&input[i]);
864 __m256 dy = _mm256_loadu_ps(&d_output[i]);
865
866 // s = sigmoid(beta * x) = 1 / (1 + exp(-beta * x))
867 __m256 neg_beta_x = _mm256_mul_ps(neg_beta_vec, x);
868 __m256 exp_neg = exp256_fast(neg_beta_x);
869 __m256 s = _mm256_div_ps(one_vec, _mm256_add_ps(one_vec, exp_neg));
870
871 // gelu_derivative = s * (1 + x * (1 - s) * beta)
872 __m256 one_minus_s = _mm256_sub_ps(one_vec, s);
873 __m256 inner = _mm256_fmadd_ps(_mm256_mul_ps(x, one_minus_s), beta_vec, one_vec);
874 __m256 gelu_deriv = _mm256_mul_ps(s, inner);
875
876 __m256 result = _mm256_mul_ps(dy, gelu_deriv);
877 _mm256_storeu_ps(&d_input[i], result);
878 }
879 // Handle remaining elements
880 for (; i < n; ++i) {
881 float x = input[i];
882 float s = 1.0f / (1.0f + expf(-beta * x));
883 float gelu_derivative = s * (1.0f + x * (1.0f - s) * beta);
884 d_input[i] = d_output[i] * gelu_derivative;
885 }
886
887#elif defined(__AVX__)
888 // AVX1: Vectorize arithmetic, use scalar exp
889 const __m256 beta_vec = _mm256_set1_ps(beta);
890 const __m256 one_vec = _mm256_set1_ps(1.0f);
891 const __m256 neg_beta_vec = _mm256_set1_ps(-beta);
892
893 size_t i = 0;
894 float neg_beta_x_arr[8] __attribute__((aligned(32)));
895 float exp_arr[8] __attribute__((aligned(32)));
896
897 for (; i + 8 <= n; i += 8) {
898 __m256 x = _mm256_loadu_ps(&input[i]);
899 __m256 dy = _mm256_loadu_ps(&d_output[i]);
900
901 // s = sigmoid(beta * x) = 1 / (1 + exp(-beta * x))
902 __m256 neg_beta_x = _mm256_mul_ps(neg_beta_vec, x);
903
904 // Compute exp scalarly
905 _mm256_store_ps(neg_beta_x_arr, neg_beta_x);
906 for (int j = 0; j < 8; ++j) {
907 exp_arr[j] = expf(neg_beta_x_arr[j]);
908 }
909 __m256 exp_neg = _mm256_load_ps(exp_arr);
910
911 __m256 s = _mm256_div_ps(one_vec, _mm256_add_ps(one_vec, exp_neg));
912
913 // gelu_derivative = s * (1 + x * (1 - s) * beta)
914 __m256 one_minus_s = _mm256_sub_ps(one_vec, s);
915 __m256 x_one_minus_s = _mm256_mul_ps(x, one_minus_s);
916 __m256 x_one_minus_s_beta = _mm256_mul_ps(x_one_minus_s, beta_vec);
917 __m256 inner = _mm256_add_ps(one_vec, x_one_minus_s_beta);
918 __m256 gelu_deriv = _mm256_mul_ps(s, inner);
919
920 __m256 result = _mm256_mul_ps(dy, gelu_deriv);
921 _mm256_storeu_ps(&d_input[i], result);
922 }
923 // Handle remaining elements
924 for (; i < n; ++i) {
925 float x = input[i];
926 float s = 1.0f / (1.0f + expf(-beta * x));
927 float gelu_derivative = s * (1.0f + x * (1.0f - s) * beta);
928 d_input[i] = d_output[i] * gelu_derivative;
929 }
930
931#else
932 // Scalar fallback
933 for (size_t i = 0; i < n; ++i) {
934 float x = input[i];
935 float s = 1.0f / (1.0f + expf(-beta * x));
936 float gelu_derivative = s * (1.0f + x * (1.0f - s) * beta);
937 d_input[i] = d_output[i] * gelu_derivative;
938 }
939#endif
940}

References __attribute__().

Referenced by gelu_backward_fast_bf16().

◆ gelu_backward_scalar()

void gelu_backward_scalar ( const float *  input,
const float *  d_output,
float *  d_input,
size_t  n 
)

Definition at line 794 of file gelu_kernels.c.

798{
799 const float sqrt_2_over_pi = 0.7978845608f;
800 const float coeff = 0.044715f;
801
802 for (size_t i = 0; i < n; ++i) {
803 float x = input[i];
804 float x3 = x * x * x;
805 float g = sqrt_2_over_pi * (x + coeff * x3);
806 float tanh_g = tanhf(g);
807 float x2 = x * x;
808 float g_prime = sqrt_2_over_pi * (1.0f + 3.0f * coeff * x2);
809 float sech2_g = 1.0f - tanh_g * tanh_g;
810 float gelu_derivative = 0.5f * (1.0f + tanh_g) + 0.5f * x * sech2_g * g_prime;
811 d_input[i] = d_output[i] * gelu_derivative;
812 }
813}

Referenced by gelu_backward_exact_bf16().

◆ gelu_erf_bf16_storage()

void gelu_erf_bf16_storage ( float *  data,
size_t  n 
)

Definition at line 609 of file gelu_kernels.c.

610{
611 const double inv_sqrt_2 = 0.707106781186547524400844362104849039;
612 ck_gelu_math_f64_fn reference_erf = ck_gelu_system_erf();
613 for (size_t i = 0; i < n; ++i) {
614 const float x = bf16_to_float(float_to_bf16(data[i]));
615 const double scaled = (double)x * inv_sqrt_2;
616 const double erf_value = reference_erf ? reference_erf(scaled) : erf(scaled);
617 const float output = (float)(0.5 * (double)x * (1.0 + erf_value));
618 data[i] = bf16_to_float(float_to_bf16(output));
619 }
620}
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
static ck_gelu_math_f64_fn ck_gelu_system_erf(void)

References bf16_to_float(), ck_gelu_system_erf(), and float_to_bf16().

◆ gelu_erf_fp64_f32_inplace()

void gelu_erf_fp64_f32_inplace ( float *  data,
size_t  n 
)

Definition at line 566 of file gelu_kernels.c.

567{
568 const double inv_sqrt_2 = 0.707106781186547524400844362104849039;
569 ck_gelu_math_f64_fn reference_erf = ck_gelu_system_erf();
570 for (size_t i = 0; i < n; ++i) {
571 const float x = data[i];
572 const double scaled = (double)x * inv_sqrt_2;
573 const double erf_value = reference_erf ? reference_erf(scaled) : erf(scaled);
574 data[i] = (float)(0.5 * (double)x * (1.0 + erf_value));
575 }
576}

References ck_gelu_system_erf().

Referenced by gelu_pytorch_erf_f32_inplace().

◆ gelu_exact_inplace()

void gelu_exact_inplace ( float *  data,
size_t  n 
)

Definition at line 557 of file gelu_kernels.c.

558{
559 for (size_t i = 0; i < n; ++i) {
560 data[i] = ck_gelu_tanh_f32(data[i]);
561 }
562}
static float ck_gelu_tanh_f32(float x)

References ck_gelu_tanh_f32().

Referenced by gelu_fast_inplace_bf16(), and mlp_token_parallel_exact().

◆ gelu_fast_inplace()

void gelu_fast_inplace ( float *  data,
size_t  n 
)

GELU activation forward (fast approximation, in-place)

Test:

test_gelu.py::TestGELUForward::test_gelu_fast_inplace

test_gelu.py::TestGELUForward::test_gelu_vs_exact

test_parity.py::test_gelu_parity

Fast GELU approximation: 0.5 * x * (1 + tanh(sqrt(2/pi) * (x + 0.044715 * x^3))) In-place on contiguous buffer.

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

Definition at line 243 of file gelu_kernels.c.

244{
245 const float sqrt_2_over_pi = 0.7978845608f;
246 const float coeff = 0.044715f;
247
248#if defined(__AVX512F__)
249 const __m512 sqrt_2_pi_vec = _mm512_set1_ps(sqrt_2_over_pi);
250 const __m512 coeff_vec = _mm512_set1_ps(coeff);
251 const __m512 half_vec = _mm512_set1_ps(0.5f);
252 const __m512 one_vec = _mm512_set1_ps(1.0f);
253
254 size_t i = 0;
255 for (; i + 16 <= n; i += 16) {
256 __m512 x = _mm512_loadu_ps(&data[i]);
257 __m512 x2 = _mm512_mul_ps(x, x);
258 __m512 x3 = _mm512_mul_ps(x2, x);
259
260 // inner = sqrt(2/pi) * (x + 0.044715 * x^3)
261 __m512 inner = _mm512_fmadd_ps(coeff_vec, x3, x);
262 inner = _mm512_mul_ps(sqrt_2_pi_vec, inner);
263
264 // result = 0.5 * x * (1 + tanh(inner))
265 __m512 tanh_val = tanh512_fast(inner);
266 __m512 one_plus_tanh = _mm512_add_ps(one_vec, tanh_val);
267 __m512 result = _mm512_mul_ps(half_vec, _mm512_mul_ps(x, one_plus_tanh));
268
269 _mm512_storeu_ps(&data[i], result);
270 }
271 // Handle remaining elements
272 for (; i < n; ++i) {
273 float x = data[i];
274 float x3 = x * x * x;
275 float inner = sqrt_2_over_pi * (x + coeff * x3);
276 data[i] = 0.5f * x * (1.0f + tanhf(inner));
277 }
278
279#elif defined(__AVX2__)
280 const __m256 sqrt_2_pi_vec = _mm256_set1_ps(sqrt_2_over_pi);
281 const __m256 coeff_vec = _mm256_set1_ps(coeff);
282 const __m256 half_vec = _mm256_set1_ps(0.5f);
283 const __m256 one_vec = _mm256_set1_ps(1.0f);
284
285 size_t i = 0;
286 for (; i + 8 <= n; i += 8) {
287 __m256 x = _mm256_loadu_ps(&data[i]);
288 __m256 x2 = _mm256_mul_ps(x, x);
289 __m256 x3 = _mm256_mul_ps(x2, x);
290
291 // inner = sqrt(2/pi) * (x + 0.044715 * x^3)
292 __m256 inner = _mm256_fmadd_ps(coeff_vec, x3, x);
293 inner = _mm256_mul_ps(sqrt_2_pi_vec, inner);
294
295 // result = 0.5 * x * (1 + tanh(inner))
296 __m256 tanh_val = tanh256_fast(inner);
297 __m256 one_plus_tanh = _mm256_add_ps(one_vec, tanh_val);
298 __m256 result = _mm256_mul_ps(half_vec, _mm256_mul_ps(x, one_plus_tanh));
299
300 _mm256_storeu_ps(&data[i], result);
301 }
302 // Handle remaining elements
303 for (; i < n; ++i) {
304 float x = data[i];
305 float x3 = x * x * x;
306 float inner = sqrt_2_over_pi * (x + coeff * x3);
307 data[i] = 0.5f * x * (1.0f + tanhf(inner));
308 }
309
310#elif defined(__AVX__)
311 // AVX1: Vectorize arithmetic, use scalar tanh
312 const __m256 sqrt_2_pi_vec = _mm256_set1_ps(sqrt_2_over_pi);
313 const __m256 coeff_vec = _mm256_set1_ps(coeff);
314 const __m256 half_vec = _mm256_set1_ps(0.5f);
315 const __m256 one_vec = _mm256_set1_ps(1.0f);
316
317 size_t i = 0;
318 float inner_arr[8] __attribute__((aligned(32)));
319 float tanh_arr[8] __attribute__((aligned(32)));
320
321 for (; i + 8 <= n; i += 8) {
322 __m256 x = _mm256_loadu_ps(&data[i]);
323 __m256 x2 = _mm256_mul_ps(x, x);
324 __m256 x3 = _mm256_mul_ps(x2, x);
325
326 // inner = sqrt(2/pi) * (x + 0.044715 * x^3)
327 __m256 coeff_x3 = _mm256_mul_ps(coeff_vec, x3);
328 __m256 inner = _mm256_mul_ps(sqrt_2_pi_vec, _mm256_add_ps(x, coeff_x3));
329
330 // Compute tanh scalarly
331 _mm256_store_ps(inner_arr, inner);
332 for (int j = 0; j < 8; ++j) {
333 tanh_arr[j] = tanhf(inner_arr[j]);
334 }
335 __m256 tanh_val = _mm256_load_ps(tanh_arr);
336
337 // result = 0.5 * x * (1 + tanh(inner))
338 __m256 one_plus_tanh = _mm256_add_ps(one_vec, tanh_val);
339 __m256 result = _mm256_mul_ps(half_vec, _mm256_mul_ps(x, one_plus_tanh));
340
341 _mm256_storeu_ps(&data[i], result);
342 }
343 // Handle remaining elements
344 for (; i < n; ++i) {
345 float x = data[i];
346 float x3 = x * x * x;
347 float inner = sqrt_2_over_pi * (x + coeff * x3);
348 data[i] = 0.5f * x * (1.0f + tanhf(inner));
349 }
350
351#else
352 // Scalar fallback
353 for (size_t i = 0; i < n; ++i) {
354 float x = data[i];
355 float x3 = x * x * x;
356 float inner = sqrt_2_over_pi * (x + coeff * x3);
357 data[i] = 0.5f * x * (1.0f + tanhf(inner));
358 }
359#endif
360}

References __attribute__().

Referenced by geglu_forward_fp32(), and mlp_token_parallel().

◆ gelu_ggml_inplace()

void gelu_ggml_inplace ( float *  data,
size_t  n 
)

Definition at line 753 of file gelu_kernels.c.

754{
757 for (size_t i = 0; i < n; ++i) {
758 const float x = data[i];
759 if (x <= -10.0f) {
760 data[i] = 0.0f;
761 continue;
762 }
763 if (x >= 10.0f) {
764 data[i] = x;
765 continue;
766 }
767 const ck_half x_fp16 = ck_gelu_runtime_fp32_to_fp16(x);
768 const ck_half y_fp16 = ck_gelu_runtime_table_f16[(uint16_t) x_fp16];
769 data[i] = ck_gelu_runtime_fp16_to_fp32(y_fp16);
770 }
771 return;
772 }
773
775 for (size_t i = 0; i < n; ++i) {
776 const float x = data[i];
777 if (x <= -10.0f) {
778 data[i] = 0.0f;
779 continue;
780 }
781 if (x >= 10.0f) {
782 data[i] = x;
783 continue;
784 }
785 const ck_half x_fp16 = ggml_fp32_to_fp16(x);
786 const ck_half y_fp16 = ck_gelu_ggml_table_f16[(uint16_t) x_fp16];
787 data[i] = ggml_fp16_to_fp32(y_fp16);
788 }
789}
static void ck_gelu_ggml_runtime_init(void)
static pthread_once_t ck_gelu_ggml_table_once
static void ck_gelu_ggml_table_init(void)
static pthread_once_t ck_gelu_ggml_runtime_once

References ck_gelu_ggml_runtime_init(), ck_gelu_ggml_runtime_once, ck_gelu_ggml_table_f16, ck_gelu_ggml_table_init(), ck_gelu_ggml_table_once, ck_gelu_runtime_fp16_to_fp32, ck_gelu_runtime_fp32_to_fp16, ck_gelu_runtime_ready, ck_gelu_runtime_table_f16, ggml_fp16_to_fp32, and ggml_fp32_to_fp16.

◆ gelu_ggml_native_inplace()

void gelu_ggml_native_inplace ( float *  data,
size_t  n 
)

Definition at line 741 of file gelu_kernels.c.

742{
744 for (size_t i = 0; i < n; ++i) {
745 const float x = data[i];
746 if (x <= -10.0f) data[i] = 0.0f;
747 else if (x >= 10.0f) data[i] = x;
748 else data[i] = ggml_fp16_to_fp32(
750 }
751}

References ck_gelu_ggml_table_f16, ck_gelu_ggml_table_init(), ck_gelu_ggml_table_once, ggml_fp16_to_fp32, and ggml_fp32_to_fp16.

Referenced by geglu_forward_ggml_native().

◆ gelu_pytorch_erf_f32_inplace()

void gelu_pytorch_erf_f32_inplace ( float *  data,
size_t  n 
)

Definition at line 580 of file gelu_kernels.c.

581{
583}
void gelu_erf_fp64_f32_inplace(float *data, size_t n)

References gelu_erf_fp64_f32_inplace().

◆ gelu_pytorch_erf_sleef_bf16_storage()

void gelu_pytorch_erf_sleef_bf16_storage ( float *  data,
size_t  n 
)

Definition at line 682 of file gelu_kernels.c.

683{
684#if defined(__AVX512F__)
685 pthread_once(&ck_pytorch_sleef_once, ck_bind_pytorch_sleef);
686 if (!ck_pytorch_sleef_expf16) {
687 fprintf(stderr,
688 "[CK] PyTorch-exact BF16 GELU requires Sleef_expf16_u10; "
689 "set CK_SLEEF_LIBRARY to libtorch_cpu.so or libsleef.so\n");
690 abort();
691 }
692
693 const __m512 alpha = _mm512_set1_ps(0.70710678118654752440f);
694 const __m512 half = _mm512_set1_ps(0.5f);
695 const __m512 one = _mm512_set1_ps(1.0f);
696 const __m512 neg_zero = _mm512_set1_ps(-0.0f);
697 const __m512 p = _mm512_set1_ps(0.3275911f);
698 const __m512 p1 = _mm512_set1_ps(0.254829592f);
699 const __m512 p2 = _mm512_set1_ps(-0.284496736f);
700 const __m512 p3 = _mm512_set1_ps(1.421413741f);
701 const __m512 p4 = _mm512_set1_ps(-1.453152027f);
702 const __m512 p5 = _mm512_set1_ps(1.061405429f);
703 size_t i = 0;
704 for (; i + 16 <= n; i += 16) {
705 __m512 x = _mm512_loadu_ps(data + i);
706 __m512 erf_arg = _mm512_mul_ps(x, alpha);
707 __m512 sign = _mm512_and_ps(neg_zero, erf_arg);
708 __m512 abs_arg = _mm512_abs_ps(erf_arg);
709 __m512 t = _mm512_div_ps(one, _mm512_fmadd_ps(p, abs_arg, one));
710 __m512 r = _mm512_fmadd_ps(p5, t, p4);
711 r = _mm512_fmadd_ps(r, t, p3);
712 r = _mm512_fmadd_ps(r, t, p2);
713 r = _mm512_fmadd_ps(r, t, p1);
714 __m512 arg_sq = _mm512_mul_ps(erf_arg, erf_arg);
715 __m512 exp_neg_sq = ck_pytorch_sleef_expf16(_mm512_xor_ps(neg_zero, arg_sq));
716 __m512 neg_exp_t = _mm512_mul_ps(_mm512_xor_ps(neg_zero, exp_neg_sq), t);
717 __m512 erf_x = _mm512_xor_ps(sign, _mm512_fmadd_ps(neg_exp_t, r, one));
718 __m512 y = _mm512_mul_ps(_mm512_mul_ps(x, half), _mm512_add_ps(one, erf_x));
719 float lanes[16];
720 _mm512_storeu_ps(lanes, y);
721 for (size_t lane = 0; lane < 16; ++lane) {
722 const uint16_t input_code = float_to_bf16(data[i + lane]);
723 const uint16_t output_code = ck_pytorch_gelu_erf_bf16_edge(
724 input_code, float_to_bf16(lanes[lane]));
725 data[i + lane] = bf16_to_float(output_code);
726 }
727 }
728 for (; i < n; ++i) {
729 const float x = bf16_to_float(float_to_bf16(data[i]));
730 const float y = (x * 0.5f) * (1.0f + erff(x * 0.70710678118654752440f));
731 data[i] = bf16_to_float(float_to_bf16(y));
732 }
733#else
734 (void)data;
735 (void)n;
736 fprintf(stderr, "[CK] PyTorch-exact BF16 GELU requires an AVX-512 build\n");
737 abort();
738#endif
739}

References bf16_to_float(), and float_to_bf16().

◆ gelu_pytorch_tanh_bf16_storage()

void gelu_pytorch_tanh_bf16_storage ( float *  data,
size_t  n 
)

Definition at line 590 of file gelu_kernels.c.

591{
592 /* PyTorch's x86 BF16 kernel widens to FP32 and evaluates tanh through
593 * SLEEF's vector u10 provider. That provider saturates at this exact
594 * inner-argument boundary; libc tanhf retains a small tail and can round
595 * to a different BF16 code before the following projection. */
596 const float sleef_tanh_saturation = 8.664339742f;
597 for (size_t i = 0; i < n; ++i) {
598 const float x = bf16_to_float(float_to_bf16(data[i]));
599 const float x3 = x * x * x;
600 const float inner = 0.7978845608f * (x + 0.044715f * x3);
601 const float tanh_inner = fabsf(inner) > sleef_tanh_saturation
602 ? copysignf(1.0f, inner)
603 : tanhf(inner);
604 const float output = 0.5f * x * (1.0f + tanh_inner);
605 data[i] = bf16_to_float(float_to_bf16(output));
606 }
607}

References bf16_to_float(), and float_to_bf16().

Variable Documentation

◆ ck_gelu_ggml_runtime_once

pthread_once_t ck_gelu_ggml_runtime_once = PTHREAD_ONCE_INIT
static

Definition at line 46 of file gelu_kernels.c.

Referenced by gelu_ggml_inplace().

◆ ck_gelu_ggml_table_f16

ck_half ck_gelu_ggml_table_f16[1u<< 16]
static

◆ ck_gelu_ggml_table_once

pthread_once_t ck_gelu_ggml_table_once = PTHREAD_ONCE_INIT
static

Definition at line 45 of file gelu_kernels.c.

Referenced by gelu_ggml_inplace(), and gelu_ggml_native_inplace().

◆ ck_gelu_reference_erf

ck_gelu_math_f64_fn ck_gelu_reference_erf = NULL
static

Definition at line 60 of file gelu_kernels.c.

Referenced by ck_gelu_reference_math_init(), and ck_gelu_system_erf().

◆ ck_gelu_reference_math_once

pthread_once_t ck_gelu_reference_math_once = PTHREAD_ONCE_INIT
static

Definition at line 61 of file gelu_kernels.c.

Referenced by ck_gelu_system_erf(), and ck_gelu_system_tanhf().

◆ ck_gelu_reference_tanhf

ck_gelu_math_f32_fn ck_gelu_reference_tanhf = NULL
static

Definition at line 59 of file gelu_kernels.c.

Referenced by ck_gelu_reference_math_init(), and ck_gelu_system_tanhf().

◆ ck_gelu_runtime_fp16_to_fp32

ck_gelu_ggml_fp16_to_fp32_fn ck_gelu_runtime_fp16_to_fp32 = NULL
static

Definition at line 56 of file gelu_kernels.c.

Referenced by ck_gelu_try_bind_runtime(), and gelu_ggml_inplace().

◆ ck_gelu_runtime_fp32_to_fp16

ck_gelu_ggml_fp32_to_fp16_fn ck_gelu_runtime_fp32_to_fp16 = NULL
static

Definition at line 55 of file gelu_kernels.c.

Referenced by ck_gelu_try_bind_runtime(), and gelu_ggml_inplace().

◆ ck_gelu_runtime_handle

void* ck_gelu_runtime_handle = NULL
static

Definition at line 57 of file gelu_kernels.c.

Referenced by ck_gelu_ggml_runtime_init().

◆ ck_gelu_runtime_ready

int ck_gelu_runtime_ready = 0
static

◆ ck_gelu_runtime_table_f16

const ck_half* ck_gelu_runtime_table_f16 = NULL
static

Definition at line 54 of file gelu_kernels.c.

Referenced by ck_gelu_try_bind_runtime(), and gelu_ggml_inplace().