39#if defined(__AVX__) || defined(__AVX2__) || defined(__AVX512F__)
64 const float *attn_out,
72 if (!output || !attn_out || !wo)
return;
73 if (tokens <= 0 || embed_dim <= 0 || num_heads <= 0 || head_dim <= 0)
return;
75 const int blocks_per_head = head_dim /
QK5_0;
76 const int blocks_per_row = embed_dim /
QK5_0;
80 const size_t token_stride = head_dim;
81 const size_t head_stride = (size_t)tokens * token_stride;
85 for (
int t = 0; t < tokens; t++) {
86 float *out_row = output + (size_t)t * embed_dim;
87 for (
int n = 0; n < embed_dim; n++) {
92 memset(output, 0, (
size_t)tokens * embed_dim *
sizeof(
float));
96 for (
int h = 0; h < num_heads; h++) {
97 const float *head_data = attn_out + (size_t)h * head_stride;
100 const int head_offset = h * blocks_per_head;
102 for (
int n_block = 0; n_block < blocks_per_head; n_block++) {
103 for (
int n = 0; n < embed_dim; n++) {
104 const block_q5_0 *w_row = weights + (size_t)n * blocks_per_row + head_offset + n_block;
109 memcpy(&qh, w_row->
qh,
sizeof(qh));
112 for (
int t = 0; t < tokens; t++) {
113 const float *token_vec = head_data + (size_t)t * token_stride + (
size_t)n_block *
QK5_0;
117 for (
int j = 0; j <
QK5_0 / 2; j++) {
118 const uint8_t packed = w_row->
qs[j];
119 const int lo = (packed & 0x0F);
120 const int hi = (packed >> 4);
121 const int xh_0 = ((qh >> (j + 0)) << 4) & 0x10;
122 const int xh_1 = ((qh >> (j + 12))) & 0x10;
123 const int q0 = (lo | xh_0) - 16;
124 const int q1 = (hi | xh_1) - 16;
126 sum += d * (float)q0 * token_vec[j];
127 sum += d * (float)q1 * token_vec[j + 16];
130 output[(size_t)t * embed_dim + n] += sum;
143static inline float hsum256_ps_head_major(__m256 v)
145 __m128 lo = _mm256_castps256_ps128(v);
146 __m128 hi = _mm256_extractf128_ps(v, 1);
147 __m128 sum128 = _mm_add_ps(lo, hi);
148 __m128 shuf = _mm_movehdup_ps(sum128);
149 __m128 sums = _mm_add_ps(sum128, shuf);
150 shuf = _mm_movehl_ps(shuf, sums);
151 sums = _mm_add_ss(sums, shuf);
152 return _mm_cvtss_f32(sums);
155static inline float dot_fp32_q5_0_block_decoded_avx(
const float *w,
158 __m256 acc = _mm256_setzero_ps();
159 for (
int i = 0; i <
QK5_0; i += 8) {
160 const __m256 wv = _mm256_loadu_ps(w + i);
161 const __m256 xv = _mm256_loadu_ps(x + i);
162 acc = _mm256_add_ps(acc, _mm256_mul_ps(wv, xv));
164 return hsum256_ps_head_major(acc);
167static inline void decode_8rows_q5_0_block(
const block_q5_0 *w0,
175 float w_dec[8][
QK5_0])
187static inline void accum_8rows_q5_0_block_decoded_avx(
float *out,
188 const float w_dec[8][
QK5_0],
191 __m256 acc = _mm256_loadu_ps(out);
192 for (
int i = 0; i <
QK5_0; i++) {
193 const __m256 wv = _mm256_setr_ps(
194 w_dec[0][i], w_dec[1][i], w_dec[2][i], w_dec[3][i],
195 w_dec[4][i], w_dec[5][i], w_dec[6][i], w_dec[7][i]);
196 const __m256 xv = _mm256_set1_ps(x[i]);
197 acc = _mm256_add_ps(acc, _mm256_mul_ps(wv, xv));
199 _mm256_storeu_ps(out, acc);
202void gemv_nt_q5_0_head_major_output_avx(
float *output,
203 const float *attn_out,
211 if (!output || !attn_out || !wo)
return;
212 if (tokens <= 0 || embed_dim <= 0 || num_heads <= 0 || head_dim <= 0)
return;
214 const int blocks_per_head = head_dim /
QK5_0;
215 const int blocks_per_row = embed_dim /
QK5_0;
218 const size_t token_stride = head_dim;
219 const size_t head_stride = (size_t)tokens * token_stride;
222 for (
int t = 0; t < tokens; t++) {
223 float *out_row = output + (size_t)t * embed_dim;
224 for (
int n = 0; n < embed_dim; n++) {
225 out_row[n] = bias[n];
229 memset(output, 0, (
size_t)tokens * embed_dim *
sizeof(
float));
232 for (
int h = 0; h < num_heads; h++) {
233 const float *head_data = attn_out + (size_t)h * head_stride;
234 const int head_offset = h * blocks_per_head;
237 for (; n + 7 < embed_dim; n += 8) {
238 for (
int n_block = 0; n_block < blocks_per_head; n_block++) {
239 const block_q5_0 *w0 = weights + (size_t)(n + 0) * blocks_per_row + head_offset + n_block;
240 const block_q5_0 *w1 = weights + (size_t)(n + 1) * blocks_per_row + head_offset + n_block;
241 const block_q5_0 *w2 = weights + (size_t)(n + 2) * blocks_per_row + head_offset + n_block;
242 const block_q5_0 *w3 = weights + (size_t)(n + 3) * blocks_per_row + head_offset + n_block;
243 const block_q5_0 *w4 = weights + (size_t)(n + 4) * blocks_per_row + head_offset + n_block;
244 const block_q5_0 *w5 = weights + (size_t)(n + 5) * blocks_per_row + head_offset + n_block;
245 const block_q5_0 *w6 = weights + (size_t)(n + 6) * blocks_per_row + head_offset + n_block;
246 const block_q5_0 *w7 = weights + (size_t)(n + 7) * blocks_per_row + head_offset + n_block;
247 float w_dec[8][
QK5_0];
249 decode_8rows_q5_0_block(w0, w1, w2, w3, w4, w5, w6, w7, w_dec);
251 for (
int t = 0; t < tokens; t++) {
252 const float *token_vec =
253 head_data + (size_t)t * token_stride + (
size_t)n_block *
QK5_0;
254 float *out_row = output + (size_t)t * embed_dim + n;
255 accum_8rows_q5_0_block_decoded_avx(out_row, w_dec, token_vec);
260 for (; n < embed_dim; n++) {
261 const block_q5_0 *w_row = weights + (size_t)n * blocks_per_row + head_offset;
262 for (
int n_block = 0; n_block < blocks_per_head; n_block++) {
267 for (
int t = 0; t < tokens; t++) {
268 const float *token_vec =
269 head_data + (size_t)t * token_stride + (
size_t)n_block *
QK5_0;
270 output[(size_t)t * embed_dim + n] +=
271 dot_fp32_q5_0_block_decoded_avx(w_dec, token_vec);
300 gemv_nt_q5_0_head_major_output_avx(output, attn_out, wo, bias,
301 tokens, embed_dim, num_heads, head_dim);
304 tokens, embed_dim, num_heads, head_dim);
324 if (!output || !attn_out || !wo)
return;
325 if (tokens <= 0 || embed_dim <= 0 || num_heads <= 0 || head_dim <= 0)
return;
327 const int blocks_per_head = head_dim /
QK8_0;
328 const int blocks_per_row = embed_dim /
QK8_0;
331 const size_t token_stride = head_dim;
332 const size_t head_stride = (size_t)tokens * token_stride;
336 for (
int t = 0; t < tokens; t++) {
337 float *out_row = output + (size_t)t * embed_dim;
338 for (
int n = 0; n < embed_dim; n++) {
339 out_row[n] = bias[n];
343 memset(output, 0, (
size_t)tokens * embed_dim *
sizeof(
float));
347 for (
int h = 0; h < num_heads; h++) {
348 const float *head_data = attn_out + (size_t)h * head_stride;
349 const int head_offset = h * blocks_per_head;
351 for (
int n_block = 0; n_block < blocks_per_head; n_block++) {
352 for (
int n = 0; n < embed_dim; n++) {
353 const block_q8_0 *w_row = weights + (size_t)n * blocks_per_row + head_offset + n_block;
356 for (
int t = 0; t < tokens; t++) {
357 const float *token_vec = head_data + (size_t)t * token_stride + (
size_t)n_block *
QK8_0;
360 for (
int j = 0; j <
QK8_0; j++) {
361 sum += d * (float)w_row->
qs[j] * token_vec[j];
364 output[(size_t)t * embed_dim + n] += sum;
Quantization block structures for weight-only quantization.
#define CK_FP16_TO_FP32(x)
void ck_gemm_nt_head_major_q8_0(const float *attn_out, const void *wo, const float *bias, float *output, int tokens, int embed_dim, int num_heads, int head_dim)
Output projection from head-major attention (Q8_0 weights)
void dequant_q5_0_block(const block_q5_0 *block, float *output)
Dequantize a single Q5_0 block to FP32.
void ck_gemm_nt_head_major_q5_0(const float *attn_out, const void *wo, const float *bias, float *output, int tokens, int embed_dim, int num_heads, int head_dim)
Output projection from head-major attention (auto-dispatch)
void dequant_q5_0_row(const void *src, float *dst, size_t n_elements)
Dequantize Q5_0 row (multiple blocks)
void gemv_nt_q5_0_head_major_output(float *output, const float *attn_out, const void *wo, const float *bias, int tokens, int embed_dim, int num_heads, int head_dim)
Output projection reading head-major attention output (Q5_0 weights)