← 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);
321
322static inline int ck_nearest_int_q8_0_ref(float fval)
323{
324 float val = fval + 12582912.f;
325 int i;
326 memcpy(&i, &val, sizeof(int));
327 return (i & 0x007fffff) - 0x00400000;
328}
329
330static void quantize_row_q8_0_ref_local(const float *x,
331 block_q8_0 *y,
332 int k)
333{
334 const int nb = k / QK8_0;
335
336 for (int i = 0; i < nb; ++i) {
337 float amax = 0.0f;
338 for (int j = 0; j < QK8_0; ++j) {
339 const float v = x[i * QK8_0 + j];
340 const float av = fabsf(v);
341 if (av > amax) {
342 amax = av;
343 }
344 }
345
346 const float d = amax / 127.0f;
347 const float id = d != 0.0f ? 1.0f / d : 0.0f;
348 y[i].d = CK_FP32_TO_FP16(d);
349
350 for (int j = 0; j < QK8_0; ++j) {
351 const float x0 = x[i * QK8_0 + j] * id;
352 int q = ck_nearest_int_q8_0_ref(x0);
353 if (q > 127) {
354 q = 127;
355 }
356 if (q < -127) {
357 q = -127;
358 }
359 y[i].qs[j] = (int8_t) q;
360 }
361 }
362}
363
364static void gemv_q8_0_q8_0_ref_rows(float *y,
365 const void *W,
366 const void *x_q8,
367 int M,
368 int K)
369{
370 const block_q8_0 *w_blocks = (const block_q8_0 *)W;
371 const int blocks_per_row = K / QK8_0;
372
373 for (int row = 0; row < M; ++row) {
375 K,
376 &y[row],
377 &w_blocks[row * blocks_per_row],
378 x_q8
379 );
380 }
381}
382
384 const void *W,
385 const float *x,
386 int M,
387 int K)
388{
389 if (!y || !W || !x || M <= 0 || K <= 0) {
390 return;
391 }
392
393 if ((K % QK8_0) != 0) {
394 gemv_q8_0(y, W, x, M, K);
395 return;
396 }
397
398 const int blocks_per_row = K / QK8_0;
399 if (blocks_per_row > CK_Q80_STACK_Q8_BLOCKS) {
400 gemv_q8_0(y, W, x, M, K);
401 return;
402 }
403
406 quantize_row_q8_0_ref_local(x, x_q8, K);
407 gemv_q8_0_q8_0_ref_rows(y, W, x_q8, M, K);
408 return;
409 }
410 quantize_row_q8_0(x, x_q8, K);
411 gemv_q8_0_q8_0_x4(y, W, x_q8, M, K);
412}
413
414void gemm_nt_q8_0_q8_0_contract(const float *A,
415 const void *B,
416 const float *bias,
417 float *C,
418 int M,
419 int N,
420 int K)
421{
422 if (!A || !B || !C || M <= 0 || N <= 0 || K <= 0) {
423 return;
424 }
425
426 const float *A_use = A;
427 const int strict = ck_strict_parity_enabled();
428 const int dump_enabled = strict && ck_q80_contract_dump_enabled();
429 int strict_cached_layer = -1;
430 int strict_dump_layer = -1;
432 const float *cached = ck_strict_consume_next_gemm_a((size_t) M * (size_t) K);
433 if (cached) {
434 A_use = cached;
435 strict_cached_layer = ck_q80_contract_cached_gemm_seq++;
436 }
437 }
438 if (dump_enabled) {
439 strict_dump_layer = strict_cached_layer >= 0
440 ? strict_cached_layer
442 }
443
444 if (dump_enabled && strict_dump_layer >= 0) {
445 ck_q80_contract_dump_tensor(strict_cached_layer >= 0
446 ? "strict_out_proj_input_cached"
447 : "strict_out_proj_input_live",
448 strict_dump_layer,
449 A_use,
450 (size_t) M * (size_t) K);
451 }
452
453 if (strict &&
454 gemm_nt_q8_0_q8_0_ggml_strict(A_use, B, bias, C, M, N, K)) {
455 if (dump_enabled && strict_dump_layer >= 0) {
456 ck_q80_contract_dump_tensor("strict_out_proj_output_ggml",
457 strict_dump_layer,
458 C,
459 (size_t) M * (size_t) N);
460 }
461 return;
462 }
463
464 if (!strict) {
465#pragma omp parallel for schedule(static) if(M > 1)
466 for (int m = 0; m < M; ++m) {
467 gemv_q8_0_q8_0_contract(&C[m * N], B, &A_use[m * K], N, K);
468 if (bias) {
469 for (int n = 0; n < N; ++n) {
470 C[m * N + n] += bias[n];
471 }
472 }
473 }
474 return;
475 }
476
477 for (int m = 0; m < M; ++m) {
478 gemv_q8_0_q8_0_contract(&C[m * N], B, &A_use[m * K], N, K);
479 if (bias) {
480 for (int n = 0; n < N; ++n) {
481 C[m * N + n] += bias[n];
482 }
483 }
484 }
485
486 if (dump_enabled && strict_dump_layer >= 0) {
487 ck_q80_contract_dump_tensor("strict_out_proj_output_fallback",
488 strict_dump_layer,
489 C,
490 (size_t) M * (size_t) N);
491 }
492}
#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 *)
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
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)