← Back to C-Kernel-Engine Docs Doxygen Source Documentation
 
Loading...
Searching...
No Matches
dequant_kernels.c
Go to the documentation of this file.
1/**
2 * @file dequant_kernels.c
3 * @brief Dequantization kernels for GGML-compatible formats
4 *
5 * CK-ENGINE KERNEL RULES:
6 * =======================
7 * 1. NO malloc/free - memory via bump allocator, pointers passed in
8 * 2. NO OpenMP - parallelization at orchestrator/codegen layer
9 * 3. API must define: inputs, outputs, workspace, and memory layouts
10 * 4. Pure computation - deterministic, no side effects
11 *
12 * After changes: make test && make llamacpp-parity-full
13 *
14 * Implements dequantization from Q4_0, Q5_0, Q5_1, Q4_K, Q6_K, Q8_0 to FP32.
15 * These kernels are used as building blocks for quantized GEMM/GEMV.
16 *
17 * Key optimization: Dequantize into registers, use immediately in FMA,
18 * never write intermediate FP32 values to memory.
19 */
20
21#include <stdint.h>
22#include <stddef.h>
23#include <string.h>
24#if defined(__AVX__) || defined(__AVX2__) || defined(__AVX512F__)
25#include <immintrin.h>
26#endif
27#include "ckernel_quant.h"
28
29/* ============================================================================
30 * Q4_0 Dequantization
31 * - 32 weights per block, 1 FP16 scale
32 * - Weights stored as signed 4-bit (-8 to +7)
33 * ============================================================================ */
34
35/**
36 * @brief Dequantize a single Q4_0 block to FP32
37 * @param block Pointer to Q4_0 block (18 bytes)
38 * @param output Output FP32 array (32 floats)
39 */
40void dequant_q4_0_block(const block_q4_0 *block, float *output)
41{
42 const float d = GGML_FP16_TO_FP32(block->d);
43
44 for (int i = 0; i < QK4_0 / 2; i++) {
45 const uint8_t packed = block->qs[i];
46
47 /* Lower nibble: elements 0..15 */
48 const int8_t q0 = (packed & 0x0F) - 8;
49 /* Upper nibble: elements 16..31 */
50 const int8_t q1 = (packed >> 4) - 8;
51
52 output[i] = d * (float)q0;
53 output[i + QK4_0 / 2] = d * (float)q1;
54 }
55}
56
57/**
58 * @brief Dequantize Q4_0 row (multiple blocks)
59 * @param src Q4_0 data
60 * @param dst FP32 output
61 * @param n_elements Number of elements to dequantize
62 */
63void dequant_q4_0_row(const void *src, float *dst, size_t n_elements)
64{
65 const block_q4_0 *blocks = (const block_q4_0 *)src;
66 const size_t n_blocks = n_elements / QK4_0;
67
68 for (size_t b = 0; b < n_blocks; b++) {
69 dequant_q4_0_block(&blocks[b], &dst[b * QK4_0]);
70 }
71}
72
73#ifdef __AVX512F__
74/**
75 * @brief Dequantize Q4_0 block using AVX-512 (16 floats at a time)
76 * @param block Pointer to Q4_0 block
77 * @param out_lo Lower 16 floats (weights 0-15)
78 * @param out_hi Upper 16 floats (weights 16-31)
79 */
80void dequant_q4_0_block_avx512(const block_q4_0 *block,
81 __m512 *out_lo, __m512 *out_hi)
82{
83 const __m512 scale = _mm512_set1_ps(GGML_FP16_TO_FP32(block->d));
84 const __m512i offset = _mm512_set1_epi32(8);
85
86 /* Load 16 bytes = 32 x 4-bit weights */
87 __m128i packed = _mm_loadu_si128((const __m128i *)block->qs);
88
89 /* Unpack lower nibbles (weights 0, 2, 4, ...) */
90 __m512i lo_nibbles = _mm512_cvtepu8_epi32(packed);
91 lo_nibbles = _mm512_and_epi32(lo_nibbles, _mm512_set1_epi32(0x0F));
92 lo_nibbles = _mm512_sub_epi32(lo_nibbles, offset);
93
94 /* Unpack upper nibbles (weights 1, 3, 5, ...) */
95 __m512i hi_nibbles = _mm512_cvtepu8_epi32(packed);
96 hi_nibbles = _mm512_srli_epi32(hi_nibbles, 4);
97 hi_nibbles = _mm512_sub_epi32(hi_nibbles, offset);
98
99 /* Convert to float and scale */
100 *out_lo = _mm512_mul_ps(_mm512_cvtepi32_ps(lo_nibbles), scale);
101 *out_hi = _mm512_mul_ps(_mm512_cvtepi32_ps(hi_nibbles), scale);
102
103 /* Note: This gives interleaved output (0,2,4... and 1,3,5...)
104 * For proper sequential order, would need shuffle/blend */
105}
106#endif /* __AVX512F__ */
107
108/* ============================================================================
109 * Q4_1 Dequantization
110 * - 32 weights per block, 1 FP16 scale + 1 FP16 min
111 * - Weights stored as unsigned 4-bit (0 to 15)
112 * ============================================================================ */
113
114/**
115 * @brief Dequantize a single Q4_1 block to FP32
116 * @param block Pointer to Q4_1 block (20 bytes)
117 * @param output Output FP32 array (32 floats)
118 */
119void dequant_q4_1_block(const block_q4_1 *block, float *output)
120{
121 const float d = GGML_FP16_TO_FP32(block->d);
122 const float m = GGML_FP16_TO_FP32(block->m);
123
124 for (int i = 0; i < QK4_1 / 2; i++) {
125 const uint8_t packed = block->qs[i];
126
127 /* Lower nibble: unsigned 0-15 */
128 const int q0 = (packed & 0x0F);
129 /* Upper nibble: unsigned 0-15 */
130 const int q1 = (packed >> 4);
131
132 /* Dequantize: w = d * q + m */
133 output[i] = d * (float)q0 + m;
134 output[i + QK4_1 / 2] = d * (float)q1 + m;
135 }
136}
137
138/**
139 * @brief Dequantize Q4_1 row (multiple blocks)
140 */
141void dequant_q4_1_row(const void *src, float *dst, size_t n_elements)
142{
143 const block_q4_1 *blocks = (const block_q4_1 *)src;
144 const size_t n_blocks = n_elements / QK4_1;
145
146 for (size_t b = 0; b < n_blocks; b++) {
147 dequant_q4_1_block(&blocks[b], &dst[b * QK4_1]);
148 }
149}
150
151/* ============================================================================
152 * Q5_0 Dequantization
153 * - 32 weights per block, 1 FP16 scale
154 * - Low 4 bits + 1 high bit packed separately
155 * - Weights are 5-bit signed (-16 to +15)
156 * ============================================================================ */
157
158/**
159 * @brief Dequantize a single Q5_0 block to FP32
160 * @param block Pointer to Q5_0 block (22 bytes)
161 * @param output Output FP32 array (32 floats)
162 */
163void dequant_q5_0_block(const block_q5_0 *block, float *output)
164{
165 const float d = GGML_FP16_TO_FP32(block->d);
166
167 /* Get high bits as a 32-bit integer */
168 uint32_t qh;
169 memcpy(&qh, block->qh, sizeof(qh));
170
171 /* llama.cpp Q5_0 layout:
172 * - Weight j uses: low nibble of qs[j], high bit from qh bit j
173 * - Weight j+16 uses: high nibble of qs[j], high bit from qh bit (j+12)
174 */
175 for (int j = 0; j < QK5_0 / 2; j++) {
176 const uint8_t packed = block->qs[j];
177
178 /* Extract low 4 bits for two weights */
179 const int lo = (packed & 0x0F);
180 const int hi = (packed >> 4);
181
182 /* Extract high bits from qh - matches llama.cpp exactly */
183 const int xh_0 = ((qh >> (j + 0)) << 4) & 0x10;
184 const int xh_1 = ((qh >> (j + 12))) & 0x10;
185
186 /* Combine: 5-bit value, range 0-31, then subtract 16 */
187 const int q0 = (lo | xh_0) - 16;
188 const int q1 = (hi | xh_1) - 16;
189
190 output[j] = d * (float)q0;
191 output[j + 16] = d * (float)q1;
192 }
193}
194
195/**
196 * @brief Dequantize Q5_0 row (multiple blocks)
197 */
198void dequant_q5_0_row(const void *src, float *dst, size_t n_elements)
199{
200 const block_q5_0 *blocks = (const block_q5_0 *)src;
201 const size_t n_blocks = n_elements / QK5_0;
202
203 for (size_t b = 0; b < n_blocks; b++) {
204 dequant_q5_0_block(&blocks[b], &dst[b * QK5_0]);
205 }
206}
207
208/* ============================================================================
209 * Q5_1 Dequantization
210 * - 32 weights per block, 1 FP16 scale + 1 FP16 min
211 * - Low 4 bits + 1 high bit packed separately
212 * - Weights are unsigned 5-bit (0 to 31), scaled and offset by min
213 * ============================================================================ */
214
215/**
216 * @brief Dequantize a single Q5_1 block to FP32
217 * @param block Pointer to Q5_1 block (24 bytes)
218 * @param output Output FP32 array (32 floats)
219 */
220void dequant_q5_1_block(const block_q5_1 *block, float *output)
221{
222 const float d = GGML_FP16_TO_FP32(block->d);
223 const float m = GGML_FP16_TO_FP32(block->m);
224
225 /* Get high bits as a 32-bit integer */
226 uint32_t qh;
227 memcpy(&qh, block->qh, sizeof(qh));
228
229 /* llama.cpp Q5_1 layout (same as Q5_0):
230 * - Weight j uses: low nibble of qs[j], high bit from qh bit j
231 * - Weight j+16 uses: high nibble of qs[j], high bit from qh bit (j+12)
232 */
233 for (int j = 0; j < QK5_1 / 2; j++) {
234 const uint8_t packed = block->qs[j];
235
236 /* Extract low 4 bits for two weights */
237 const int lo = (packed & 0x0F);
238 const int hi = (packed >> 4);
239
240 /* Extract high bits from qh - matches llama.cpp exactly */
241 const int xh_0 = ((qh >> (j + 0)) << 4) & 0x10;
242 const int xh_1 = ((qh >> (j + 12))) & 0x10;
243
244 /* Combine: 5-bit unsigned value, range 0-31 */
245 const int q0 = (lo | xh_0);
246 const int q1 = (hi | xh_1);
247
248 /* Dequantize: w = d * q + m */
249 output[j] = d * (float)q0 + m;
250 output[j + 16] = d * (float)q1 + m;
251 }
252}
253
254/**
255 * @brief Dequantize Q5_1 row (multiple blocks)
256 */
257void dequant_q5_1_row(const void *src, float *dst, size_t n_elements)
258{
259 const block_q5_1 *blocks = (const block_q5_1 *)src;
260 const size_t n_blocks = n_elements / QK5_1;
261
262 for (size_t b = 0; b < n_blocks; b++) {
263 dequant_q5_1_block(&blocks[b], &dst[b * QK5_1]);
264 }
265}
266
267/* ============================================================================
268 * Q8_0 Dequantization
269 * - 32 weights per block, 1 FP16 scale
270 * - Weights stored as signed 8-bit
271 * ============================================================================ */
272
273/**
274 * @brief Dequantize a single Q8_0 block to FP32
275 */
276void dequant_q8_0_block(const block_q8_0 *block, float *output)
277{
278 const float d = GGML_FP16_TO_FP32(block->d);
279
280 for (int i = 0; i < QK8_0; i++) {
281 output[i] = d * (float)block->qs[i];
282 }
283}
284
285/**
286 * @brief Dequantize Q8_0 row (multiple blocks)
287 */
288void dequant_q8_0_row(const void *src, float *dst, size_t n_elements)
289{
290 const block_q8_0 *blocks = (const block_q8_0 *)src;
291 const size_t n_blocks = n_elements / QK8_0;
292
293 for (size_t b = 0; b < n_blocks; b++) {
294 dequant_q8_0_block(&blocks[b], &dst[b * QK8_0]);
295 }
296}
297
298#ifdef __AVX512F__
299/**
300 * @brief Dequantize Q8_0 block using AVX-512
301 */
302void dequant_q8_0_block_avx512(const block_q8_0 *block,
303 __m512 *out0, __m512 *out1)
304{
305 const __m512 scale = _mm512_set1_ps(GGML_FP16_TO_FP32(block->d));
306
307 /* Load 32 x int8 as two __m128i */
308 __m128i q0 = _mm_loadu_si128((const __m128i *)&block->qs[0]);
309 __m128i q1 = _mm_loadu_si128((const __m128i *)&block->qs[16]);
310
311 /* Sign-extend to 32-bit and convert to float */
312 __m512i i0 = _mm512_cvtepi8_epi32(q0);
313 __m512i i1 = _mm512_cvtepi8_epi32(q1);
314
315 *out0 = _mm512_mul_ps(_mm512_cvtepi32_ps(i0), scale);
316 *out1 = _mm512_mul_ps(_mm512_cvtepi32_ps(i1), scale);
317}
318#endif /* __AVX512F__ */
319
320/* ============================================================================
321 * Q4_K Dequantization (Primary Target for Q4_K_M)
322 * - 256 weights per super-block
323 * - 8 sub-blocks of 32 weights each
324 * - Two-level scaling: super-block d/dmin + sub-block 6-bit scales
325 * ============================================================================ */
326
327/**
328 * @brief Dequantize a single Q4_K block to FP32
329 *
330 * This matches llama.cpp's dequantize_row_q4_K exactly:
331 * - Formula: weight = d * scale * q - dmin * m
332 * - Layout: 4 iterations of 64 weights each
333 * - First 32: low nibbles of qs[0..31] with scale[2*iter], min[2*iter]
334 * - Next 32: high nibbles of qs[0..31] with scale[2*iter+1], min[2*iter+1]
335 */
336void dequant_q4_k_block(const block_q4_K *block, float *output)
337{
338 const float d = GGML_FP16_TO_FP32(block->d);
339 const float dmin = GGML_FP16_TO_FP32(block->dmin);
340
341 /* Unpack the 6-bit sub-block scales and mins */
342 uint8_t sc[8], m[8];
343 unpack_q4_k_scales(block->scales, sc, m);
344
345 /* llama.cpp layout: 4 iterations of 64 weights each */
346 for (int iter = 0; iter < 4; iter++) {
347 const float d1 = d * (float)sc[2 * iter];
348 const float m1 = dmin * (float)m[2 * iter];
349 const float d2 = d * (float)sc[2 * iter + 1];
350 const float m2 = dmin * (float)m[2 * iter + 1];
351
352 const uint8_t *qs = &block->qs[iter * 32];
353 float *out = &output[iter * 64];
354
355 /* First 32 weights: low nibbles */
356 for (int l = 0; l < 32; l++) {
357 const int q = (qs[l] & 0x0F);
358 out[l] = d1 * (float)q - m1;
359 }
360
361 /* Next 32 weights: high nibbles */
362 for (int l = 0; l < 32; l++) {
363 const int q = (qs[l] >> 4);
364 out[32 + l] = d2 * (float)q - m2;
365 }
366 }
367}
368
369/**
370 * @brief Dequantize Q4_K row (multiple blocks)
371 */
372void dequant_q4_k_row(const void *src, float *dst, size_t n_elements)
373{
374 const block_q4_K *blocks = (const block_q4_K *)src;
375 const size_t n_blocks = n_elements / QK_K;
376
377 for (size_t b = 0; b < n_blocks; b++) {
378 dequant_q4_k_block(&blocks[b], &dst[b * QK_K]);
379 }
380}
381
382/* ============================================================================
383 * Q6_K Dequantization
384 * - 256 weights per block
385 * - 16 sub-blocks of 16 weights, int8 scales + FP16 super-scale
386 * ============================================================================ */
387
388/**
389 * @brief Dequantize a single Q6_K block to FP32
390 */
391void dequant_q6_k_block(const block_q6_K *block, float *output)
392{
393 const float d = GGML_FP16_TO_FP32(block->d);
394 const uint8_t *ql = block->ql;
395 const uint8_t *qh = block->qh;
396 const int8_t *sc = block->scales;
397 float *y = output;
398
399 for (int n = 0; n < QK_K; n += 128) {
400 for (int l = 0; l < 32; ++l) {
401 const int is = l / 16;
402 const int8_t q1 = (int8_t)((ql[l + 0] & 0xF) | (((qh[l] >> 0) & 3) << 4)) - 32;
403 const int8_t q2 = (int8_t)((ql[l + 32] & 0xF) | (((qh[l] >> 2) & 3) << 4)) - 32;
404 const int8_t q3 = (int8_t)((ql[l + 0] >> 4) | (((qh[l] >> 4) & 3) << 4)) - 32;
405 const int8_t q4 = (int8_t)((ql[l + 32] >> 4) | (((qh[l] >> 6) & 3) << 4)) - 32;
406
407 y[l + 0] = d * (float)sc[is + 0] * (float)q1;
408 y[l + 32] = d * (float)sc[is + 2] * (float)q2;
409 y[l + 64] = d * (float)sc[is + 4] * (float)q3;
410 y[l + 96] = d * (float)sc[is + 6] * (float)q4;
411 }
412 y += 128;
413 ql += 64;
414 qh += 32;
415 sc += 8;
416 }
417}
418
419/**
420 * @brief Dequantize Q6_K row (multiple blocks)
421 */
422void dequant_q6_k_row(const void *src, float *dst, size_t n_elements)
423{
424 const block_q6_K *blocks = (const block_q6_K *)src;
425 const size_t n_blocks = n_elements / QK_K;
426
427 for (size_t b = 0; b < n_blocks; b++) {
428 dequant_q6_k_block(&blocks[b], &dst[b * QK_K]);
429 }
430}
431
432#ifdef __AVX512F__
433/**
434 * @brief Dequantize one Q4_K sub-block (32 weights) using AVX-512
435 *
436 * @param qs Pointer to 16 bytes of packed 4-bit weights
437 * @param scale Pre-computed d * sub_scale
438 * @param min_val Pre-computed dmin * sub_min
439 * @param out0 Output: weights 0-15
440 * @param out1 Output: weights 16-31
441 */
442/**
443 * @brief Dequantize full Q4_K block using AVX-512
444 *
445 * This matches llama.cpp's dequantize_row_q4_K exactly:
446 * - Formula: weight = d * scale * q - dmin * m
447 * - Layout: 4 iterations of 64 weights each
448 * - First 32: low nibbles of qs[0..31] with scale[2*iter], min[2*iter]
449 * - Next 32: high nibbles of qs[0..31] with scale[2*iter+1], min[2*iter+1]
450 */
451void dequant_q4_k_block_avx512(const block_q4_K *block, float *output)
452{
453 const float d = GGML_FP16_TO_FP32(block->d);
454 const float dmin = GGML_FP16_TO_FP32(block->dmin);
455
456 uint8_t sc[8], m[8];
457 unpack_q4_k_scales(block->scales, sc, m);
458
459 const __m512i mask_lo = _mm512_set1_epi32(0x0F);
460
461 /* llama.cpp layout: 4 iterations of 64 weights each */
462 for (int iter = 0; iter < 4; iter++) {
463 const float d1 = d * (float)sc[2 * iter];
464 const float m1 = dmin * (float)m[2 * iter];
465 const float d2 = d * (float)sc[2 * iter + 1];
466 const float m2 = dmin * (float)m[2 * iter + 1];
467
468 const __m512 vd1 = _mm512_set1_ps(d1);
469 const __m512 vm1 = _mm512_set1_ps(m1);
470 const __m512 vd2 = _mm512_set1_ps(d2);
471 const __m512 vm2 = _mm512_set1_ps(m2);
472
473 const uint8_t *qs = &block->qs[iter * 32];
474 float *out = &output[iter * 64];
475
476 /* Process first 32 weights (low nibbles) in two 16-float chunks */
477 for (int chunk = 0; chunk < 2; chunk++) {
478 __m128i packed = _mm_loadu_si128((const __m128i *)&qs[chunk * 16]);
479 __m512i bytes = _mm512_cvtepu8_epi32(packed);
480 __m512i lo = _mm512_and_epi32(bytes, mask_lo);
481 /* w = d1 * q - m1: fnmadd computes -(a*b) + c = c - a*b = -m1 + d1*q */
482 __m512 w = _mm512_fnmadd_ps(_mm512_set1_ps(1.0f), vm1,
483 _mm512_mul_ps(_mm512_cvtepi32_ps(lo), vd1));
484 _mm512_storeu_ps(&out[chunk * 16], w);
485 }
486
487 /* Process next 32 weights (high nibbles) in two 16-float chunks */
488 for (int chunk = 0; chunk < 2; chunk++) {
489 __m128i packed = _mm_loadu_si128((const __m128i *)&qs[chunk * 16]);
490 __m512i bytes = _mm512_cvtepu8_epi32(packed);
491 __m512i hi = _mm512_srli_epi32(bytes, 4);
492 /* w = d2 * q - m2 */
493 __m512 w = _mm512_fnmadd_ps(_mm512_set1_ps(1.0f), vm2,
494 _mm512_mul_ps(_mm512_cvtepi32_ps(hi), vd2));
495 _mm512_storeu_ps(&out[32 + chunk * 16], w);
496 }
497 }
498}
499#endif /* __AVX512F__ */
500
501/* ============================================================================
502 * Generic Dequantization Dispatch
503 * ============================================================================ */
504
505#include "ckernel_dtype.h"
506
507/**
508 * @brief Dequantize a row of quantized data to FP32
509 * @param dtype Data type (must be quantized type)
510 * @param src Source quantized data
511 * @param dst Destination FP32 buffer
512 * @param n_elements Number of elements
513 */
514void dequant_row(CKDataType dtype, const void *src, float *dst, size_t n_elements)
515{
516 switch (dtype) {
517 case CK_DT_Q4_0:
518 dequant_q4_0_row(src, dst, n_elements);
519 break;
520 case CK_DT_Q4_1:
521 dequant_q4_1_row(src, dst, n_elements);
522 break;
523 case CK_DT_Q5_0:
524 dequant_q5_0_row(src, dst, n_elements);
525 break;
526 case CK_DT_Q5_1:
527 dequant_q5_1_row(src, dst, n_elements);
528 break;
529 case CK_DT_Q4_K:
530 dequant_q4_k_row(src, dst, n_elements);
531 break;
532 case CK_DT_Q6_K:
533 dequant_q6_k_row(src, dst, n_elements);
534 break;
535 case CK_DT_Q8_0:
536 dequant_q8_0_row(src, dst, n_elements);
537 break;
538 default:
539 /* Not a quantized type - no-op or error */
540 break;
541 }
542}
CKDataType
Supported data types in C-Kernel-Engine.
@ CK_DT_Q4_K
@ CK_DT_Q4_0
@ CK_DT_Q8_0
@ CK_DT_Q5_0
@ CK_DT_Q6_K
@ CK_DT_Q4_1
@ CK_DT_Q5_1
Quantization block structures for weight-only quantization.
#define QK5_0
#define GGML_FP16_TO_FP32
#define QK5_1
#define QK4_0
#define QK4_1
static void unpack_q4_k_scales(const uint8_t *scales, uint8_t *sc, uint8_t *m)
Unpack Q4_K sub-block scales and mins.
#define QK8_0
#define QK_K
void dequant_q4_0_row(const void *src, float *dst, size_t n_elements)
Dequantize Q4_0 row (multiple blocks)
void dequant_q5_0_block(const block_q5_0 *block, float *output)
Dequantize a single Q5_0 block to FP32.
void dequant_q5_0_row(const void *src, float *dst, size_t n_elements)
Dequantize Q5_0 row (multiple blocks)
void dequant_q8_0_block(const block_q8_0 *block, float *output)
Dequantize a single Q8_0 block to FP32.
void dequant_q4_1_block(const block_q4_1 *block, float *output)
Dequantize a single Q4_1 block to FP32.
void dequant_q6_k_block(const block_q6_K *block, float *output)
Dequantize a single Q6_K block to FP32.
void dequant_q4_k_block(const block_q4_K *block, float *output)
Dequantize a single Q4_K block to FP32.
void dequant_q4_0_block(const block_q4_0 *block, float *output)
Dequantize a single Q4_0 block to FP32.
void dequant_row(CKDataType dtype, const void *src, float *dst, size_t n_elements)
Dequantize a row of quantized data to FP32.
void dequant_q8_0_row(const void *src, float *dst, size_t n_elements)
Dequantize Q8_0 row (multiple blocks)
void dequant_q5_1_block(const block_q5_1 *block, float *output)
Dequantize a single Q5_1 block to FP32.
void dequant_q5_1_row(const void *src, float *dst, size_t n_elements)
Dequantize Q5_1 row (multiple blocks)
void dequant_q4_1_row(const void *src, float *dst, size_t n_elements)
Dequantize Q4_1 row (multiple blocks)
void dequant_q6_k_row(const void *src, float *dst, size_t n_elements)
Dequantize Q6_K row (multiple blocks)
void dequant_q4_k_row(const void *src, float *dst, size_t n_elements)
Dequantize Q4_K row (multiple blocks)
uint8_t qs[32/2]
uint8_t qs[32/2]
uint8_t scales[12]
uint8_t qs[256/2]
uint8_t qh[4]
uint8_t qs[32/2]
uint8_t qs[32/2]
uint8_t qh[4]
uint8_t ql[256/2]
int8_t scales[256/16]
uint8_t qh[256/4]
int8_t qs[32]