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