← Back to C-Kernel-Engine Docs Doxygen Source Documentation
 
Loading...
Searching...
No Matches
gemm_kernels_q6k_q8k.c File Reference

Q6_K (weights) x Q8_K (activations) kernels for inference. More...

#include <assert.h>
#include <math.h>
#include <string.h>
#include <stdint.h>
#include <stddef.h>
#include <stdlib.h>
#include "ckernel_engine.h"
#include "ckernel_quant.h"

Go to the source code of this file.

Functions

static float ck_dot_q6_k_q8_k_fast_or_ref (const block_q6_K *w, const block_q8_K *x, int K)
 
void ck_q6_k_prepare_weight (const void *src, void *dst, int N, int K)
 
size_t ck_q6_k_prepared_block_size (void)
 
const char * ck_q6_k_prepared_provider_name (void)
 
const char * ck_q6_k_q8_k_provider_name (void)
 
static int ck_q6k_q8k_force_ref (void)
 
static float dot_q6_k_q8_k_ref (const block_q6_K *w, const block_q8_K *x, int K)
 Scalar dot product for Q6_K x Q8_K.
 
void gemm_nt_q6_k_q8_k (const void *A_q8, const void *B, const float *bias, float *C, int M, int N, int K)
 
void gemm_nt_q6_k_q8_k_m2 (const void *A_q8, const void *B, const float *bias, float *C, int M, int N, int K)
 
void gemm_nt_q6_k_q8_k_m4_tile (const void *A_q8, const void *B, const float *bias, float *C, int M, int N, int K, int m0, int m1, int n0, int n1)
 
void gemm_nt_q6_k_q8_k_prepared (const void *A_q8, const void *B_prepared, const float *bias, float *C, int M, int N, int K)
 
void gemm_nt_q6_k_q8_k_prepared_avx512_vnni (const void *A_q8, const void *B_prepared, const float *bias, float *C, int M, int N, int K)
 
void gemm_nt_q6_k_q8_k_prepared_tile (const void *A_q8, const void *B_prepared, const float *bias, float *C, int M, int N, int K, int m0, int m1, int n0, int n1)
 
static void gemm_nt_q6_k_q8_k_prepared_tile_impl (const void *A_q8, const void *B_prepared, const float *bias, float *C, int M, int N, int K, int m0, int m1, int n0, int n1, int use_avx512_vnni)
 
void gemm_nt_q6_k_q8_k_tile (const void *A_q8, const void *B, const float *bias, float *C, int M, int N, int K, int m0, int m1, int n0, int n1)
 Compute one C[m0:m1, n0:n1] tile for Q8_K activations x Q6_K weights.
 
void gemm_nt_q6_k_q8_k_tiled (const void *A_q8, const void *B, const float *bias, float *C, int M, int N, int K)
 Experimental single-thread tiled NT GEMM wrapper.
 
void gemm_nt_q6_k_q8_k_tiled_parity (const void *A_q8, const void *B, const float *bias, float *C, int M, int N, int K)
 NT GEMM: C = A @ B^T where A is Q8_K and B is Q6_K.
 
void gemm_nt_q6_k_q8_k_tiled_parity_tile (const void *A_q8, const void *B, const float *bias, float *C, int M, int N, int K, int m0, int m1, int n0, int n1)
 
void gemm_q6_k_q8_k (float *Y, const void *W, const void *X_q8, int M, int N, int K)
 GEMM: Y = W @ X^T where W is Q6_K and X is Q8_K.
 
void gemv_q6_k_q8_k (float *y, const void *W, const void *x_q8, int M, int K)
 GEMV: y = W @ x where W is Q6_K and x is Q8_K.
 
void gemv_q6_k_q8_k_avx (float *y, const void *W, const void *x_q8, int M, int K)
 
void gemv_q6_k_q8_k_avx2 (float *y, const void *W, const void *x_q8, int M, int K)
 
void gemv_q6_k_q8_k_avx512 (float *y, const void *W, const void *x_q8, int M, int K)
 
void gemv_q6_k_q8_k_avx512_vbmi (float *y, const void *W, const void *x_q8, int M, int K)
 
void gemv_q6_k_q8_k_parallel (float *y, const void *W, const void *x_q8, int M, int K, int ith, int nth)
 Parallel reference GEMV for Q6_K × Q8_K.
 
void gemv_q6_k_q8_k_parallel_simd (float *y, const void *W, const void *x_q8, int M, int K, int ith, int nth)
 Parallel SIMD GEMV for Q6_K × Q8_K.
 
void gemv_q6_k_q8_k_ref (float *y, const void *W, const void *x_q8, int M, int K)
 
void gemv_q6_k_q8_k_sse (float *y, const void *W, const void *x_q8, int M, int K)
 
void vec_dot_q6_k_q8_k (int n, float *s, const void *vx, const void *vy)
 Q6_K x Q8_K dot product (single row)
 

Detailed Description

Q6_K (weights) x Q8_K (activations) kernels for inference.

CK-ENGINE KERNEL RULES:

  1. NO malloc/free - memory via bump allocator, pointers passed in
  2. NO OpenMP - parallelization at orchestrator/codegen layer
  3. API must define: inputs, outputs, workspace, and memory layouts
  4. Pure computation - deterministic, no side effects

After changes: make test && make llamacpp-parity-full

Implements decode-style matvec/matmul where weights are Q6_K and the activations are quantized on-the-fly to Q8_K. This is inference-only; no backward pass is provided here.

Q6_K Format (256 weights per block):

  • d: FP16 super-block scale
  • ql: 128 bytes (low 4 bits of each weight)
  • qh: 64 bytes (high 2 bits of each weight)
  • scales: 16 int8 sub-block scales

Q8_K Format (256 weights per block):

  • d: FP32 scale
  • qs: 256 int8 values
  • bsums: 16 int16 block sums

Definition in file gemm_kernels_q6k_q8k.c.

Function Documentation

◆ ck_dot_q6_k_q8_k_fast_or_ref()

static float ck_dot_q6_k_q8_k_fast_or_ref ( const block_q6_K *  w,
const block_q8_K *  x,
int  K 
)
inlinestatic

Definition at line 1729 of file gemm_kernels_q6k_q8k.c.

1732{
1733 if (ck_q6k_q8k_force_ref()) {
1734 return dot_q6_k_q8_k_ref(w, x, K);
1735 }
1736#if defined(__AVX2__)
1737 /* Short strict prefill uses the pinned x86 graph's AVX2 leaf reduction.
1738 * Batched strict prefill uses the separate tiled integer-slab kernel. */
1739 return dot_q6_k_q8_k_avx2(w, x, K);
1740#elif defined(__AVX__)
1741 if (ck_strict_parity_enabled()) return dot_q6_k_q8_k_ref(w, x, K);
1742 return dot_q6_k_q8_k_avx(w, x, K);
1743#elif defined(__SSE4_1__)
1744 if (ck_strict_parity_enabled()) return dot_q6_k_q8_k_ref(w, x, K);
1745 return dot_q6_k_q8_k_sse(w, x, K);
1746#elif defined(__ARM_NEON) || defined(__aarch64__)
1747 if (ck_strict_parity_enabled()) return dot_q6_k_q8_k_ref(w, x, K);
1748 return dot_q6_k_q8_k_neon(w, x, K);
1749#else
1750 return dot_q6_k_q8_k_ref(w, x, K);
1751#endif
1752}
int ck_strict_parity_enabled(void)
static int ck_q6k_q8k_force_ref(void)
static float dot_q6_k_q8_k_ref(const block_q6_K *w, const block_q8_K *x, int K)
Scalar dot product for Q6_K x Q8_K.

References ck_q6k_q8k_force_ref(), ck_strict_parity_enabled(), and dot_q6_k_q8_k_ref().

Referenced by gemm_nt_q6_k_q8_k(), and gemm_nt_q6_k_q8_k_tile().

◆ ck_q6_k_prepare_weight()

void ck_q6_k_prepare_weight ( const void *  src,
void *  dst,
int  N,
int  K 
)

Definition at line 74 of file gemm_kernels_q6k_q8k.c.

75{
76 if (!src || !dst || N <= 0 || K <= 0 || (K % QK_K) != 0) return;
77 const block_q6_K *input = (const block_q6_K *)src;
78 block_q6_K_prepared *output = (block_q6_K_prepared *)dst;
79 const size_t blocks = (size_t)N * (size_t)(K / QK_K);
80
81 for (size_t b = 0; b < blocks; ++b) {
82 output[b].d = input[b].d;
83 memcpy(output[b].scales, input[b].scales, sizeof(output[b].scales));
84 for (int n = 0; n < QK_K; n += 128) {
85 const uint8_t *ql = input[b].ql + n / 2;
86 const uint8_t *qh = input[b].qh + n / 4;
87 for (int l = 0; l < 32; ++l) {
88 output[b].qs[n + l + 0] =
89 (uint8_t)((ql[l] & 0x0f) | (((qh[l] >> 0) & 3) << 4));
90 output[b].qs[n + l + 32] =
91 (uint8_t)((ql[l + 32] & 0x0f) | (((qh[l] >> 2) & 3) << 4));
92 output[b].qs[n + l + 64] =
93 (uint8_t)((ql[l] >> 4) | (((qh[l] >> 4) & 3) << 4));
94 output[b].qs[n + l + 96] =
95 (uint8_t)((ql[l + 32] >> 4) | (((qh[l] >> 6) & 3) << 4));
96 }
97 }
98 }
99}
#define QK_K
uint8_t ql[256/2]
uint8_t qh[256/4]

References block_q6_K::d, block_q6_K::qh, QK_K, and block_q6_K::ql.

◆ ck_q6_k_prepared_block_size()

size_t ck_q6_k_prepared_block_size ( void  )

Definition at line 57 of file gemm_kernels_q6k_q8k.c.

58{
59 return sizeof(block_q6_K_prepared);
60}

◆ ck_q6_k_prepared_provider_name()

const char * ck_q6_k_prepared_provider_name ( void  )

Definition at line 62 of file gemm_kernels_q6k_q8k.c.

63{
64#if defined(__AVX512F__) && defined(__AVX512BW__) && \
65 defined(__AVX512VNNI__)
66 return "q6_k_prepared_avx512_vnni_exact";
67#elif defined(__AVX2__)
68 return "q6_k_prepared_avx2_exact";
69#else
70 return "q6_k_prepared_unavailable";
71#endif
72}

◆ ck_q6_k_q8_k_provider_name()

const char * ck_q6_k_q8_k_provider_name ( void  )

Definition at line 1389 of file gemm_kernels_q6k_q8k.c.

1390{
1392 return "q6_k_q8_k_ref";
1393 }
1394#if defined(__AVX2__)
1395 return "q6_k_q8_k_avx2";
1396#elif defined(__AVX__)
1397 return "q6_k_q8_k_avx";
1398#elif defined(__SSE4_1__)
1399 return "q6_k_q8_k_sse";
1400#elif defined(__ARM_NEON) || defined(__aarch64__)
1401 return "q6_k_q8_k_neon";
1402#else
1403 return "q6_k_q8_k_ref";
1404#endif
1405}

References ck_q6k_q8k_force_ref(), and ck_strict_parity_enabled().

◆ ck_q6k_q8k_force_ref()

static int ck_q6k_q8k_force_ref ( void  )
static

Definition at line 108 of file gemm_kernels_q6k_q8k.c.

109{
110 static int cached = -1;
111 if (cached < 0) {
112 const char *env = getenv("CK_DEBUG_Q6K_Q8K_REF");
113 cached = (env && env[0] && env[0] != '0') ? 1 : 0;
114 }
115 return cached;
116}

Referenced by ck_dot_q6_k_q8_k_fast_or_ref(), ck_q6_k_q8_k_provider_name(), gemm_nt_q6_k_q8_k_m4_tile(), gemv_q6_k_q8_k(), and gemv_q6_k_q8_k_parallel_simd().

◆ dot_q6_k_q8_k_ref()

static float dot_q6_k_q8_k_ref ( const block_q6_K *  w,
const block_q8_K *  x,
int  K 
)
static

Scalar dot product for Q6_K x Q8_K.

Q6_K layout: 256 weights per block

  • ql[0..127]: low 4 bits for all 256 weights (packed 2 per byte)
  • qh[0..63]: high 2 bits for all 256 weights (packed 4 per byte)
  • scales[0..15]: int8 scale for each 16-weight sub-block
  • d: FP16 super-block scale

The dequantization formula for each weight is: weight = d * scale[sub] * (q6_value - 32) where q6_value is the 6-bit unsigned value (0..63).

Definition at line 135 of file gemm_kernels_q6k_q8k.c.

138{
139 const int nb = K / QK_K;
140 float sumf = 0.0f;
141 float sums[8] = {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f};
142
143 for (int i = 0; i < nb; ++i) {
144 const float d = GGML_FP16_TO_FP32(w[i].d) * x[i].d;
145
146 const uint8_t *ql = w[i].ql;
147 const uint8_t *qh = w[i].qh;
148 const int8_t *sc = w[i].scales;
149 const int8_t *q8 = x[i].qs;
150 int32_t aux32[8] = {0, 0, 0, 0, 0, 0, 0, 0};
151
152 /* Process 256 weights in 2 iterations of 128 */
153 for (int n = 0; n < QK_K; n += 128) {
154 /* Each iteration processes 128 weights:
155 * - ql[0..63] contains low 4 bits
156 * - qh[0..31] contains high 2 bits
157 * - Interleaved pattern: weights 0-31, 32-63, 64-95, 96-127
158 */
159 for (int l = 0; l < 32; ++l) {
160 /* Sub-block index: each scale covers 16 weights */
161 const int is = l / 16;
162
163 /* Extract 6-bit values from packed format */
164 /* q1: weights l+0 (low nibble of ql[l], bits 0-1 of qh[l]) */
165 const int8_t q1 = (int8_t)((ql[l + 0] & 0xF) | (((qh[l] >> 0) & 3) << 4)) - 32;
166 /* q2: weights l+32 (low nibble of ql[l+32], bits 2-3 of qh[l]) */
167 const int8_t q2 = (int8_t)((ql[l + 32] & 0xF) | (((qh[l] >> 2) & 3) << 4)) - 32;
168 /* q3: weights l+64 (high nibble of ql[l], bits 4-5 of qh[l]) */
169 const int8_t q3 = (int8_t)((ql[l + 0] >> 4) | (((qh[l] >> 4) & 3) << 4)) - 32;
170 /* q4: weights l+96 (high nibble of ql[l+32], bits 6-7 of qh[l]) */
171 const int8_t q4 = (int8_t)((ql[l + 32] >> 4) | (((qh[l] >> 6) & 3) << 4)) - 32;
172
173 aux32[l & 7] += (int)sc[is + 0] * (int)q1 * (int)q8[l + 0];
174 aux32[l & 7] += (int)sc[is + 2] * (int)q2 * (int)q8[l + 32];
175 aux32[l & 7] += (int)sc[is + 4] * (int)q3 * (int)q8[l + 64];
176 aux32[l & 7] += (int)sc[is + 6] * (int)q4 * (int)q8[l + 96];
177 }
178 q8 += 128;
179 ql += 64;
180 qh += 32;
181 sc += 8;
182 }
183
184 for (int l = 0; l < 8; ++l) {
185 sums[l] += d * (float)aux32[l];
186 }
187 }
188
189 for (int l = 0; l < 8; ++l) {
190 sumf += sums[l];
191 }
192 return sumf;
193}
#define GGML_FP16_TO_FP32
int8_t scales[256/16]
int8_t qs[256]

References block_q8_K::d, GGML_FP16_TO_FP32, block_q6_K::qh, QK_K, block_q6_K::ql, block_q8_K::qs, and block_q6_K::scales.

Referenced by ck_dot_q6_k_q8_k_fast_or_ref(), gemv_q6_k_q8_k_parallel(), gemv_q6_k_q8_k_parallel_simd(), gemv_q6_k_q8_k_ref(), and vec_dot_q6_k_q8_k().

◆ gemm_nt_q6_k_q8_k()

void gemm_nt_q6_k_q8_k ( const void *  A_q8,
const void *  B,
const float *  bias,
float *  C,
int  M,
int  N,
int  K 
)

Definition at line 1548 of file gemm_kernels_q6k_q8k.c.

1553{
1554 if (!A_q8 || !B || !C) {
1555 return;
1556 }
1557 if (M <= 0 || N <= 0 || K <= 0) {
1558 return;
1559 }
1560#if defined(__AVX2__) && defined(__FMA__)
1561 if (M >= 8 && ck_strict_parity_enabled()) {
1562 gemm_nt_q6_k_q8_k_tiled_parity(A_q8, B, bias, C, M, N, K);
1563 return;
1564 }
1565#endif
1566
1567 /* Prefill GEMM uses the pinned x86 leaf reduction for short row groups
1568 * and the tiled reduction for strict batches of at least eight rows.
1569 * CK_DEBUG_Q6K_Q8K_REF=1 still forces the scalar reference leaf. */
1570 const block_q8_K *A = (const block_q8_K *)A_q8;
1571 const block_q6_K *W = (const block_q6_K *)B;
1572 const int blocks_per_vec = K / QK_K;
1573 const int blocks_per_row = K / QK_K;
1574
1575 for (int m = 0; m < M; ++m) {
1576 const block_q8_K *a_row = A + (size_t)m * (size_t)blocks_per_vec;
1577 float *c_row = C + (size_t)m * (size_t)N;
1578 for (int n = 0; n < N; ++n) {
1579 const block_q6_K *w_row = W + (size_t)n * (size_t)blocks_per_row;
1580 const float b = bias ? bias[n] : 0.0f;
1581 c_row[n] = ck_dot_q6_k_q8_k_fast_or_ref(w_row, a_row, K) + b;
1582 }
1583 }
1584}
void gemm_nt_q6_k_q8_k_tiled_parity(const void *A_q8, const void *B, const float *bias, float *C, int M, int N, int K)
NT GEMM: C = A @ B^T where A is Q8_K and B is Q6_K.
static float ck_dot_q6_k_q8_k_fast_or_ref(const block_q6_K *w, const block_q8_K *x, int K)
#define C(color)
Definition show_config.c:39

References C, ck_dot_q6_k_q8_k_fast_or_ref(), ck_strict_parity_enabled(), gemm_nt_q6_k_q8_k_tiled_parity(), and QK_K.

Referenced by ck_test_gemm_q6_k(), gemm_nt_q8_k_mlp_dispatch(), and gemm_nt_q8_k_qkv_dispatch().

◆ gemm_nt_q6_k_q8_k_m2()

void gemm_nt_q6_k_q8_k_m2 ( const void *  A_q8,
const void *  B,
const float *  bias,
float *  C,
int  M,
int  N,
int  K 
)

Definition at line 1854 of file gemm_kernels_q6k_q8k.c.

1857{
1858 if (!A_q8 || !B || !C || M != 2 || N <= 0 || K <= 0 ||
1859 (K % QK_K) != 0) return;
1860 gemm_nt_q6_k_q8_k_m4_tile(A_q8, B, bias, C, M, N, K,
1861 0, M, 0, N);
1862}
void gemm_nt_q6_k_q8_k_m4_tile(const void *A_q8, const void *B, const float *bias, float *C, int M, int N, int K, int m0, int m1, int n0, int n1)

References C, gemm_nt_q6_k_q8_k_m4_tile(), and QK_K.

◆ gemm_nt_q6_k_q8_k_m4_tile()

void gemm_nt_q6_k_q8_k_m4_tile ( const void *  A_q8,
const void *  B,
const float *  bias,
float *  C,
int  M,
int  N,
int  K,
int  m0,
int  m1,
int  n0,
int  n1 
)

Definition at line 1797 of file gemm_kernels_q6k_q8k.c.

1804{
1805 if (!A_q8 || !B || !C || M <= 0 || N <= 0 || K <= 0 ||
1806 K % QK_K != 0) {
1807 return;
1808 }
1809 if (m0 < 0) m0 = 0;
1810 if (n0 < 0) n0 = 0;
1811 if (m1 > M) m1 = M;
1812 if (n1 > N) n1 = N;
1813 if (m0 >= m1 || n0 >= n1) return;
1814
1815#if defined(__AVX2__)
1817 const block_q8_K *A = (const block_q8_K *)A_q8;
1818 const block_q6_K *W = (const block_q6_K *)B;
1819 const int blocks_per_vec = K / QK_K;
1820 for (int n = n0; n < n1; ++n) {
1821 const block_q6_K *w_row =
1822 W + (size_t)n * (size_t)blocks_per_vec;
1823 const float b = bias ? bias[n] : 0.0f;
1824 int m = m0;
1825 for (; m + 4 <= m1; m += 4) {
1826 float values[4];
1827 dot_q6_k_q8_k_avx2_m4(
1828 w_row, A + (size_t)m * (size_t)blocks_per_vec,
1829 blocks_per_vec, 4, K, values);
1830 for (int r = 0; r < 4; ++r) {
1831 C[(size_t)(m + r) * (size_t)N + (size_t)n] = values[r] + b;
1832 }
1833 }
1834 if (m < m1) {
1835 float values[4];
1836 const int rows = m1 - m;
1837 dot_q6_k_q8_k_avx2_m4(
1838 w_row, A + (size_t)m * (size_t)blocks_per_vec,
1839 blocks_per_vec, rows, K, values);
1840 for (int r = 0; r < rows; ++r) {
1841 C[(size_t)(m + r) * (size_t)N + (size_t)n] = values[r] + b;
1842 }
1843 }
1844 }
1845 return;
1846 }
1847#endif
1849 A_q8, B, bias, C, M, N, K, m0, m1, n0, n1);
1850}
void gemm_nt_q6_k_q8_k_tile(const void *A_q8, const void *B, const float *bias, float *C, int M, int N, int K, int m0, int m1, int n0, int n1)
Compute one C[m0:m1, n0:n1] tile for Q8_K activations x Q6_K weights.

References C, ck_q6k_q8k_force_ref(), ck_strict_parity_enabled(), gemm_nt_q6_k_q8_k_tile(), and QK_K.

Referenced by gemm_nt_q6_k_q8_k_m2().

◆ gemm_nt_q6_k_q8_k_prepared()

void gemm_nt_q6_k_q8_k_prepared ( const void *  A_q8,
const void *  B_prepared,
const float *  bias,
float *  C,
int  M,
int  N,
int  K 
)

Definition at line 1719 of file gemm_kernels_q6k_q8k.c.

1724{
1726 A_q8, B_prepared, bias, C, M, N, K, 0, M, 0, N);
1727}
void gemm_nt_q6_k_q8_k_prepared_tile(const void *A_q8, const void *B_prepared, const float *bias, float *C, int M, int N, int K, int m0, int m1, int n0, int n1)

References C, and gemm_nt_q6_k_q8_k_prepared_tile().

◆ gemm_nt_q6_k_q8_k_prepared_avx512_vnni()

void gemm_nt_q6_k_q8_k_prepared_avx512_vnni ( const void *  A_q8,
const void *  B_prepared,
const float *  bias,
float *  C,
int  M,
int  N,
int  K 
)

Definition at line 1707 of file gemm_kernels_q6k_q8k.c.

1713{
1715 A_q8, B_prepared, bias, C, M, N, K,
1716 0, M, 0, N, 1);
1717}
static void gemm_nt_q6_k_q8_k_prepared_tile_impl(const void *A_q8, const void *B_prepared, const float *bias, float *C, int M, int N, int K, int m0, int m1, int n0, int n1, int use_avx512_vnni)

References C, and gemm_nt_q6_k_q8_k_prepared_tile_impl().

◆ gemm_nt_q6_k_q8_k_prepared_tile()

void gemm_nt_q6_k_q8_k_prepared_tile ( const void *  A_q8,
const void *  B_prepared,
const float *  bias,
float *  C,
int  M,
int  N,
int  K,
int  m0,
int  m1,
int  n0,
int  n1 
)

Definition at line 1689 of file gemm_kernels_q6k_q8k.c.

1696{
1697 int use_avx512_vnni = 0;
1698#if defined(__AVX512F__) && defined(__AVX512BW__) && \
1699 defined(__AVX512VNNI__)
1700 use_avx512_vnni = 1;
1701#endif
1703 A_q8, B_prepared, bias, C, M, N, K,
1704 m0, m1, n0, n1, use_avx512_vnni);
1705}

References C, and gemm_nt_q6_k_q8_k_prepared_tile_impl().

Referenced by gemm_nt_q6_k_q8_k_prepared().

◆ gemm_nt_q6_k_q8_k_prepared_tile_impl()

static void gemm_nt_q6_k_q8_k_prepared_tile_impl ( const void *  A_q8,
const void *  B_prepared,
const float *  bias,
float *  C,
int  M,
int  N,
int  K,
int  m0,
int  m1,
int  n0,
int  n1,
int  use_avx512_vnni 
)
static

Definition at line 1636 of file gemm_kernels_q6k_q8k.c.

1645{
1646#if !defined(__AVX2__)
1647 (void)A_q8; (void)B_prepared; (void)bias; (void)C;
1648 (void)M; (void)N; (void)K; (void)m0; (void)m1; (void)n0; (void)n1;
1649 (void)use_avx512_vnni;
1650#else
1651#if !defined(__AVX512F__) || !defined(__AVX512BW__) || \
1652 !defined(__AVX512VNNI__)
1653 (void)use_avx512_vnni;
1654#endif
1655 if (!A_q8 || !B_prepared || !C || M <= 0 || N <= 0 || K <= 0 ||
1656 (K % QK_K) != 0) return;
1657 if (m0 < 0) m0 = 0;
1658 if (n0 < 0) n0 = 0;
1659 if (m1 > M) m1 = M;
1660 if (n1 > N) n1 = N;
1661 if (m0 >= m1 || n0 >= n1) return;
1662
1663 const block_q8_K *A = (const block_q8_K *)A_q8;
1664 const block_q6_K_prepared *W =
1665 (const block_q6_K_prepared *)B_prepared;
1666 const int blocks_per_row = K / QK_K;
1667 for (int n = n0; n < n1; ++n) {
1668 const block_q6_K_prepared *w_row =
1669 W + (size_t)n * (size_t)blocks_per_row;
1670 const float b = bias ? bias[n] : 0.0f;
1671 for (int m = m0; m < m1; ++m) {
1672 const block_q8_K *a_row =
1673 A + (size_t)m * (size_t)blocks_per_row;
1674#if defined(__AVX512F__) && defined(__AVX512BW__) && \
1675 defined(__AVX512VNNI__)
1676 const float dot = use_avx512_vnni
1677 ? dot_q6_k_prepared_q8_k_avx512_vnni(w_row, a_row, K)
1678 : dot_q6_k_prepared_q8_k_avx2(w_row, a_row, K);
1679#else
1680 const float dot = dot_q6_k_prepared_q8_k_avx2(w_row, a_row, K);
1681#endif
1682 C[(size_t)m * (size_t)N + (size_t)n] =
1683 dot + b;
1684 }
1685 }
1686#endif
1687}

References C, and QK_K.

Referenced by gemm_nt_q6_k_q8_k_prepared_avx512_vnni(), and gemm_nt_q6_k_q8_k_prepared_tile().

◆ gemm_nt_q6_k_q8_k_tile()

void gemm_nt_q6_k_q8_k_tile ( const void *  A_q8,
const void *  B,
const float *  bias,
float *  C,
int  M,
int  N,
int  K,
int  m0,
int  m1,
int  n0,
int  n1 
)

Compute one C[m0:m1, n0:n1] tile for Q8_K activations x Q6_K weights.

Pure tile math only: no threadpool, no global scheduling, no allocation. The orchestrator decides how to split tile jobs across cores.

Definition at line 1760 of file gemm_kernels_q6k_q8k.c.

1767{
1768 if (!A_q8 || !B || !C) {
1769 return;
1770 }
1771 if (M <= 0 || N <= 0 || K <= 0 || K % QK_K != 0) {
1772 return;
1773 }
1774 if (m0 < 0) m0 = 0;
1775 if (n0 < 0) n0 = 0;
1776 if (m1 > M) m1 = M;
1777 if (n1 > N) n1 = N;
1778 if (m0 >= m1 || n0 >= n1) {
1779 return;
1780 }
1781
1782 const block_q8_K *A = (const block_q8_K *)A_q8;
1783 const block_q6_K *W = (const block_q6_K *)B;
1784 const int blocks_per_vec = K / QK_K;
1785 const int blocks_per_row = K / QK_K;
1786
1787 for (int n = n0; n < n1; ++n) {
1788 const block_q6_K *w_row = W + (size_t)n * (size_t)blocks_per_row;
1789 const float b = bias ? bias[n] : 0.0f;
1790 for (int m = m0; m < m1; ++m) {
1791 const block_q8_K *a_row = A + (size_t)m * (size_t)blocks_per_vec;
1792 C[(size_t)m * (size_t)N + (size_t)n] = ck_dot_q6_k_q8_k_fast_or_ref(w_row, a_row, K) + b;
1793 }
1794 }
1795}

References C, ck_dot_q6_k_q8_k_fast_or_ref(), and QK_K.

Referenced by gemm_nt_q6_k_q8_k_m4_tile(), and gemm_nt_q6_k_q8_k_tiled().

◆ gemm_nt_q6_k_q8_k_tiled()

void gemm_nt_q6_k_q8_k_tiled ( const void *  A_q8,
const void *  B,
const float *  bias,
float *  C,
int  M,
int  N,
int  K 
)

Experimental single-thread tiled NT GEMM wrapper.

Kept as a separate symbol from gemm_nt_q6_k_q8_k for benchmarks and parity. Production prefill should prefer the v8 2D tile scheduler when enabled.

Definition at line 1870 of file gemm_kernels_q6k_q8k.c.

1875{
1876 enum { TILE_M = 8, TILE_N = 16 };
1877 for (int n0 = 0; n0 < N; n0 += TILE_N) {
1878 const int n1 = (n0 + TILE_N < N) ? (n0 + TILE_N) : N;
1879 for (int m0 = 0; m0 < M; m0 += TILE_M) {
1880 const int m1 = (m0 + TILE_M < M) ? (m0 + TILE_M) : M;
1881 gemm_nt_q6_k_q8_k_tile(A_q8, B, bias, C, M, N, K, m0, m1, n0, n1);
1882 }
1883 }
1884}

References C, and gemm_nt_q6_k_q8_k_tile().

◆ gemm_nt_q6_k_q8_k_tiled_parity()

void gemm_nt_q6_k_q8_k_tiled_parity ( const void *  A_q8,
const void *  B,
const float *  bias,
float *  C,
int  M,
int  N,
int  K 
)

NT GEMM: C = A @ B^T where A is Q8_K and B is Q6_K.

This is the typical inference pattern:

  • A: Activations in Q8_K format [M x K]
  • B: Weights in Q6_K format [N x K]
  • C: Output [M x N]
Parameters
A_q8Input activations in Q8_K format
BWeight matrix in Q6_K format
biasOptional bias vector [N]
COutput matrix
MBatch size (number of tokens)
NOutput dimension
KInput dimension

Definition at line 1626 of file gemm_kernels_q6k_q8k.c.

1631{
1633 M, N, K, 0, M, 0, N);
1634}
void gemm_nt_q6_k_q8_k_tiled_parity_tile(const void *A_q8, const void *B, const float *bias, float *C, int M, int N, int K, int m0, int m1, int n0, int n1)

References C, and gemm_nt_q6_k_q8_k_tiled_parity_tile().

Referenced by gemm_nt_q6_k_q8_k().

◆ gemm_nt_q6_k_q8_k_tiled_parity_tile()

void gemm_nt_q6_k_q8_k_tiled_parity_tile ( const void *  A_q8,
const void *  B,
const float *  bias,
float *  C,
int  M,
int  N,
int  K,
int  m0,
int  m1,
int  n0,
int  n1 
)

Definition at line 1588 of file gemm_kernels_q6k_q8k.c.

1594{
1595 if (!A_q8 || !B || !C || M <= 0 || N <= 0 || K <= 0 || K % QK_K) return;
1596 if (m0 < 0 || n0 < 0 || m1 > M || n1 > N || m0 >= m1 || n0 >= n1) return;
1597 const block_q8_K *A = (const block_q8_K *) A_q8;
1598 const block_q6_K *W = (const block_q6_K *) B;
1599 const int nb = K / QK_K;
1600 for (int m = m0; m < m1; ++m) {
1601 for (int n = n0; n < n1; ++n) {
1602 float acc = 0.0f;
1603 for (int b = 0; b < nb; ++b) {
1604 const block_q6_K *w = W + (size_t)n * (size_t)nb + b;
1605 const block_q8_K *a = A + (size_t)m * (size_t)nb + b;
1606 int32_t dot = 0;
1607 for (int e = 0; e < QK_K; ++e) {
1608 const int group = (e / 128) * 128;
1609 const int pos = e % 128;
1610 const int lane = pos % 32;
1611 const int ql_off = group / 2 + lane + ((pos / 32) & 1) * 32;
1612 const int qh_off = group / 4 + lane;
1613 const int ql_shift = (pos >= 64) ? 4 : 0;
1614 const int qh_shift = (pos / 32) * 2;
1615 const int code = ((w->ql[ql_off] >> ql_shift) & 15) |
1616 (((w->qh[qh_off] >> qh_shift) & 3) << 4);
1617 dot += (code - 32) * (int)w->scales[e / 16] * (int)a->qs[e];
1618 }
1619 acc = fmaf(GGML_FP16_TO_FP32(w->d) * (float)dot, a->d, acc);
1620 }
1621 C[(size_t)m * (size_t)N + n] = acc + (bias ? bias[n] : 0.0f);
1622 }
1623 }
1624}

References C, block_q6_K::d, block_q8_K::d, GGML_FP16_TO_FP32, block_q6_K::qh, QK_K, block_q6_K::ql, block_q8_K::qs, and block_q6_K::scales.

Referenced by gemm_nt_q6_k_q8_k_tiled_parity().

◆ gemm_q6_k_q8_k()

void gemm_q6_k_q8_k ( float *  Y,
const void *  W,
const void *  X_q8,
int  M,
int  N,
int  K 
)

GEMM: Y = W @ X^T where W is Q6_K and X is Q8_K.

Parameters
YOutput matrix [N x M] in row-major
WWeight matrix in Q6_K format [M x K]
X_q8Input matrix in Q8_K format [N x K]
MNumber of output rows (output dim)
NNumber of input vectors (batch size)
KInput dimension

Definition at line 1508 of file gemm_kernels_q6k_q8k.c.

1512{
1513 if (!Y || !W || !X_q8 || M <= 0 || N <= 0 || K <= 0) {
1514 return;
1515 }
1516
1517 const block_q8_K *X = (const block_q8_K *)X_q8;
1518 const int blocks_per_vec = K / QK_K;
1519
1520 for (int n = 0; n < N; ++n) {
1521 const block_q8_K *x_row = X + (size_t)n * (size_t)blocks_per_vec;
1522 gemv_q6_k_q8_k(&Y[n * M], W, x_row, M, K);
1523 }
1524}
void gemv_q6_k_q8_k(float *y, const void *W, const void *x_q8, int M, int K)
GEMV: y = W @ x where W is Q6_K and x is Q8_K.

References gemv_q6_k_q8_k(), and QK_K.

◆ gemv_q6_k_q8_k()

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

GEMV: y = W @ x where W is Q6_K and x is Q8_K.

Definition at line 1356 of file gemm_kernels_q6k_q8k.c.

1360{
1362 gemv_q6_k_q8_k_ref(y, W, x_q8, M, K);
1363 return;
1364 }
1365
1366#if defined(__AVX2__)
1367 /* llama.cpp's x86 Q6_K production graph keeps the AVX2 reduction order
1368 * even when AVX-512 is available. Wider ISA availability is not a license
1369 * to change this numerical contract. */
1370 gemv_q6_k_q8_k_avx2(y, W, x_q8, M, K);
1371 return;
1372#elif defined(__AVX__)
1373 gemv_q6_k_q8_k_avx(y, W, x_q8, M, K);
1374 return;
1375#elif defined(__SSE4_1__)
1376 gemv_q6_k_q8_k_sse(y, W, x_q8, M, K);
1377 return;
1378#elif defined(__ARM_NEON) || defined(__aarch64__)
1379 /* NEON keeps the same integer-exact per-block dot as the scalar
1380 * reference but accumulates the FP32 block contributions sequentially
1381 * instead of through eight partial lanes, so strict parity mode (handled
1382 * above) still uses the reference reduction order. */
1383 gemv_q6_k_q8_k_neon(y, W, x_q8, M, K);
1384 return;
1385#endif
1386 gemv_q6_k_q8_k_ref(y, W, x_q8, M, K);
1387}
void gemv_q6_k_q8_k_ref(float *y, const void *W, const void *x_q8, int M, int K)
void gemv_q6_k_q8_k_sse(float *y, const void *W, const void *x_q8, int M, int K)
void gemv_q6_k_q8_k_avx(float *y, const void *W, const void *x_q8, int M, int K)
void gemv_q6_k_q8_k_avx2(float *y, const void *W, const void *x_q8, int M, int K)

References ck_q6k_q8k_force_ref(), ck_strict_parity_enabled(), gemv_q6_k_q8_k_avx(), gemv_q6_k_q8_k_avx2(), gemv_q6_k_q8_k_ref(), and gemv_q6_k_q8_k_sse().

Referenced by ck_moe_q4k_mixed_route_work(), ck_test_gemv_q6_k(), ck_test_vec_dot_q6_k_q8_k(), gemm_q6_k_q8_k(), moe_swiglu_expert_forward_q4k_q6k_workspace(), and moe_swiglu_shared_forward_q4k_q6k_workspace().

◆ gemv_q6_k_q8_k_avx()

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

Referenced by gemv_q6_k_q8_k().

◆ gemv_q6_k_q8_k_avx2()

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

Referenced by gemv_q6_k_q8_k().

◆ gemv_q6_k_q8_k_avx512()

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

◆ gemv_q6_k_q8_k_avx512_vbmi()

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

◆ gemv_q6_k_q8_k_parallel()

void gemv_q6_k_q8_k_parallel ( float *  y,
const void *  W,
const void *  x_q8,
int  M,
int  K,
int  ith,
int  nth 
)

Parallel reference GEMV for Q6_K × Q8_K.

Caller provides ith (thread index) and nth (total threads). Each thread processes rows [r0, r1).

Definition at line 1422 of file gemm_kernels_q6k_q8k.c.

1427{
1428 if (!y || !W || !x_q8 || M <= 0 || K <= 0) return;
1429 if (ith < 0 || nth <= 0 || ith >= nth) return;
1430
1431 /* Compute row range for this thread */
1432 const int dr = (M + nth - 1) / nth;
1433 const int r0 = dr * ith;
1434 const int r1 = (r0 + dr < M) ? (r0 + dr) : M;
1435
1436 if (r0 >= M) return;
1437
1438 const block_q6_K *blocks = (const block_q6_K *)W;
1439 const block_q8_K *x = (const block_q8_K *)x_q8;
1440 const int blocks_per_row = K / QK_K;
1441
1442 for (int row = r0; row < r1; ++row) {
1443 const block_q6_K *w_row = blocks + (size_t)row * (size_t)blocks_per_row;
1444 y[row] = dot_q6_k_q8_k_ref(w_row, x, K);
1445 }
1446}

References dot_q6_k_q8_k_ref(), and QK_K.

◆ gemv_q6_k_q8_k_parallel_simd()

void gemv_q6_k_q8_k_parallel_simd ( float *  y,
const void *  W,
const void *  x_q8,
int  M,
int  K,
int  ith,
int  nth 
)

Parallel SIMD GEMV for Q6_K × Q8_K.

Uses best available SIMD (AVX/SSE) with row prefetching. Caller provides ith/nth from OpenMP region.

Definition at line 1454 of file gemm_kernels_q6k_q8k.c.

1459{
1460 if (!y || !W || !x_q8 || M <= 0 || K <= 0) return;
1461 if (ith < 0 || nth <= 0 || ith >= nth) return;
1462
1463 const int dr = (M + nth - 1) / nth;
1464 const int r0 = dr * ith;
1465 const int r1 = (r0 + dr < M) ? (r0 + dr) : M;
1466
1467 if (r0 >= M) return;
1468
1469 const block_q6_K *blocks = (const block_q6_K *)W;
1470 const block_q8_K *x = (const block_q8_K *)x_q8;
1471 const int blocks_per_row = K / QK_K;
1472 const int strict = ck_strict_parity_enabled() || ck_q6k_q8k_force_ref();
1473
1474 for (int row = r0; row < r1; ++row) {
1475 const block_q6_K *w_row = blocks + (size_t)row * (size_t)blocks_per_row;
1476#if defined(__AVX2__)
1477 y[row] = strict ? dot_q6_k_q8_k_ref(w_row, x, K)
1478 : dot_q6_k_q8_k_avx2(w_row, x, K);
1479#elif defined(__AVX__)
1480 y[row] = strict ? dot_q6_k_q8_k_ref(w_row, x, K)
1481 : dot_q6_k_q8_k_avx(w_row, x, K);
1482#elif defined(__SSE4_1__)
1483 y[row] = strict ? dot_q6_k_q8_k_ref(w_row, x, K)
1484 : dot_q6_k_q8_k_sse(w_row, x, K);
1485#elif defined(__ARM_NEON) || defined(__aarch64__)
1486 y[row] = strict ? dot_q6_k_q8_k_ref(w_row, x, K)
1487 : dot_q6_k_q8_k_neon(w_row, x, K);
1488#else
1489 y[row] = dot_q6_k_q8_k_ref(w_row, x, K);
1490#endif
1491 }
1492}

References ck_q6k_q8k_force_ref(), ck_strict_parity_enabled(), dot_q6_k_q8_k_ref(), and QK_K.

◆ gemv_q6_k_q8_k_ref()

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

Definition at line 285 of file gemm_kernels_q6k_q8k.c.

289{
290 if (!y || !W || !x_q8 || M <= 0 || K <= 0) {
291 return;
292 }
293
294 const block_q6_K *blocks = (const block_q6_K *)W;
295 const block_q8_K *x = (const block_q8_K *)x_q8;
296 const int blocks_per_row = K / QK_K;
297
298 for (int row = 0; row < M; ++row) {
299 const block_q6_K *w_row = blocks + (size_t)row * (size_t)blocks_per_row;
300 y[row] = dot_q6_k_q8_k_ref(w_row, x, K);
301 }
302}

References dot_q6_k_q8_k_ref(), and QK_K.

Referenced by gemv_q6_k_q8_k().

◆ gemv_q6_k_q8_k_sse()

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

Referenced by gemv_q6_k_q8_k().

◆ vec_dot_q6_k_q8_k()

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

Q6_K x Q8_K dot product (single row)

Definition at line 1339 of file gemm_kernels_q6k_q8k.c.

1340{
1341 if (!s || !vx || !vy || n <= 0) {
1342 return;
1343 }
1344
1345 const block_q6_K *x = (const block_q6_K *)vx;
1346 const block_q8_K *y = (const block_q8_K *)vy;
1347
1348 /* This is the architecture-neutral scalar oracle. Production x86 dispatch
1349 * uses the separately parity-tested SIMD reduction tree. */
1350 *s = dot_q6_k_q8_k_ref(x, y, n);
1351}

References dot_q6_k_q8_k_ref().