20#if defined(__x86_64__) || defined(__i386__)
23#if defined(__ARM_NEON) || defined(__aarch64__)
55 float val = fval + 12582912.f;
57 memcpy(&i, &val,
sizeof(
int));
58 return (i & 0x007fffff) - 0x00400000;
62#pragma clang fp contract(off)
63#elif defined(__GNUC__)
67 if (!x || !vy || k <= 0) {
70 assert(k %
QK_K == 0);
71 const int nb = k /
QK_K;
74 for (
int i = 0; i < nb; ++i) {
77 for (
int j = 0; j <
QK_K; ++j) {
78 float ax = fabsf(x[j]);
86 memset(y[i].qs, 0,
sizeof(y[i].qs));
87 memset(y[i].bsums, 0,
sizeof(y[i].bsums));
92 const float iscale = -127.0f / max;
93 for (
int j = 0; j <
QK_K; ++j) {
96 float scaled = iscale * x[j];
104 y[i].
qs[j] = (int8_t)v;
107 for (
int j = 0; j <
QK_K / 16; ++j) {
109 const int8_t *qs = &y[i].
qs[j * 16];
110 for (
int ii = 0; ii < 16; ++ii) {
113 y[i].
bsums[j] = (int16_t)sum;
116 y[i].
d = 1.0f / iscale;
121#if defined(__ARM_NEON) || defined(__aarch64__)
128#if defined(__clang__)
129#pragma clang fp contract(off)
130#elif defined(__GNUC__)
133void quantize_row_q8_k_neon(
const float *x,
void *vy,
int k) {
134 if (!x || !vy || k <= 0) {
137 assert(k %
QK_K == 0);
138 const int nb = k /
QK_K;
141 for (
int i = 0; i < nb; ++i) {
142 float32x4_t vmax = vdupq_n_f32(0.0f);
143 for (
int j = 0; j <
QK_K; j += 16) {
144 const float32x4_t a0 = vabsq_f32(vld1q_f32(x + j + 0));
145 const float32x4_t a1 = vabsq_f32(vld1q_f32(x + j + 4));
146 const float32x4_t a2 = vabsq_f32(vld1q_f32(x + j + 8));
147 const float32x4_t a3 = vabsq_f32(vld1q_f32(x + j + 12));
148 vmax = vmaxq_f32(vmax, vmaxq_f32(vmaxq_f32(a0, a1),
152 vst1q_f32(lanes, vmax);
153 const float amax = fmaxf(fmaxf(lanes[0], lanes[1]),
154 fmaxf(lanes[2], lanes[3]));
157 memset(y[i].qs, 0,
sizeof(y[i].qs));
158 memset(y[i].bsums, 0,
sizeof(y[i].bsums));
165 for (
int j = 0; j <
QK_K; ++j) {
166 if (fabsf(x[j]) == amax) {
172 const float iscale = -127.0f / max;
173 const float32x4_t viscale = vdupq_n_f32(iscale);
174 const float32x4_t vmagic = vdupq_n_f32(12582912.0f);
175 const int32x4_t mmant = vdupq_n_s32(0x007fffff);
176 const int32x4_t mbias = vdupq_n_s32(0x00400000);
177 const int32x4_t vlo = vdupq_n_s32(-128);
178 const int32x4_t vhi = vdupq_n_s32(127);
180 for (
int j = 0; j <
QK_K; j += 16) {
183 int32x4_t v0 = vreinterpretq_s32_f32(vaddq_f32(
184 vmulq_f32(viscale, vld1q_f32(x + j + 0)), vmagic));
185 int32x4_t v1 = vreinterpretq_s32_f32(vaddq_f32(
186 vmulq_f32(viscale, vld1q_f32(x + j + 4)), vmagic));
187 int32x4_t v2 = vreinterpretq_s32_f32(vaddq_f32(
188 vmulq_f32(viscale, vld1q_f32(x + j + 8)), vmagic));
189 int32x4_t v3 = vreinterpretq_s32_f32(vaddq_f32(
190 vmulq_f32(viscale, vld1q_f32(x + j + 12)), vmagic));
191 v0 = vsubq_s32(vandq_s32(v0, mmant), mbias);
192 v1 = vsubq_s32(vandq_s32(v1, mmant), mbias);
193 v2 = vsubq_s32(vandq_s32(v2, mmant), mbias);
194 v3 = vsubq_s32(vandq_s32(v3, mmant), mbias);
195 v0 = vminq_s32(vmaxq_s32(v0, vlo), vhi);
196 v1 = vminq_s32(vmaxq_s32(v1, vlo), vhi);
197 v2 = vminq_s32(vmaxq_s32(v2, vlo), vhi);
198 v3 = vminq_s32(vmaxq_s32(v3, vlo), vhi);
199 const int16x8_t p0 = vcombine_s16(vmovn_s32(v0), vmovn_s32(v1));
200 const int16x8_t p1 = vcombine_s16(vmovn_s32(v2), vmovn_s32(v3));
201 vst1q_s8(&y[i].qs[j], vcombine_s8(vmovn_s16(p0), vmovn_s16(p1)));
204 for (
int j = 0; j <
QK_K / 16; ++j) {
205 const int8x16_t q = vld1q_s8(&y[i].qs[j * 16]);
206 const int32x4_t s0 = vpaddlq_s16(vmovl_s8(vget_low_s8(q)));
207 const int32x4_t s1 = vpaddlq_s16(vmovl_s8(vget_high_s8(q)));
208 const int32x4_t s = vaddq_s32(s0, s1);
211 y[i].
bsums[j] = (int16_t)(sl[0] + sl[1] + sl[2] + sl[3]);
214 y[i].
d = 1.0f / iscale;
225#if defined(__ARM_NEON) || defined(__aarch64__)
226void quantize_row_q8_k_neon(
const float *x,
void *vy,
int k);
230 const char *ref_env = getenv(
"CK_DEBUG_Q8K_REF");
231 if (ref_env && atoi(ref_env) != 0) {
235#if defined(__AVX512F__) && defined(__AVX512BW__)
237#elif defined(__AVX2__)
239#elif defined(__AVX__)
241#elif defined(__SSE4_1__)
243#elif defined(__ARM_NEON) || defined(__aarch64__)
244 quantize_row_q8_k_neon(x, vy, k);
251 const char *ref_env = getenv(
"CK_DEBUG_Q8K_REF");
252 if (ref_env && atoi(ref_env) != 0) {
253 return "q8_k_quantize_ref";
255#if defined(__AVX512F__) && defined(__AVX512BW__)
256 return "q8_k_quantize_avx512";
257#elif defined(__AVX2__)
258 return "q8_k_quantize_avx2";
259#elif defined(__AVX__)
260 return "q8_k_quantize_avx";
261#elif defined(__SSE4_1__)
262 return "q8_k_quantize_sse";
263#elif defined(__ARM_NEON) || defined(__aarch64__)
264 return "q8_k_quantize_neon";
266 return "q8_k_quantize_ref";
271 int num_rows,
int k) {
272 if (!x || !vy || num_rows <= 0 || k <= 0) {
275 assert(k %
QK_K == 0);
278 const int blocks_per_row = k /
QK_K;
285 for (
int row = 0; row < num_rows; ++row) {
287 x + (
size_t)row * (
size_t)k,
288 y + (
size_t)row * (
size_t)blocks_per_row,
297 const int nb = k /
QK_K;
300 for (
int i = 0; i < nb; ++i) {
301 uint8_t sc[8], m_val[8];
308 for (
int j = 0; j <
QK_K / 16; ++j) {
309 sumi += (int)x[i].bsums[j] * (
int)m_val[j / 2];
312 int32_t scaled_sum = 0;
313 for (
int group = 0; group < 4; ++group) {
314 const uint8_t *qs = &w[i].
qs[group * 32];
315 const int8_t *q8_lo = &x[i].
qs[group * 64];
316 const int8_t *q8_hi = q8_lo + 32;
319 for (
int l = 0; l < 32; ++l) {
320 lo += (int32_t)(qs[l] & 0x0F) * (int32_t)q8_lo[l];
321 hi += (int32_t)(qs[l] >> 4) * (int32_t)q8_hi[l];
323 scaled_sum += (int32_t)sc[2 * group] * lo;
324 scaled_sum += (int32_t)sc[2 * group + 1] * hi;
326 sumf += d * (float)scaled_sum - dmin * (
float)sumi;
331#if defined(__ARM_FEATURE_DOTPROD)
332static int32_t reduce_s32x4(int32x4_t v) {
335 return lanes[0] + lanes[1] + lanes[2] + lanes[3];
347static float dot_q4_k_q8_k_neon(
const block_q4_K *w,
351 const int nb = k /
QK_K;
353 const uint8x16_t m4 = vdupq_n_u8(0x0F);
355 for (
int i = 0; i < nb; ++i) {
356 uint8_t sc[8], m_val[8];
364 for (
int j = 0; j < 16; ++j) {
365 mv[j] = (int16_t)m_val[j / 2];
367 const int16x8_t b0 = vld1q_s16(x[i].bsums);
368 const int16x8_t b1 = vld1q_s16(x[i].bsums + 8);
369 const int16x8_t m0 = vld1q_s16(mv);
370 const int16x8_t m1 = vld1q_s16(mv + 8);
371 int32x4_t sumi_v = vmull_s16(vget_low_s16(b0), vget_low_s16(m0));
372 sumi_v = vmlal_s16(sumi_v, vget_high_s16(b0), vget_high_s16(m0));
373 sumi_v = vmlal_s16(sumi_v, vget_low_s16(b1), vget_low_s16(m1));
374 sumi_v = vmlal_s16(sumi_v, vget_high_s16(b1), vget_high_s16(m1));
375 const int32_t sumi = reduce_s32x4(sumi_v);
379 int32x4_t scaled_v = vdupq_n_s32(0);
380 for (
int group = 0; group < 4; ++group) {
381 const uint8_t *qs = &w[i].
qs[group * 32];
382 const int8_t *q8_lo = &x[i].
qs[group * 64];
383 const int8_t *q8_hi = q8_lo + 32;
385 const uint8x16_t q0 = vld1q_u8(qs);
386 const uint8x16_t q1 = vld1q_u8(qs + 16);
388 const int8x16_t lo0 = vreinterpretq_s8_u8(vandq_u8(q0, m4));
389 const int8x16_t lo1 = vreinterpretq_s8_u8(vandq_u8(q1, m4));
390 const int8x16_t hi0 = vreinterpretq_s8_u8(vshrq_n_u8(q0, 4));
391 const int8x16_t hi1 = vreinterpretq_s8_u8(vshrq_n_u8(q1, 4));
393 int32x4_t lo = vdotq_s32(vdupq_n_s32(0), lo0, vld1q_s8(q8_lo));
394 lo = vdotq_s32(lo, lo1, vld1q_s8(q8_lo + 16));
395 int32x4_t hi = vdotq_s32(vdupq_n_s32(0), hi0, vld1q_s8(q8_hi));
396 hi = vdotq_s32(hi, hi1, vld1q_s8(q8_hi + 16));
398 scaled_v = vmlaq_s32(scaled_v, lo,
399 vdupq_n_s32((int32_t)sc[2 * group]));
400 scaled_v = vmlaq_s32(scaled_v, hi,
401 vdupq_n_s32((int32_t)sc[2 * group + 1]));
403 const int32_t scaled_sum = reduce_s32x4(scaled_v);
404 sumf += d * (float)scaled_sum - dmin * (
float)sumi;
409static void gemv_q4_k_q8_k_neon(
float *y,
416 const int blocks_per_row = K /
QK_K;
418 for (
int row = 0; row < M; ++row) {
419 const block_q4_K *w_row = blocks + (size_t)row * (
size_t)blocks_per_row;
420 y[row] = dot_q4_k_q8_k_neon(w_row, x, K);
430 if (!y || !W || !x_q8 || M <= 0 || K <= 0) {
436 const int blocks_per_row = K /
QK_K;
438 for (
int row = 0; row < M; ++row) {
439 const block_q4_K *w_row = blocks + (size_t)row * (
size_t)blocks_per_row;
446 static int cached = -1;
448 const char *env = getenv(
"CK_DEBUG_Q4K_Q8_REF");
449 cached = (env && env[0] && env[0] !=
'0') ? 1 : 0;
470 if (!y || !W || !x_q8 || M <= 0 || K <= 0) {
473 if (ith < 0 || nth <= 0 || ith >= nth) {
478 const int dr = (M + nth - 1) / nth;
479 const int r0 = dr * ith;
480 const int r1 = (r0 + dr < M) ? (r0 + dr) : M;
488 const int blocks_per_row = K /
QK_K;
491 for (
int row = r0; row < r1; ++row) {
492 const block_q4_K *w_row = blocks + (size_t)row * (
size_t)blocks_per_row;
506#if defined(__AVX512VNNI__) && defined(__AVX512VL__) && !defined(CK_NO_AVX512_VNNI)
509#elif defined(__AVX2__)
511#elif defined(__AVX__)
514#elif defined(__SSE4_1__)
516#elif defined(__ARM_FEATURE_DOTPROD)
522 gemv_q4_k_q8_k_neon(y, W, x_q8, M, K);
531 return "q4_k_q8_k_ref";
533#if defined(__AVX512VNNI__) && defined(__AVX512VL__) && !defined(CK_NO_AVX512_VNNI)
534 return "q4_k_q8_k_vnni";
535#elif defined(__AVX2__)
536 return "q4_k_q8_k_avx2";
537#elif defined(__AVX__)
538 return "q4_k_q8_k_avx";
539#elif defined(__SSE4_1__)
540 return "q4_k_q8_k_sse";
541#elif defined(__ARM_FEATURE_DOTPROD)
542 return "q4_k_q8_k_neon";
544 return "q4_k_q8_k_ref";
553 if (!Y || !W || !X_q8 || M <= 0 || N <= 0 || K <= 0) {
558 const int blocks_per_vec = K /
QK_K;
560 for (
int n = 0; n < N; ++n) {
561 const block_q8_K *x_row = X + (size_t)n * (
size_t)blocks_per_vec;
575} gemm_q4_k_q8_k_work_t;
579 gemm_q4_k_q8_k_work_t *a = (gemm_q4_k_q8_k_work_t *)args;
580 if (!a || ith < 0 || nth <= 0 || ith >= nth) {
584 const int dr = (a->M_out + nth - 1) / nth;
585 const int r0 = dr * ith;
586 const int r1 = (r0 + dr < a->M_out) ? (r0 + dr) : a->M_out;
587 if (r0 >= a->M_out) {
592 const block_q4_K *w_start = blocks + (size_t)r0 * (
size_t)a->blocks_per_row;
593 const int rows = r1 - r0;
595 for (
int n = 0; n < a->N_batch; ++n) {
596 const block_q8_K *x_row = a->X + (size_t)n * (
size_t)a->blocks_per_vec;
610 if (!Y || !W || !X_q8 || M <= 0 || N <= 0 || K <= 0) {
615 const int blocks_per_vec = K /
QK_K;
616 const int blocks_per_row = K /
QK_K;
617 const size_t work_items = (size_t)M * (
size_t)N;
619 if (work_items >= 4096u && M >= 512 && N > 1) {
622 int active_threads = pool_threads;
623 if (active_threads > M) {
626 if (active_threads > 1) {
627 gemm_q4_k_q8_k_work_t work = {
634 .blocks_per_vec = blocks_per_vec,
635 .blocks_per_row = blocks_per_row,
642 for (
int n = 0; n < N; ++n) {
643 const block_q8_K *x_row = X + (size_t)n * (
size_t)blocks_per_vec;
654 if (!A_q8 || !B || !
C) {
657 if (M <= 0 || N <= 0 || K <= 0) {
667 for (
int i = 0; i < M; ++i) {
668 float *row =
C + (size_t)i * (
size_t)N;
669 for (
int j = 0; j < N; ++j) {
Persistent pthread thread pool for CK-Engine inference.
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)
Quantization block structures for weight-only quantization.
#define CK_FP16_TO_FP32(x)
static void unpack_q4_k_scales(const uint8_t *scales, uint8_t *sc, uint8_t *m)
Unpack Q4_K sub-block scales and mins.
void gemv_q4_k_q8_k_avx2(float *y, const void *W, const void *x_q8, int M, int K)
void quantize_row_q8_k_avx512(const float *x, void *vy, int k)
void quantize_row_q8_k_avx2(const float *x, void *vy, int k)
void gemv_q4_k_q8_k_vnni(float *y, const void *W, const void *x_q8, int M, int K)
void quantize_row_q8_k(const float *x, void *vy, int k)
void gemm_nt_q4_k_q8_k(const void *A_q8, const void *B, const float *bias, float *C, int M, int N, int K)
void gemv_q4_k_q8_k_parallel(float *y, const void *W, const void *x_q8, int M, int K, int ith, int nth)
void quantize_batch_q8_k_4row_nearest_even(const float *x, void *vy, int num_rows, int k)
void gemm_q4_k_q8_k_ref(float *Y, const void *W, const void *X_q8, int M, int N, int K)
static int ck_nearest_int(float fval)
void quantize_row_q8_k_avx(const float *x, void *vy, int k)
void gemv_q4_k_q8_k(float *y, const void *W, const void *x_q8, int M, int K)
void gemv_q4_k_q8_k_ref(float *y, const void *W, const void *x_q8, int M, int K)
void gemm_q4_k_q8_k(float *Y, const void *W, const void *X_q8, int M, int N, int K)
const char * ck_q4_k_q8_k_provider_name(void)
static int ck_q4k_q8k_force_ref(void)
const char * ck_quantize_row_q8_k_provider_name(void)
void quantize_row_q8_k_sse(const float *x, void *vy, int k)
static float dot_q4_k_q8_k_ref(const block_q4_K *w, const block_q8_K *x, int k)
static void gemm_q4_k_q8_k_thread_fn(int ith, int nth, void *args)
void quantize_row_q8_k_ref(const float *x, void *vy, int k)
void gemv_q4_k_q8_k_avx(float *y, const void *W, const void *x_q8, int M, int K)
void gemv_q4_k_q8_k_sse(float *y, const void *W, const void *x_q8, int M, int K)
__attribute__((visibility("default"))) CKTokenizer *ck_tokenizer_create(CKTokenizerType type)