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

GEMM/GEMV kernels with Q5_0 quantized weights. More...

#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include <stdio.h>
#include "ckernel_quant.h"
#include "ck_features.h"

Go to the source code of this file.

Functions

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)
 
float dot_q5_0 (const void *w_q5_0, const float *x, int K)
 
void gemm_nt_q5_0 (const float *A, const void *B, const float *bias, float *C, int M, int N, int K)
 
void gemm_nt_q5_0_q8_0 (const void *A_q8, const void *B_q5, const float *bias, float *C, int M, int N, int K)
 Batch GEMM with Q5_0 weights and Q8_0 activations for prefill.
 
void gemm_nt_q5_0_q8_0_m2n4 (const void *A_q8, const void *B_q5, const float *bias, float *C, int M, int N, int K)
 
void gemm_nt_q5_0_q8_0_m2n4_tile (const void *A_q8, const void *B_q5, const float *bias, float *C, int M, int N, int K, int ldc)
 
void gemm_nt_q5_0_q8_0_m4n2 (const void *A_q8, const void *B_q5, const float *bias, float *C, int M, int N, int K)
 
void gemm_nt_q5_0_q8_0_m4n2_tile (const void *A_q8, const void *B_q5, const float *bias, float *C, int M, int N, int K, int ldc)
 
void gemm_nt_q5_0_ref (const float *A, const void *B, const float *bias, float *C, int M, int N, int K)
 GEMM with transposed Q5_0 weights: C = A @ B^T.
 
void gemm_nt_q5_0_sse_v2 (const float *A, const void *B, const float *bias, float *C, int M, int N, int K)
 
void gemm_q5_0 (float *Y, const void *W, const float *X, int M, int N, int K)
 Matrix-matrix multiply with Q5_0 weights.
 
void gemm_q5_0_backward (float *dX, const void *W, const float *dY, int M, int N, int K)
 Batched backward pass.
 
void gemv_q5_0 (float *y, const void *W, const float *x, int M, int K)
 Auto-dispatch GEMV for Q5_0 weights based on CPU features.
 
void gemv_q5_0_backward (float *dX, const void *W, const float *dY, int M, int K)
 Auto-dispatch backward.
 
void gemv_q5_0_backward_ref (float *dX, const void *W, const float *dY, int M, int K)
 Backward pass: compute input gradient.
 
void gemv_q5_0_parallel (float *y, const void *W, const float *x, int M, int K, int ith, int nth)
 Parallel reference GEMV for Q5_0 × FP32.
 
void gemv_q5_0_parallel_simd (float *y, const void *W, const float *x, int M, int K, int ith, int nth)
 Parallel SIMD GEMV for Q5_0 × FP32 with prefetching.
 
void gemv_q5_0_q8_0 (float *y, const void *W, const void *x_q8, int M, int K)
 Matrix-vector multiply with Q5_0 weights and Q8_0 input.
 
void gemv_q5_0_q8_0_parallel_simd (float *y, const void *W, const void *x_q8, int M, int K, int ith, int nth)
 Parallel SIMD GEMV for Q5_0 x Q8_0 with prefetching.
 
void gemv_q5_0_ref (float *y, const void *W, const float *x, int M, int K)
 Matrix-vector multiply with Q5_0 weights (scalar reference)
 
void vec_dot_q5_0_q8_0 (int n, float *s, const void *vx, const void *vy)
 Auto-dispatch quantized dot product Q5_0 x Q8_0.
 
void vec_dot_q5_0_q8_0_ref (int n, float *s, const void *vx, const void *vy)
 Quantized dot product: Q5_0 weights x Q8_0 input (scalar reference)
 

Detailed Description

GEMM/GEMV kernels with Q5_0 quantized weights.

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

Q5_0 Format:

  • 32 weights per block
  • 1 FP16 scale per block
  • Low 4-bits stored like Q4_0 (16 bytes)
  • High 1-bit packed separately (4 bytes)
  • 22 bytes per 32 weights = 5.5 bits/weight

Dequantization: w = scale * (q5 - 16) where q5 = low4bit | (highbit << 4), giving values 0-31, then subtract 16 for signed -16 to +15

Operations: Forward: Y = W @ X (W is Q5_0, X and Y are FP32) Backward: dX = W^T @ dY (gradient w.r.t. input)

Definition in file gemm_kernels_q5_0.c.

Function Documentation

◆ dequant_q5_0_block()

void dequant_q5_0_block ( const block_q5_0 block,
float *  output 
)

Dequantize a single Q5_0 block to FP32.

Parameters
blockPointer to Q5_0 block (22 bytes)
outputOutput FP32 array (32 floats)

Definition at line 163 of file dequant_kernels.c.

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}
#define QK5_0
#define GGML_FP16_TO_FP32
uint8_t qh[4]
uint8_t qs[32/2]

References block_q5_0::d, GGML_FP16_TO_FP32, block_q5_0::qh, QK5_0, and block_q5_0::qs.

Referenced by dequant_q5_0_row().

◆ dequant_q5_0_row()

void dequant_q5_0_row ( const void *  src,
float *  dst,
size_t  n_elements 
)

Dequantize Q5_0 row (multiple blocks)

Definition at line 198 of file dequant_kernels.c.

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}
void dequant_q5_0_block(const block_q5_0 *block, float *output)
Dequantize a single Q5_0 block to FP32.

Referenced by dequant_row().

◆ dot_q5_0()

float dot_q5_0 ( const void *  w_q5_0,
const float *  x,
int  K 
)

Definition at line 872 of file gemm_kernels_q5_0.c.

873{
874 float result;
875 gemv_q5_0(&result, w_q5_0, x, 1, K);
876 return result;
877}
void gemv_q5_0(float *y, const void *W, const float *x, int M, int K)
Auto-dispatch GEMV for Q5_0 weights based on CPU features.

References gemv_q5_0().

◆ gemm_nt_q5_0()

void gemm_nt_q5_0 ( const float *  A,
const void *  B,
const float *  bias,
float *  C,
int  M,
int  N,
int  K 
)

Definition at line 833 of file gemm_kernels_q5_0.c.

838{
839 /* For decode (M=1), use direct GEMV which has AVX optimization */
840 if (M == 1) {
841 /* gemm_q5_0 expects column-major output, but we need row-major
842 * So we call gemv_q5_0 directly for each output element */
843 gemv_q5_0(C, B, A, N, K);
844 if (bias) {
845 for (int n = 0; n < N; n++) {
846 C[n] += bias[n];
847 }
848 }
849 return;
850 }
851
852 /* For prefill (M>1), use GEMM which dispatches to GEMV with AVX/AVX512 */
853 /* gemm_q5_0 produces Y as [batch x M_out]. Here:
854 * batch = M (tokens)
855 * M_out = N (output channels) */
856 gemm_q5_0(C, B, A, /*M_out=*/N, /*N_batch=*/M, K);
857
858 if (bias) {
859 for (int m = 0; m < M; m++) {
860 float *row = C + (size_t)m * (size_t)N;
861 for (int n = 0; n < N; n++) {
862 row[n] += bias[n];
863 }
864 }
865 }
866}
void gemm_q5_0(float *Y, const void *W, const float *X, int M, int N, int K)
Matrix-matrix multiply with Q5_0 weights.
#define C(color)
Definition show_config.c:39

References C, gemm_q5_0(), and gemv_q5_0().

Referenced by ck_gemm_nt_quant(), qwen2_0_5b_decode_layer_0_decode(), qwen2_0_5b_decode_layer_10_decode(), qwen2_0_5b_decode_layer_11_decode(), qwen2_0_5b_decode_layer_12_decode(), qwen2_0_5b_decode_layer_13_decode(), qwen2_0_5b_decode_layer_14_decode(), qwen2_0_5b_decode_layer_15_decode(), qwen2_0_5b_decode_layer_16_decode(), qwen2_0_5b_decode_layer_17_decode(), qwen2_0_5b_decode_layer_18_decode(), qwen2_0_5b_decode_layer_19_decode(), qwen2_0_5b_decode_layer_1_decode(), qwen2_0_5b_decode_layer_20_decode(), qwen2_0_5b_decode_layer_21_decode(), qwen2_0_5b_decode_layer_22_decode(), qwen2_0_5b_decode_layer_23_decode(), qwen2_0_5b_decode_layer_2_decode(), qwen2_0_5b_decode_layer_3_decode(), qwen2_0_5b_decode_layer_4_decode(), qwen2_0_5b_decode_layer_5_decode(), qwen2_0_5b_decode_layer_6_decode(), qwen2_0_5b_decode_layer_7_decode(), qwen2_0_5b_decode_layer_8_decode(), and qwen2_0_5b_decode_layer_9_decode().

◆ gemm_nt_q5_0_q8_0()

void gemm_nt_q5_0_q8_0 ( const void *  A_q8,
const void *  B_q5,
const float *  bias,
float *  C,
int  M,
int  N,
int  K 
)

Batch GEMM with Q5_0 weights and Q8_0 activations for prefill.

Computes C = A @ B^T + bias where: A: [M x K] Q8_0 quantized activations (M tokens, K features) B: [N x K] Q5_0 quantized weights (N outputs, K features) C: [M x N] FP32 output

This is the INT8 batch kernel for prefill, using pre-quantized activations to avoid FP32->Q8_0 conversion overhead per operation.

Parameters
A_q8Input activations in Q8_0 format [M rows of K/32 blocks each]
B_q5Weights in Q5_0 format [N rows of K/32 blocks each]
biasOptional bias vector [N], NULL if not used
COutput matrix [M x N], row-major FP32
MBatch size (number of tokens)
NOutput dimension (number of output features)
KInput dimension (must be multiple of 32)

Definition at line 1728 of file gemm_kernels_q5_0.c.

1736{
1737 const block_q5_0 *weights = (const block_q5_0 *)B_q5;
1738 const block_q8_0 *inputs = (const block_q8_0 *)A_q8;
1739 const int blocks_per_row = K / QK5_0;
1740
1741 for (int m = 0; m < M; m++) {
1742 const block_q8_0 *input_row = &inputs[m * blocks_per_row];
1743
1744 for (int n = 0; n < N; n++) {
1745 const block_q5_0 *weight_row = &weights[n * blocks_per_row];
1746 float *out = &C[m * N + n];
1747
1748 /* Dispatches to vec_dot_q5_0_q8_0_avx (2x block unrolled) on AVX */
1749 vec_dot_q5_0_q8_0(K, out, weight_row, input_row);
1750
1751 if (bias) {
1752 *out += bias[n];
1753 }
1754 }
1755 }
1756}
void vec_dot_q5_0_q8_0(int n, float *s, const void *vx, const void *vy)
Auto-dispatch quantized dot product Q5_0 x Q8_0.

References C, QK5_0, and vec_dot_q5_0_q8_0().

Referenced by ck_test_gemm_q5_0(), gemm_nt_q5_0_q8_0_m2n4_tile(), gemm_nt_q8_0_dispatch(), and gemm_nt_q8_0_mlp_dispatch().

◆ gemm_nt_q5_0_q8_0_m2n4()

void gemm_nt_q5_0_q8_0_m2n4 ( const void *  A_q8,
const void *  B_q5,
const float *  bias,
float *  C,
int  M,
int  N,
int  K 
)

Definition at line 1894 of file gemm_kernels_q5_0.c.

1902{
1903 gemm_nt_q5_0_q8_0_m2n4_tile(A_q8, B_q5, bias, C, M, N, K, N);
1904}
void gemm_nt_q5_0_q8_0_m2n4_tile(const void *A_q8, const void *B_q5, const float *bias, float *C, int M, int N, int K, int ldc)

References C, and gemm_nt_q5_0_q8_0_m2n4_tile().

◆ gemm_nt_q5_0_q8_0_m2n4_tile()

void gemm_nt_q5_0_q8_0_m2n4_tile ( const void *  A_q8,
const void *  B_q5,
const float *  bias,
float *  C,
int  M,
int  N,
int  K,
int  ldc 
)

Definition at line 1766 of file gemm_kernels_q5_0.c.

1775{
1776#if defined(__AVX2__)
1777 if (!A_q8 || !B_q5 || !C || M <= 0 || N <= 0 || K <= 0 ||
1778 (K % QK5_0) != 0 || ldc < N) {
1779 return;
1780 }
1781
1782 const block_q8_0 *a = (const block_q8_0 *)A_q8;
1783 const block_q5_0 *w = (const block_q5_0 *)B_q5;
1784 const int nb = K / QK5_0;
1785 int m = 0;
1786
1787 for (; m + 1 < M; m += 2) {
1788 const block_q8_0 *a0 = a + (size_t)(m + 0) * (size_t)nb;
1789 const block_q8_0 *a1 = a + (size_t)(m + 1) * (size_t)nb;
1790 int n = 0;
1791
1792 for (; n + 3 < N; n += 4) {
1793 const block_q5_0 *w0 = w + (size_t)(n + 0) * (size_t)nb;
1794 const block_q5_0 *w1 = w + (size_t)(n + 1) * (size_t)nb;
1795 const block_q5_0 *w2 = w + (size_t)(n + 2) * (size_t)nb;
1796 const block_q5_0 *w3 = w + (size_t)(n + 3) * (size_t)nb;
1797 __m256 acc00 = _mm256_setzero_ps();
1798 __m256 acc01 = _mm256_setzero_ps();
1799 __m256 acc02 = _mm256_setzero_ps();
1800 __m256 acc03 = _mm256_setzero_ps();
1801 __m256 acc10 = _mm256_setzero_ps();
1802 __m256 acc11 = _mm256_setzero_ps();
1803 __m256 acc12 = _mm256_setzero_ps();
1804 __m256 acc13 = _mm256_setzero_ps();
1805
1806 for (int ib = 0; ib < nb; ++ib) {
1807 __m256i qw0 = bytes_from_nibbles_32_avx(w0[ib].qs);
1808 __m256i qw1 = bytes_from_nibbles_32_avx(w1[ib].qs);
1809 __m256i qw2 = bytes_from_nibbles_32_avx(w2[ib].qs);
1810 __m256i qw3 = bytes_from_nibbles_32_avx(w3[ib].qs);
1811 const __m256i sign = _mm256_set1_epi8((char)0xF0);
1812 qw0 = _mm256_or_si256(qw0, _mm256_andnot_si256(bytes_from_bits_32_avx(w0[ib].qh), sign));
1813 qw1 = _mm256_or_si256(qw1, _mm256_andnot_si256(bytes_from_bits_32_avx(w1[ib].qh), sign));
1814 qw2 = _mm256_or_si256(qw2, _mm256_andnot_si256(bytes_from_bits_32_avx(w2[ib].qh), sign));
1815 qw3 = _mm256_or_si256(qw3, _mm256_andnot_si256(bytes_from_bits_32_avx(w3[ib].qh), sign));
1816
1817 const __m256i qa0 = _mm256_loadu_si256((const __m256i *)a0[ib].qs);
1818 const __m256i qa1 = _mm256_loadu_si256((const __m256i *)a1[ib].qs);
1819 const float da0 = CK_FP16_TO_FP32(a0[ib].d);
1820 const float da1 = CK_FP16_TO_FP32(a1[ib].d);
1821 const float dw0 = CK_FP16_TO_FP32(w0[ib].d);
1822 const float dw1 = CK_FP16_TO_FP32(w1[ib].d);
1823 const float dw2 = CK_FP16_TO_FP32(w2[ib].d);
1824 const float dw3 = CK_FP16_TO_FP32(w3[ib].d);
1825 const __m256 p00 = mul_sum_i8_pairs_float_avx(qw0, qa0);
1826 const __m256 p01 = mul_sum_i8_pairs_float_avx(qw1, qa0);
1827 const __m256 p02 = mul_sum_i8_pairs_float_avx(qw2, qa0);
1828 const __m256 p03 = mul_sum_i8_pairs_float_avx(qw3, qa0);
1829 const __m256 p10 = mul_sum_i8_pairs_float_avx(qw0, qa1);
1830 const __m256 p11 = mul_sum_i8_pairs_float_avx(qw1, qa1);
1831 const __m256 p12 = mul_sum_i8_pairs_float_avx(qw2, qa1);
1832 const __m256 p13 = mul_sum_i8_pairs_float_avx(qw3, qa1);
1833#if defined(__FMA__)
1834 acc00 = _mm256_fmadd_ps(_mm256_set1_ps(dw0 * da0), p00, acc00);
1835 acc01 = _mm256_fmadd_ps(_mm256_set1_ps(dw1 * da0), p01, acc01);
1836 acc02 = _mm256_fmadd_ps(_mm256_set1_ps(dw2 * da0), p02, acc02);
1837 acc03 = _mm256_fmadd_ps(_mm256_set1_ps(dw3 * da0), p03, acc03);
1838 acc10 = _mm256_fmadd_ps(_mm256_set1_ps(dw0 * da1), p10, acc10);
1839 acc11 = _mm256_fmadd_ps(_mm256_set1_ps(dw1 * da1), p11, acc11);
1840 acc12 = _mm256_fmadd_ps(_mm256_set1_ps(dw2 * da1), p12, acc12);
1841 acc13 = _mm256_fmadd_ps(_mm256_set1_ps(dw3 * da1), p13, acc13);
1842#else
1843 acc00 = _mm256_add_ps(_mm256_mul_ps(_mm256_set1_ps(dw0 * da0), p00), acc00);
1844 acc01 = _mm256_add_ps(_mm256_mul_ps(_mm256_set1_ps(dw1 * da0), p01), acc01);
1845 acc02 = _mm256_add_ps(_mm256_mul_ps(_mm256_set1_ps(dw2 * da0), p02), acc02);
1846 acc03 = _mm256_add_ps(_mm256_mul_ps(_mm256_set1_ps(dw3 * da0), p03), acc03);
1847 acc10 = _mm256_add_ps(_mm256_mul_ps(_mm256_set1_ps(dw0 * da1), p10), acc10);
1848 acc11 = _mm256_add_ps(_mm256_mul_ps(_mm256_set1_ps(dw1 * da1), p11), acc11);
1849 acc12 = _mm256_add_ps(_mm256_mul_ps(_mm256_set1_ps(dw2 * da1), p12), acc12);
1850 acc13 = _mm256_add_ps(_mm256_mul_ps(_mm256_set1_ps(dw3 * da1), p13), acc13);
1851#endif
1852 }
1853
1854 C[(size_t)(m + 0) * (size_t)ldc + n + 0] = hsum_float_8_avx(acc00) + (bias ? bias[n + 0] : 0.0f);
1855 C[(size_t)(m + 0) * (size_t)ldc + n + 1] = hsum_float_8_avx(acc01) + (bias ? bias[n + 1] : 0.0f);
1856 C[(size_t)(m + 0) * (size_t)ldc + n + 2] = hsum_float_8_avx(acc02) + (bias ? bias[n + 2] : 0.0f);
1857 C[(size_t)(m + 0) * (size_t)ldc + n + 3] = hsum_float_8_avx(acc03) + (bias ? bias[n + 3] : 0.0f);
1858 C[(size_t)(m + 1) * (size_t)ldc + n + 0] = hsum_float_8_avx(acc10) + (bias ? bias[n + 0] : 0.0f);
1859 C[(size_t)(m + 1) * (size_t)ldc + n + 1] = hsum_float_8_avx(acc11) + (bias ? bias[n + 1] : 0.0f);
1860 C[(size_t)(m + 1) * (size_t)ldc + n + 2] = hsum_float_8_avx(acc12) + (bias ? bias[n + 2] : 0.0f);
1861 C[(size_t)(m + 1) * (size_t)ldc + n + 3] = hsum_float_8_avx(acc13) + (bias ? bias[n + 3] : 0.0f);
1862 }
1863
1864 for (; n < N; ++n) {
1865 const block_q5_0 *wn = w + (size_t)n * (size_t)nb;
1866 vec_dot_q5_0_q8_0(K, &C[(size_t)(m + 0) * (size_t)ldc + n], wn, a0);
1867 vec_dot_q5_0_q8_0(K, &C[(size_t)(m + 1) * (size_t)ldc + n], wn, a1);
1868 if (bias) {
1869 C[(size_t)(m + 0) * (size_t)ldc + n] += bias[n];
1870 C[(size_t)(m + 1) * (size_t)ldc + n] += bias[n];
1871 }
1872 }
1873 }
1874
1875 if (m < M) {
1877 a + (size_t)m * (size_t)nb, w, bias,
1878 C + (size_t)m * (size_t)ldc, 1, N, K);
1879 }
1880#else
1881 if (ldc == N) {
1882 gemm_nt_q5_0_q8_0(A_q8, B_q5, bias, C, M, N, K);
1883 } else {
1884 const int nb = K / QK5_0;
1885 const block_q8_0 *a = (const block_q8_0 *)A_q8;
1886 for (int m = 0; m < M; ++m) {
1887 gemm_nt_q5_0_q8_0(a + (size_t)m * (size_t)nb, B_q5, bias,
1888 C + (size_t)m * (size_t)ldc, 1, N, K);
1889 }
1890 }
1891#endif
1892}
#define CK_FP16_TO_FP32(x)
void gemm_nt_q5_0_q8_0(const void *A_q8, const void *B_q5, const float *bias, float *C, int M, int N, int K)
Batch GEMM with Q5_0 weights and Q8_0 activations for prefill.

References C, CK_FP16_TO_FP32, gemm_nt_q5_0_q8_0(), QK5_0, and vec_dot_q5_0_q8_0().

Referenced by gemm_nt_q5_0_q8_0_m2n4(), and gemm_nt_q5_0_q8_0_m4n2_tile().

◆ gemm_nt_q5_0_q8_0_m4n2()

void gemm_nt_q5_0_q8_0_m4n2 ( const void *  A_q8,
const void *  B_q5,
const float *  bias,
float *  C,
int  M,
int  N,
int  K 
)

Definition at line 2017 of file gemm_kernels_q5_0.c.

2025{
2026 gemm_nt_q5_0_q8_0_m4n2_tile(A_q8, B_q5, bias, C, M, N, K, N);
2027}
void gemm_nt_q5_0_q8_0_m4n2_tile(const void *A_q8, const void *B_q5, const float *bias, float *C, int M, int N, int K, int ldc)

References C, and gemm_nt_q5_0_q8_0_m4n2_tile().

◆ gemm_nt_q5_0_q8_0_m4n2_tile()

void gemm_nt_q5_0_q8_0_m4n2_tile ( const void *  A_q8,
const void *  B_q5,
const float *  bias,
float *  C,
int  M,
int  N,
int  K,
int  ldc 
)

Definition at line 1908 of file gemm_kernels_q5_0.c.

1917{
1918#if defined(__AVX2__)
1919 if (!A_q8 || !B_q5 || !C || M <= 0 || N <= 0 || K <= 0 ||
1920 (K % QK5_0) != 0 || ldc < N) {
1921 return;
1922 }
1923
1924 const block_q8_0 *a = (const block_q8_0 *)A_q8;
1925 const block_q5_0 *w = (const block_q5_0 *)B_q5;
1926 const int nb = K / QK5_0;
1927 int m = 0;
1928
1929 for (; m + 3 < M; m += 4) {
1930 const block_q8_0 *a0 = a + (size_t)(m + 0) * (size_t)nb;
1931 const block_q8_0 *a1 = a + (size_t)(m + 1) * (size_t)nb;
1932 const block_q8_0 *a2 = a + (size_t)(m + 2) * (size_t)nb;
1933 const block_q8_0 *a3 = a + (size_t)(m + 3) * (size_t)nb;
1934 int n = 0;
1935
1936 for (; n + 1 < N; n += 2) {
1937 const block_q5_0 *w0 = w + (size_t)(n + 0) * (size_t)nb;
1938 const block_q5_0 *w1 = w + (size_t)(n + 1) * (size_t)nb;
1939 __m256 acc00 = _mm256_setzero_ps();
1940 __m256 acc01 = _mm256_setzero_ps();
1941 __m256 acc10 = _mm256_setzero_ps();
1942 __m256 acc11 = _mm256_setzero_ps();
1943 __m256 acc20 = _mm256_setzero_ps();
1944 __m256 acc21 = _mm256_setzero_ps();
1945 __m256 acc30 = _mm256_setzero_ps();
1946 __m256 acc31 = _mm256_setzero_ps();
1947
1948 for (int ib = 0; ib < nb; ++ib) {
1949 __m256i qw0 = bytes_from_nibbles_32_avx(w0[ib].qs);
1950 __m256i qw1 = bytes_from_nibbles_32_avx(w1[ib].qs);
1951 const __m256i sign = _mm256_set1_epi8((char)0xF0);
1952 qw0 = _mm256_or_si256(qw0, _mm256_andnot_si256(bytes_from_bits_32_avx(w0[ib].qh), sign));
1953 qw1 = _mm256_or_si256(qw1, _mm256_andnot_si256(bytes_from_bits_32_avx(w1[ib].qh), sign));
1954
1955 const __m256i qa0 = _mm256_loadu_si256((const __m256i *)a0[ib].qs);
1956 const __m256i qa1 = _mm256_loadu_si256((const __m256i *)a1[ib].qs);
1957 const __m256i qa2 = _mm256_loadu_si256((const __m256i *)a2[ib].qs);
1958 const __m256i qa3 = _mm256_loadu_si256((const __m256i *)a3[ib].qs);
1959 const float dw0 = CK_FP16_TO_FP32(w0[ib].d);
1960 const float dw1 = CK_FP16_TO_FP32(w1[ib].d);
1961 const float da0 = CK_FP16_TO_FP32(a0[ib].d);
1962 const float da1 = CK_FP16_TO_FP32(a1[ib].d);
1963 const float da2 = CK_FP16_TO_FP32(a2[ib].d);
1964 const float da3 = CK_FP16_TO_FP32(a3[ib].d);
1965#if defined(__FMA__)
1966 acc00 = _mm256_fmadd_ps(_mm256_set1_ps(dw0 * da0), mul_sum_i8_pairs_float_avx(qw0, qa0), acc00);
1967 acc01 = _mm256_fmadd_ps(_mm256_set1_ps(dw1 * da0), mul_sum_i8_pairs_float_avx(qw1, qa0), acc01);
1968 acc10 = _mm256_fmadd_ps(_mm256_set1_ps(dw0 * da1), mul_sum_i8_pairs_float_avx(qw0, qa1), acc10);
1969 acc11 = _mm256_fmadd_ps(_mm256_set1_ps(dw1 * da1), mul_sum_i8_pairs_float_avx(qw1, qa1), acc11);
1970 acc20 = _mm256_fmadd_ps(_mm256_set1_ps(dw0 * da2), mul_sum_i8_pairs_float_avx(qw0, qa2), acc20);
1971 acc21 = _mm256_fmadd_ps(_mm256_set1_ps(dw1 * da2), mul_sum_i8_pairs_float_avx(qw1, qa2), acc21);
1972 acc30 = _mm256_fmadd_ps(_mm256_set1_ps(dw0 * da3), mul_sum_i8_pairs_float_avx(qw0, qa3), acc30);
1973 acc31 = _mm256_fmadd_ps(_mm256_set1_ps(dw1 * da3), mul_sum_i8_pairs_float_avx(qw1, qa3), acc31);
1974#else
1975 acc00 = _mm256_add_ps(_mm256_mul_ps(_mm256_set1_ps(dw0 * da0), mul_sum_i8_pairs_float_avx(qw0, qa0)), acc00);
1976 acc01 = _mm256_add_ps(_mm256_mul_ps(_mm256_set1_ps(dw1 * da0), mul_sum_i8_pairs_float_avx(qw1, qa0)), acc01);
1977 acc10 = _mm256_add_ps(_mm256_mul_ps(_mm256_set1_ps(dw0 * da1), mul_sum_i8_pairs_float_avx(qw0, qa1)), acc10);
1978 acc11 = _mm256_add_ps(_mm256_mul_ps(_mm256_set1_ps(dw1 * da1), mul_sum_i8_pairs_float_avx(qw1, qa1)), acc11);
1979 acc20 = _mm256_add_ps(_mm256_mul_ps(_mm256_set1_ps(dw0 * da2), mul_sum_i8_pairs_float_avx(qw0, qa2)), acc20);
1980 acc21 = _mm256_add_ps(_mm256_mul_ps(_mm256_set1_ps(dw1 * da2), mul_sum_i8_pairs_float_avx(qw1, qa2)), acc21);
1981 acc30 = _mm256_add_ps(_mm256_mul_ps(_mm256_set1_ps(dw0 * da3), mul_sum_i8_pairs_float_avx(qw0, qa3)), acc30);
1982 acc31 = _mm256_add_ps(_mm256_mul_ps(_mm256_set1_ps(dw1 * da3), mul_sum_i8_pairs_float_avx(qw1, qa3)), acc31);
1983#endif
1984 }
1985
1986 C[(size_t)(m + 0) * (size_t)ldc + n + 0] = hsum_float_8_avx(acc00) + (bias ? bias[n + 0] : 0.0f);
1987 C[(size_t)(m + 0) * (size_t)ldc + n + 1] = hsum_float_8_avx(acc01) + (bias ? bias[n + 1] : 0.0f);
1988 C[(size_t)(m + 1) * (size_t)ldc + n + 0] = hsum_float_8_avx(acc10) + (bias ? bias[n + 0] : 0.0f);
1989 C[(size_t)(m + 1) * (size_t)ldc + n + 1] = hsum_float_8_avx(acc11) + (bias ? bias[n + 1] : 0.0f);
1990 C[(size_t)(m + 2) * (size_t)ldc + n + 0] = hsum_float_8_avx(acc20) + (bias ? bias[n + 0] : 0.0f);
1991 C[(size_t)(m + 2) * (size_t)ldc + n + 1] = hsum_float_8_avx(acc21) + (bias ? bias[n + 1] : 0.0f);
1992 C[(size_t)(m + 3) * (size_t)ldc + n + 0] = hsum_float_8_avx(acc30) + (bias ? bias[n + 0] : 0.0f);
1993 C[(size_t)(m + 3) * (size_t)ldc + n + 1] = hsum_float_8_avx(acc31) + (bias ? bias[n + 1] : 0.0f);
1994 }
1995
1996 if (n < N) {
1997 const block_q5_0 *wn = w + (size_t)n * (size_t)nb;
1998 const block_q8_0 *rows[4] = {a0, a1, a2, a3};
1999 for (int r = 0; r < 4; ++r) {
2000 float *out = &C[(size_t)(m + r) * (size_t)ldc + n];
2001 vec_dot_q5_0_q8_0(K, out, wn, rows[r]);
2002 if (bias) *out += bias[n];
2003 }
2004 }
2005 }
2006
2007 if (m < M) {
2009 a + (size_t)m * (size_t)nb, w, bias,
2010 C + (size_t)m * (size_t)ldc, M - m, N, K, ldc);
2011 }
2012#else
2013 gemm_nt_q5_0_q8_0_m2n4_tile(A_q8, B_q5, bias, C, M, N, K, ldc);
2014#endif
2015}

References C, CK_FP16_TO_FP32, gemm_nt_q5_0_q8_0_m2n4_tile(), QK5_0, and vec_dot_q5_0_q8_0().

Referenced by gemm_nt_q5_0_q8_0_m4n2().

◆ gemm_nt_q5_0_ref()

void gemm_nt_q5_0_ref ( const float *  A,
const void *  B,
const float *  bias,
float *  C,
int  M,
int  N,
int  K 
)

GEMM with transposed Q5_0 weights: C = A @ B^T.

Parameters
AInput activations [M x K], row-major FP32
BWeight matrix in Q5_0 format [N x K], row-major quantized
biasOptional bias [N], NULL if not used
COutput [M x N], row-major FP32
MBatch size (number of tokens)
NOutput dimension (number of rows in B)
KInput dimension

Definition at line 790 of file gemm_kernels_q5_0.c.

795{
796 const block_q5_0 *blocks = (const block_q5_0 *)B;
797 const int blocks_per_row = K / QK5_0;
798
799 for (int m = 0; m < M; m++) {
800 const float *a_row = &A[m * K];
801
802 for (int n = 0; n < N; n++) {
803 float sum = 0.0f;
804
805 for (int b = 0; b < blocks_per_row; b++) {
806 const block_q5_0 *block = &blocks[n * blocks_per_row + b];
807 const float d = CK_FP16_TO_FP32(block->d);
808 const float *ap = &a_row[b * QK5_0];
809
810 uint32_t qh;
811 memcpy(&qh, block->qh, sizeof(qh));
812
813 /* llama.cpp Q5_0 layout - note j+12 for second weight high bit */
814 for (int j = 0; j < QK5_0 / 2; j++) {
815 const uint8_t packed = block->qs[j];
816 const int lo = (packed & 0x0F);
817 const int hi = (packed >> 4);
818 const int xh_0 = ((qh >> (j + 0)) << 4) & 0x10;
819 const int xh_1 = ((qh >> (j + 12))) & 0x10;
820 const int q0 = (lo | xh_0) - 16;
821 const int q1 = (hi | xh_1) - 16;
822
823 sum += d * (float)q0 * ap[j];
824 sum += d * (float)q1 * ap[j + 16];
825 }
826 }
827
828 C[m * N + n] = sum + (bias ? bias[n] : 0.0f);
829 }
830 }
831}

References C, CK_FP16_TO_FP32, block_q5_0::d, block_q5_0::qh, QK5_0, and block_q5_0::qs.

Referenced by gemm_nt_q5_0_sse_v2().

◆ gemm_nt_q5_0_sse_v2()

void gemm_nt_q5_0_sse_v2 ( const float *  A,
const void *  B,
const float *  bias,
float *  C,
int  M,
int  N,
int  K 
)

Definition at line 77 of file gemm_kernels_q5_0_sse_v2.c.

82{
83 if (K % QK_K != 0) {
84 gemm_nt_q5_0_ref(A, B, bias, C, M, N, K);
85 return;
86 }
87
88 size_t q8_size = (K / QK_K) * sizeof(block_q8_K);
89 block_q8_K *A_q8 = (block_q8_K *)alloca(q8_size);
90
91 const block_q5_0 *weights = (const block_q5_0 *)B;
92 const int blocks_per_row = K / 32;
93
94 for (int m = 0; m < M; m++) {
95 quantize_row_q8_k(&A[m * K], A_q8, K);
96
97 for (int n = 0; n < N; n++) {
98 float sumf = 0.0f;
99 const block_q5_0 *w_row = weights + n * blocks_per_row;
100
101 for (int b = 0; b < blocks_per_row; b++) {
102 int q8_block_idx = (b * 32) / QK_K;
103 int q8_offset = (b * 32) % QK_K;
104 sumf += dot_q5_0_q8_k_32_sse(&w_row[b], &A_q8[q8_block_idx], q8_offset);
105 }
106
107 C[m * N + n] = sumf + (bias ? bias[n] : 0.0f);
108 }
109 }
110}
#define QK_K
void quantize_row_q8_k(const float *x, void *vy, int k)
void gemm_nt_q5_0_ref(const float *A, const void *B, const float *bias, float *C, int M, int N, int K)
GEMM with transposed Q5_0 weights: C = A @ B^T.
static float dot_q5_0_q8_k_32_sse(const block_q5_0 *bw, const block_q8_K *ba, int q8_offset)

◆ gemm_q5_0()

void gemm_q5_0 ( float *  Y,
const void *  W,
const float *  X,
int  M,
int  N,
int  K 
)

Matrix-matrix multiply with Q5_0 weights.

Definition at line 684 of file gemm_kernels_q5_0.c.

688{
689 for (int n = 0; n < N; n++) {
690 gemv_q5_0(&Y[n * M], W, &X[n * K], M, K);
691 }
692}

References gemv_q5_0().

Referenced by gemm_nt_q5_0().

◆ gemm_q5_0_backward()

void gemm_q5_0_backward ( float *  dX,
const void *  W,
const float *  dY,
int  M,
int  N,
int  K 
)

Batched backward pass.

Definition at line 764 of file gemm_kernels_q5_0.c.

768{
769 for (int n = 0; n < N; n++) {
770 gemv_q5_0_backward(&dX[n * K], W, &dY[n * M], M, K);
771 }
772}
void gemv_q5_0_backward(float *dX, const void *W, const float *dY, int M, int K)
Auto-dispatch backward.

References gemv_q5_0_backward().

◆ gemv_q5_0()

void gemv_q5_0 ( float *  y,
const void *  W,
const float *  x,
int  M,
int  K 
)

Auto-dispatch GEMV for Q5_0 weights based on CPU features.

Dispatch priority (best available):

  1. AVX-512 (512-bit vectors) - Intel Skylake-X+
  2. AVX2+FMA (256-bit vectors) - Intel Haswell+
  3. AVX (256-bit vectors) - Intel Sandy Bridge+
  4. SSE4.1 (128-bit vectors) - Intel Nehalem+
  5. Reference (scalar) - Fallback

Uses ck_features.h for standardized feature detection.

Parameters
yOutput vector [M]
WWeight matrix in Q5_0 format [M x K]
xInput vector [K]
MNumber of output rows
KNumber of input columns (hidden dimension)

Definition at line 550 of file gemm_kernels_q5_0.c.

554{
555// Dispatch order: AVX512 > AVX2 > AVX > SSE > ref
556#if defined(__AVX512F__)
557 gemv_q5_0_avx512(y, W, x, M, K);
558#elif defined(__AVX2__)
559 gemv_q5_0_avx2(y, W, x, M, K);
560#elif defined(__AVX__)
561 gemv_q5_0_avx(y, W, x, M, K);
562#elif defined(__SSE4_1__)
563 gemv_q5_0_ref(y, W, x, M, K);
564#else
565 gemv_q5_0_ref(y, W, x, M, K);
566#endif
567}
void gemv_q5_0_ref(float *y, const void *W, const float *x, int M, int K)
Matrix-vector multiply with Q5_0 weights (scalar reference)

References gemv_q5_0_ref().

Referenced by dot_q5_0(), gemm_nt_q5_0(), and gemm_q5_0().

◆ gemv_q5_0_backward()

void gemv_q5_0_backward ( float *  dX,
const void *  W,
const float *  dY,
int  M,
int  K 
)

Auto-dispatch backward.

Definition at line 753 of file gemm_kernels_q5_0.c.

757{
758 gemv_q5_0_backward_ref(dX, W, dY, M, K);
759}
void gemv_q5_0_backward_ref(float *dX, const void *W, const float *dY, int M, int K)
Backward pass: compute input gradient.

References gemv_q5_0_backward_ref().

Referenced by gemm_q5_0_backward().

◆ gemv_q5_0_backward_ref()

void gemv_q5_0_backward_ref ( float *  dX,
const void *  W,
const float *  dY,
int  M,
int  K 
)

Backward pass: compute input gradient.

Parameters
dXOutput gradient w.r.t. input [K]
WWeight matrix in Q5_0 format [M x K]
dYGradient w.r.t. output [M]
MNumber of output rows
KNumber of columns (input dimension)

Definition at line 707 of file gemm_kernels_q5_0.c.

711{
712 const block_q5_0 *blocks = (const block_q5_0 *)W;
713 const int blocks_per_row = K / QK5_0;
714
715 /* Zero output gradient */
716 memset(dX, 0, K * sizeof(float));
717
718 /* Accumulate: dX += W^T @ dY */
719 for (int row = 0; row < M; row++) {
720 const float dy = dY[row];
721
722 for (int b = 0; b < blocks_per_row; b++) {
723 const block_q5_0 *block = &blocks[row * blocks_per_row + b];
724 const float d = CK_FP16_TO_FP32(block->d);
725 float *dxp = &dX[b * QK5_0];
726
727 /* Get high bits */
728 uint32_t qh;
729 memcpy(&qh, block->qh, sizeof(qh));
730
731 /* llama.cpp Q5_0 layout - note j+12 for second weight high bit */
732 for (int j = 0; j < QK5_0 / 2; j++) {
733 const uint8_t packed = block->qs[j];
734
735 /* Extract and reconstruct 5-bit values */
736 const int lo = (packed & 0x0F);
737 const int hi = (packed >> 4);
738 const int xh_0 = ((qh >> (j + 0)) << 4) & 0x10;
739 const int xh_1 = ((qh >> (j + 12))) & 0x10;
740 const int q0 = (lo | xh_0) - 16;
741 const int q1 = (hi | xh_1) - 16;
742
743 dxp[j] += d * (float)q0 * dy;
744 dxp[j + 16] += d * (float)q1 * dy;
745 }
746 }
747 }
748}

References CK_FP16_TO_FP32, block_q5_0::d, block_q5_0::qh, QK5_0, and block_q5_0::qs.

Referenced by gemv_q5_0_backward().

◆ gemv_q5_0_parallel()

void gemv_q5_0_parallel ( float *  y,
const void *  W,
const float *  x,
int  M,
int  K,
int  ith,
int  nth 
)

Parallel reference GEMV for Q5_0 × FP32.

Definition at line 579 of file gemm_kernels_q5_0.c.

584{
585 if (!y || !W || !x || M <= 0 || K <= 0) return;
586 if (ith < 0 || nth <= 0 || ith >= nth) return;
587
588 const int dr = (M + nth - 1) / nth;
589 const int r0 = dr * ith;
590 const int r1 = (r0 + dr < M) ? (r0 + dr) : M;
591
592 if (r0 >= M) return;
593
594 const block_q5_0 *blocks = (const block_q5_0 *)W;
595 const int blocks_per_row = K / QK5_0;
596
597 for (int row = r0; row < r1; row++) {
598 float sum = 0.0f;
599 for (int b = 0; b < blocks_per_row; b++) {
600 const block_q5_0 *block = &blocks[row * blocks_per_row + b];
601 const float d = CK_FP16_TO_FP32(block->d);
602 const float *xp = &x[b * QK5_0];
603
604 uint32_t qh;
605 memcpy(&qh, block->qh, sizeof(qh));
606
607 for (int j = 0; j < QK5_0 / 2; j++) {
608 const uint8_t packed = block->qs[j];
609 const int lo = (packed & 0x0F);
610 const int hi = (packed >> 4);
611 const int xh_0 = ((qh >> (j + 0)) << 4) & 0x10;
612 const int xh_1 = ((qh >> (j + 12))) & 0x10;
613 const int w0 = (lo | xh_0) - 16;
614 const int w1 = (hi | xh_1) - 16;
615 sum += d * (w0 * xp[j] + w1 * xp[j + QK5_0/2]);
616 }
617 }
618 y[row] = sum;
619 }
620}

References CK_FP16_TO_FP32, block_q5_0::d, block_q5_0::qh, QK5_0, and block_q5_0::qs.

Referenced by gemv_q5_0_parallel_simd().

◆ gemv_q5_0_parallel_simd()

void gemv_q5_0_parallel_simd ( float *  y,
const void *  W,
const float *  x,
int  M,
int  K,
int  ith,
int  nth 
)

Parallel SIMD GEMV for Q5_0 × FP32 with prefetching.

Definition at line 625 of file gemm_kernels_q5_0.c.

630{
631 if (!y || !W || !x || M <= 0 || K <= 0) return;
632 if (ith < 0 || nth <= 0 || ith >= nth) return;
633
634 const int dr = (M + nth - 1) / nth;
635 const int r0 = dr * ith;
636 const int r1 = (r0 + dr < M) ? (r0 + dr) : M;
637
638 if (r0 >= M) return;
639
640#if defined(__AVX__) || defined(__SSE4_1__)
641 const block_q5_0 *blocks = (const block_q5_0 *)W;
642 const int blocks_per_row = K / QK5_0;
643 /* Prefetch first few rows */
644 const int PREFETCH_ROWS = 4;
645 for (int p = 0; p < PREFETCH_ROWS && r0 + p < r1; ++p) {
646 const char *row_ptr = (const char *)(blocks + (r0 + p) * blocks_per_row);
647 _mm_prefetch(row_ptr, _MM_HINT_T0);
648 _mm_prefetch(row_ptr + 64, _MM_HINT_T0);
649 }
650
651 for (int row = r0; row < r1; ++row) {
652 /* Prefetch rows ahead */
653 if (row + PREFETCH_ROWS < r1) {
654 const char *prefetch_ptr = (const char *)(blocks + (row + PREFETCH_ROWS) * blocks_per_row);
655 _mm_prefetch(prefetch_ptr, _MM_HINT_T0);
656 _mm_prefetch(prefetch_ptr + 64, _MM_HINT_T0);
657 }
658
659 /* Use SIMD dot product for this row */
660#if defined(__AVX512F__)
661 /* Call single-row AVX512 implementation */
662 gemv_q5_0_avx512(&y[row], (const char *)blocks + row * blocks_per_row * sizeof(block_q5_0), x, 1, K);
663#elif defined(__AVX2__)
664 gemv_q5_0_avx2(&y[row], (const char *)blocks + row * blocks_per_row * sizeof(block_q5_0), x, 1, K);
665#elif defined(__AVX__)
666 gemv_q5_0_avx(&y[row], (const char *)blocks + row * blocks_per_row * sizeof(block_q5_0), x, 1, K);
667#else
668 gemv_q5_0_ref(&y[row], (const char *)blocks + row * blocks_per_row * sizeof(block_q5_0), x, 1, K);
669#endif
670 }
671#else
672 /* Fallback to reference parallel */
673 gemv_q5_0_parallel(y, W, x, M, K, ith, nth);
674#endif
675}
void gemv_q5_0_parallel(float *y, const void *W, const float *x, int M, int K, int ith, int nth)
Parallel reference GEMV for Q5_0 × FP32.

References gemv_q5_0_parallel(), gemv_q5_0_ref(), and QK5_0.

◆ gemv_q5_0_q8_0()

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

Matrix-vector multiply with Q5_0 weights and Q8_0 input.

Parameters
yOutput vector [M]
WWeight matrix in Q5_0 format [M x K]
x_q8Input vector in Q8_0 format [K]
MNumber of output rows
KNumber of columns (must be multiple of 32)

Definition at line 1640 of file gemm_kernels_q5_0.c.

1644{
1645 const block_q5_0 *w_blocks = (const block_q5_0 *)W;
1646 const block_q8_0 *x_blocks = (const block_q8_0 *)x_q8;
1647 const int blocks_per_row = K / QK5_0;
1648
1649 for (int row = 0; row < M; row++) {
1650 vec_dot_q5_0_q8_0(K, &y[row],
1651 &w_blocks[row * blocks_per_row],
1652 x_blocks);
1653 }
1654}

References QK5_0, and vec_dot_q5_0_q8_0().

Referenced by ck_moe_q4k_mixed_route_work(), ck_test_gemv_q5_0(), ck_test_gemv_q5_0_q8_0(), moe_swiglu_expert_forward_q4k_q5_0_workspace(), and moe_swiglu_shared_forward_q4k_q5_0_gated_workspace().

◆ gemv_q5_0_q8_0_parallel_simd()

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

Parallel SIMD GEMV for Q5_0 x Q8_0 with prefetching.

Each thread processes rows [r0, r1) where r0 = ith * ceil(M/nth). Uses vec_dot_q5_0_q8_0 dispatch (auto-selects AVX512/AVX/SSE/scalar).

Definition at line 1662 of file gemm_kernels_q5_0.c.

1667{
1668 if (!y || !W || !x_q8 || M <= 0 || K <= 0) return;
1669 if (ith < 0 || nth <= 0 || ith >= nth) return;
1670
1671 const int dr = (M + nth - 1) / nth;
1672 const int r0 = dr * ith;
1673 const int r1 = (r0 + dr < M) ? (r0 + dr) : M;
1674
1675 if (r0 >= M) return;
1676
1677 const block_q5_0 *w_blocks = (const block_q5_0 *)W;
1678 const block_q8_0 *x_blocks = (const block_q8_0 *)x_q8;
1679 const int blocks_per_row = K / QK5_0;
1680
1681#if defined(__AVX__) || defined(__SSE4_1__)
1682 const int PREFETCH_ROWS = 4;
1683 for (int p = 0; p < PREFETCH_ROWS && r0 + p < r1; ++p) {
1684 const char *row_ptr = (const char *)(w_blocks + (r0 + p) * blocks_per_row);
1685 _mm_prefetch(row_ptr, _MM_HINT_T0);
1686 _mm_prefetch(row_ptr + 64, _MM_HINT_T0);
1687 }
1688
1689 for (int row = r0; row < r1; ++row) {
1690 if (row + PREFETCH_ROWS < r1) {
1691 const char *pf = (const char *)(w_blocks + (row + PREFETCH_ROWS) * blocks_per_row);
1692 _mm_prefetch(pf, _MM_HINT_T0);
1693 _mm_prefetch(pf + 64, _MM_HINT_T0);
1694 }
1695
1696 vec_dot_q5_0_q8_0(K, &y[row],
1697 &w_blocks[row * blocks_per_row],
1698 x_blocks);
1699 }
1700#else
1701 for (int row = r0; row < r1; row++) {
1702 vec_dot_q5_0_q8_0(K, &y[row],
1703 &w_blocks[row * blocks_per_row],
1704 x_blocks);
1705 }
1706#endif
1707}

References QK5_0, and vec_dot_q5_0_q8_0().

◆ gemv_q5_0_ref()

void gemv_q5_0_ref ( float *  y,
const void *  W,
const float *  x,
int  M,
int  K 
)

Matrix-vector multiply with Q5_0 weights (scalar reference)

Parameters
yOutput vector [M]
WWeight matrix in Q5_0 format [M x K]
xInput vector [K]
MNumber of output rows
KNumber of columns (must be multiple of 32)

Definition at line 67 of file gemm_kernels_q5_0.c.

71{
72 const block_q5_0 *blocks = (const block_q5_0 *)W;
73 const int blocks_per_row = K / QK5_0;
74
75 for (int row = 0; row < M; row++) {
76 float sum = 0.0f;
77
78 for (int b = 0; b < blocks_per_row; b++) {
79 const block_q5_0 *block = &blocks[row * blocks_per_row + b];
80 const float d = CK_FP16_TO_FP32(block->d);
81 const float *xp = &x[b * QK5_0];
82
83 /* Get high bits as 32-bit integer */
84 uint32_t qh;
85 memcpy(&qh, block->qh, sizeof(qh));
86
87 /* llama.cpp Q5_0 layout:
88 * - Weight j uses: low nibble of qs[j], high bit from qh bit j
89 * - Weight j+16 uses: high nibble of qs[j], high bit from qh bit (j+12)
90 * Note: j+12 not j+16 for the high bit of the second weight!
91 */
92 for (int j = 0; j < QK5_0 / 2; j++) {
93 const uint8_t packed = block->qs[j];
94
95 /* Extract nibbles */
96 const int lo = (packed & 0x0F);
97 const int hi = (packed >> 4);
98
99 /* Extract high bits - matches llama.cpp exactly */
100 const int xh_0 = ((qh >> (j + 0)) << 4) & 0x10;
101 const int xh_1 = ((qh >> (j + 12))) & 0x10;
102
103 /* Combine to 5-bit signed value */
104 const int q0 = (lo | xh_0) - 16;
105 const int q1 = (hi | xh_1) - 16;
106
107 /* Weights at indices j and j+16 */
108 sum += d * (float)q0 * xp[j];
109 sum += d * (float)q1 * xp[j + 16];
110 }
111 }
112
113 y[row] = sum;
114 }
115}

References CK_FP16_TO_FP32, block_q5_0::d, block_q5_0::qh, QK5_0, and block_q5_0::qs.

Referenced by gemv_q5_0(), and gemv_q5_0_parallel_simd().

◆ vec_dot_q5_0_q8_0()

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

Auto-dispatch quantized dot product Q5_0 x Q8_0.

Dispatch priority:

  1. AVX512 (best performance on modern Intel/AMD)
  2. AVX (256-bit float ops, works on Sandy/Ivy Bridge and newer)
  3. SSSE3 (128-bit fallback)
  4. Reference scalar (last resort)

Definition at line 1602 of file gemm_kernels_q5_0.c.

1603{
1604#if defined(__AVX2__)
1605 /* llama.cpp uses the packed AVX2 dot on AVX-512 hosts as well. It keeps
1606 * Q5/Q8 data in byte lanes and avoids the per-block 32-bit lane expansion
1607 * overhead of the baseline AVX-512 path. */
1608 vec_dot_q5_0_q8_0_avx2(n, s, vx, vy);
1609#elif defined(__AVX512F__)
1610 vec_dot_q5_0_q8_0_avx512(n, s, vx, vy);
1611#elif defined(__ARM_NEON) || defined(__aarch64__)
1612 vec_dot_q5_0_q8_0_neon(n, s, vx, vy);
1613#elif defined(__AVX__)
1614 /* AVX for 256-bit float ops (works on Ivy Bridge and newer) */
1615 vec_dot_q5_0_q8_0_avx(n, s, vx, vy);
1616#elif defined(__SSSE3__)
1617 /* SSSE3 - most efficient on older CPUs */
1618 vec_dot_q5_0_q8_0_sse(n, s, vx, vy);
1619#else
1620 vec_dot_q5_0_q8_0_ref(n, s, vx, vy);
1621#endif
1622}
void vec_dot_q5_0_q8_0_ref(int n, float *s, const void *vx, const void *vy)
Quantized dot product: Q5_0 weights x Q8_0 input (scalar reference)

References vec_dot_q5_0_q8_0_ref().

Referenced by ck_test_vec_dot_q5_0_q8_0(), gemm_nt_q5_0_q8_0(), gemm_nt_q5_0_q8_0_m2n4_tile(), gemm_nt_q5_0_q8_0_m4n2_tile(), gemv_fused_q5_0_bias_parallel_omp(), gemv_q5_0_from_fp32(), gemv_q5_0_q8_0(), gemv_q5_0_q8_0_parallel_omp(), gemv_q5_0_q8_0_parallel_simd(), mega_fused_attention_decode_q5_0(), mega_fused_attention_decode_q5_0_parallel_simd(), out_proj_head_major_q5_0_q8_0(), and out_proj_head_major_q5_0_q8_0().

◆ vec_dot_q5_0_q8_0_ref()

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

Quantized dot product: Q5_0 weights x Q8_0 input (scalar reference)

Parameters
nNumber of elements (must be multiple of 32)
sOutput: scalar dot product result
vxQ5_0 quantized weights
vyQ8_0 quantized input

Definition at line 901 of file gemm_kernels_q5_0.c.

902{
903 const int qk = QK5_0; /* 32 */
904 const int nb = n / qk;
905
906 const block_q5_0 *x = (const block_q5_0 *)vx;
907 const block_q8_0 *y = (const block_q8_0 *)vy;
908
909 float sumf = 0.0f;
910
911 for (int ib = 0; ib < nb; ib++) {
912 /* Load high bits for this block */
913 uint32_t qh;
914 memcpy(&qh, x[ib].qh, sizeof(qh));
915
916 int sumi0 = 0;
917 int sumi1 = 0;
918
919 for (int j = 0; j < qk / 2; j++) {
920 /* Extract high bits - matches llama.cpp exactly */
921 const uint8_t xh_0 = ((qh & (1u << (j + 0))) >> (j + 0)) << 4;
922 const uint8_t xh_1 = ((qh & (1u << (j + 16))) >> (j + 12));
923
924 /* Reconstruct 5-bit signed values (-16 to +15) */
925 const int32_t x0 = (int8_t)(((x[ib].qs[j] & 0x0F) | xh_0) - 16);
926 const int32_t x1 = (int8_t)(((x[ib].qs[j] >> 4) | xh_1) - 16);
927
928 /* Integer dot product with Q8_0 values */
929 sumi0 += x0 * y[ib].qs[j];
930 sumi1 += x1 * y[ib].qs[j + qk / 2];
931 }
932
933 int sumi = sumi0 + sumi1;
934 sumf += (CK_FP16_TO_FP32(x[ib].d) * CK_FP16_TO_FP32(y[ib].d)) * sumi;
935 }
936
937 *s = sumf;
938}
int8_t qs[32]

References CK_FP16_TO_FP32, QK5_0, and block_q8_0::qs.

Referenced by vec_dot_q5_0_q8_0().