1use std::collections::HashMap;
24use parking_lot::Mutex;
27
28#[derive(Default)]
34pub struct DerivedWeights {
35 rowwise_fp8: Mutex<HashMap<u64, (u64, u64)>>,
37 bf16: Mutex<HashMap<u64, u64>>,
39 cutlass_nvfp4_t: Mutex<HashMap<u64, u64>>,
41 cutlass_nvfp4_from_fp8: Mutex<HashMap<u64, (u64, u64)>>,
43}
44
45#[derive(Clone, Copy, Debug, PartialEq, Eq)]
47pub enum Derivation {
48 RowwiseFp8,
49 Bf16,
50 CutlassNvfp4Transposed,
51 CutlassNvfp4FromFp8,
52}
53
54impl DerivedWeights {
55 pub fn new() -> Self {
56 Self::default()
57 }
58
59 pub fn get_ptr(&self, kind: Derivation, key: u64) -> Option<u64> {
64 let map = match kind {
65 Derivation::Bf16 => &self.bf16,
66 Derivation::CutlassNvfp4Transposed => &self.cutlass_nvfp4_t,
67 _ => return None,
68 };
69 map.lock().get(&key).copied()
70 }
71
72 pub fn insert_ptr(&self, kind: Derivation, key: u64, value: u64) {
73 let map = match kind {
74 Derivation::Bf16 => &self.bf16,
75 Derivation::CutlassNvfp4Transposed => &self.cutlass_nvfp4_t,
76 _ => return,
77 };
78 map.lock().entry(key).or_insert(value);
79 }
80
81 pub fn get_pair(&self, kind: Derivation, key: u64) -> Option<(u64, u64)> {
82 let map = match kind {
83 Derivation::RowwiseFp8 => &self.rowwise_fp8,
84 Derivation::CutlassNvfp4FromFp8 => &self.cutlass_nvfp4_from_fp8,
85 _ => return None,
86 };
87 map.lock().get(&key).copied()
88 }
89
90 pub fn insert_pair(&self, kind: Derivation, key: u64, value: (u64, u64)) {
91 let map = match kind {
92 Derivation::RowwiseFp8 => &self.rowwise_fp8,
93 Derivation::CutlassNvfp4FromFp8 => &self.cutlass_nvfp4_from_fp8,
94 _ => return,
95 };
96 map.lock().entry(key).or_insert(value);
97 }
98
99 pub fn get_or_build_ptr(
107 &self,
108 kind: Derivation,
109 key: u64,
110 build: impl FnOnce() -> anyhow::Result<u64>,
111 ) -> anyhow::Result<u64> {
112 let map = match kind {
113 Derivation::Bf16 => &self.bf16,
114 Derivation::CutlassNvfp4Transposed => &self.cutlass_nvfp4_t,
115 _ => unreachable!("pair-valued derivation routed to get_or_build_ptr"),
116 };
117 if let Some(&hit) = map.lock().get(&key) {
118 return Ok(hit);
119 }
120 let built = build()?;
121 Ok(*map.lock().entry(key).or_insert(built))
122 }
123
124 pub fn get_or_build_pair(
126 &self,
127 kind: Derivation,
128 key: u64,
129 build: impl FnOnce() -> anyhow::Result<(u64, u64)>,
130 ) -> anyhow::Result<(u64, u64)> {
131 let map = match kind {
132 Derivation::RowwiseFp8 => &self.rowwise_fp8,
133 Derivation::CutlassNvfp4FromFp8 => &self.cutlass_nvfp4_from_fp8,
134 _ => unreachable!("single-valued derivation routed to get_or_build_pair"),
135 };
136 if let Some(&hit) = map.lock().get(&key) {
137 return Ok(hit);
138 }
139 let built = build()?;
140 Ok(*map.lock().entry(key).or_insert(built))
141 }
142
143 pub fn len(&self) -> usize {
146 self.rowwise_fp8.lock().len()
147 + self.bf16.lock().len()
148 + self.cutlass_nvfp4_t.lock().len()
149 + self.cutlass_nvfp4_from_fp8.lock().len()
150 }
151
152 pub fn is_empty(&self) -> bool {
153 self.len() == 0
154 }
155}
156
157impl std::fmt::Debug for DerivedWeights {
158 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
159 f.debug_struct("DerivedWeights")
160 .field("entries", &self.len())
161 .finish()
162 }
163}
164
165impl atlas_core::scope::ModelResource<dyn spark_runtime::gpu::GpuBackend> for DerivedWeights {
171 fn label(&self) -> &'static str {
172 "derived weights"
173 }
174
175 fn release(&mut self, gpu: &dyn spark_runtime::gpu::GpuBackend) -> anyhow::Result<()> {
176 let mut owned: Vec<u64> = Vec::new();
177 for (_, (a, b)) in self.rowwise_fp8.lock().drain() {
180 owned.push(a);
181 owned.push(b);
182 }
183 owned.extend(self.bf16.lock().drain().map(|(_, v)| v));
184 owned.extend(self.cutlass_nvfp4_t.lock().drain().map(|(_, v)| v));
185 for (_, (a, b)) in self.cutlass_nvfp4_from_fp8.lock().drain() {
186 owned.push(a);
187 owned.push(b);
188 }
189 let mut first_error = None;
190 for raw in owned {
191 if let Err(e) = gpu.free(spark_runtime::gpu::DevicePtr(raw))
192 && first_error.is_none()
193 {
194 first_error = Some(e);
195 }
196 }
197 match first_error {
198 Some(e) => Err(e),
199 None => Ok(()),
200 }
201 }
202}
203
204#[cfg(test)]
205mod tests {
206 use super::*;
207 use atlas_core::scope::ModelResource;
208 use spark_runtime::gpu::GpuBackend;
209 use spark_runtime::gpu::mock::MockGpuBackend;
210 use std::sync::atomic::{AtomicUsize, Ordering};
211
212 #[test]
213 fn a_fresh_cache_is_empty() {
214 assert!(DerivedWeights::new().is_empty());
215 }
216
217 #[test]
218 fn a_derivation_is_built_once_per_key() {
219 let d = DerivedWeights::new();
220 let builds = AtomicUsize::new(0);
221 let build_for = |ptr: u64| {
222 d.get_or_build_ptr(Derivation::Bf16, ptr, || {
223 builds.fetch_add(1, Ordering::Relaxed);
224 Ok(ptr + 1000)
225 })
226 .unwrap()
227 };
228 assert_eq!(build_for(10), 1010);
229 assert_eq!(build_for(10), 1010);
230 assert_eq!(builds.load(Ordering::Relaxed), 1);
231 assert_eq!(build_for(20), 1020);
232 assert_eq!(builds.load(Ordering::Relaxed), 2);
233 assert_eq!(d.len(), 2);
234 }
235
236 #[test]
237 fn the_derivations_do_not_share_a_keyspace() {
238 let d = DerivedWeights::new();
241 let bf16 = d
242 .get_or_build_ptr(Derivation::Bf16, 0x1000, || Ok(0xB16))
243 .unwrap();
244 let nvfp4 = d
245 .get_or_build_ptr(Derivation::CutlassNvfp4Transposed, 0x1000, || Ok(0x4444))
246 .unwrap();
247 assert_eq!(bf16, 0xB16);
248 assert_eq!(nvfp4, 0x4444);
249 assert_eq!(d.len(), 2);
250 }
251
252 #[test]
253 fn a_failed_build_is_not_memoized() {
254 let d = DerivedWeights::new();
255 assert!(
256 d.get_or_build_ptr(Derivation::Bf16, 7, || anyhow::bail!("oom"))
257 .is_err()
258 );
259 assert!(d.is_empty(), "a failure must not poison the key");
260 assert_eq!(
261 d.get_or_build_ptr(Derivation::Bf16, 7, || Ok(99)).unwrap(),
262 99
263 );
264 }
265
266 #[test]
267 fn two_models_memoize_independently_even_on_a_recycled_pointer() {
268 let a = DerivedWeights::new();
272 let recycled = 0x7f00_0000u64;
273 assert_eq!(
274 a.get_or_build_ptr(Derivation::Bf16, recycled, || Ok(0xAAAA))
275 .unwrap(),
276 0xAAAA
277 );
278 drop(a);
279
280 let b = DerivedWeights::new();
281 assert_eq!(
282 b.get_or_build_ptr(Derivation::Bf16, recycled, || Ok(0xBBBB))
283 .unwrap(),
284 0xBBBB,
285 "the same address must resolve to the NEW model's derivation"
286 );
287 }
288
289 #[test]
290 fn pair_derivations_round_trip_without_sharing_a_keyspace() {
291 let d = DerivedWeights::new();
292 let got = d
293 .get_or_build_pair(Derivation::RowwiseFp8, 5, || Ok((11, 22)))
294 .unwrap();
295 assert_eq!(got, (11, 22));
296 assert_eq!(
297 d.get_or_build_pair(Derivation::CutlassNvfp4FromFp8, 5, || Ok((33, 44)))
298 .unwrap(),
299 (33, 44)
300 );
301 assert_eq!(
302 d.get_or_build_pair(Derivation::RowwiseFp8, 5, || Ok((99, 99)))
303 .unwrap(),
304 (11, 22),
305 "cached, not rebuilt"
306 );
307 assert_eq!(d.len(), 2);
308 }
309
310 #[test]
311 fn release_frees_only_derived_values_and_drains_every_map() {
312 let gpu = MockGpuBackend::new();
313 let mut d = DerivedWeights::new();
314 let keys: Vec<u64> = (0..4).map(|_| gpu.alloc(1).unwrap().0).collect();
315 let values: Vec<u64> = (0..6).map(|_| gpu.alloc(1).unwrap().0).collect();
316
317 d.insert_pair(Derivation::RowwiseFp8, keys[0], (values[0], values[1]));
318 d.insert_ptr(Derivation::Bf16, keys[1], values[2]);
319 d.insert_ptr(Derivation::CutlassNvfp4Transposed, keys[2], values[3]);
320 d.insert_pair(
321 Derivation::CutlassNvfp4FromFp8,
322 keys[3],
323 (values[4], values[5]),
324 );
325 assert_eq!(gpu.alloc_count(), 10);
326 assert_eq!(d.len(), 4);
327
328 d.release(&gpu).unwrap();
329
330 assert!(d.is_empty(), "freed pointers must not remain memoized");
331 assert_eq!(gpu.alloc_count(), 4, "source-weight keys remain GPU-owned");
332 for key in keys {
333 gpu.free(spark_runtime::gpu::DevicePtr(key)).unwrap();
334 }
335 }
336}