1use anyhow::{Result, ensure};
14use metrale_gpu_runtime::gpu::{DevicePtr, GpuBackend};
15use metrale_model_weights::weights::expert_stream::{ExpertLru, ExpertSource};
16
17use super::{MoeV41, MoeV41LayerWeights, MoeV41Stage, MoeV41Timing, RouterWeights, prefetch_k};
18use metrale_model_layers::layers::ops::{
19 kquant_mmvq_experts_wn, kquant_q8_1_rows, kquant_q8_1_rows_bytes, kquant_swiglu_q8_1_rows,
20};
21
22impl MoeV41 {
23 #[allow(clippy::type_complexity)]
27 pub(super) fn plan(
28 &self,
29 indices: &[usize],
30 weights: &[f32],
31 m: usize,
32 ) -> (Vec<u8>, Vec<u8>, Vec<(usize, usize, usize)>) {
33 let c = &self.cfg;
34 let mut groups: std::collections::BTreeMap<usize, Vec<(i32, f32, usize)>> =
35 std::collections::BTreeMap::new();
36 for t in 0..m {
37 for kk in 0..c.topk {
38 let a = t * c.topk + kk;
39 groups
40 .entry(indices[a])
41 .or_default()
42 .push((t as i32, weights[a], a));
43 }
44 }
45 let mut rows_host: Vec<u8> = Vec::with_capacity(m * c.topk * 4);
46 let mut w_host: Vec<u8> = Vec::with_capacity(m * c.topk * 4);
47 let mut plan: Vec<(usize, usize, usize)> = Vec::with_capacity(groups.len());
48 for members in groups.values() {
49 let off = rows_host.len() / 4;
50 for &(t, rw, _a) in members {
51 rows_host.extend_from_slice(&t.to_le_bytes());
52 w_host.extend_from_slice(&rw.to_le_bytes());
53 }
54 plan.push((members[0].2, off, members.len()));
55 }
56 (rows_host, w_host, plan)
57 }
58
59 pub(super) fn upload_expert_table(
62 &self,
63 gpu: &dyn GpuBackend,
64 plan: &[(usize, usize, usize)],
65 slots: &[metrale_model_weights::weights::expert_stream::ExpertSlot],
66 stream: u64,
67 ) -> Result<usize> {
68 let ne = plan.len();
69 let mut ptrs: Vec<u8> = Vec::with_capacity(3 * ne * 8);
70 for which in 0..3 {
71 for &(a0, _, _) in plan {
72 let slot = slots[a0];
73 let p = [slot.gate, slot.up, slot.down][which];
74 ptrs.extend_from_slice(&p.0.to_le_bytes());
75 }
76 }
77 gpu.copy_h2d_async(&ptrs, self.ptrs_dev, stream)?;
78 Ok(ne)
79 }
80
81 pub fn ffn_input_q8_m1(&self, gpu: &dyn GpuBackend, x: DevicePtr, stream: u64) -> Result<()> {
84 kquant_q8_1_rows(
85 gpu,
86 self.k.q8_rows,
87 x,
88 self.a_q8,
89 1,
90 self.cfg.dim as u32,
91 stream,
92 )
93 }
94
95 pub(super) fn routed_m1(
102 &self,
103 gpu: &dyn GpuBackend,
104 x: DevicePtr,
105 ne: usize,
106 x_pre: bool,
107 stream: u64,
108 ) -> Result<()> {
109 let c = &self.cfg;
110 let table = |which: usize| DevicePtr(self.ptrs_dev.0 + (which * ne * 8) as u64);
111 if !x_pre {
112 self.ffn_input_q8_m1(gpu, x, stream)?;
113 }
114 let up_view = DevicePtr(self.gate_out.0 + (ne * c.inter * 2) as u64);
120 kquant_mmvq_experts_wn(
121 gpu,
122 self.k.mmvq_q2k_experts,
123 table(0),
124 self.a_q8,
125 self.gate_out,
126 c.inter as u32,
127 c.dim as u32,
128 1,
129 2 * ne as u32,
130 0,
131 self.k.experts_warps,
132 stream,
133 )?;
134 kquant_swiglu_q8_1_rows(
135 gpu,
136 self.k.swiglu_q8,
137 self.gate_out,
138 up_view,
139 self.weight_dev,
140 self.h,
141 self.h_q8,
142 ne as u32,
143 c.inter as u32,
144 c.swiglu_limit,
145 stream,
146 )?;
147 kquant_mmvq_experts_wn(
148 gpu,
149 self.k.mmvq_q3k_experts,
150 table(2),
151 self.h_q8,
152 self.down_out,
153 c.dim as u32,
154 c.inter as u32,
155 1,
156 ne as u32,
157 kquant_q8_1_rows_bytes(1, c.inter as u32) as u32,
158 self.k.experts_warps,
159 stream,
160 )?;
161 self.launch_n(gpu, self.k.sum_rows, c.dim, stream, |l| {
166 l.arg_ptr(self.acc)
167 .arg_ptr(self.down_out)
168 .arg_u32(ne as u32)
169 .arg_u32(c.dim as u32)
170 })
171 }
172
173 pub fn stage_m1<S: ExpertSource + ?Sized>(
180 &self,
181 gpu: &dyn GpuBackend,
182 w: &MoeV41LayerWeights,
183 lru: &mut ExpertLru,
184 src: &S,
185 reader_threads: usize,
186 next: Option<&RouterWeights>,
187 x: DevicePtr,
188 stream: u64,
189 ) -> Result<MoeV41Stage> {
190 let t0 = std::time::Instant::now();
191 let predict = next.filter(|_| prefetch_k() > 0 && lru.has_pool());
192 if let Some(nw) = predict {
193 self.predict_launch(gpu, nw, x, stream)?;
194 }
195 let (weights, indices) = self.route_select(gpu, w, 1, stream)?;
196 let predicted = match predict {
197 Some(nw) => self.predict_select(gpu, nw, prefetch_k(), stream)?,
198 None => Vec::new(),
199 };
200 let t1 = std::time::Instant::now();
201 lru.begin_token();
202 let before = lru.stats();
203 let keys: Vec<(u32, u32)> = indices.iter().map(|&e| (w.layer, e as u32)).collect();
204 let slots = lru.fetch_many_on(gpu, stream, src, &keys, &predicted, reader_threads)?;
205 let after = lru.stats();
206 let t2 = std::time::Instant::now();
207 let (rows_host, w_host, plan) = self.plan(&indices, &weights, 1);
208 gpu.copy_h2d_async(&rows_host, self.rows_dev, stream)?;
209 gpu.copy_h2d_async(&w_host, self.weight_dev, stream)?;
210 let ne = self.upload_expert_table(gpu, &plan, &slots, stream)?;
211 Ok(MoeV41Stage {
212 ne,
213 weights,
214 indices,
215 timing: MoeV41Timing {
216 route_ms: (t1 - t0).as_secs_f64() * 1e3,
217 fetch_ms: (t2 - t1).as_secs_f64() * 1e3,
218 compute_ms: 0.0,
219 hits: after.hits - before.hits,
220 misses: after.misses - before.misses,
221 bytes_read: after.bytes_read - before.bytes_read,
222 },
223 })
224 }
225
226 pub fn compute_m1(
233 &self,
234 gpu: &dyn GpuBackend,
235 w: &MoeV41LayerWeights,
236 x: DevicePtr,
237 ne: usize,
238 stream: u64,
239 ) -> Result<DevicePtr> {
240 let c = &self.cfg;
241 ensure!(
242 ne == c.topk,
243 "moe_v41: {ne} distinct experts for one token, expected the top-{}",
244 c.topk
245 );
246 gpu.memset_async(self.acc, 0, c.dim * 4, stream)?;
247 self.routed_m1(gpu, x, ne, false, stream)?;
248 self.shared_expert(gpu, w, x, 1, true, stream)?;
249 Ok(self.out)
250 }
251
252 pub fn shared_expert_on(
258 &self,
259 gpu: &dyn GpuBackend,
260 w: &MoeV41LayerWeights,
261 x: DevicePtr,
262 side: u64,
263 ) -> Result<()> {
264 self.shared_expert_body(gpu, w, x, 1, self.a_q8, self.sh_q8, true, side)
265 }
266
267 pub fn compute_m1_joined(
273 &self,
274 gpu: &dyn GpuBackend,
275 x: DevicePtr,
276 ne: usize,
277 join: u64,
278 stream: u64,
279 ) -> Result<DevicePtr> {
280 let c = &self.cfg;
281 ensure!(
282 ne == c.topk,
283 "moe_v41: {ne} distinct experts for one token, expected the top-{}",
284 c.topk
285 );
286 gpu.memset_async(self.acc, 0, c.dim * 4, stream)?;
287 self.routed_m1(gpu, x, ne, true, stream)?;
288 gpu.stream_wait_event(stream, join)?;
289 self.shared_expert_tail(gpu, 1, stream)?;
290 Ok(self.out)
291 }
292}