cubecl_runtime/memory_management/
handle.rs1use crate::memory_management::MemoryHandle;
2use alloc::{
3 sync::{Arc, Weak},
4 vec::Vec,
5};
6use core::sync::atomic::{AtomicU64, Ordering};
7
8#[derive(Debug)]
10pub struct ManagedMemoryHandle {
11 descriptor: Arc<ManagedMemoryDescriptor>,
12 handle_count: Arc<()>,
14}
15
16#[derive(Debug)]
18pub struct ManagedMemoryBinding {
19 descriptor: Arc<ManagedMemoryDescriptor>,
20}
21
22#[derive(Debug, Clone)]
26pub struct WeakMemoryBinding {
27 descriptor: Weak<ManagedMemoryDescriptor>,
28}
29
30#[derive(Debug, Default)]
32pub struct SharedMemoryBindings {
33 pub bindings: Vec<ManagedMemoryBinding>,
35}
36
37impl Clone for ManagedMemoryHandle {
38 fn clone(&self) -> Self {
39 Self {
40 descriptor: self.descriptor.clone(),
41 handle_count: self.handle_count.clone(),
42 }
43 }
44}
45
46#[doc(hidden)]
58pub struct ManagedMemoryDescriptor {
59 #[doc(hidden)]
60 pub id: ManagedMemoryId,
61 location: AtomicU64,
62}
63
64impl core::fmt::Debug for ManagedMemoryDescriptor {
65 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
66 f.debug_struct("ManagedMemoryDescriptor")
67 .field("id", &self.id)
68 .field("location", &self.location())
69 .finish()
70 }
71}
72
73#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash)]
74pub struct ManagedMemoryId {
76 #[doc(hidden)]
77 pub value: usize,
78}
79
80impl PartialEq for ManagedMemoryDescriptor {
81 fn eq(&self, other: &Self) -> bool {
82 self.id == other.id
83 }
84}
85
86impl Eq for ManagedMemoryDescriptor {}
87
88#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
89#[doc(hidden)]
91pub struct MemoryLocation {
92 pub pool: u8,
94 pub page: u16,
96 pub slice: u32,
98 pub init: u8,
100}
101
102impl ManagedMemoryDescriptor {
103 #[doc(hidden)]
105 pub fn update_location(&self, location: MemoryLocation) {
106 self.location.store(location.to_bits(), Ordering::Relaxed);
107 }
108
109 #[doc(hidden)]
111 pub fn update_slice(&self, slice: u32) {
112 self.modify(|location| MemoryLocation { slice, ..location });
113 }
114
115 #[doc(hidden)]
117 pub fn update_page(&self, page: u16) {
118 self.modify(|location| MemoryLocation { page, ..location });
119 }
120
121 #[doc(hidden)]
123 pub fn location(&self) -> MemoryLocation {
124 MemoryLocation::from_bits(self.location.load(Ordering::Relaxed))
125 }
126
127 #[doc(hidden)]
132 pub fn is_allocated(&self) -> bool {
133 self.location().init != 0
134 }
135
136 #[doc(hidden)]
137 pub fn slice(&self) -> usize {
138 self.location().slice as usize
139 }
140
141 #[doc(hidden)]
142 pub fn page(&self) -> usize {
143 self.location().page as usize
144 }
145
146 fn modify(&self, update: impl Fn(MemoryLocation) -> MemoryLocation) {
147 let _ = self
149 .location
150 .try_update(Ordering::Relaxed, Ordering::Relaxed, |bits| {
151 Some(update(MemoryLocation::from_bits(bits)).to_bits())
152 });
153 }
154}
155
156impl MemoryLocation {
157 fn to_bits(self) -> u64 {
160 self.pool as u64
161 | (self.page as u64) << 8
162 | (self.slice as u64) << 24
163 | (self.init as u64) << 56
164 }
165
166 fn from_bits(bits: u64) -> Self {
167 Self {
168 pool: bits as u8,
169 page: (bits >> 8) as u16,
170 slice: (bits >> 24) as u32,
171 init: (bits >> 56) as u8,
172 }
173 }
174
175 #[doc(hidden)]
177 pub fn new(pool: u8, page: u16, slice: u32) -> Self {
178 Self {
179 pool,
180 page,
181 slice,
182 init: 1,
183 }
184 }
185
186 #[doc(hidden)]
188 pub fn uninit() -> Self {
189 Self {
190 pool: 0,
191 page: 0,
192 slice: 0,
193 init: 0,
194 }
195 }
196}
197
198impl ManagedMemoryHandle {
199 pub fn new() -> Self {
201 let value = Self::gen_id();
202
203 Self {
204 descriptor: Arc::new(ManagedMemoryDescriptor {
205 id: ManagedMemoryId { value },
206 location: AtomicU64::new(MemoryLocation::uninit().to_bits()),
207 }),
208 handle_count: Arc::new(()),
209 }
210 }
211
212 #[doc(hidden)]
214 pub fn descriptor(&self) -> &ManagedMemoryDescriptor {
215 &self.descriptor
216 }
217
218 pub fn can_mut(&self) -> bool {
220 Arc::strong_count(&self.handle_count) <= 2
221 }
222
223 pub fn is_bound(&self) -> bool {
229 Arc::strong_count(&self.descriptor) > Arc::strong_count(&self.handle_count)
230 }
231
232 pub fn is_free(&self) -> bool {
234 Arc::strong_count(&self.descriptor) <= 1
235 }
236
237 pub fn binding(self) -> ManagedMemoryBinding {
239 ManagedMemoryBinding {
240 descriptor: self.descriptor.clone(),
241 }
242 }
243
244 fn gen_id() -> usize {
245 static COUNTER: core::sync::atomic::AtomicUsize = core::sync::atomic::AtomicUsize::new(0);
246 let value = COUNTER.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
247 if value == usize::MAX {
248 core::panic!("Memory ID overflowed");
249 }
250 value
251 }
252}
253
254impl ManagedMemoryBinding {
255 #[doc(hidden)]
257 pub fn descriptor(&self) -> &ManagedMemoryDescriptor {
258 &self.descriptor
259 }
260
261 pub fn id(&self) -> ManagedMemoryId {
264 self.descriptor.id
265 }
266
267 pub fn downgrade(&self) -> WeakMemoryBinding {
269 WeakMemoryBinding {
270 descriptor: Arc::downgrade(&self.descriptor),
271 }
272 }
273}
274
275impl WeakMemoryBinding {
276 pub fn is_alive(&self) -> bool {
279 self.descriptor.strong_count() != 0
280 }
281
282 pub fn upgrade(&self) -> Option<ManagedMemoryBinding> {
287 if self.descriptor.strong_count() <= 1 {
288 return None;
289 }
290 let descriptor = self.descriptor.upgrade()?;
291 Some(ManagedMemoryBinding { descriptor })
292 }
293}
294
295impl Default for ManagedMemoryHandle {
296 fn default() -> Self {
297 Self::new()
298 }
299}
300
301impl Clone for ManagedMemoryBinding {
302 fn clone(&self) -> Self {
303 Self {
304 descriptor: self.descriptor.clone(),
305 }
306 }
307}
308
309impl MemoryHandle<ManagedMemoryBinding> for ManagedMemoryHandle {
310 fn can_mut(&self) -> bool {
311 self.can_mut()
312 }
313
314 fn binding(self) -> ManagedMemoryBinding {
315 self.binding()
316 }
317}
318
319impl SharedMemoryBindings {
320 pub fn clear(&mut self) {
322 self.bindings.clear();
323 }
324
325 pub fn is_empty(&self) -> bool {
327 self.bindings.is_empty()
328 }
329
330 pub fn push(&mut self, binding: ManagedMemoryBinding) {
332 self.bindings.push(binding)
333 }
334}
335
336impl cubecl_common::pool::Reclaim for SharedMemoryBindings {
337 fn reclaim(&mut self) {
338 self.clear();
339 }
340}
341
342pub fn optimal_align(shape: usize, elem_size: usize, buffer_align: usize) -> usize {
346 if shape == 1 {
347 elem_size
348 } else {
349 (shape * elem_size)
350 .next_power_of_two()
351 .clamp(16, buffer_align)
352 }
353}
354
355#[cfg(test)]
356mod tests {
357 use super::*;
358
359 #[test]
360 fn test_memory_id_mutability() {
361 let handle1 = ManagedMemoryHandle::new();
362 handle1.descriptor().update_slice(4);
363 assert_eq!(handle1.descriptor().slice(), 4);
364
365 let handle2 = ManagedMemoryHandle::new();
366 handle2
367 .clone()
368 .descriptor()
369 .update_location(handle1.descriptor().location());
370 assert_eq!(handle2.descriptor().slice(), 4);
371 }
372
373 #[test]
374 fn test_location_visible_through_shared_arc() {
375 let handle = ManagedMemoryHandle::new();
376 let handle2 = handle.clone();
377
378 let location = MemoryLocation::new(1, 2, 3);
379 handle.descriptor().update_location(location);
380
381 assert_eq!(handle2.descriptor().location().pool, 1);
382 assert_eq!(handle2.descriptor().location().page, 2);
383 assert_eq!(handle2.descriptor().location().slice, 3);
384 assert_eq!(handle2.descriptor().location().init, 1);
385
386 handle.descriptor().update_slice(42);
387 assert_eq!(handle2.descriptor().slice(), 42);
388 }
389
390 #[test]
393 fn a_location_survives_packing_at_every_extreme() {
394 let fields = |location: MemoryLocation| {
395 (location.pool, location.page, location.slice, location.init)
396 };
397
398 for location in [
399 MemoryLocation::uninit(),
400 MemoryLocation::new(1, 2, 3),
401 MemoryLocation::new(u8::MAX, u16::MAX, u32::MAX),
402 MemoryLocation {
403 init: u8::MAX,
404 ..MemoryLocation::uninit()
405 },
406 ] {
407 let packed = MemoryLocation::from_bits(location.to_bits());
408
409 assert_eq!(fields(packed), fields(location));
410 }
411 }
412}