spark_storage/
predictor_ref.rs1use half::bf16;
16
17#[inline]
18fn b2f(x: bf16) -> f32 {
19 x.to_f32()
20}
21#[inline]
22fn f2b(x: f32) -> bf16 {
23 bf16::from_f32(x)
24}
25
26pub fn project_q_ref(
27 q: &[bf16],
28 p: &[bf16],
29 num_q_heads: usize,
30 head_dim: usize,
31 r: usize,
32) -> Vec<bf16> {
33 let mut out = vec![bf16::from_f32(0.0); num_q_heads * r];
34 for h in 0..num_q_heads {
35 for o in 0..r {
36 let mut acc = 0.0_f32;
37 for i in 0..head_dim {
38 acc += b2f(q[h * head_dim + i]) * b2f(p[i * r + o]);
39 }
40 out[h * r + o] = f2b(acc);
41 }
42 }
43 out
44}
45
46pub fn project_kv_block_ref(
48 k_block: &[bf16],
49 p: &[bf16],
50 block_size: usize,
51 num_kv_heads: usize,
52 head_dim: usize,
53 r: usize,
54) -> Vec<bf16> {
55 let mut out = vec![bf16::from_f32(0.0); num_kv_heads * block_size * r];
56 for kh in 0..num_kv_heads {
57 for tok in 0..block_size {
58 let k_base = (tok * num_kv_heads + kh) * head_dim;
59 for o in 0..r {
60 let mut acc = 0.0_f32;
61 for i in 0..head_dim {
62 acc += b2f(k_block[k_base + i]) * b2f(p[i * r + o]);
63 }
64 out[(kh * block_size + tok) * r + o] = f2b(acc);
65 }
66 }
67 }
68 out
69}
70
71pub fn predictor_score_ref(
72 q_proj: &[bf16],
73 k_lr_seq: &[bf16],
74 num_q_heads: usize,
75 num_kv_heads: usize,
76 block_size: usize,
77 r: usize,
78 num_active_blocks: usize,
79) -> Vec<f32> {
80 assert!(num_q_heads.is_multiple_of(num_kv_heads));
81 let gqa = num_q_heads / num_kv_heads;
82 let per_block = num_kv_heads * block_size * r;
83 let mut scores = vec![f32::NEG_INFINITY; num_active_blocks];
84 for blk in 0..num_active_blocks {
85 let mut best = f32::NEG_INFINITY;
86 for qh in 0..num_q_heads {
87 let kh = qh / gqa;
88 for tok in 0..block_size {
89 let mut dot = 0.0_f32;
90 for i in 0..r {
91 let q = b2f(q_proj[qh * r + i]);
92 let k = b2f(k_lr_seq[blk * per_block + (kh * block_size + tok) * r + i]);
93 dot += q * k;
94 }
95 if dot > best {
96 best = dot;
97 }
98 }
99 }
100 scores[blk] = best;
101 }
102 scores
103}
104
105pub fn ground_truth_block_weights(
109 q: &[bf16],
110 k: &[bf16],
111 num_q_heads: usize,
112 num_kv_heads: usize,
113 head_dim: usize,
114 block_size: usize,
115 num_blocks: usize,
116) -> Vec<f32> {
117 let gqa = num_q_heads / num_kv_heads;
118 let scale = 1.0_f32 / (head_dim as f32).sqrt();
119 let total_tokens = num_blocks * block_size;
120 let mut block_weights = vec![0.0_f32; num_blocks];
121
122 #[allow(clippy::needless_range_loop)]
124 for qh in 0..num_q_heads {
125 let kh = qh / gqa;
126 let mut logits = vec![0.0_f32; total_tokens];
128 for t in 0..total_tokens {
129 let blk = t / block_size;
130 let off = t % block_size;
131 let k_base = (off * num_kv_heads + kh) * head_dim;
132 let mut dot = 0.0_f32;
133 for i in 0..head_dim {
134 dot += b2f(q[qh * head_dim + i])
135 * b2f(k[blk * block_size * num_kv_heads * head_dim + k_base + i]);
136 }
137 logits[t] = dot * scale;
138 }
139 let lmax = logits.iter().cloned().fold(f32::NEG_INFINITY, f32::max);
140 let sum: f32 = logits.iter().map(|l| (l - lmax).exp()).sum();
141 for blk in 0..num_blocks {
142 let mut bw = 0.0_f32;
143 for off in 0..block_size {
144 let t = blk * block_size + off;
145 bw += (logits[t] - lmax).exp() / sum;
146 }
147 if bw > block_weights[blk] {
148 block_weights[blk] = bw;
149 }
150 }
151 }
152 block_weights
153}