trueno/backends/q4k/gemv/
avx512.rs1use super::super::{parse_q4k_header, SUPER_BLOCK_BYTES, SUPER_BLOCK_SIZE};
8
9#[cfg(target_arch = "x86_64")]
13#[target_feature(enable = "avx512f", enable = "avx512bw", enable = "fma")]
14pub(crate) unsafe fn matmul_q4k_f32_avx512(
15 q4k_data: &[u8],
16 input: &[f32],
17 out_dim: usize,
18 in_dim: usize,
19) -> Vec<f32> {
20 unsafe {
21 use std::arch::x86_64::*;
22
23 let num_blocks_per_row = (in_dim + SUPER_BLOCK_SIZE - 1) / SUPER_BLOCK_SIZE;
24 let row_bytes = num_blocks_per_row * SUPER_BLOCK_BYTES;
25 let low_mask = _mm512_set1_epi32(0x0F);
26
27 let mut output: Vec<f32> = Vec::with_capacity(out_dim);
29 output.set_len(out_dim);
31
32 for out_idx in 0..out_dim {
33 let row_start = out_idx * row_bytes;
34 let mut acc = _mm512_setzero_ps();
35
36 for sb_idx in 0..num_blocks_per_row {
37 let sb_start = row_start + sb_idx * SUPER_BLOCK_BYTES;
38 let sb_data = &q4k_data[sb_start..sb_start + SUPER_BLOCK_BYTES];
39 let input_offset = sb_idx * SUPER_BLOCK_SIZE;
40 process_q4k_superblock_avx512(
41 sb_data,
42 input,
43 input_offset,
44 in_dim,
45 low_mask,
46 &mut acc,
47 );
48 }
49
50 output[out_idx] = hsum_avx512(acc);
51 }
52
53 contract_post_dequant!(output);
54 output
55 }
56}
57
58#[cfg(target_arch = "x86_64")]
66#[target_feature(enable = "avx512f")]
67#[inline]
68unsafe fn load16_bounded(
69 input_ptr: *const f32,
70 base: usize,
71 in_dim: usize,
72) -> std::arch::x86_64::__m512 {
73 use std::arch::x86_64::*;
74 unsafe {
75 if base + 16 <= in_dim {
76 _mm512_loadu_ps(input_ptr.add(base))
77 } else if base < in_dim {
78 let mask: __mmask16 = ((1u32 << (in_dim - base)) - 1) as __mmask16;
80 _mm512_maskz_loadu_ps(mask, input_ptr.add(base))
81 } else {
82 _mm512_setzero_ps()
83 }
84 }
85}
86
87#[cfg(target_arch = "x86_64")]
103#[target_feature(enable = "avx512f", enable = "avx512bw", enable = "fma")]
104unsafe fn process_q4k_superblock_avx512(
105 sb_data: &[u8],
106 input: &[f32],
107 input_offset: usize,
108 in_dim: usize,
109 low_mask: std::arch::x86_64::__m512i,
110 acc: &mut std::arch::x86_64::__m512,
111) {
112 unsafe {
113 use std::arch::x86_64::*;
114
115 let (d, dmin, scales, mins) = parse_q4k_header(sb_data);
116 let qs = sb_data.get(16..144).expect("Q4_K: need ≥144 bytes for qs");
117 let qs_ptr = qs.as_ptr();
118 let input_ptr = input.as_ptr();
119
120 _mm_prefetch(sb_data.as_ptr().add(SUPER_BLOCK_BYTES) as *const i8, _MM_HINT_T0);
123 _mm_prefetch(sb_data.as_ptr().add(SUPER_BLOCK_BYTES + 64) as *const i8, _MM_HINT_T0);
124
125 for chunk_i in 0..4 {
126 let chunk_start = chunk_i * 64;
127 let q_start = chunk_i * 32;
128 let input_base_lo0 = input_offset + chunk_start;
129
130 if input_base_lo0 >= in_dim {
134 continue;
135 }
136
137 let d1 = d * f32::from(scales[chunk_i * 2]);
138 let dm1 = dmin * f32::from(mins[chunk_i * 2]);
139 let d2 = d * f32::from(scales[chunk_i * 2 + 1]);
140 let dm2 = dmin * f32::from(mins[chunk_i * 2 + 1]);
141
142 let d1_vec = _mm512_set1_ps(d1);
143 let dm1_vec = _mm512_set1_ps(dm1);
144 let d2_vec = _mm512_set1_ps(d2);
145 let dm2_vec = _mm512_set1_ps(dm2);
146
147 let q0 = _mm_loadu_si128(qs_ptr.add(q_start) as *const __m128i);
153 let q0_i32 = _mm512_cvtepu8_epi32(q0);
154 let q1 = _mm_loadu_si128(qs_ptr.add(q_start + 16) as *const __m128i);
155 let q1_i32 = _mm512_cvtepu8_epi32(q1);
156 let dq0 = _mm512_fmsub_ps(
157 d1_vec,
158 _mm512_cvtepi32_ps(_mm512_and_si512(q0_i32, low_mask)),
159 dm1_vec,
160 );
161 let dq1 = _mm512_fmsub_ps(
162 d1_vec,
163 _mm512_cvtepi32_ps(_mm512_and_si512(q1_i32, low_mask)),
164 dm1_vec,
165 );
166 let dqh0 =
167 _mm512_fmsub_ps(d2_vec, _mm512_cvtepi32_ps(_mm512_srli_epi32(q0_i32, 4)), dm2_vec);
168 let dqh1 =
169 _mm512_fmsub_ps(d2_vec, _mm512_cvtepi32_ps(_mm512_srli_epi32(q1_i32, 4)), dm2_vec);
170
171 let input_base_hi0 = input_base_lo0 + 32;
172
173 if input_base_lo0 + 64 <= in_dim {
174 let x0 = _mm512_loadu_ps(input_ptr.add(input_base_lo0));
178 let x1 = _mm512_loadu_ps(input_ptr.add(input_base_lo0 + 16));
179 let xh0 = _mm512_loadu_ps(input_ptr.add(input_base_hi0));
180 let xh1 = _mm512_loadu_ps(input_ptr.add(input_base_hi0 + 16));
181 *acc = _mm512_fmadd_ps(dq0, x0, *acc);
182 *acc = _mm512_fmadd_ps(dq1, x1, *acc);
183 *acc = _mm512_fmadd_ps(dqh0, xh0, *acc);
184 *acc = _mm512_fmadd_ps(dqh1, xh1, *acc);
185 } else {
186 let x0 = load16_bounded(input_ptr, input_base_lo0, in_dim);
195 let x1 = load16_bounded(input_ptr, input_base_lo0 + 16, in_dim);
196 let xh0 = load16_bounded(input_ptr, input_base_hi0, in_dim);
197 let xh1 = load16_bounded(input_ptr, input_base_hi0 + 16, in_dim);
198 *acc = _mm512_fmadd_ps(dq0, x0, *acc);
199 *acc = _mm512_fmadd_ps(dq1, x1, *acc);
200 *acc = _mm512_fmadd_ps(dqh0, xh0, *acc);
201 *acc = _mm512_fmadd_ps(dqh1, xh1, *acc);
202 }
203 }
204 }
205}
206
207#[cfg(target_arch = "x86_64")]
210#[target_feature(enable = "avx512f")]
211unsafe fn hsum_avx512(v: std::arch::x86_64::__m512) -> f32 {
212 use std::arch::x86_64::*;
213 let lo256 = _mm512_castps512_ps256(v);
215 let hi_shifted = _mm512_shuffle_f32x4(v, v, 0b_01_00_11_10); let hi256 = _mm512_castps512_ps256(hi_shifted);
218 let sum256 = _mm256_add_ps(lo256, hi256);
219 let hi128 = _mm256_extractf128_ps(sum256, 1);
221 let lo128 = _mm256_castps256_ps128(sum256);
222 let sum128 = _mm_add_ps(lo128, hi128);
223 let hi64 = _mm_movehl_ps(sum128, sum128);
224 let sum64 = _mm_add_ps(sum128, hi64);
225 let hi32 = _mm_shuffle_ps(sum64, sum64, 1);
226 let sum32 = _mm_add_ss(sum64, hi32);
227 _mm_cvtss_f32(sum32)
228}
229
230#[cfg(target_arch = "x86_64")]
233#[target_feature(enable = "avx512f", enable = "avx512bw", enable = "fma")]
234pub(crate) unsafe fn compute_chunk_q4k_avx512(
235 q4k_data: &[u8],
236 input: &[f32],
237 chunk: &mut [f32],
238 start_row: usize,
239 out_dim: usize,
240 in_dim: usize,
241 num_blocks_per_row: usize,
242 row_bytes: usize,
243) {
244 unsafe {
245 use std::arch::x86_64::*;
246
247 let low_mask = _mm512_set1_epi32(0x0F);
248
249 for (local_idx, out_val) in chunk.iter_mut().enumerate() {
250 let out_idx = start_row + local_idx;
251 if out_idx >= out_dim {
252 break;
253 }
254 let row_start = out_idx * row_bytes;
255 let mut acc = _mm512_setzero_ps();
256
257 for sb_idx in 0..num_blocks_per_row {
258 let sb_start = row_start + sb_idx * SUPER_BLOCK_BYTES;
259 let sb_data = &q4k_data[sb_start..sb_start + SUPER_BLOCK_BYTES];
260 let input_offset = sb_idx * SUPER_BLOCK_SIZE;
261 process_q4k_superblock_avx512(
262 sb_data,
263 input,
264 input_offset,
265 in_dim,
266 low_mask,
267 &mut acc,
268 );
269 }
270
271 *out_val = hsum_avx512(acc);
272 }
273 }
274 contract_post_dequant!(chunk);
275}