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

Quantization block structures for weight-only quantization. More...

#include <stdint.h>
#include <stddef.h>
#include <math.h>
#include "ckernel_dtype.h"

Go to the source code of this file.

Data Structures

struct  block_nvfp4
 
struct  block_q4_0
 
struct  block_q4_1
 
struct  block_q4_K
 
struct  block_q5_0
 
struct  block_q5_1
 
struct  block_q6_K
 
struct  block_q8_0
 
struct  block_q8_K
 

Macros

#define CK_FP16_TO_FP32(x)   ck_fp16_to_fp32(x)
 
#define CK_FP16_TO_FP32_SIMD(x)   ck_fp16_to_fp32_simd(x)
 
#define CK_FP16_TO_FP32_SOFT(x)   ck_fp16_to_fp32_soft(x)
 
#define CK_FP32_TO_FP16(x)   ck_fp32_to_fp16(x)
 
#define CK_FP32_TO_FP16_SIMD(x)   ck_fp32_to_fp16_simd(x)
 
#define CK_FP32_TO_FP16_SOFT(x)   ck_fp32_to_fp16_soft(x)
 
#define ggml_fp16_to_fp32   ck_fp16_to_fp32
 
#define GGML_FP16_TO_FP32   CK_FP16_TO_FP32
 
#define ggml_fp32_to_fp16   ck_fp32_to_fp16
 
#define GGML_FP32_TO_FP16   CK_FP32_TO_FP16
 
#define K_SCALE_SIZE   12
 
#define QK4_0   32
 
#define QK4_1   32
 
#define QK5_0   32
 
#define QK5_1   32
 
#define QK8_0   32
 
#define QK_K   256
 
#define QK_NVFP4   64
 
#define QK_NVFP4_SUB   16
 

Typedefs

typedef uint16_t ck_half
 
typedef ck_half ggml_half
 

Functions

static float ck_fp16_to_fp32 (ck_half h)
 
static float ck_fp16_to_fp32_soft (ck_half h)
 Convert FP16 (ck_half) to FP32 — software implementation.
 
static float ck_fp32_from_bits (uint32_t u32)
 
static uint32_t ck_fp32_to_bits (float f)
 
static ck_half ck_fp32_to_fp16 (float f)
 
static ck_half ck_fp32_to_fp16_soft (float f)
 Convert FP32 to FP16 (ck_half) — software implementation.
 
static size_t ck_quant_block_size (int type)
 Get the block size (number of weights per block) for a quant type.
 
static size_t ck_quant_row_size (int type, int64_t n_elements)
 Calculate total bytes needed for n_elements with given quant type.
 
static size_t ck_quant_type_size (int type)
 Get the byte size per block for a quant type.
 
float ck_ue4m3_to_fp32 (uint8_t value)
 
void dequantize_row_nvfp4 (const void *weights, float *output, int k, float weight_scale)
 
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_unroll_avx (const void *A_q8, const void *B_q5, const float *bias, float *C, int M, int N, int K)
 
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_nt_q6_k_ref (const float *A, const void *B, const float *bias, float *C, int M, int N, int K)
 
void gemm_nt_q6_k_sse (const float *A, const void *B, const float *bias, float *C, int M, int N, int K)
 
void gemv_nvfp4_q8_0 (float *output, const void *weights, const float *weight_scales, const void *activations, int rows, int cols)
 
void gemv_q4_k_q8_k_sse (float *y, const void *W, const void *x_q8, int M, int K)
 
int moe_swiglu_expert_forward_nvfp4_workspace (const float *hidden, const int *indices, const float *routing_weights, const void *expert_gate, const float *expert_gate_scales, const void *expert_up, const float *expert_up_scales, const void *expert_down, const float *expert_down_scales, float *output, int rows, int hidden_dim, int intermediate_dim, int n_experts, int top_k, void *workspace, size_t workspace_bytes)
 
size_t moe_swiglu_nvfp4_workspace_bytes (int hidden_dim, int intermediate_dim)
 
int moe_swiglu_shared_forward_nvfp4_workspace (const float *hidden, const float *routed, const void *shared_gate, const float *shared_gate_scale, const void *shared_up, const float *shared_up_scale, const void *shared_down, const float *shared_down_scale, float *output, int rows, int hidden_dim, int intermediate_dim, float combination_scale, void *workspace, size_t workspace_bytes)
 
void quantize_row_q8_0 (const float *x, void *vy, int k)
 Quantize FP32 to Q8_0 format (scalar reference)
 
void quantize_row_q8_k_sse (const float *x, void *vy, int k)
 
void rmsnorm_q8_k_fused (const float *input, const float *gamma, void *vy, int tokens, int d_model, int aligned_embed_dim, float eps)
 
static void unpack_q4_k_scales (const uint8_t *scales, uint8_t *sc, uint8_t *m)
 Unpack Q4_K sub-block scales and mins.
 
void vec_dot_nvfp4_q8_0 (int n, float *output, const void *weights, const void *activations, float weight_scale)
 
void vec_dot_nvfp4_q8_0_ref (int n, float *output, const void *weights, const void *activations, float weight_scale)
 
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_q8_0_q8_0 (int n, float *s, const void *vx, const void *vy)
 Auto-dispatch quantized dot product Q8_0 x Q8_0.
 

Detailed Description

Quantization block structures for weight-only quantization.

Defines block structures for various quantization formats used in LLM inference. Primary focus on Q4_K_M which is commonly used for LLM weight compression.

Block structures are compatible with llama.cpp/GGML for model loading.

Definition in file ckernel_quant.h.

Macro Definition Documentation

◆ CK_FP16_TO_FP32

#define CK_FP16_TO_FP32 (   x)    ck_fp16_to_fp32(x)

Definition at line 440 of file ckernel_quant.h.

◆ CK_FP16_TO_FP32_SIMD

#define CK_FP16_TO_FP32_SIMD (   x)    ck_fp16_to_fp32_simd(x)

Definition at line 442 of file ckernel_quant.h.

◆ CK_FP16_TO_FP32_SOFT

#define CK_FP16_TO_FP32_SOFT (   x)    ck_fp16_to_fp32_soft(x)

Definition at line 444 of file ckernel_quant.h.

◆ CK_FP32_TO_FP16

#define CK_FP32_TO_FP16 (   x)    ck_fp32_to_fp16(x)

Definition at line 441 of file ckernel_quant.h.

◆ CK_FP32_TO_FP16_SIMD

#define CK_FP32_TO_FP16_SIMD (   x)    ck_fp32_to_fp16_simd(x)

Definition at line 443 of file ckernel_quant.h.

◆ CK_FP32_TO_FP16_SOFT

#define CK_FP32_TO_FP16_SOFT (   x)    ck_fp32_to_fp16_soft(x)

Definition at line 445 of file ckernel_quant.h.

◆ ggml_fp16_to_fp32

#define ggml_fp16_to_fp32   ck_fp16_to_fp32

Definition at line 449 of file ckernel_quant.h.

◆ GGML_FP16_TO_FP32

#define GGML_FP16_TO_FP32   CK_FP16_TO_FP32

Definition at line 451 of file ckernel_quant.h.

◆ ggml_fp32_to_fp16

#define ggml_fp32_to_fp16   ck_fp32_to_fp16

Definition at line 450 of file ckernel_quant.h.

◆ GGML_FP32_TO_FP16

#define GGML_FP32_TO_FP16   CK_FP32_TO_FP16

Definition at line 452 of file ckernel_quant.h.

◆ K_SCALE_SIZE

#define K_SCALE_SIZE   12

Definition at line 176 of file ckernel_quant.h.

◆ QK4_0

#define QK4_0   32

Definition at line 36 of file ckernel_quant.h.

◆ QK4_1

#define QK4_1   32

Definition at line 51 of file ckernel_quant.h.

◆ QK5_0

#define QK5_0   32

Definition at line 68 of file ckernel_quant.h.

◆ QK5_1

#define QK5_1   32

Definition at line 85 of file ckernel_quant.h.

◆ QK8_0

#define QK8_0   32

Definition at line 102 of file ckernel_quant.h.

◆ QK_K

#define QK_K   256

Definition at line 175 of file ckernel_quant.h.

◆ QK_NVFP4

#define QK_NVFP4   64

Definition at line 124 of file ckernel_quant.h.

◆ QK_NVFP4_SUB

#define QK_NVFP4_SUB   16

Definition at line 125 of file ckernel_quant.h.

Typedef Documentation

◆ ck_half

typedef uint16_t ck_half

Definition at line 27 of file ckernel_quant.h.

◆ ggml_half

typedef ck_half ggml_half

Definition at line 448 of file ckernel_quant.h.

Function Documentation

◆ ck_fp16_to_fp32()

static float ck_fp16_to_fp32 ( ck_half  h)
inlinestatic

Definition at line 423 of file ckernel_quant.h.

423 {
424#if defined(__F16C__)
425 return ck_fp16_to_fp32_simd(h);
426#else
427 return ck_fp16_to_fp32_soft(h);
428#endif
429}
static float ck_fp16_to_fp32_soft(ck_half h)
Convert FP16 (ck_half) to FP32 — software implementation.

References ck_fp16_to_fp32_soft().

Referenced by ck_mrope_round_storage(), and qwen4_ple_gate_conv_inject_impl().

◆ ck_fp16_to_fp32_soft()

static float ck_fp16_to_fp32_soft ( ck_half  h)
inlinestatic

Convert FP16 (ck_half) to FP32 — software implementation.

Definition at line 325 of file ckernel_quant.h.

325 {
326 uint32_t sign = (h & 0x8000) << 16;
327 uint32_t exp = (h >> 10) & 0x1F;
328 uint32_t mant = h & 0x3FF;
329
330 uint32_t result;
331
332 if (exp == 0) {
333 if (mant == 0) {
334 result = sign;
335 } else {
336 /* Denormalized - convert to normalized FP32 */
337 exp = 1;
338 while ((mant & 0x400) == 0) {
339 mant <<= 1;
340 exp--;
341 }
342 mant &= 0x3FF;
343 result = sign | ((exp + 127 - 15) << 23) | (mant << 13);
344 }
345 } else if (exp == 31) {
346 result = sign | 0x7F800000 | (mant << 13);
347 } else {
348 result = sign | ((exp + 127 - 15) << 23) | (mant << 13);
349 }
350
351 union { uint32_t u; float f; } u;
352 u.u = result;
353 return u.f;
354}

Referenced by ck_fp16_to_fp32().

◆ ck_fp32_from_bits()

static float ck_fp32_from_bits ( uint32_t  u32)
inlinestatic

Definition at line 362 of file ckernel_quant.h.

362 {
363 union { uint32_t u; float f; } u;
364 u.u = u32;
365 return u.f;
366}

Referenced by ck_fp32_to_fp16_soft().

◆ ck_fp32_to_bits()

static uint32_t ck_fp32_to_bits ( float  f)
inlinestatic

Definition at line 356 of file ckernel_quant.h.

356 {
357 union { float f; uint32_t u; } u;
358 u.f = f;
359 return u.u;
360}

Referenced by ck_fp32_to_fp16_soft().

◆ ck_fp32_to_fp16()

static ck_half ck_fp32_to_fp16 ( float  f)
inlinestatic

Definition at line 431 of file ckernel_quant.h.

431 {
432#if defined(__F16C__)
433 return ck_fp32_to_fp16_simd(f);
434#else
435 return ck_fp32_to_fp16_soft(f);
436#endif
437}
static ck_half ck_fp32_to_fp16_soft(float f)
Convert FP32 to FP16 (ck_half) — software implementation.

References ck_fp32_to_fp16_soft().

Referenced by ck_mrope_round_storage().

◆ ck_fp32_to_fp16_soft()

static ck_half ck_fp32_to_fp16_soft ( float  f)
inlinestatic

Convert FP32 to FP16 (ck_half) — software implementation.

Definition at line 371 of file ckernel_quant.h.

371 {
372#if (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) || defined(__GNUC__) && !defined(__STRICT_ANSI__)) && (!defined(__cplusplus) || __cplusplus >= 201703L)
373 const float scale_to_inf = 0x1.0p+112f;
374 const float scale_to_zero = 0x1.0p-110f;
375#else
376 const float scale_to_inf = ck_fp32_from_bits(UINT32_C(0x77800000));
377 const float scale_to_zero = ck_fp32_from_bits(UINT32_C(0x08800000));
378#endif
379 float base = (fabsf(f) * scale_to_inf) * scale_to_zero;
380
381 const uint32_t w = ck_fp32_to_bits(f);
382 const uint32_t shl1_w = w + w;
383 const uint32_t sign = w & UINT32_C(0x80000000);
384 uint32_t bias = shl1_w & UINT32_C(0xFF000000);
385 if (bias < UINT32_C(0x71000000)) {
386 bias = UINT32_C(0x71000000);
387 }
388
389 base = ck_fp32_from_bits((bias >> 1) + UINT32_C(0x07800000)) + base;
390 const uint32_t bits = ck_fp32_to_bits(base);
391 const uint32_t exp_bits = (bits >> 13) & UINT32_C(0x00007C00);
392 const uint32_t mantissa_bits = bits & UINT32_C(0x00000FFF);
393 const uint32_t nonsign = exp_bits + mantissa_bits;
394
395 return (ck_half) ((sign >> 16) | (shl1_w > UINT32_C(0xFF000000) ? UINT16_C(0x7E00) : nonsign));
396}
static uint32_t ck_fp32_to_bits(float f)
static float ck_fp32_from_bits(uint32_t u32)
uint16_t ck_half

References ck_fp32_from_bits(), and ck_fp32_to_bits().

Referenced by ck_fp32_to_fp16().

◆ ck_quant_block_size()

static size_t ck_quant_block_size ( int  type)
inlinestatic

Get the block size (number of weights per block) for a quant type.

Definition at line 227 of file ckernel_quant.h.

227 {
228 switch (type) {
229 case 0: return QK4_0; /* Q4_0 */
230 case 1: return QK8_0; /* Q8_0 */
231 case 2: return QK_K; /* Q4_K */
232 case 3: return QK_K; /* Q8_K */
233 case CK_DT_Q4_1: return QK4_1;
234 case CK_DT_Q5_0: return QK5_0;
235 case CK_DT_Q5_1: return QK5_1;
236 case CK_DT_Q6_K: return QK_K;
237 default: return 1;
238 }
239}
@ CK_DT_Q5_0
@ CK_DT_Q6_K
@ CK_DT_Q4_1
@ CK_DT_Q5_1
#define QK5_0
#define QK5_1
#define QK4_0
#define QK4_1
#define QK8_0
#define QK_K

References CK_DT_Q4_1, CK_DT_Q5_0, CK_DT_Q5_1, CK_DT_Q6_K, QK4_0, QK4_1, QK5_0, QK5_1, QK8_0, and QK_K.

Referenced by ck_quant_row_size().

◆ ck_quant_row_size()

static size_t ck_quant_row_size ( int  type,
int64_t  n_elements 
)
inlinestatic

Calculate total bytes needed for n_elements with given quant type.

Definition at line 261 of file ckernel_quant.h.

261 {
262 size_t block_size = ck_quant_block_size(type);
263 size_t type_size = ck_quant_type_size(type);
264 return (n_elements / block_size) * type_size;
265}
static size_t ck_quant_type_size(int type)
Get the byte size per block for a quant type.
static size_t ck_quant_block_size(int type)
Get the block size (number of weights per block) for a quant type.

References ck_quant_block_size(), and ck_quant_type_size().

◆ ck_quant_type_size()

static size_t ck_quant_type_size ( int  type)
inlinestatic

Get the byte size per block for a quant type.

Definition at line 244 of file ckernel_quant.h.

244 {
245 switch (type) {
246 case 0: return sizeof(block_q4_0);
247 case 1: return sizeof(block_q8_0);
248 case 2: return sizeof(block_q4_K);
249 case 3: return sizeof(block_q8_K);
250 case CK_DT_Q4_1: return sizeof(block_q4_1);
251 case CK_DT_Q5_0: return sizeof(block_q5_0);
252 case CK_DT_Q5_1: return sizeof(block_q5_1);
253 case CK_DT_Q6_K: return sizeof(block_q6_K);
254 default: return 4; /* FP32 */
255 }
256}

References CK_DT_Q4_1, CK_DT_Q5_0, CK_DT_Q5_1, and CK_DT_Q6_K.

Referenced by ck_quant_row_size().

◆ ck_ue4m3_to_fp32()

float ck_ue4m3_to_fp32 ( uint8_t  value)

Definition at line 53 of file gemm_kernels_nvfp4.c.

54{
55 return ck_ue4m3_to_fp32_inline(value);
56}
static float ck_ue4m3_to_fp32_inline(uint8_t value)

References ck_ue4m3_to_fp32_inline().

◆ dequantize_row_nvfp4()

void dequantize_row_nvfp4 ( const void *  weights,
float *  output,
int  k,
float  weight_scale 
)

Definition at line 58 of file gemm_kernels_nvfp4.c.

60{
61 assert(k >= 0 && k % QK_NVFP4 == 0);
62 const block_nvfp4 *blocks = (const block_nvfp4 *)weights;
63 const int block_count = k / QK_NVFP4;
64
65 for (int block_index = 0; block_index < block_count; ++block_index) {
66 const block_nvfp4 *block = &blocks[block_index];
67 for (int sub = 0; sub < QK_NVFP4 / QK_NVFP4_SUB; ++sub) {
68 const float scale = ck_ue4m3_to_fp32_inline(block->d[sub]) * weight_scale;
69 const uint8_t *packed = &block->qs[sub * (QK_NVFP4_SUB / 2)];
70 float *dst = &output[block_index * QK_NVFP4 + sub * QK_NVFP4_SUB];
71 for (int lane = 0; lane < QK_NVFP4_SUB / 2; ++lane) {
72 const uint8_t pair = packed[lane];
73 dst[lane] = scale * (float)ck_nvfp4_e2m1_x2[pair & 0x0f];
74 dst[lane + QK_NVFP4_SUB / 2] =
75 scale * (float)ck_nvfp4_e2m1_x2[pair >> 4];
76 }
77 }
78 }
79}
#define QK_NVFP4_SUB
#define QK_NVFP4
static const int8_t ck_nvfp4_e2m1_x2[16]
uint8_t qs[64/2]
uint8_t d[64/16]

References ck_nvfp4_e2m1_x2, ck_ue4m3_to_fp32_inline(), block_nvfp4::d, QK_NVFP4, QK_NVFP4_SUB, and block_nvfp4::qs.

◆ 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.
#define C(color)
Definition show_config.c:39

References C, QK5_0, and vec_dot_q5_0_q8_0().

Referenced by gemm_nt_q8_0_dispatch(), and gemm_nt_q8_0_mlp_dispatch().

◆ gemm_nt_q5_0_q8_0_unroll_avx()

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

◆ 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}
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)

References C, dot_q5_0_q8_k_32_sse(), gemm_nt_q5_0_ref(), QK_K, and quantize_row_q8_k().

◆ gemm_nt_q6_k_ref()

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

Definition at line 243 of file gemm_kernels_q6k.c.

248{
249 gemm_nt_q6_k(A, B, bias, C, M, N, K);
250}
void gemm_nt_q6_k(const float *A, const void *B, const float *bias, float *C, int M, int N, int K)

References C, and gemm_nt_q6_k().

Referenced by gemm_nt_q6_k_sse().

◆ gemm_nt_q6_k_sse()

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

Definition at line 66 of file gemm_kernels_q6k_sse.c.

71{
72 if (K % QK_K != 0) {
73 gemm_nt_q6_k_ref(A, B, bias, C, M, N, K);
74 return;
75 }
76
77 size_t q8_size = (K / QK_K) * sizeof(block_q8_K);
78 block_q8_K *A_q8 = (block_q8_K *)alloca(q8_size);
79
80 const block_q6_K *weights = (const block_q6_K *)B;
81 const int blocks_per_row = K / QK_K;
82
83 for (int m = 0; m < M; m++) {
84 quantize_row_q8_k(&A[m * K], A_q8, K);
85
86 for (int n = 0; n < N; n++) {
87 float sumf = 0.0f;
88 const block_q6_K *w_row = weights + n * blocks_per_row;
89
90 for (int b = 0; b < blocks_per_row; b++) {
91 sumf += dot_q6_k_q8_k_256_sse(&w_row[b], &A_q8[b]);
92 }
93
94 C[m * N + n] = sumf + (bias ? bias[n] : 0.0f);
95 }
96 }
97}
void gemm_nt_q6_k_ref(const float *A, const void *B, const float *bias, float *C, int M, int N, int K)
static float dot_q6_k_q8_k_256_sse(const block_q6_K *bw, const block_q8_K *ba)
void quantize_row_q8_k(const float *x, void *vy, int k)

References C, dot_q6_k_q8_k_256_sse(), gemm_nt_q6_k_ref(), QK_K, and quantize_row_q8_k().

◆ gemv_nvfp4_q8_0()

void gemv_nvfp4_q8_0 ( float *  output,
const void *  weights,
const float *  weight_scales,
const void *  activations,
int  rows,
int  cols 
)

Definition at line 203 of file gemm_kernels_nvfp4.c.

206{
207 assert(rows >= 0 && cols >= 0 && cols % QK_NVFP4 == 0);
208 const size_t row_bytes = (size_t)(cols / QK_NVFP4) * sizeof(block_nvfp4);
209 const uint8_t *weight_bytes = (const uint8_t *)weights;
210 for (int row = 0; row < rows; ++row) {
211 const float scale = weight_scales ? weight_scales[row] : 1.0f;
212 vec_dot_nvfp4_q8_0(cols, &output[row],
213 weight_bytes + (size_t)row * row_bytes,
214 activations, scale);
215 }
216}
void vec_dot_nvfp4_q8_0(int n, float *output, const void *weights, const void *activations, float weight_scale)

References QK_NVFP4, and vec_dot_nvfp4_q8_0().

◆ gemv_q4_k_q8_k_sse()

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

Definition at line 33 of file gemm_kernels_q4k_sse.c.

37{
38 const block_q4_K *blocks = (const block_q4_K *)W;
39 const block_q8_K *x = (const block_q8_K *)x_q8;
40 const int blocks_per_row = K / QK_K;
41
42 const __m128i mask_low = _mm_set1_epi8(0x0F);
43
44 for (int row = 0; row < M; ++row) {
45 float sumf = 0.0f;
46 const block_q4_K *w_row = blocks + row * blocks_per_row;
47
48 for (int i = 0; i < blocks_per_row; ++i) {
49 const block_q4_K *b4 = &w_row[i];
50 const block_q8_K *b8 = &x[i];
51
52 // Unpack scales (same as ref)
53 uint8_t sc[8], m_val[8];
54 unpack_q4_k_scales(b4->scales, sc, m_val);
55
56 float d = CK_FP16_TO_FP32(b4->d) * b8->d;
57 float dmin = CK_FP16_TO_FP32(b4->dmin) * b8->d;
58
59 int is = 0;
60 int q_offset = 0;
61
62 // Process 4 chunks of 64 elements (256 total)
63 for (int j = 0; j < QK_K; j += 64) {
64 // We process 32 bytes of qs (covering 64 elements via low/high nibbles)
65 // We access qs[0..31] relative to q_offset
66
67 // Accumulators for this 64-element chunk
68 __m128i acc_lo = _mm_setzero_si128();
69 __m128i acc_hi = _mm_setzero_si128();
70
71 // Inner loop: 2 iters of 16 bytes (32 elements)
72 for (int l = 0; l < 32; l += 16) {
73 // Load 16 bytes of Q4
74 __m128i q4_vec = _mm_loadu_si128((const __m128i *)(b4->qs + q_offset + l));
75
76 // Low nibbles -> correspond to q8_lo (elements j+l .. j+l+15)
77 __m128i q4_lo = _mm_and_si128(q4_vec, mask_low);
78
79 // High nibbles -> correspond to q8_hi (elements j+32+l .. j+32+l+15)
80 __m128i q4_hi = _mm_and_si128(_mm_srli_epi16(q4_vec, 4), mask_low);
81
82 // Load Q8
83 __m128i q8_lo_vec = _mm_loadu_si128((const __m128i *)(b8->qs + j + l));
84 __m128i q8_hi_vec = _mm_loadu_si128((const __m128i *)(b8->qs + j + 32 + l));
85
86 // Expand and Multiply-Add: Q4(u8) * Q8(s8) -> i32
87 // Since Q4 is u8 and Q8 is s8, we use intermediate i16
88
89 // LO PART
90 __m128i q4_lo_16_L = _mm_cvtepu8_epi16(q4_lo); // lower 8 -> 16
91 __m128i q8_lo_16_L = _mm_cvtepi8_epi16(q8_lo_vec);
92 __m128i prod_lo_L = _mm_madd_epi16(q4_lo_16_L, q8_lo_16_L); // i32
93 acc_lo = _mm_add_epi32(acc_lo, prod_lo_L);
94
95 __m128i q4_lo_16_H = _mm_cvtepu8_epi16(_mm_srli_si128(q4_lo, 8)); // upper 8 -> 16
96 __m128i q8_lo_16_H = _mm_cvtepi8_epi16(_mm_srli_si128(q8_lo_vec, 8));
97 __m128i prod_lo_H = _mm_madd_epi16(q4_lo_16_H, q8_lo_16_H); // i32
98 acc_lo = _mm_add_epi32(acc_lo, prod_lo_H);
99
100 // HI PART
101 __m128i q4_hi_16_L = _mm_cvtepu8_epi16(q4_hi);
102 __m128i q8_hi_16_L = _mm_cvtepi8_epi16(q8_hi_vec);
103 __m128i prod_hi_L = _mm_madd_epi16(q4_hi_16_L, q8_hi_16_L);
104 acc_hi = _mm_add_epi32(acc_hi, prod_hi_L);
105
106 __m128i q4_hi_16_H = _mm_cvtepu8_epi16(_mm_srli_si128(q4_hi, 8));
107 __m128i q8_hi_16_H = _mm_cvtepi8_epi16(_mm_srli_si128(q8_hi_vec, 8));
108 __m128i prod_hi_H = _mm_madd_epi16(q4_hi_16_H, q8_hi_16_H);
109 acc_hi = _mm_add_epi32(acc_hi, prod_hi_H);
110 }
111
112 int32_t sum_q4q8_lo = hsum_epi32_sse(acc_lo);
113 int32_t sum_q4q8_hi = hsum_epi32_sse(acc_hi);
114
115 /* bsums: each bsum is 16 elements */
116 int32_t bsum_lo = (int32_t)b8->bsums[j / 16] +
117 (int32_t)b8->bsums[j / 16 + 1];
118 int32_t bsum_hi = (int32_t)b8->bsums[(j + 32) / 16] +
119 (int32_t)b8->bsums[(j + 32) / 16 + 1];
120
121 sumf += d * (float)sc[is] * (float)sum_q4q8_lo;
122 sumf -= dmin * (float)m_val[is] * (float)bsum_lo;
123 sumf += d * (float)sc[is + 1] * (float)sum_q4q8_hi;
124 sumf -= dmin * (float)m_val[is + 1] * (float)bsum_hi;
125
126 q_offset += 32;
127 is += 2;
128 }
129 }
130 y[row] = sumf;
131 }
132}
#define CK_FP16_TO_FP32(x)
static void unpack_q4_k_scales(const uint8_t *scales, uint8_t *sc, uint8_t *m)
Unpack Q4_K sub-block scales and mins.
static int32_t hsum_epi32_sse(__m128i v)
uint8_t scales[12]
uint8_t qs[256/2]
int8_t qs[256]
int16_t bsums[256/16]

References block_q8_K::bsums, CK_FP16_TO_FP32, block_q4_K::d, block_q8_K::d, block_q4_K::dmin, hsum_epi32_sse(), QK_K, block_q4_K::qs, block_q8_K::qs, block_q4_K::scales, and unpack_q4_k_scales().

◆ moe_swiglu_expert_forward_nvfp4_workspace()

int moe_swiglu_expert_forward_nvfp4_workspace ( const float *  hidden,
const int *  indices,
const float *  routing_weights,
const void *  expert_gate,
const float *  expert_gate_scales,
const void *  expert_up,
const float *  expert_up_scales,
const void *  expert_down,
const float *  expert_down_scales,
float *  output,
int  rows,
int  hidden_dim,
int  intermediate_dim,
int  n_experts,
int  top_k,
void *  workspace,
size_t  workspace_bytes 
)

Definition at line 318 of file gemm_kernels_nvfp4.c.

325{
326 const size_t required = moe_swiglu_nvfp4_workspace_bytes(
327 hidden_dim, intermediate_dim);
328 if (!hidden || !indices || !routing_weights || !expert_gate ||
329 !expert_gate_scales || !expert_up || !expert_up_scales ||
330 !expert_down || !expert_down_scales || !output || !workspace ||
331 required == 0 || workspace_bytes < required || rows <= 0 ||
332 n_experts <= 0 || top_k <= 0 || top_k > n_experts) {
333 return -1;
334 }
335
336 const size_t hidden_q8_bytes = ck_nvfp4_align64(
337 ck_dtype_row_bytes(CK_DT_Q8_0, (size_t)hidden_dim));
338 const size_t gate_up_bytes = ck_nvfp4_align64(
339 2u * (size_t)intermediate_dim * sizeof(float));
340 const size_t act_q8_bytes = ck_nvfp4_align64(
341 ck_dtype_row_bytes(CK_DT_Q8_0, (size_t)intermediate_dim));
342 float *expert_output = (float *)((uint8_t *)workspace + hidden_q8_bytes +
343 gate_up_bytes + act_q8_bytes);
344 const size_t up_expert_bytes = (size_t)intermediate_dim *
345 ck_dtype_row_bytes(CK_DT_NVFP4, (size_t)hidden_dim);
346 const size_t down_expert_bytes = (size_t)hidden_dim *
347 ck_dtype_row_bytes(CK_DT_NVFP4, (size_t)intermediate_dim);
348 memset(output, 0, (size_t)rows * (size_t)hidden_dim * sizeof(float));
349
350 for (int row = 0; row < rows; ++row) {
351 const float *x = hidden + (size_t)row * (size_t)hidden_dim;
352 float *y = output + (size_t)row * (size_t)hidden_dim;
353 for (int slot = 0; slot < top_k; ++slot) {
354 const size_t route = (size_t)row * (size_t)top_k + (size_t)slot;
355 const int expert = indices[route];
356 if (expert < 0 || expert >= n_experts) {
357 return -2;
358 }
360 x,
361 (const uint8_t *)expert_gate + (size_t)expert * up_expert_bytes,
362 expert_gate_scales[expert],
363 (const uint8_t *)expert_up + (size_t)expert * up_expert_bytes,
364 expert_up_scales[expert],
365 (const uint8_t *)expert_down + (size_t)expert * down_expert_bytes,
366 expert_down_scales[expert], expert_output,
367 hidden_dim, intermediate_dim, workspace);
368 const float route_weight = routing_weights[route];
369 for (int h = 0; h < hidden_dim; ++h) {
370 y[h] += route_weight * expert_output[h];
371 }
372 }
373 }
374 return 0;
375}
@ CK_DT_Q8_0
@ CK_DT_NVFP4
static size_t ck_dtype_row_bytes(CKDataType dt, size_t n_elements)
Calculate total bytes for n_elements of given dtype.
static int ck_moe_swiglu_nvfp4_projection(const float *hidden, const void *gate, float gate_scale, const void *up, float up_scale, const void *down, float down_scale, float *result, int hidden_dim, int intermediate_dim, void *workspace)
static size_t ck_nvfp4_align64(size_t value)
size_t moe_swiglu_nvfp4_workspace_bytes(int hidden_dim, int intermediate_dim)

References CK_DT_NVFP4, CK_DT_Q8_0, ck_dtype_row_bytes(), ck_moe_swiglu_nvfp4_projection(), ck_nvfp4_align64(), and moe_swiglu_nvfp4_workspace_bytes().

◆ moe_swiglu_nvfp4_workspace_bytes()

size_t moe_swiglu_nvfp4_workspace_bytes ( int  hidden_dim,
int  intermediate_dim 
)

Definition at line 274 of file gemm_kernels_nvfp4.c.

275{
276 if (hidden_dim <= 0 || intermediate_dim <= 0 || hidden_dim % 64 != 0 ||
277 intermediate_dim % 64 != 0) {
278 return 0;
279 }
280 size_t bytes = ck_nvfp4_align64(
281 ck_dtype_row_bytes(CK_DT_Q8_0, (size_t)hidden_dim));
282 bytes += ck_nvfp4_align64(2u * (size_t)intermediate_dim * sizeof(float));
283 bytes += ck_nvfp4_align64(
284 ck_dtype_row_bytes(CK_DT_Q8_0, (size_t)intermediate_dim));
285 bytes += ck_nvfp4_align64((size_t)hidden_dim * sizeof(float));
286 return bytes;
287}

References CK_DT_Q8_0, ck_dtype_row_bytes(), and ck_nvfp4_align64().

Referenced by moe_swiglu_expert_forward_nvfp4_workspace(), and moe_swiglu_shared_forward_nvfp4_workspace().

◆ moe_swiglu_shared_forward_nvfp4_workspace()

int moe_swiglu_shared_forward_nvfp4_workspace ( const float *  hidden,
const float *  routed,
const void *  shared_gate,
const float *  shared_gate_scale,
const void *  shared_up,
const float *  shared_up_scale,
const void *  shared_down,
const float *  shared_down_scale,
float *  output,
int  rows,
int  hidden_dim,
int  intermediate_dim,
float  combination_scale,
void *  workspace,
size_t  workspace_bytes 
)

Definition at line 377 of file gemm_kernels_nvfp4.c.

384{
385 const size_t required = moe_swiglu_nvfp4_workspace_bytes(
386 hidden_dim, intermediate_dim);
387 if (!hidden || !shared_gate || !shared_gate_scale || !shared_up ||
388 !shared_up_scale || !shared_down || !shared_down_scale || !output ||
389 !workspace || required == 0 || workspace_bytes < required || rows <= 0) {
390 return -1;
391 }
392 const size_t hidden_q8_bytes = ck_nvfp4_align64(
393 ck_dtype_row_bytes(CK_DT_Q8_0, (size_t)hidden_dim));
394 const size_t gate_up_bytes = ck_nvfp4_align64(
395 2u * (size_t)intermediate_dim * sizeof(float));
396 const size_t act_q8_bytes = ck_nvfp4_align64(
397 ck_dtype_row_bytes(CK_DT_Q8_0, (size_t)intermediate_dim));
398 float *shared_output = (float *)((uint8_t *)workspace + hidden_q8_bytes +
399 gate_up_bytes + act_q8_bytes);
400 for (int row = 0; row < rows; ++row) {
402 hidden + (size_t)row * (size_t)hidden_dim,
403 shared_gate, shared_gate_scale[0], shared_up, shared_up_scale[0],
404 shared_down, shared_down_scale[0], shared_output,
405 hidden_dim, intermediate_dim, workspace);
406 float *y = output + (size_t)row * (size_t)hidden_dim;
407 const float *route = routed ? routed + (size_t)row * (size_t)hidden_dim : NULL;
408 for (int h = 0; h < hidden_dim; ++h) {
409 y[h] = combination_scale *
410 (shared_output[h] + (route ? route[h] : 0.0f));
411 }
412 }
413 return 0;
414}

References CK_DT_Q8_0, ck_dtype_row_bytes(), ck_moe_swiglu_nvfp4_projection(), ck_nvfp4_align64(), and moe_swiglu_nvfp4_workspace_bytes().

◆ quantize_row_q8_0()

void quantize_row_q8_0 ( const float *  x,
void *  vy,
int  k 
)

Quantize FP32 to Q8_0 format (scalar reference)

Parameters
xInput FP32 values
vyOutput Q8_0 blocks
kNumber of elements (must be multiple of 32)

Definition at line 125 of file gemm_kernels_q8_0.c.

126{
127 block_q8_0 *y = (block_q8_0 *)vy;
128 const int nb = k / QK8_0; /* QK8_0 = 32 */
129
130#if defined(__AVX__)
131 const __m256 sign_bit = _mm256_set1_ps(-0.0f);
132
133 for (int i = 0; i < nb; i++) {
134 __m256 v0 = _mm256_loadu_ps(x + 0);
135 __m256 v1 = _mm256_loadu_ps(x + 8);
136 __m256 v2 = _mm256_loadu_ps(x + 16);
137 __m256 v3 = _mm256_loadu_ps(x + 24);
138 x += QK8_0;
139
140 __m256 max_abs = _mm256_andnot_ps(sign_bit, v0);
141 max_abs = _mm256_max_ps(max_abs, _mm256_andnot_ps(sign_bit, v1));
142 max_abs = _mm256_max_ps(max_abs, _mm256_andnot_ps(sign_bit, v2));
143 max_abs = _mm256_max_ps(max_abs, _mm256_andnot_ps(sign_bit, v3));
144
145 __m128 max4 = _mm_max_ps(_mm256_extractf128_ps(max_abs, 1),
146 _mm256_castps256_ps128(max_abs));
147 max4 = _mm_max_ps(max4, _mm_movehl_ps(max4, max4));
148 max4 = _mm_max_ss(max4, _mm_movehdup_ps(max4));
149 const float max_scalar = _mm_cvtss_f32(max4);
150
151#if defined(__INTEL_LLVM_COMPILER)
152 const float d = ck_q8_0_div_rounded_f32(max_scalar, 127.0f);
153 const float id = max_scalar != 0.0f
154 ? ck_q8_0_div_rounded_f32(127.0f, max_scalar)
155 : 0.0f;
156#else
157 const float d = max_scalar / 127.0f;
158 const float id = max_scalar != 0.0f ? 127.0f / max_scalar : 0.0f;
159#endif
160 y[i].d = CK_FP32_TO_FP16(d);
161
162 const __m256 mul = _mm256_set1_ps(id);
163 v0 = _mm256_mul_ps(v0, mul);
164 v1 = _mm256_mul_ps(v1, mul);
165 v2 = _mm256_mul_ps(v2, mul);
166 v3 = _mm256_mul_ps(v3, mul);
167
168 /* Match llama.cpp x86 Q8 quantization: nearest-even rounding. */
169 v0 = _mm256_round_ps(v0, _MM_ROUND_NEAREST);
170 v1 = _mm256_round_ps(v1, _MM_ROUND_NEAREST);
171 v2 = _mm256_round_ps(v2, _MM_ROUND_NEAREST);
172 v3 = _mm256_round_ps(v3, _MM_ROUND_NEAREST);
173
174 __m256i i0 = _mm256_cvtps_epi32(v0);
175 __m256i i1 = _mm256_cvtps_epi32(v1);
176 __m256i i2 = _mm256_cvtps_epi32(v2);
177 __m256i i3 = _mm256_cvtps_epi32(v3);
178
179#if defined(__AVX2__)
180 i0 = _mm256_packs_epi32(i0, i1);
181 i2 = _mm256_packs_epi32(i2, i3);
182 i0 = _mm256_packs_epi16(i0, i2);
183
184 const __m256i perm = _mm256_setr_epi32(0, 4, 1, 5, 2, 6, 3, 7);
185 i0 = _mm256_permutevar8x32_epi32(i0, perm);
186 _mm256_storeu_si256((__m256i *)y[i].qs, i0);
187#else
188 __m128i ni0 = _mm256_castsi256_si128(i0);
189 __m128i ni1 = _mm256_extractf128_si256(i0, 1);
190 __m128i ni2 = _mm256_castsi256_si128(i1);
191 __m128i ni3 = _mm256_extractf128_si256(i1, 1);
192 __m128i ni4 = _mm256_castsi256_si128(i2);
193 __m128i ni5 = _mm256_extractf128_si256(i2, 1);
194 __m128i ni6 = _mm256_castsi256_si128(i3);
195 __m128i ni7 = _mm256_extractf128_si256(i3, 1);
196
197 ni0 = _mm_packs_epi32(ni0, ni1);
198 ni2 = _mm_packs_epi32(ni2, ni3);
199 ni4 = _mm_packs_epi32(ni4, ni5);
200 ni6 = _mm_packs_epi32(ni6, ni7);
201
202 ni0 = _mm_packs_epi16(ni0, ni2);
203 ni4 = _mm_packs_epi16(ni4, ni6);
204
205 _mm_storeu_si128((__m128i *)(y[i].qs + 0), ni0);
206 _mm_storeu_si128((__m128i *)(y[i].qs + 16), ni4);
207#endif
208 }
209#else
210 for (int i = 0; i < nb; i++) {
211 const float *xb = x + i * QK8_0;
212
213 /* Find max absolute value in block */
214 float amax = 0.0f;
215 for (int j = 0; j < QK8_0; j++) {
216 float av = xb[j] >= 0 ? xb[j] : -xb[j];
217 if (av > amax) amax = av;
218 }
219
220 /* Compute scale: d = max / 127 */
221 float d = amax / 127.0f;
222 float id = d != 0.0f ? 127.0f / amax : 0.0f;
223
224 /* Store scale as FP16 */
225 y[i].d = CK_FP32_TO_FP16(d);
226
227 /* Quantize values */
228 for (int j = 0; j < QK8_0; j++) {
229 float v = xb[j] * id;
230 int q = ck_nearest_int_q8_0(v);
231 if (q > 127) q = 127;
232 if (q < -127) q = -127;
233 y[i].qs[j] = (int8_t)q;
234 }
235 }
236#endif
237}
#define CK_FP32_TO_FP16(x)
static int ck_nearest_int_q8_0(float fval)
int8_t qs[32]
int32_t id
Definition tokenizer.h:316

◆ quantize_row_q8_k_sse()

void quantize_row_q8_k_sse ( const float *  x,
void *  vy,
int  k 
)

Definition at line 22 of file quantize_row_q8_k_sse.c.

22 {
23 if (!x || !vy || k <= 0) {
24 return;
25 }
26 assert(k % QK_K == 0);
27
28 const int nb = k / QK_K;
29 block_q8_K *y = (block_q8_K *)vy;
30
31 for (int i = 0; i < nb; ++i) {
32 /* Keep the exact signed-max selection contract from llama.cpp/ref. */
33 float max = 0.0f;
34 float amax = 0.0f;
35 for (int j = 0; j < QK_K; ++j) {
36 const float xv = x[j];
37 const float ax = fabsf(xv);
38 if (ax > amax) {
39 amax = ax;
40 max = xv;
41 }
42 }
43
44 if (amax == 0.0f) {
45 y[i].d = 0.0f;
46 memset(y[i].qs, 0, sizeof(y[i].qs));
47 memset(y[i].bsums, 0, sizeof(y[i].bsums));
48 x += QK_K;
49 continue;
50 }
51
52 const float iscale = -127.0f / max;
53 const __m128 v_iscale = _mm_set1_ps(iscale);
54 const __m128 v_magic = _mm_set1_ps(12582912.0f);
55 const __m128i v_mantissa = _mm_set1_epi32(0x007fffff);
56 const __m128i v_bias = _mm_set1_epi32(0x00400000);
57 const __m128i v_min = _mm_set1_epi32(-128);
58 const __m128i v_max = _mm_set1_epi32(127);
59
60 for (int j = 0; j < QK_K; j += 16) {
61 const __m128 x0 = _mm_loadu_ps(x + j + 0);
62 const __m128 x1 = _mm_loadu_ps(x + j + 4);
63 const __m128 x2 = _mm_loadu_ps(x + j + 8);
64 const __m128 x3 = _mm_loadu_ps(x + j + 12);
65
66 __m128i q0 = _mm_sub_epi32(
67 _mm_and_si128(_mm_castps_si128(_mm_add_ps(_mm_mul_ps(x0, v_iscale), v_magic)), v_mantissa),
68 v_bias);
69 __m128i q1 = _mm_sub_epi32(
70 _mm_and_si128(_mm_castps_si128(_mm_add_ps(_mm_mul_ps(x1, v_iscale), v_magic)), v_mantissa),
71 v_bias);
72 __m128i q2 = _mm_sub_epi32(
73 _mm_and_si128(_mm_castps_si128(_mm_add_ps(_mm_mul_ps(x2, v_iscale), v_magic)), v_mantissa),
74 v_bias);
75 __m128i q3 = _mm_sub_epi32(
76 _mm_and_si128(_mm_castps_si128(_mm_add_ps(_mm_mul_ps(x3, v_iscale), v_magic)), v_mantissa),
77 v_bias);
78
79 q0 = _mm_min_epi32(_mm_max_epi32(q0, v_min), v_max);
80 q1 = _mm_min_epi32(_mm_max_epi32(q1, v_min), v_max);
81 q2 = _mm_min_epi32(_mm_max_epi32(q2, v_min), v_max);
82 q3 = _mm_min_epi32(_mm_max_epi32(q3, v_min), v_max);
83
84 const __m128i q01 = _mm_packs_epi32(q0, q1);
85 const __m128i q23 = _mm_packs_epi32(q2, q3);
86 const __m128i q0123 = _mm_packs_epi16(q01, q23);
87
88 _mm_storeu_si128((__m128i *)(y[i].qs + j), q0123);
89
90 int sum = 0;
91 for (int ii = 0; ii < 16; ++ii) {
92 sum += y[i].qs[j + ii];
93 }
94 y[i].bsums[j / 16] = (int16_t)sum;
95 }
96
97 y[i].d = 1.0f / iscale;
98 x += QK_K;
99 }
100}

References block_q8_K::bsums, block_q8_K::d, QK_K, and block_q8_K::qs.

◆ rmsnorm_q8_k_fused()

void rmsnorm_q8_k_fused ( const float *  input,
const float *  gamma,
void *  vy,
int  tokens,
int  d_model,
int  aligned_embed_dim,
float  eps 
)

Definition at line 144 of file rmsnorm_q8_k_fused.c.

151{
152 const int T = tokens;
153 const int D = d_model;
154 block_q8_K *y = (block_q8_K *)vy;
155
156 for (int t = 0; t < T; ++t) {
157 const float *x = input + (size_t)t * (size_t)aligned_embed_dim;
158
159 float sum_sq = 0.0f;
160 for (int d = 0; d < D; ++d) {
161 sum_sq += x[d] * x[d];
162 }
163 const float rstd = 1.0f / sqrtf(sum_sq / (float)D + eps);
164
165 for (int b = 0; b < D / QK_K; ++b) {
166 const float *xb = x + b * QK_K;
167 const float *gb = gamma + b * QK_K;
168 block_q8_K *out_block = &y[t * (D / QK_K) + b];
169
170 float norm_buf[QK_K];
171 float max_val = 0.0f;
172 for (int d = 0; d < QK_K; ++d) {
173 const float normalized = xb[d] * rstd * gb[d];
174 norm_buf[d] = normalized;
175 const float abs_val = fabsf(normalized);
176 if (abs_val > max_val) {
177 max_val = abs_val;
178 }
179 }
180
181 if (max_val == 0.0f) {
182 out_block->d = 0.0f;
183 memset(out_block->qs, 0, QK_K);
184 memset(out_block->bsums, 0, sizeof(out_block->bsums));
185 continue;
186 }
187
188 const float iscale = -127.0f / max_val;
189 out_block->d = 1.0f / iscale;
190 for (int j = 0; j < QK_K; j += 16) {
191 int bsum = 0;
192 for (int k = 0; k < 16; ++k) {
193 int q = (int)lrintf(norm_buf[j + k] * iscale);
194 if (q < -128) q = -128;
195 if (q > 127) q = 127;
196 out_block->qs[j + k] = (int8_t)q;
197 bsum += q;
198 }
199 out_block->bsums[j / 16] = (int16_t)bsum;
200 }
201 }
202 }
203}

References block_q8_K::bsums, block_q8_K::d, QK_K, and block_q8_K::qs.

◆ unpack_q4_k_scales()

static void unpack_q4_k_scales ( const uint8_t *  scales,
uint8_t *  sc,
uint8_t *  m 
)
inlinestatic

Unpack Q4_K sub-block scales and mins.

Parameters
scalesThe packed scales[12] array from block_q4_K
scOutput: 8 unpacked scale values (multiply by super-block d)
mOutput: 8 unpacked min values (multiply by super-block dmin)

This matches llama.cpp's get_scale_min_k4() function exactly. The 12-byte scales array layout:

  • bytes 0-3: 6-bit scales[0-3] (high 2 bits used for scales[4-7])
  • bytes 4-7: 6-bit mins[0-3] (high 2 bits used for mins[4-7])
  • bytes 8-11: low 4 bits for scales[4-7], high 4 bits for mins[4-7]

Definition at line 287 of file ckernel_quant.h.

288 {
289 /* Direct 6-bit values for indices 0-3 */
290 sc[0] = scales[0] & 0x3F;
291 sc[1] = scales[1] & 0x3F;
292 sc[2] = scales[2] & 0x3F;
293 sc[3] = scales[3] & 0x3F;
294
295 m[0] = scales[4] & 0x3F;
296 m[1] = scales[5] & 0x3F;
297 m[2] = scales[6] & 0x3F;
298 m[3] = scales[7] & 0x3F;
299
300 /* 6-bit values for indices 4-7: low 4 bits from bytes 8-11,
301 * high 2 bits from upper bits of bytes 0-3 (scales) and 4-7 (mins) */
302 sc[4] = (scales[8] & 0x0F) | ((scales[0] >> 6) << 4);
303 sc[5] = (scales[9] & 0x0F) | ((scales[1] >> 6) << 4);
304 sc[6] = (scales[10] & 0x0F) | ((scales[2] >> 6) << 4);
305 sc[7] = (scales[11] & 0x0F) | ((scales[3] >> 6) << 4);
306
307 m[4] = (scales[8] >> 4) | ((scales[4] >> 6) << 4);
308 m[5] = (scales[9] >> 4) | ((scales[5] >> 6) << 4);
309 m[6] = (scales[10] >> 4) | ((scales[6] >> 6) << 4);
310 m[7] = (scales[11] >> 4) | ((scales[7] >> 6) << 4);
311}

Referenced by dequant_q4_k_block(), dot_q4_k_q8_k_ref(), gemv_q4_k_backward_ref(), gemv_q4_k_q8_k_sse(), gemv_q4_k_ref(), pack_q4_k_to_packed_meta(), pack_q4_k_to_packed_meta_x16(), pack_q4_k_to_packed_meta_x8(), pack_q4_k_to_packed_u8(), pack_q4_k_to_packed_u8_x16(), pack_q4_k_to_packed_vnni_x16(), and pack_q4_k_to_packed_vnni_x8().

◆ vec_dot_nvfp4_q8_0()

void vec_dot_nvfp4_q8_0 ( int  n,
float *  output,
const void *  weights,
const void *  activations,
float  weight_scale 
)

Definition at line 125 of file gemm_kernels_nvfp4.c.

127{
128#if defined(__AVX2__)
129 assert(n >= 0 && n % QK_NVFP4 == 0);
130 const block_nvfp4 *w = (const block_nvfp4 *)weights;
131 const block_q8_0 *x = (const block_q8_0 *)activations;
132 const int block_count = n / QK_NVFP4;
133 const __m128i lut = _mm_loadu_si128(
134 (const __m128i *)ck_nvfp4_e2m1_x2);
135 const __m128i nibble_mask = _mm_set1_epi8(0x0f);
136 const __m256i ones = _mm256_set1_epi16(1);
137 __m256 accumulated = _mm256_setzero_ps();
138
139 for (int block_index = 0; block_index < block_count; ++block_index) {
140 const block_nvfp4 *block = &w[block_index];
141 const __m128i packed01 = _mm_loadu_si128(
142 (const __m128i *)(block->qs + 0));
143 const __m128i packed23 = _mm_loadu_si128(
144 (const __m128i *)(block->qs + 16));
145 const __m128i low01 = _mm_shuffle_epi8(
146 lut, _mm_and_si128(packed01, nibble_mask));
147 const __m128i high01 = _mm_shuffle_epi8(
148 lut, _mm_and_si128(_mm_srli_epi16(packed01, 4), nibble_mask));
149 const __m128i low23 = _mm_shuffle_epi8(
150 lut, _mm_and_si128(packed23, nibble_mask));
151 const __m128i high23 = _mm_shuffle_epi8(
152 lut, _mm_and_si128(_mm_srli_epi16(packed23, 4), nibble_mask));
153
154 __m256i values01 = _mm256_castsi128_si256(
155 _mm_unpacklo_epi64(low01, high01));
156 values01 = _mm256_inserti128_si256(
157 values01, _mm_unpackhi_epi64(low01, high01), 1);
158 __m256i values23 = _mm256_castsi128_si256(
159 _mm_unpacklo_epi64(low23, high23));
160 values23 = _mm256_inserti128_si256(
161 values23, _mm_unpackhi_epi64(low23, high23), 1);
162
163 const __m256i q8_01 = _mm256_loadu_si256(
164 (const __m256i *)x[2 * block_index + 0].qs);
165 const __m256i q8_23 = _mm256_loadu_si256(
166 (const __m256i *)x[2 * block_index + 1].qs);
167 const __m256i dot01 = _mm256_madd_epi16(
168 ck_nvfp4_mul_add_i8_avx2(values01, q8_01), ones);
169 const __m256i dot23 = _mm256_madd_epi16(
170 ck_nvfp4_mul_add_i8_avx2(values23, q8_23), ones);
171
172 const float q8_scale0 =
173 CK_FP16_TO_FP32(x[2 * block_index + 0].d);
174 const float q8_scale1 =
175 CK_FP16_TO_FP32(x[2 * block_index + 1].d);
176 const float scale0 = ck_ue4m3_to_fp32_inline(block->d[0]) * q8_scale0;
177 const float scale1 = ck_ue4m3_to_fp32_inline(block->d[1]) * q8_scale0;
178 const float scale2 = ck_ue4m3_to_fp32_inline(block->d[2]) * q8_scale1;
179 const float scale3 = ck_ue4m3_to_fp32_inline(block->d[3]) * q8_scale1;
180 const __m256 scales01 = _mm256_insertf128_ps(
181 _mm256_castps128_ps256(_mm_set1_ps(scale0)),
182 _mm_set1_ps(scale1), 1);
183 const __m256 scales23 = _mm256_insertf128_ps(
184 _mm256_castps128_ps256(_mm_set1_ps(scale2)),
185 _mm_set1_ps(scale3), 1);
186 accumulated = _mm256_fmadd_ps(
187 scales01, _mm256_cvtepi32_ps(dot01), accumulated);
188 accumulated = _mm256_fmadd_ps(
189 scales23, _mm256_cvtepi32_ps(dot23), accumulated);
190 }
191
192 __m128 sum4 = _mm_add_ps(
193 _mm256_castps256_ps128(accumulated),
194 _mm256_extractf128_ps(accumulated, 1));
195 sum4 = _mm_hadd_ps(sum4, sum4);
196 sum4 = _mm_hadd_ps(sum4, sum4);
197 *output = _mm_cvtss_f32(sum4) * weight_scale;
198#else
199 vec_dot_nvfp4_q8_0_ref(n, output, weights, activations, weight_scale);
200#endif
201}
void vec_dot_nvfp4_q8_0_ref(int n, float *output, const void *weights, const void *activations, float weight_scale)

References CK_FP16_TO_FP32, ck_nvfp4_e2m1_x2, ck_ue4m3_to_fp32_inline(), block_nvfp4::d, QK_NVFP4, block_nvfp4::qs, and vec_dot_nvfp4_q8_0_ref().

Referenced by ck_nvfp4_gemv_rows(), and gemv_nvfp4_q8_0().

◆ vec_dot_nvfp4_q8_0_ref()

void vec_dot_nvfp4_q8_0_ref ( int  n,
float *  output,
const void *  weights,
const void *  activations,
float  weight_scale 
)

Definition at line 81 of file gemm_kernels_nvfp4.c.

83{
84 assert(n >= 0 && n % QK_NVFP4 == 0);
85 const block_nvfp4 *w = (const block_nvfp4 *)weights;
86 const block_q8_0 *x = (const block_q8_0 *)activations;
87 const int block_count = n / QK_NVFP4;
88 float sum = 0.0f;
89
90 for (int block_index = 0; block_index < block_count; ++block_index) {
91 for (int sub = 0; sub < QK_NVFP4 / QK_NVFP4_SUB; ++sub) {
92 const int q8_block = sub / 2;
93 const int q8_offset = (sub % 2) * QK_NVFP4_SUB;
94 const float scale = ck_ue4m3_to_fp32_inline(w[block_index].d[sub]) *
95 CK_FP16_TO_FP32(x[2 * block_index + q8_block].d) *
96 weight_scale;
97 const uint8_t *packed =
98 &w[block_index].qs[sub * (QK_NVFP4_SUB / 2)];
99 const int8_t *q8 = &x[2 * block_index + q8_block].qs[q8_offset];
100 int integer_sum = 0;
101 for (int lane = 0; lane < QK_NVFP4_SUB / 2; ++lane) {
102 const uint8_t pair = packed[lane];
103 integer_sum += (int)q8[lane] *
104 (int)ck_nvfp4_e2m1_x2[pair & 0x0f];
105 integer_sum += (int)q8[lane + QK_NVFP4_SUB / 2] *
106 (int)ck_nvfp4_e2m1_x2[pair >> 4];
107 }
108 sum += scale * (float)integer_sum;
109 }
110 }
111 *output = sum;
112}

References CK_FP16_TO_FP32, ck_nvfp4_e2m1_x2, ck_ue4m3_to_fp32_inline(), QK_NVFP4, QK_NVFP4_SUB, block_q8_0::qs, and block_nvfp4::qs.

Referenced by vec_dot_nvfp4_q8_0().

◆ 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 out_proj_head_major_q5_0_q8_0(), and out_proj_head_major_q5_0_q8_0().

◆ vec_dot_q8_0_q8_0()

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

Auto-dispatch quantized dot product Q8_0 x Q8_0.

Definition at line 1368 of file gemm_kernels_q8_0.c.

1369{
1370 if (ck_q8_0_q8_0_debug_ref()) {
1371 vec_dot_q8_0_q8_0_ref(n, s, vx, vy);
1372 return;
1373 }
1374#ifdef __AVX512F__
1375 vec_dot_q8_0_q8_0_avx512(n, s, vx, vy);
1376#elif defined(__AVX2__)
1377 vec_dot_q8_0_q8_0_avx2(n, s, vx, vy);
1378#elif defined(__ARM_NEON) || defined(__aarch64__)
1379 vec_dot_q8_0_q8_0_neon(n, s, vx, vy);
1380#elif defined(__AVX__)
1381 vec_dot_q8_0_q8_0_avx(n, s, vx, vy);
1382#elif defined(__SSE4_1__)
1383 vec_dot_q8_0_q8_0_sse(n, s, vx, vy);
1384#else
1385 vec_dot_q8_0_q8_0_ref(n, s, vx, vy);
1386#endif
1387}
static int ck_q8_0_q8_0_debug_ref(void)
void vec_dot_q8_0_q8_0_ref(int n, float *s, const void *vx, const void *vy)
Quantized dot product: Q8_0 weights x Q8_0 input (scalar reference)

References ck_q8_0_q8_0_debug_ref(), and vec_dot_q8_0_q8_0_ref().

Referenced by out_proj_head_major_q8_0_q8_0(), and out_proj_head_major_q8_0_q8_0().