← Back to C-Kernel-Engine Docs Doxygen Source Documentation
 
Loading...
Searching...
No Matches
gemm_kernels_q8_0_q8_0_contract.c
Go to the documentation of this file.
1/**
2 * @file gemm_kernels_q8_0_q8_0_contract.c
3 * @brief FP32 API adapters that enforce Q8_0 x Q8_0 activation contract
4 */
5
6#ifndef _GNU_SOURCE
7#define _GNU_SOURCE
8#endif
9
10#include "ckernel_engine.h"
11#include "ckernel_quant.h"
12#include "ggml_runtime_compat.h"
13
14#include <dlfcn.h>
15#include <stdint.h>
16#include <math.h>
17#include <stdio.h>
18#include <stdlib.h>
19#include <string.h>
20
21#define CK_Q80_STACK_Q8_BLOCKS 1024
22
23typedef struct {
24 char magic[8];
25 uint32_t version;
26 int32_t layer_id;
27 char op_name[32];
28 uint32_t dtype;
29 uint32_t rank;
30 int64_t shape[4];
31 uint32_t elem_count;
32 int32_t token_id;
33 uint8_t reserved[32];
34} __attribute__((packed)) ck_q80_contract_dump_header_t;
35
36static const char ck_q80_contract_magic[8] = {'C', 'K', 'D', 'M', 'P', '\0', '\0', '\0'};
37static const uint32_t ck_q80_contract_version = 1u;
39
41{
42 const char *v = getenv("CK_STRICT_GEMM_DUMP");
43 return v && v[0] && strcmp(v, "0") != 0;
44}
45
47{
48 const char *v = getenv("CK_STRICT_GEMM_USE_CACHED_A");
49 return v && v[0] && strcmp(v, "0") != 0;
50}
51
52static void ck_q80_contract_dump_tensor(const char *name,
53 int layer_id,
54 const float *data,
55 size_t elem_count)
56{
57 const char *dir = getenv("CK_PARITY_DIR");
58 if (!dir || !dir[0] || !data || elem_count == 0 || !name || !name[0]) {
59 return;
60 }
61
62 char path[4096];
63 snprintf(path, sizeof(path), "%s/%s", dir, "strict_internal.bin");
64 FILE *f = fopen(path, "ab");
65 if (!f) {
66 return;
67 }
68
69 ck_q80_contract_dump_header_t h;
70 memset(&h, 0, sizeof(h));
71 memcpy(h.magic, ck_q80_contract_magic, sizeof(ck_q80_contract_magic));
72 h.version = ck_q80_contract_version;
73 h.layer_id = layer_id;
74 strncpy(h.op_name, name, sizeof(h.op_name) - 1);
75 h.dtype = 0u;
76 h.rank = 1u;
77 h.shape[0] = (int64_t) elem_count;
78 h.elem_count = (uint32_t) elem_count;
79 h.token_id = 0;
80
81 fwrite(&h, sizeof(h), 1, f);
82 fwrite(data, sizeof(float), elem_count, f);
83 fclose(f);
84}
85
86typedef struct ggml_context *(*ck_q80_ggml_init_fn)(struct ggml_init_params);
87typedef void (*ck_q80_ggml_free_fn)(struct ggml_context *);
88typedef struct ggml_tensor *(*ck_q80_ggml_new_tensor_2d_fn)(struct ggml_context *, enum ggml_type, int64_t, int64_t);
89typedef struct ggml_tensor *(*ck_q80_ggml_mul_mat_fn)(struct ggml_context *, struct ggml_tensor *, struct ggml_tensor *);
90typedef struct ggml_cgraph *(*ck_q80_ggml_new_graph_fn)(struct ggml_context *);
91typedef void (*ck_q80_ggml_build_forward_expand_fn)(struct ggml_cgraph *, struct ggml_tensor *);
92typedef enum ggml_status (*ck_q80_ggml_graph_compute_with_ctx_fn)(struct ggml_context *, struct ggml_cgraph *, int);
93typedef void (*ck_q80_ggml_cpu_init_fn)(void);
94typedef void *(*ck_q80_ggml_get_data_fn)(const struct ggml_tensor *);
95typedef float *(*ck_q80_ggml_get_data_f32_fn)(const struct ggml_tensor *);
96typedef size_t (*ck_q80_ggml_nbytes_fn)(const struct ggml_tensor *);
97
99{
100 static int tried = 0;
101 static ck_q80_ggml_init_fn fn = NULL;
102 if (!tried) {
103 tried = 1;
104 fn = (ck_q80_ggml_init_fn) dlsym(RTLD_DEFAULT, "ggml_init");
105 }
106 return fn;
107}
108
110{
111 static int tried = 0;
112 static ck_q80_ggml_free_fn fn = NULL;
113 if (!tried) {
114 tried = 1;
115 fn = (ck_q80_ggml_free_fn) dlsym(RTLD_DEFAULT, "ggml_free");
116 }
117 return fn;
118}
119
121{
122 static int tried = 0;
123 static ck_q80_ggml_new_tensor_2d_fn fn = NULL;
124 if (!tried) {
125 tried = 1;
126 fn = (ck_q80_ggml_new_tensor_2d_fn) dlsym(RTLD_DEFAULT, "ggml_new_tensor_2d");
127 }
128 return fn;
129}
130
132{
133 static int tried = 0;
134 static ck_q80_ggml_mul_mat_fn fn = NULL;
135 if (!tried) {
136 tried = 1;
137 fn = (ck_q80_ggml_mul_mat_fn) dlsym(RTLD_DEFAULT, "ggml_mul_mat");
138 }
139 return fn;
140}
141
143{
144 static int tried = 0;
145 static ck_q80_ggml_new_graph_fn fn = NULL;
146 if (!tried) {
147 tried = 1;
148 fn = (ck_q80_ggml_new_graph_fn) dlsym(RTLD_DEFAULT, "ggml_new_graph");
149 }
150 return fn;
151}
152
154{
155 static int tried = 0;
157 if (!tried) {
158 tried = 1;
159 fn = (ck_q80_ggml_build_forward_expand_fn) dlsym(RTLD_DEFAULT, "ggml_build_forward_expand");
160 }
161 return fn;
162}
163
165{
166 static int tried = 0;
168 if (!tried) {
169 tried = 1;
170 fn = (ck_q80_ggml_graph_compute_with_ctx_fn) dlsym(RTLD_DEFAULT, "ggml_graph_compute_with_ctx");
171 }
172 return fn;
173}
174
176{
177 static int tried = 0;
178 static ck_q80_ggml_cpu_init_fn fn = NULL;
179 if (!tried) {
180 tried = 1;
181 fn = (ck_q80_ggml_cpu_init_fn) dlsym(RTLD_DEFAULT, "ggml_cpu_init");
182 }
183 return fn;
184}
185
187{
188 static int tried = 0;
189 static ck_q80_ggml_get_data_fn fn = NULL;
190 if (!tried) {
191 tried = 1;
192 fn = (ck_q80_ggml_get_data_fn) dlsym(RTLD_DEFAULT, "ggml_get_data");
193 }
194 return fn;
195}
196
198{
199 static int tried = 0;
200 static ck_q80_ggml_get_data_f32_fn fn = NULL;
201 if (!tried) {
202 tried = 1;
203 fn = (ck_q80_ggml_get_data_f32_fn) dlsym(RTLD_DEFAULT, "ggml_get_data_f32");
204 }
205 return fn;
206}
207
209{
210 static int tried = 0;
211 static ck_q80_ggml_nbytes_fn fn = NULL;
212 if (!tried) {
213 tried = 1;
214 fn = (ck_q80_ggml_nbytes_fn) dlsym(RTLD_DEFAULT, "ggml_nbytes");
215 }
216 return fn;
217}
218
219static int gemm_nt_q8_0_q8_0_ggml_strict(const float *A,
220 const void *B,
221 const float *bias,
222 float *C,
223 int M,
224 int N,
225 int K)
226{
238
239 if (!ggml_cpu_init_fn || !ggml_init_fn || !ggml_free_fn || !ggml_new_tensor_2d_fn ||
240 !ggml_mul_mat_fn || !ggml_new_graph_fn || !ggml_build_forward_expand_fn ||
241 !ggml_graph_compute_with_ctx_fn || !ggml_get_data_fn || !ggml_get_data_f32_fn ||
242 !ggml_nbytes_fn) {
243 return 0;
244 }
245
246 ggml_cpu_init_fn();
247
248 const size_t output_bytes = (size_t) M * (size_t) N * sizeof(float);
249 const size_t mem_size = ((size_t) 128 * 1024 * 1024) + output_bytes;
250
251 struct ggml_init_params params = {
253 .mem_buffer = NULL,
254 .no_alloc = false,
255 };
256 struct ggml_context *ctx = ggml_init_fn(params);
257 if (!ctx) {
258 return 0;
259 }
260
261 int ok = 0;
262 struct ggml_tensor *w = ggml_new_tensor_2d_fn(ctx, GGML_TYPE_Q8_0, K, N);
263 struct ggml_tensor *x = ggml_new_tensor_2d_fn(ctx, GGML_TYPE_F32, K, M);
264 if (!w || !x) {
265 ggml_free_fn(ctx);
266 return 0;
267 }
268
269 void *w_data = ggml_get_data_fn(w);
270 void *x_data = ggml_get_data_fn(x);
271 const size_t w_nbytes = ggml_nbytes_fn(w);
272 if (!w_data || !x_data || w_nbytes == 0) {
273 ggml_free_fn(ctx);
274 return 0;
275 }
276
277 memcpy(w_data, B, w_nbytes);
278 memcpy(x_data, A, (size_t) M * (size_t) K * sizeof(float));
279
280 struct ggml_tensor *y = ggml_mul_mat_fn(ctx, w, x);
281 if (!y) {
282 ggml_free_fn(ctx);
283 return 0;
284 }
285
286 struct ggml_cgraph *gf = ggml_new_graph_fn(ctx);
287 if (!gf) {
288 ggml_free_fn(ctx);
289 return 0;
290 }
291 ggml_build_forward_expand_fn(gf, y);
292 if (ggml_graph_compute_with_ctx_fn(ctx, gf, 1) != GGML_STATUS_SUCCESS) {
293 ggml_free_fn(ctx);
294 return 0;
295 }
296
297 {
298 const float *src = ggml_get_data_f32_fn(y);
299 if (!src) {
300 ggml_free_fn(ctx);
301 return 0;
302 }
303 for (int m = 0; m < M; ++m) {
304 memcpy(C + (size_t) m * (size_t) N,
305 src + (size_t) m * (size_t) N,
306 (size_t) N * sizeof(float));
307 if (bias) {
308 for (int n = 0; n < N; ++n) {
309 C[(size_t) m * (size_t) N + (size_t) n] += bias[n];
310 }
311 }
312 }
313 }
314
315 ok = 1;
316 ggml_free_fn(ctx);
317 return ok;
318}
319
320void vec_dot_q8_0_q8_0_ref(int n, float *s, const void *vx, const void *vy);
321void gemm_q8_0_q8_0_m2n4_strided(float *C, int ldc, const void *W,
322 const void *A_q8, int M, int N, int K);
323
324static inline int ck_nearest_int_q8_0_ref(float fval)
325{
326 float val = fval + 12582912.f;
327 int i;
328 memcpy(&i, &val, sizeof(int));
329 return (i & 0x007fffff) - 0x00400000;
330}
331
332static void quantize_row_q8_0_ref_local(const float *x,
333 block_q8_0 *y,
334 int k)
335{
336 const int nb = k / QK8_0;
337
338 for (int i = 0; i < nb; ++i) {
339 float amax = 0.0f;
340 for (int j = 0; j < QK8_0; ++j) {
341 const float v = x[i * QK8_0 + j];
342 const float av = fabsf(v);
343 if (av > amax) {
344 amax = av;
345 }
346 }
347
348 const float d = amax / 127.0f;
349 const float id = d != 0.0f ? 1.0f / d : 0.0f;
350 y[i].d = CK_FP32_TO_FP16(d);
351
352 for (int j = 0; j < QK8_0; ++j) {
353 const float x0 = x[i * QK8_0 + j] * id;
354 int q = ck_nearest_int_q8_0_ref(x0);
355 if (q > 127) {
356 q = 127;
357 }
358 if (q < -127) {
359 q = -127;
360 }
361 y[i].qs[j] = (int8_t) q;
362 }
363 }
364}
365
366static void gemv_q8_0_q8_0_ref_rows(float *y,
367 const void *W,
368 const void *x_q8,
369 int M,
370 int K)
371{
372 const block_q8_0 *w_blocks = (const block_q8_0 *)W;
373 const int blocks_per_row = K / QK8_0;
374
375 for (int row = 0; row < M; ++row) {
377 K,
378 &y[row],
379 &w_blocks[row * blocks_per_row],
380 x_q8
381 );
382 }
383}
384
386 const void *W,
387 const float *x,
388 int M,
389 int K)
390{
391 if (!y || !W || !x || M <= 0 || K <= 0) {
392 return;
393 }
394
395 if ((K % QK8_0) != 0) {
396 gemv_q8_0(y, W, x, M, K);
397 return;
398 }
399
400 const int blocks_per_row = K / QK8_0;
401 if (blocks_per_row > CK_Q80_STACK_Q8_BLOCKS) {
402 gemv_q8_0(y, W, x, M, K);
403 return;
404 }
405
408 quantize_row_q8_0_ref_local(x, x_q8, K);
409 gemv_q8_0_q8_0_ref_rows(y, W, x_q8, M, K);
410 return;
411 }
412 quantize_row_q8_0(x, x_q8, K);
413 gemv_q8_0_q8_0_x4(y, W, x_q8, M, K);
414}
415
416/* Two decode rows share each Q8_0 weight block. Keep each row's integer
417 * accumulation and block-order FP32 reduction independent. */
418void gemm_nt_q8_0_q8_0_contract_m2(const float *A, const void *B,
419 const float *bias, float *C,
420 int M, int N, int K)
421{
422 if (!A || !B || !C || M != 2 || N <= 0 || K <= 0 ||
423 (K % QK8_0) != 0 || K / QK8_0 > CK_Q80_STACK_Q8_BLOCKS) return;
424 const int nb = K / QK8_0;
428 quantize_row_q8_0_ref_local(A + K, x + nb, K);
429 } else {
430 quantize_row_q8_0(A, x, K);
431 quantize_row_q8_0(A + K, x + nb, K);
432 }
434 gemv_q8_0_q8_0_ref_rows(C, B, x, N, K);
435 gemv_q8_0_q8_0_ref_rows(C + N, B, x + nb, N, K);
436 } else {
437 gemm_q8_0_q8_0_m2n4_strided(C, N, B, x, 2, N, K);
438 }
439 if (bias) {
440 for (int n = 0; n < N; ++n) {
441 C[n] += bias[n];
442 C[(size_t)N + n] += bias[n];
443 }
444 }
445}
446
447void gemm_nt_q8_0_q8_0_contract(const float *A,
448 const void *B,
449 const float *bias,
450 float *C,
451 int M,
452 int N,
453 int K)
454{
455 if (!A || !B || !C || M <= 0 || N <= 0 || K <= 0) {
456 return;
457 }
458
459 const float *A_use = A;
460 const int strict = ck_strict_parity_enabled();
461 const int dump_enabled = strict && ck_q80_contract_dump_enabled();
462 int strict_cached_layer = -1;
463 int strict_dump_layer = -1;
465 const float *cached = ck_strict_consume_next_gemm_a((size_t) M * (size_t) K);
466 if (cached) {
467 A_use = cached;
468 strict_cached_layer = ck_q80_contract_cached_gemm_seq++;
469 }
470 }
471 if (dump_enabled) {
472 strict_dump_layer = strict_cached_layer >= 0
473 ? strict_cached_layer
475 }
476
477 if (dump_enabled && strict_dump_layer >= 0) {
478 ck_q80_contract_dump_tensor(strict_cached_layer >= 0
479 ? "strict_out_proj_input_cached"
480 : "strict_out_proj_input_live",
481 strict_dump_layer,
482 A_use,
483 (size_t) M * (size_t) K);
484 }
485
486 if (strict &&
487 gemm_nt_q8_0_q8_0_ggml_strict(A_use, B, bias, C, M, N, K)) {
488 if (dump_enabled && strict_dump_layer >= 0) {
489 ck_q80_contract_dump_tensor("strict_out_proj_output_ggml",
490 strict_dump_layer,
491 C,
492 (size_t) M * (size_t) N);
493 }
494 return;
495 }
496
497 if (!strict) {
498#pragma omp parallel for schedule(static) if(M > 1)
499 for (int m = 0; m < M; ++m) {
500 gemv_q8_0_q8_0_contract(&C[m * N], B, &A_use[m * K], N, K);
501 if (bias) {
502 for (int n = 0; n < N; ++n) {
503 C[m * N + n] += bias[n];
504 }
505 }
506 }
507 return;
508 }
509
510 for (int m = 0; m < M; ++m) {
511 gemv_q8_0_q8_0_contract(&C[m * N], B, &A_use[m * K], N, K);
512 if (bias) {
513 for (int n = 0; n < N; ++n) {
514 C[m * N + n] += bias[n];
515 }
516 }
517 }
518
519 if (dump_enabled && strict_dump_layer >= 0) {
520 ck_q80_contract_dump_tensor("strict_out_proj_output_fallback",
521 strict_dump_layer,
522 C,
523 (size_t) M * (size_t) N);
524 }
525}
#define RTLD_DEFAULT
static const char * op_name(CKOpType op)
void gemv_q8_0(float *y, const void *W, const float *x, int M, int K)
Auto-dispatch GEMV for Q8_0 weights based on CPU features.
const float * ck_strict_consume_next_gemm_a(size_t elems)
void gemv_q8_0_q8_0_x4(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)
int ck_strict_parity_enabled(void)
Quantization block structures for weight-only quantization.
#define CK_FP32_TO_FP16(x)
#define QK8_0
static ck_q80_ggml_new_tensor_2d_fn ck_q80_resolve_ggml_new_tensor_2d(void)
void(* ck_q80_ggml_free_fn)(struct ggml_context *)
void *(* ck_q80_ggml_get_data_fn)(const struct ggml_tensor *)
void gemm_nt_q8_0_q8_0_contract_m2(const float *A, const void *B, const float *bias, float *C, int M, int N, int K)
static void quantize_row_q8_0_ref_local(const float *x, block_q8_0 *y, int k)
static int gemm_nt_q8_0_q8_0_ggml_strict(const float *A, const void *B, const float *bias, float *C, int M, int N, int K)
static ck_q80_ggml_nbytes_fn ck_q80_resolve_ggml_nbytes(void)
static ck_q80_ggml_free_fn ck_q80_resolve_ggml_free(void)
static ck_q80_ggml_get_data_f32_fn ck_q80_resolve_ggml_get_data_f32(void)
void gemv_q8_0_q8_0_contract(float *y, const void *W, const float *x, int M, int K)
static ck_q80_ggml_cpu_init_fn ck_q80_resolve_ggml_cpu_init(void)
struct ggml_cgraph *(* ck_q80_ggml_new_graph_fn)(struct ggml_context *)
static ck_q80_ggml_graph_compute_with_ctx_fn ck_q80_resolve_ggml_graph_compute_with_ctx(void)
struct ggml_tensor *(* ck_q80_ggml_new_tensor_2d_fn)(struct ggml_context *, enum ggml_type, int64_t, int64_t)
static int ck_q80_contract_cached_input_enabled(void)
static void ck_q80_contract_dump_tensor(const char *name, int layer_id, const float *data, size_t elem_count)
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)
void(* ck_q80_ggml_build_forward_expand_fn)(struct ggml_cgraph *, struct ggml_tensor *)
static ck_q80_ggml_init_fn ck_q80_resolve_ggml_init(void)
static ck_q80_ggml_new_graph_fn ck_q80_resolve_ggml_new_graph(void)
static const char ck_q80_contract_magic[8]
struct ggml_tensor *(* ck_q80_ggml_mul_mat_fn)(struct ggml_context *, struct ggml_tensor *, struct ggml_tensor *)
void(* ck_q80_ggml_cpu_init_fn)(void)
enum ggml_status(* ck_q80_ggml_graph_compute_with_ctx_fn)(struct ggml_context *, struct ggml_cgraph *, int)
static const uint32_t ck_q80_contract_version
struct ggml_context *(* ck_q80_ggml_init_fn)(struct ggml_init_params)
size_t(* ck_q80_ggml_nbytes_fn)(const struct ggml_tensor *)
static int ck_q80_contract_cached_gemm_seq
float *(* ck_q80_ggml_get_data_f32_fn)(const struct ggml_tensor *)
static int ck_nearest_int_q8_0_ref(float fval)
static ck_q80_ggml_build_forward_expand_fn ck_q80_resolve_ggml_build_forward_expand(void)
static void gemv_q8_0_q8_0_ref_rows(float *y, const void *W, const void *x_q8, int M, int K)
#define CK_Q80_STACK_Q8_BLOCKS
void gemm_q8_0_q8_0_m2n4_strided(float *C, int ldc, const void *W, const void *A_q8, int M, int N, int K)
static ck_q80_ggml_get_data_fn ck_q80_resolve_ggml_get_data(void)
static int ck_q80_contract_dump_enabled(void)
static ck_q80_ggml_mul_mat_fn ck_q80_resolve_ggml_mul_mat(void)
void gemm_nt_q8_0_q8_0_contract(const float *A, const void *B, const float *bias, float *C, int M, int N, int K)
@ GGML_STATUS_SUCCESS
@ GGML_TYPE_F32
@ GGML_TYPE_Q8_0
#define C(color)
Definition show_config.c:39
int8_t qs[32]
int32_t id
Definition tokenizer.h:316
__attribute__((visibility("default"))) CKTokenizer *ck_tokenizer_create(CKTokenizerType type)