1#![allow(dead_code)]
7
8use crate::attention::{AttentionParams, LayerWeights, TransformerConfig};
9
10pub struct CpuKvCache {
11 pub k: Vec<f32>,
12 pub v: Vec<f32>,
13 pub len: usize,
14}
15
16impl Default for CpuKvCache {
17 fn default() -> Self {
18 Self::new()
19 }
20}
21
22impl CpuKvCache {
23 pub fn new() -> Self {
24 Self {
25 k: Vec::new(),
26 v: Vec::new(),
27 len: 0,
28 }
29 }
30}
31
32pub fn cpu_layer_forward(
33 input: &[f32],
34 tokens: usize,
35 w: &LayerWeights,
36 cfg: &TransformerConfig,
37 cos: &[f32],
38 sin: &[f32],
39 kv_cache: &mut CpuKvCache,
40 pos_offset: usize,
41) -> Vec<f32> {
42 let h = cfg.hidden_size;
43 let nh = cfg.num_heads;
44 let nkv = cfg.num_kv_heads;
45 let hd = cfg.head_dim;
46 let im = cfg.intermediate_size;
47 let eps = cfg.rms_norm_eps;
48
49 let ln_out = rms_norm(input, &w.input_ln_w, tokens, h, eps);
51
52 let q = matmul_at_bt(&ln_out, &w.q_proj_w, tokens, nh * hd, h);
54 let k = matmul_at_bt(&ln_out, &w.k_proj_w, tokens, nkv * hd, h);
55 let v = matmul_at_bt(&ln_out, &w.v_proj_w, tokens, nkv * hd, h);
56
57 let mut q_r = if w.q_norm_w.is_empty() {
60 transpose_no_norm(&q, tokens, nh, hd)
61 } else {
62 transpose_and_norm(&q, tokens, nh, hd, &w.q_norm_w, eps)
63 };
64 let mut k_r = if w.k_norm_w.is_empty() {
65 transpose_no_norm(&k, tokens, nkv, hd)
66 } else {
67 transpose_and_norm(&k, tokens, nkv, hd, &w.k_norm_w, eps)
68 };
69 let v_r = transpose_no_norm(&v, tokens, nkv, hd);
70
71 apply_rope(&mut q_r, nh, tokens, hd, cos, sin, pos_offset);
72 apply_rope(&mut k_r, nkv, tokens, hd, cos, sin, pos_offset);
73
74 let (full_k, full_v, kv_len) = update_kv(kv_cache, &k_r, &v_r, nkv, tokens, hd);
76
77 let params = AttentionParams {
79 batch: 1,
80 num_heads: nh,
81 num_kv_heads: nkv,
82 q_len: tokens,
83 kv_len,
84 head_dim: hd,
85 causal: tokens > 1,
86 pos_offset,
87 sliding_window: 0,
88 };
89 let mut attn_out = vec![0.0f32; nh * tokens * hd];
90 super::fused_attention(&q_r, &full_k, &full_v, &mut attn_out, ¶ms);
91
92 let attn_flat = untranspose(&attn_out, tokens, nh, hd);
94 let o_out = matmul_at_bt(&attn_flat, &w.o_proj_w, tokens, h, nh * hd);
95
96 let o_scaled = if let Some(ref scale) = w.attn_layer_scale {
98 scale_vec(&o_out, scale)
99 } else {
100 o_out
101 };
102 let hidden = add_vecs(input, &o_scaled);
103
104 let post_ln = rms_norm(&hidden, &w.post_ln_w, tokens, h, eps);
106 let gate = matmul_at_bt(&post_ln, &w.gate_proj_w, tokens, im, h);
107 let up = matmul_at_bt(&post_ln, &w.up_proj_w, tokens, im, h);
108 let silu_out = silu_mul(&gate, &up);
109 let mlp_out = matmul_at_bt(&silu_out, &w.down_proj_w, tokens, h, im);
110
111 let mlp_scaled = if let Some(ref scale) = w.mlp_layer_scale {
113 scale_vec(&mlp_out, scale)
114 } else {
115 mlp_out
116 };
117 add_vecs(&hidden, &mlp_scaled)
118}
119
120#[cfg(target_os = "macos")]
123extern "C" {
124 fn cblas_sgemm(
125 order: i32,
126 ta: i32,
127 tb: i32,
128 m: i32,
129 n: i32,
130 k: i32,
131 alpha: f32,
132 a: *const f32,
133 lda: i32,
134 b: *const f32,
135 ldb: i32,
136 beta: f32,
137 c: *mut f32,
138 ldc: i32,
139 );
140 fn vDSP_dotpr(
142 a: *const f32,
143 a_stride: i32,
144 b: *const f32,
145 b_stride: i32,
146 result: *mut f32,
147 n: u64,
148 );
149 fn vDSP_vsmul(
151 a: *const f32,
152 a_stride: i32,
153 scalar: *const f32,
154 result: *mut f32,
155 r_stride: i32,
156 n: u64,
157 );
158}
159
160fn matmul_at_bt(a: &[f32], b: &[f32], m: usize, n: usize, k: usize) -> Vec<f32> {
161 let mut c = vec![0.0f32; m * n];
162 #[cfg(target_os = "macos")]
163 unsafe {
164 cblas_sgemm(
165 101,
166 111,
167 112,
168 m as i32,
169 n as i32,
170 k as i32,
171 1.0,
172 a.as_ptr(),
173 k as i32,
174 b.as_ptr(),
175 k as i32,
176 0.0,
177 c.as_mut_ptr(),
178 n as i32,
179 );
180 }
181 #[cfg(not(target_os = "macos"))]
182 for i in 0..m {
183 for j in 0..n {
184 let mut s = 0.0f64;
185 for p in 0..k {
186 s += a[i * k + p] as f64 * b[j * k + p] as f64;
187 }
188 c[i * n + j] = s as f32;
189 }
190 }
191 c
192}
193
194fn rms_norm(x: &[f32], w: &[f32], tokens: usize, dim: usize, eps: f64) -> Vec<f32> {
195 let mut out = vec![0.0f32; tokens * dim];
196 let eps_f32 = eps as f32;
197 for t in 0..tokens {
198 let row = &x[t * dim..(t + 1) * dim];
199 let o = &mut out[t * dim..(t + 1) * dim];
200 let sum_sq;
202 #[cfg(target_os = "macos")]
203 {
204 let mut dot = 0.0f32;
205 unsafe {
206 vDSP_dotpr(row.as_ptr(), 1, row.as_ptr(), 1, &mut dot, dim as u64);
207 }
208 sum_sq = dot;
209 }
210 #[cfg(not(target_os = "macos"))]
211 {
212 let mut v = 0.0f32;
213 for &val in row {
214 v += val * val;
215 }
216 sum_sq = v;
217 }
218 let inv = 1.0f32 / (sum_sq / dim as f32 + eps_f32).sqrt();
219 for i in 0..dim {
220 o[i] = row[i] * inv * w[i];
221 }
222 }
223 out
224}
225
226fn transpose_and_norm(
227 flat: &[f32],
228 tokens: usize,
229 heads: usize,
230 hd: usize,
231 w: &[f32],
232 eps: f64,
233) -> Vec<f32> {
234 let mut out = vec![0.0f32; heads * tokens * hd];
235 let eps_f32 = eps as f32;
236 for t in 0..tokens {
237 for hi in 0..heads {
238 let src = t * heads * hd + hi * hd;
239 let dst = hi * tokens * hd + t * hd;
240 let sum_sq;
242 #[cfg(target_os = "macos")]
243 {
244 let mut dot = 0.0f32;
245 unsafe {
246 vDSP_dotpr(
247 flat[src..].as_ptr(),
248 1,
249 flat[src..].as_ptr(),
250 1,
251 &mut dot,
252 hd as u64,
253 );
254 }
255 sum_sq = dot;
256 }
257 #[cfg(not(target_os = "macos"))]
258 {
259 let mut v = 0.0f32;
260 for d in 0..hd {
261 v += flat[src + d] * flat[src + d];
262 }
263 sum_sq = v;
264 }
265 let inv = 1.0f32 / (sum_sq / hd as f32 + eps_f32).sqrt();
266 for d in 0..hd {
267 out[dst + d] = flat[src + d] * inv * w[d];
268 }
269 }
270 }
271 out
272}
273
274fn transpose_no_norm(flat: &[f32], tokens: usize, heads: usize, hd: usize) -> Vec<f32> {
275 let mut out = vec![0.0f32; heads * tokens * hd];
276 for t in 0..tokens {
277 for hi in 0..heads {
278 for d in 0..hd {
279 out[hi * tokens * hd + t * hd + d] = flat[t * heads * hd + hi * hd + d];
280 }
281 }
282 }
283 out
284}
285
286fn untranspose(data: &[f32], tokens: usize, heads: usize, hd: usize) -> Vec<f32> {
287 let mut out = vec![0.0f32; tokens * heads * hd];
288 for t in 0..tokens {
289 for hi in 0..heads {
290 for d in 0..hd {
291 out[t * heads * hd + hi * hd + d] = data[hi * tokens * hd + t * hd + d];
292 }
293 }
294 }
295 out
296}
297
298fn apply_rope(
299 data: &mut [f32],
300 heads: usize,
301 seq: usize,
302 hd: usize,
303 cos: &[f32],
304 sin: &[f32],
305 offset: usize,
306) {
307 let half = hd / 2;
308 for h in 0..heads {
309 for s in 0..seq {
310 let pos = offset + s;
311 let base = h * seq * hd + s * hd;
312 for i in 0..half {
313 let c = cos[pos * half + i];
314 let si = sin[pos * half + i];
315 let x0 = data[base + i];
316 let x1 = data[base + half + i];
317 data[base + i] = x0 * c - x1 * si;
318 data[base + half + i] = x1 * c + x0 * si;
319 }
320 }
321 }
322}
323
324fn repeat_kv(kv: &[f32], nkv: usize, n_rep: usize, seq: usize, hd: usize) -> Vec<f32> {
325 if n_rep == 1 {
326 return kv.to_vec();
327 }
328 let nh = nkv * n_rep;
329 let mut out = vec![0.0f32; nh * seq * hd];
330 for kh in 0..nkv {
331 for r in 0..n_rep {
332 let dst = kh * n_rep + r;
333 out[dst * seq * hd..(dst + 1) * seq * hd]
334 .copy_from_slice(&kv[kh * seq * hd..(kh + 1) * seq * hd]);
335 }
336 }
337 out
338}
339
340fn update_kv(
341 cache: &mut CpuKvCache,
342 k: &[f32],
343 v: &[f32],
344 nkv: usize,
345 new: usize,
346 hd: usize,
347) -> (Vec<f32>, Vec<f32>, usize) {
348 if cache.len == 0 {
349 cache.k = k.to_vec();
350 cache.v = v.to_vec();
351 cache.len = new;
352 (k.to_vec(), v.to_vec(), new)
353 } else {
354 let old = cache.len;
355 let total = old + new;
356 let mut fk = vec![0.0f32; nkv * total * hd];
357 let mut fv = vec![0.0f32; nkv * total * hd];
358 for h in 0..nkv {
359 fk[h * total * hd..h * total * hd + old * hd]
360 .copy_from_slice(&cache.k[h * old * hd..(h + 1) * old * hd]);
361 fk[h * total * hd + old * hd..h * total * hd + total * hd]
362 .copy_from_slice(&k[h * new * hd..(h + 1) * new * hd]);
363 fv[h * total * hd..h * total * hd + old * hd]
364 .copy_from_slice(&cache.v[h * old * hd..(h + 1) * old * hd]);
365 fv[h * total * hd + old * hd..h * total * hd + total * hd]
366 .copy_from_slice(&v[h * new * hd..(h + 1) * new * hd]);
367 }
368 cache.k = fk.clone();
369 cache.v = fv.clone();
370 cache.len = total;
371 (fk, fv, total)
372 }
373}
374
375fn scale_vec(vec: &[f32], scale: &[f32]) -> Vec<f32> {
377 let s_len = scale.len();
378 vec.iter()
379 .enumerate()
380 .map(|(i, &v)| v * scale[i % s_len])
381 .collect()
382}
383
384fn add_vecs(a: &[f32], b: &[f32]) -> Vec<f32> {
385 a.iter().zip(b).map(|(x, y)| x + y).collect()
386}
387
388fn silu_mul(gate: &[f32], up: &[f32]) -> Vec<f32> {
389 gate.iter()
390 .zip(up)
391 .map(|(g, u)| {
392 let s = g / (1.0 + (-g).exp());
393 s * u
394 })
395 .collect()
396}