1use std::cell::{Cell, RefCell};
23use std::sync::{Arc, RwLock};
24
25#[derive(Debug, Clone)]
27pub struct LayerHostBind {
28 pub gate: Vec<*const f32>,
29 pub up: Vec<*const f32>,
30 pub down: Vec<*const f32>,
31 pub stride: usize,
32}
33
34#[derive(Debug, Clone)]
36pub struct MoeHostBind {
37 pub layers: Vec<LayerHostBind>,
38}
39
40unsafe impl Send for LayerHostBind {}
43unsafe impl Sync for LayerHostBind {}
44unsafe impl Send for MoeHostBind {}
45unsafe impl Sync for MoeHostBind {}
46
47static HOST_BIND: RwLock<Option<MoeHostBind>> = RwLock::new(None);
48static LAST_STATS: RwLock<Option<MoeResidencyStats>> = RwLock::new(None);
49
50thread_local! {
51 static GMM_ORD: Cell<usize> = const { Cell::new(0) };
58}
59
60#[derive(Debug, Default, Clone)]
61pub struct MoeResidencyStats {
62 pub gpu_expert_calls: u64,
63 pub cpu_expert_calls: u64,
64 pub gpu_tokens: u64,
65 pub cpu_tokens: u64,
66}
67
68struct MoeResidencyCtx {
69 merged: Option<Arc<[bool]>>,
71 per_layer: Option<Arc<Vec<Arc<[bool]>>>>,
73 stats: MoeResidencyStats,
74}
75
76thread_local! {
77 static CTX: RefCell<Option<MoeResidencyCtx>> = const { RefCell::new(None) };
78}
79
80pub fn set_mask(mask: Option<Arc<[bool]>>) {
82 CTX.with(|c| {
83 *c.borrow_mut() = Some(MoeResidencyCtx {
84 merged: mask,
85 per_layer: None,
86 stats: MoeResidencyStats::default(),
87 });
88 });
89}
90
91pub fn set_per_layer_masks(layers: Option<Arc<Vec<Arc<[bool]>>>>) {
93 CTX.with(|c| {
94 *c.borrow_mut() = Some(MoeResidencyCtx {
95 merged: None,
96 per_layer: layers,
97 stats: MoeResidencyStats::default(),
98 });
99 });
100}
101
102pub fn clear_mask() {
103 CTX.with(|c| *c.borrow_mut() = None);
104}
105
106pub fn bind_host_weights(bind: Option<MoeHostBind>) {
107 *HOST_BIND.write().unwrap() = bind;
108}
109
110pub fn reset_gmm_counters() {
111 GMM_ORD.with(|c| c.set(0));
112}
113
114pub fn next_gmm_ord() -> usize {
116 GMM_ORD.with(|c| {
117 let v = c.get();
118 c.set(v + 1);
119 v
120 })
121}
122
123pub fn host_expert_weight_ptr(ord: usize, expert: usize) -> Option<*const f32> {
125 let bind = HOST_BIND.read().unwrap();
126 let bind = bind.as_ref()?;
127 let layer = bind.layers.get(ord / 3)?;
128 let ptrs = match ord % 3 {
129 0 => &layer.gate,
130 1 => &layer.up,
131 _ => &layer.down,
132 };
133 ptrs.get(expert).copied()
134}
135
136pub fn peek_stats() -> Option<MoeResidencyStats> {
137 CTX.with(|c| c.borrow().as_ref().map(|ctx| ctx.stats.clone()))
138}
139
140pub fn take_last_forward_stats() -> Option<MoeResidencyStats> {
142 LAST_STATS.write().unwrap().take()
143}
144
145pub(crate) fn stash_last_forward_stats(stats: MoeResidencyStats) {
146 *LAST_STATS.write().unwrap() = Some(stats);
147}
148
149fn expert_on_device_inner(ctx: &MoeResidencyCtx, layer: Option<usize>, e: usize) -> bool {
150 if let Some(layers) = ctx.per_layer.as_ref() {
151 if let Some(li) = layer {
152 return layers
153 .get(li)
154 .and_then(|m| m.get(e).copied())
155 .unwrap_or(true);
156 }
157 }
158 ctx.merged
159 .as_ref()
160 .and_then(|m| m.get(e).copied())
161 .unwrap_or(true)
162}
163
164pub fn expert_on_device_for_layer(layer: usize, e: usize) -> bool {
166 CTX.with(|c| {
167 let borrow = c.borrow();
168 let Some(ctx) = borrow.as_ref() else {
169 return true;
170 };
171 expert_on_device_inner(ctx, Some(layer), e)
172 })
173}
174
175pub fn expert_on_device(e: usize) -> bool {
177 expert_on_device_for_layer(0, e)
178}
179
180pub fn record_expert_tokens(layer: usize, e: usize, num_tokens: usize) {
181 if num_tokens == 0 {
182 return;
183 }
184 CTX.with(|c| {
185 let mut borrow = c.borrow_mut();
186 let Some(ctx) = borrow.as_mut() else {
187 return;
188 };
189 let on_device = expert_on_device_inner(ctx, Some(layer), e);
190 if on_device {
191 ctx.stats.gpu_expert_calls += 1;
192 ctx.stats.gpu_tokens += num_tokens as u64;
193 } else {
194 ctx.stats.cpu_expert_calls += 1;
195 ctx.stats.cpu_tokens += num_tokens as u64;
196 }
197 });
198}
199
200pub fn take_stats() -> Option<MoeResidencyStats> {
202 CTX.with(|c| c.borrow_mut().take().map(|ctx| ctx.stats))
203}
204
205#[cfg(test)]
206mod tests {
207 use super::*;
208 use std::sync::Arc;
209
210 #[test]
211 fn gmm_ordinal_is_thread_local() {
212 use std::sync::Barrier;
213 let barrier = Arc::new(Barrier::new(2));
217 let handles: Vec<_> = (0..2)
218 .map(|_| {
219 let b = barrier.clone();
220 std::thread::spawn(move || {
221 reset_gmm_counters();
222 b.wait(); (0..5).map(|_| next_gmm_ord()).collect::<Vec<_>>()
224 })
225 })
226 .collect();
227 for h in handles {
228 assert_eq!(
229 h.join().unwrap(),
230 vec![0, 1, 2, 3, 4],
231 "GroupedMatMul ordinal must be per-thread monotonic"
232 );
233 }
234 }
235
236 #[test]
237 fn per_layer_masks_are_layer_local() {
238 let per = Arc::new(vec![
239 Arc::from([false, true, true, true]),
240 Arc::from([true, false, true, true]),
241 ]);
242 set_per_layer_masks(Some(per));
243 assert!(!expert_on_device_for_layer(0, 0));
244 assert!(expert_on_device_for_layer(0, 1));
245 assert!(expert_on_device_for_layer(1, 0));
246 assert!(!expert_on_device_for_layer(1, 1));
247 clear_mask();
248 }
249}