← Back to C-Kernel-Engine Docs Doxygen Source Documentation
 
Loading...
Searching...
No Matches
gemv_omp.c File Reference
#include "ckernel_quant.h"

Go to the source code of this file.

Functions

void gemv_fused_q5_0_bias_parallel_omp (float *y, const void *W, const float *x, const float *bias, int M, int K)
 
void gemv_q5_0_q8_0_parallel_omp (float *y, const void *W, const void *x_q8, int M, int K)
 
void gemv_q8_0_q8_0_parallel_omp (float *y, const void *W, const void *x_q8, int M, int K)
 
void quantize_row_q8_0 (const float *x, void *y, int k)
 Quantize FP32 to Q8_0 format (scalar reference)
 
void vec_dot_q5_0_q8_0 (int n, float *s, const void *vx, const void *vy)
 Auto-dispatch quantized dot product Q5_0 x Q8_0.
 
void vec_dot_q8_0_q8_0 (int n, float *s, const void *vx, const void *vy)
 Auto-dispatch quantized dot product Q8_0 x Q8_0.
 

Function Documentation

◆ gemv_fused_q5_0_bias_parallel_omp()

void gemv_fused_q5_0_bias_parallel_omp ( float *  y,
const void *  W,
const float *  x,
const float *  bias,
int  M,
int  K 
)

Definition at line 98 of file gemv_omp.c.

103{
104 const block_q5_0 *w_blocks = (const block_q5_0 *)W;
105 const int blocks_per_row = K / QK5_0;
106
107 /* Quantize input ONCE (serial, fast — K=896 → 28 blocks = 952 bytes) */
108 block_q8_0 x_q8[K / QK8_0];
109 quantize_row_q8_0(x, (void *)x_q8, K);
110
111 /* Parallel GEMV over output rows */
112 #pragma omp parallel for schedule(static)
113 for (int row = 0; row < M; row++) {
114 vec_dot_q5_0_q8_0(K, &y[row],
115 &w_blocks[row * blocks_per_row],
116 x_q8);
117 if (bias) y[row] += bias[row];
118 }
119}
#define QK5_0
#define QK8_0
void vec_dot_q5_0_q8_0(int n, float *s, const void *vx, const void *vy)
Auto-dispatch quantized dot product Q5_0 x Q8_0.
void quantize_row_q8_0(const float *x, void *y, int k)
Quantize FP32 to Q8_0 format (scalar reference)

References QK5_0, QK8_0, quantize_row_q8_0(), and vec_dot_q5_0_q8_0().

◆ gemv_q5_0_q8_0_parallel_omp()

void gemv_q5_0_q8_0_parallel_omp ( float *  y,
const void *  W,
const void *  x_q8,
int  M,
int  K 
)

Definition at line 74 of file gemv_omp.c.

78{
79 const block_q5_0 *w_blocks = (const block_q5_0 *)W;
80 const block_q8_0 *x_blocks = (const block_q8_0 *)x_q8;
81 const int blocks_per_row = K / QK5_0;
82
83 #pragma omp parallel for schedule(static)
84 for (int row = 0; row < M; row++) {
85 vec_dot_q5_0_q8_0(K, &y[row],
86 &w_blocks[row * blocks_per_row],
87 x_blocks);
88 }
89}

References QK5_0, and vec_dot_q5_0_q8_0().

◆ gemv_q8_0_q8_0_parallel_omp()

void gemv_q8_0_q8_0_parallel_omp ( float *  y,
const void *  W,
const void *  x_q8,
int  M,
int  K 
)

Definition at line 52 of file gemv_omp.c.

56{
57 const block_q8_0 *w_blocks = (const block_q8_0 *)W;
58 const block_q8_0 *x_blocks = (const block_q8_0 *)x_q8;
59 const int blocks_per_row = K / QK8_0;
60
61 #pragma omp parallel for schedule(static)
62 for (int row = 0; row < M; row++) {
63 vec_dot_q8_0_q8_0(K, &y[row],
64 &w_blocks[row * blocks_per_row],
65 x_blocks);
66 }
67}
void vec_dot_q8_0_q8_0(int n, float *s, const void *vx, const void *vy)
Auto-dispatch quantized dot product Q8_0 x Q8_0.

References QK8_0, and vec_dot_q8_0_q8_0().

◆ quantize_row_q8_0()

void quantize_row_q8_0 ( const float *  x,
void *  vy,
int  k 
)
extern

Quantize FP32 to Q8_0 format (scalar reference)

Parameters
xInput FP32 values
vyOutput Q8_0 blocks
kNumber of elements (must be multiple of 32)

Definition at line 125 of file gemm_kernels_q8_0.c.

126{
127 block_q8_0 *y = (block_q8_0 *)vy;
128 const int nb = k / QK8_0; /* QK8_0 = 32 */
129
130#if defined(__AVX__)
131 const __m256 sign_bit = _mm256_set1_ps(-0.0f);
132
133 for (int i = 0; i < nb; i++) {
134 __m256 v0 = _mm256_loadu_ps(x + 0);
135 __m256 v1 = _mm256_loadu_ps(x + 8);
136 __m256 v2 = _mm256_loadu_ps(x + 16);
137 __m256 v3 = _mm256_loadu_ps(x + 24);
138 x += QK8_0;
139
140 __m256 max_abs = _mm256_andnot_ps(sign_bit, v0);
141 max_abs = _mm256_max_ps(max_abs, _mm256_andnot_ps(sign_bit, v1));
142 max_abs = _mm256_max_ps(max_abs, _mm256_andnot_ps(sign_bit, v2));
143 max_abs = _mm256_max_ps(max_abs, _mm256_andnot_ps(sign_bit, v3));
144
145 __m128 max4 = _mm_max_ps(_mm256_extractf128_ps(max_abs, 1),
146 _mm256_castps256_ps128(max_abs));
147 max4 = _mm_max_ps(max4, _mm_movehl_ps(max4, max4));
148 max4 = _mm_max_ss(max4, _mm_movehdup_ps(max4));
149 const float max_scalar = _mm_cvtss_f32(max4);
150
151#if defined(__INTEL_LLVM_COMPILER)
152 const float d = ck_q8_0_div_rounded_f32(max_scalar, 127.0f);
153 const float id = max_scalar != 0.0f
154 ? ck_q8_0_div_rounded_f32(127.0f, max_scalar)
155 : 0.0f;
156#else
157 const float d = max_scalar / 127.0f;
158 const float id = max_scalar != 0.0f ? 127.0f / max_scalar : 0.0f;
159#endif
160 y[i].d = CK_FP32_TO_FP16(d);
161
162 const __m256 mul = _mm256_set1_ps(id);
163 v0 = _mm256_mul_ps(v0, mul);
164 v1 = _mm256_mul_ps(v1, mul);
165 v2 = _mm256_mul_ps(v2, mul);
166 v3 = _mm256_mul_ps(v3, mul);
167
168 /* Match llama.cpp x86 Q8 quantization: nearest-even rounding. */
169 v0 = _mm256_round_ps(v0, _MM_ROUND_NEAREST);
170 v1 = _mm256_round_ps(v1, _MM_ROUND_NEAREST);
171 v2 = _mm256_round_ps(v2, _MM_ROUND_NEAREST);
172 v3 = _mm256_round_ps(v3, _MM_ROUND_NEAREST);
173
174 __m256i i0 = _mm256_cvtps_epi32(v0);
175 __m256i i1 = _mm256_cvtps_epi32(v1);
176 __m256i i2 = _mm256_cvtps_epi32(v2);
177 __m256i i3 = _mm256_cvtps_epi32(v3);
178
179#if defined(__AVX2__)
180 i0 = _mm256_packs_epi32(i0, i1);
181 i2 = _mm256_packs_epi32(i2, i3);
182 i0 = _mm256_packs_epi16(i0, i2);
183
184 const __m256i perm = _mm256_setr_epi32(0, 4, 1, 5, 2, 6, 3, 7);
185 i0 = _mm256_permutevar8x32_epi32(i0, perm);
186 _mm256_storeu_si256((__m256i *)y[i].qs, i0);
187#else
188 __m128i ni0 = _mm256_castsi256_si128(i0);
189 __m128i ni1 = _mm256_extractf128_si256(i0, 1);
190 __m128i ni2 = _mm256_castsi256_si128(i1);
191 __m128i ni3 = _mm256_extractf128_si256(i1, 1);
192 __m128i ni4 = _mm256_castsi256_si128(i2);
193 __m128i ni5 = _mm256_extractf128_si256(i2, 1);
194 __m128i ni6 = _mm256_castsi256_si128(i3);
195 __m128i ni7 = _mm256_extractf128_si256(i3, 1);
196
197 ni0 = _mm_packs_epi32(ni0, ni1);
198 ni2 = _mm_packs_epi32(ni2, ni3);
199 ni4 = _mm_packs_epi32(ni4, ni5);
200 ni6 = _mm_packs_epi32(ni6, ni7);
201
202 ni0 = _mm_packs_epi16(ni0, ni2);
203 ni4 = _mm_packs_epi16(ni4, ni6);
204
205 _mm_storeu_si128((__m128i *)(y[i].qs + 0), ni0);
206 _mm_storeu_si128((__m128i *)(y[i].qs + 16), ni4);
207#endif
208 }
209#else
210 for (int i = 0; i < nb; i++) {
211 const float *xb = x + i * QK8_0;
212
213 /* Find max absolute value in block */
214 float amax = 0.0f;
215 for (int j = 0; j < QK8_0; j++) {
216 float av = xb[j] >= 0 ? xb[j] : -xb[j];
217 if (av > amax) amax = av;
218 }
219
220 /* Compute scale: d = max / 127 */
221 float d = amax / 127.0f;
222 float id = d != 0.0f ? 127.0f / amax : 0.0f;
223
224 /* Store scale as FP16 */
225 y[i].d = CK_FP32_TO_FP16(d);
226
227 /* Quantize values */
228 for (int j = 0; j < QK8_0; j++) {
229 float v = xb[j] * id;
230 int q = ck_nearest_int_q8_0(v);
231 if (q > 127) q = 127;
232 if (q < -127) q = -127;
233 y[i].qs[j] = (int8_t)q;
234 }
235 }
236#endif
237}
#define CK_FP32_TO_FP16(x)
static int ck_nearest_int_q8_0(float fval)
int8_t qs[32]
int32_t id
Definition tokenizer.h:316

Referenced by gemv_fused_q5_0_bias_parallel_omp(), and quantize_batch_q8_0().

◆ vec_dot_q5_0_q8_0()

void vec_dot_q5_0_q8_0 ( int  n,
float *  s,
const void *  vx,
const void *  vy 
)
extern

Auto-dispatch quantized dot product Q5_0 x Q8_0.

Dispatch priority:

  1. AVX512 (best performance on modern Intel/AMD)
  2. AVX (256-bit float ops, works on Sandy/Ivy Bridge and newer)
  3. SSSE3 (128-bit fallback)
  4. Reference scalar (last resort)

Definition at line 1602 of file gemm_kernels_q5_0.c.

1603{
1604#if defined(__AVX2__)
1605 /* llama.cpp uses the packed AVX2 dot on AVX-512 hosts as well. It keeps
1606 * Q5/Q8 data in byte lanes and avoids the per-block 32-bit lane expansion
1607 * overhead of the baseline AVX-512 path. */
1608 vec_dot_q5_0_q8_0_avx2(n, s, vx, vy);
1609#elif defined(__AVX512F__)
1610 vec_dot_q5_0_q8_0_avx512(n, s, vx, vy);
1611#elif defined(__ARM_NEON) || defined(__aarch64__)
1612 vec_dot_q5_0_q8_0_neon(n, s, vx, vy);
1613#elif defined(__AVX__)
1614 /* AVX for 256-bit float ops (works on Ivy Bridge and newer) */
1615 vec_dot_q5_0_q8_0_avx(n, s, vx, vy);
1616#elif defined(__SSSE3__)
1617 /* SSSE3 - most efficient on older CPUs */
1618 vec_dot_q5_0_q8_0_sse(n, s, vx, vy);
1619#else
1620 vec_dot_q5_0_q8_0_ref(n, s, vx, vy);
1621#endif
1622}
void vec_dot_q5_0_q8_0_ref(int n, float *s, const void *vx, const void *vy)
Quantized dot product: Q5_0 weights x Q8_0 input (scalar reference)

Referenced by gemm_nt_q5_0_q8_0(), gemm_nt_q5_0_q8_0_m2n4_tile(), gemm_nt_q5_0_q8_0_m4n2_tile(), gemv_fused_q5_0_bias_parallel_omp(), gemv_q5_0_q8_0(), gemv_q5_0_q8_0_parallel_omp(), and gemv_q5_0_q8_0_parallel_simd().

◆ vec_dot_q8_0_q8_0()

void vec_dot_q8_0_q8_0 ( int  n,
float *  s,
const void *  vx,
const void *  vy 
)
extern

Auto-dispatch quantized dot product Q8_0 x Q8_0.

Definition at line 1368 of file gemm_kernels_q8_0.c.

1369{
1370 if (ck_q8_0_q8_0_debug_ref()) {
1371 vec_dot_q8_0_q8_0_ref(n, s, vx, vy);
1372 return;
1373 }
1374#ifdef __AVX512F__
1375 vec_dot_q8_0_q8_0_avx512(n, s, vx, vy);
1376#elif defined(__AVX2__)
1377 vec_dot_q8_0_q8_0_avx2(n, s, vx, vy);
1378#elif defined(__ARM_NEON) || defined(__aarch64__)
1379 vec_dot_q8_0_q8_0_neon(n, s, vx, vy);
1380#elif defined(__AVX__)
1381 vec_dot_q8_0_q8_0_avx(n, s, vx, vy);
1382#elif defined(__SSE4_1__)
1383 vec_dot_q8_0_q8_0_sse(n, s, vx, vy);
1384#else
1385 vec_dot_q8_0_q8_0_ref(n, s, vx, vy);
1386#endif
1387}
static int ck_q8_0_q8_0_debug_ref(void)
void vec_dot_q8_0_q8_0_ref(int n, float *s, const void *vx, const void *vy)
Quantized dot product: Q8_0 weights x Q8_0 input (scalar reference)

Referenced by gemv_q8_0_q8_0(), gemv_q8_0_q8_0_parallel(), gemv_q8_0_q8_0_parallel_omp(), and gemv_q8_0_q8_0_parallel_simd().