spark_model/layers/moe_v41/
route.rs1use anyhow::{Result, ensure};
8use spark_runtime::gpu::{DevicePtr, GpuBackend};
9use spark_runtime::kernel_args::KernelLaunch;
10
11use super::{MoeV41, MoeV41LayerWeights, RouterWeights, route_from_logits};
12
13impl MoeV41 {
14 pub fn route(
16 &self,
17 gpu: &dyn GpuBackend,
18 w: &MoeV41LayerWeights,
19 x: DevicePtr,
20 m: usize,
21 stream: u64,
22 ) -> Result<(Vec<f32>, Vec<usize>)> {
23 self.route_launch(gpu, w, x, m, stream)?;
24 self.route_select(gpu, w, m, stream)
25 }
26
27 pub fn route_launch(
30 &self,
31 gpu: &dyn GpuBackend,
32 w: &MoeV41LayerWeights,
33 x: DevicePtr,
34 m: usize,
35 stream: u64,
36 ) -> Result<()> {
37 let c = &self.cfg;
38 ensure!(
39 m >= 1 && m <= c.max_tokens,
40 "moe_v41: {m} tokens outside 1..={}",
41 c.max_tokens
42 );
43 let (kernel, grid, block, smem) = if m <= 8 {
50 if router_staged() {
51 (
52 self.k.router_gemv_staged,
53 [c.n_routed as u32, m as u32, 1],
54 [256, 1, 1],
55 c.dim as u32 * 4,
56 )
57 } else {
58 (
59 self.k.router_gemv,
60 [(c.n_routed as u32).div_ceil(64), m as u32, 1],
61 [64, 1, 1],
62 0,
63 )
64 }
65 } else {
66 (
67 self.k.gemm_f32out,
68 [(c.n_routed as u32).div_ceil(16), (m as u32).div_ceil(16), 1],
69 [16, 16, 1],
70 0,
71 )
72 };
73 KernelLaunch::new(gpu, kernel)
74 .grid(grid)
75 .block(block)
76 .shared_mem(smem)
77 .arg_ptr(x)
78 .arg_ptr(w.gate_w)
79 .arg_ptr(self.logits)
80 .arg_u32(m as u32)
81 .arg_u32(c.n_routed as u32)
82 .arg_u32(c.dim as u32)
83 .launch(stream)
84 }
85
86 pub fn route_select(
89 &self,
90 gpu: &dyn GpuBackend,
91 w: &MoeV41LayerWeights,
92 m: usize,
93 stream: u64,
94 ) -> Result<(Vec<f32>, Vec<usize>)> {
95 let c = &self.cfg;
96 let mut bytes = vec![0u8; m * c.n_routed * 4];
100 gpu.copy_d2h_on_stream(self.logits, &mut bytes, stream)?;
101 let logits: Vec<f32> = bytes
102 .chunks_exact(4)
103 .map(|b| f32::from_le_bytes([b[0], b[1], b[2], b[3]]))
104 .collect();
105 ensure!(
106 w.gate_bias.len() == c.n_routed,
107 "gate bias has {} entries for {} experts",
108 w.gate_bias.len(),
109 c.n_routed
110 );
111 Ok(route_from_logits(&logits, m, &w.gate_bias, c))
112 }
113}
114
115impl MoeV41 {
116 pub fn predict_launch(
120 &self,
121 gpu: &dyn GpuBackend,
122 next: &RouterWeights,
123 x: DevicePtr,
124 stream: u64,
125 ) -> Result<()> {
126 let c = &self.cfg;
127 let (kernel, grid, block, smem) = if router_staged() {
128 (
129 self.k.router_gemv_staged,
130 [c.n_routed as u32, 1, 1],
131 [256, 1, 1],
132 c.dim as u32 * 4,
133 )
134 } else {
135 (
136 self.k.router_gemv,
137 [(c.n_routed as u32).div_ceil(64), 1, 1],
138 [64, 1, 1],
139 0,
140 )
141 };
142 KernelLaunch::new(gpu, kernel)
143 .grid(grid)
144 .block(block)
145 .shared_mem(smem)
146 .arg_ptr(x)
147 .arg_ptr(next.gate_w)
148 .arg_ptr(self.pred_logits)
149 .arg_u32(1)
150 .arg_u32(c.n_routed as u32)
151 .arg_u32(c.dim as u32)
152 .launch(stream)
153 }
154
155 pub fn predict_select(
158 &self,
159 gpu: &dyn GpuBackend,
160 next: &RouterWeights,
161 k: usize,
162 stream: u64,
163 ) -> Result<Vec<(u32, u32)>> {
164 let c = &self.cfg;
165 let mut bytes = vec![0u8; c.n_routed * 4];
166 gpu.copy_d2h_on_stream(self.pred_logits, &mut bytes, stream)?;
167 let logits: Vec<f32> = bytes
168 .chunks_exact(4)
169 .map(|b| f32::from_le_bytes([b[0], b[1], b[2], b[3]]))
170 .collect();
171 let mut wide = c.clone();
172 wide.topk = k.min(c.n_routed);
173 let (_, ids) = route_from_logits(&logits, 1, &next.gate_bias, &wide);
174 Ok(ids.into_iter().map(|e| (next.layer, e as u32)).collect())
175 }
176
177 pub fn route_predict(
181 &self,
182 gpu: &dyn GpuBackend,
183 w: &MoeV41LayerWeights,
184 x: DevicePtr,
185 k: usize,
186 stream: u64,
187 ) -> Result<Vec<usize>> {
188 let c = &self.cfg;
189 self.route_launch(gpu, w, x, 1, stream)?;
190 let mut bytes = vec![0u8; c.n_routed * 4];
191 gpu.copy_d2h_on_stream(self.logits, &mut bytes, stream)?;
192 let logits: Vec<f32> = bytes
193 .chunks_exact(4)
194 .map(|b| f32::from_le_bytes([b[0], b[1], b[2], b[3]]))
195 .collect();
196 let mut wide = c.clone();
197 wide.topk = k.min(c.n_routed);
198 Ok(route_from_logits(&logits, 1, &w.gate_bias, &wide).1)
199 }
200}
201
202fn router_staged() -> bool {
203 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
204 *ON.get_or_init(|| !std::env::var("ATLAS_DS41_ROUTER_STAGED").is_ok_and(|v| v == "0"))
205}