cubecl_runtime/memory_management/
handle.rs1use crate::memory_management::MemoryHandle;
2use alloc::{sync::Arc, vec::Vec};
3use core::sync::atomic::{AtomicU64, Ordering};
4
5#[derive(Debug)]
7pub struct ManagedMemoryHandle {
8 descriptor: Arc<ManagedMemoryDescriptor>,
9 handle_count: Arc<()>,
11}
12
13#[derive(Debug)]
15pub struct ManagedMemoryBinding {
16 descriptor: Arc<ManagedMemoryDescriptor>,
17}
18
19#[derive(Debug, Default)]
21pub struct SharedMemoryBindings {
22 pub bindings: Vec<ManagedMemoryBinding>,
24}
25
26impl Clone for ManagedMemoryHandle {
27 fn clone(&self) -> Self {
28 Self {
29 descriptor: self.descriptor.clone(),
30 handle_count: self.handle_count.clone(),
31 }
32 }
33}
34
35#[doc(hidden)]
47pub struct ManagedMemoryDescriptor {
48 #[doc(hidden)]
49 pub id: ManagedMemoryId,
50 location: AtomicU64,
51}
52
53impl core::fmt::Debug for ManagedMemoryDescriptor {
54 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
55 f.debug_struct("ManagedMemoryDescriptor")
56 .field("id", &self.id)
57 .field("location", &self.location())
58 .finish()
59 }
60}
61
62#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash)]
63pub struct ManagedMemoryId {
65 #[doc(hidden)]
66 pub value: usize,
67}
68
69impl PartialEq for ManagedMemoryDescriptor {
70 fn eq(&self, other: &Self) -> bool {
71 self.id == other.id
72 }
73}
74
75impl Eq for ManagedMemoryDescriptor {}
76
77#[derive(Clone, Copy, Debug)]
78#[doc(hidden)]
80pub struct MemoryLocation {
81 pub pool: u8,
83 pub page: u16,
85 pub slice: u32,
87 pub init: u8,
89}
90
91impl ManagedMemoryDescriptor {
92 #[doc(hidden)]
94 pub fn update_location(&self, location: MemoryLocation) {
95 self.location.store(location.to_bits(), Ordering::Relaxed);
96 }
97
98 #[doc(hidden)]
100 pub fn update_slice(&self, slice: u32) {
101 self.modify(|location| MemoryLocation { slice, ..location });
102 }
103
104 #[doc(hidden)]
106 pub fn update_page(&self, page: u16) {
107 self.modify(|location| MemoryLocation { page, ..location });
108 }
109
110 #[doc(hidden)]
112 pub fn location(&self) -> MemoryLocation {
113 MemoryLocation::from_bits(self.location.load(Ordering::Relaxed))
114 }
115
116 #[doc(hidden)]
121 pub fn is_allocated(&self) -> bool {
122 self.location().init != 0
123 }
124
125 #[doc(hidden)]
126 pub fn slice(&self) -> usize {
127 self.location().slice as usize
128 }
129
130 #[doc(hidden)]
131 pub fn page(&self) -> usize {
132 self.location().page as usize
133 }
134
135 fn modify(&self, update: impl Fn(MemoryLocation) -> MemoryLocation) {
136 let _ = self
138 .location
139 .fetch_update(Ordering::Relaxed, Ordering::Relaxed, |bits| {
140 Some(update(MemoryLocation::from_bits(bits)).to_bits())
141 });
142 }
143}
144
145impl MemoryLocation {
146 fn to_bits(self) -> u64 {
149 self.pool as u64
150 | (self.page as u64) << 8
151 | (self.slice as u64) << 24
152 | (self.init as u64) << 56
153 }
154
155 fn from_bits(bits: u64) -> Self {
156 Self {
157 pool: bits as u8,
158 page: (bits >> 8) as u16,
159 slice: (bits >> 24) as u32,
160 init: (bits >> 56) as u8,
161 }
162 }
163
164 #[doc(hidden)]
166 pub fn new(pool: u8, page: u16, slice: u32) -> Self {
167 Self {
168 pool,
169 page,
170 slice,
171 init: 1,
172 }
173 }
174
175 #[doc(hidden)]
177 pub fn uninit() -> Self {
178 Self {
179 pool: 0,
180 page: 0,
181 slice: 0,
182 init: 0,
183 }
184 }
185}
186
187impl ManagedMemoryHandle {
188 pub fn new() -> Self {
190 let value = Self::gen_id();
191
192 Self {
193 descriptor: Arc::new(ManagedMemoryDescriptor {
194 id: ManagedMemoryId { value },
195 location: AtomicU64::new(MemoryLocation::uninit().to_bits()),
196 }),
197 handle_count: Arc::new(()),
198 }
199 }
200
201 #[doc(hidden)]
203 pub fn descriptor(&self) -> &ManagedMemoryDescriptor {
204 &self.descriptor
205 }
206
207 pub fn can_mut(&self) -> bool {
209 Arc::strong_count(&self.handle_count) <= 2
210 }
211
212 pub fn is_free(&self) -> bool {
214 Arc::strong_count(&self.descriptor) <= 1
215 }
216
217 pub fn binding(self) -> ManagedMemoryBinding {
219 ManagedMemoryBinding {
220 descriptor: self.descriptor.clone(),
221 }
222 }
223
224 fn gen_id() -> usize {
225 static COUNTER: core::sync::atomic::AtomicUsize = core::sync::atomic::AtomicUsize::new(0);
226 let value = COUNTER.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
227 if value == usize::MAX {
228 core::panic!("Memory ID overflowed");
229 }
230 value
231 }
232}
233
234impl ManagedMemoryBinding {
235 #[doc(hidden)]
237 pub fn descriptor(&self) -> &ManagedMemoryDescriptor {
238 &self.descriptor
239 }
240
241 pub fn id(&self) -> ManagedMemoryId {
244 self.descriptor.id
245 }
246}
247
248impl Default for ManagedMemoryHandle {
249 fn default() -> Self {
250 Self::new()
251 }
252}
253
254impl Clone for ManagedMemoryBinding {
255 fn clone(&self) -> Self {
256 Self {
257 descriptor: self.descriptor.clone(),
258 }
259 }
260}
261
262impl MemoryHandle<ManagedMemoryBinding> for ManagedMemoryHandle {
263 fn can_mut(&self) -> bool {
264 self.can_mut()
265 }
266
267 fn binding(self) -> ManagedMemoryBinding {
268 self.binding()
269 }
270}
271
272impl SharedMemoryBindings {
273 pub fn clear(&mut self) {
275 self.bindings.clear();
276 }
277
278 pub fn is_empty(&self) -> bool {
280 self.bindings.is_empty()
281 }
282
283 pub fn push(&mut self, binding: ManagedMemoryBinding) {
285 self.bindings.push(binding)
286 }
287}
288
289impl cubecl_common::pool::Reclaim for SharedMemoryBindings {
290 fn reclaim(&mut self) {
291 self.clear();
292 }
293}
294
295pub fn optimal_align(shape: usize, elem_size: usize, buffer_align: usize) -> usize {
299 if shape == 1 {
300 elem_size
301 } else {
302 (shape * elem_size)
303 .next_power_of_two()
304 .clamp(16, buffer_align)
305 }
306}
307
308#[cfg(test)]
309mod tests {
310 use super::*;
311
312 #[test]
313 fn test_memory_id_mutability() {
314 let handle1 = ManagedMemoryHandle::new();
315 handle1.descriptor().update_slice(4);
316 assert_eq!(handle1.descriptor().slice(), 4);
317
318 let handle2 = ManagedMemoryHandle::new();
319 handle2
320 .clone()
321 .descriptor()
322 .update_location(handle1.descriptor().location());
323 assert_eq!(handle2.descriptor().slice(), 4);
324 }
325
326 #[test]
327 fn test_location_visible_through_shared_arc() {
328 let handle = ManagedMemoryHandle::new();
329 let handle2 = handle.clone();
330
331 let location = MemoryLocation::new(1, 2, 3);
332 handle.descriptor().update_location(location);
333
334 assert_eq!(handle2.descriptor().location().pool, 1);
335 assert_eq!(handle2.descriptor().location().page, 2);
336 assert_eq!(handle2.descriptor().location().slice, 3);
337 assert_eq!(handle2.descriptor().location().init, 1);
338
339 handle.descriptor().update_slice(42);
340 assert_eq!(handle2.descriptor().slice(), 42);
341 }
342
343 #[test]
346 fn a_location_survives_packing_at_every_extreme() {
347 let fields = |location: MemoryLocation| {
348 (location.pool, location.page, location.slice, location.init)
349 };
350
351 for location in [
352 MemoryLocation::uninit(),
353 MemoryLocation::new(1, 2, 3),
354 MemoryLocation::new(u8::MAX, u16::MAX, u32::MAX),
355 MemoryLocation {
356 init: u8::MAX,
357 ..MemoryLocation::uninit()
358 },
359 ] {
360 let packed = MemoryLocation::from_bits(location.to_bits());
361
362 assert_eq!(fields(packed), fields(location));
363 }
364 }
365}