Skip to main content

cubecl_runtime/storage/
base.rs

1use crate::{memory_management::ManagedMemoryBinding, server::IoError, storage_id_type};
2use core::fmt::Debug;
3
4// This ID is used to map a handle to its actual data.
5storage_id_type!(StorageId);
6
7impl core::fmt::Display for StorageId {
8    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
9        f.write_fmt(format_args!("StorageId({})", self.value))
10    }
11}
12
13/// Defines if data uses a full memory chunk or a slice of it.
14#[derive(Clone, Debug, PartialEq, Eq)]
15pub struct StorageUtilization {
16    /// The offset in bytes from the chunk start.
17    pub offset: u64,
18    /// The size of the slice in bytes.
19    pub size: u64,
20}
21
22/// Contains the [storage id](StorageId) of a resource and the way it is used.
23#[derive(new, Clone, Debug)]
24pub struct StorageHandle {
25    /// Storage id.
26    pub id: StorageId,
27    /// How the storage is used.
28    pub utilization: StorageUtilization,
29}
30
31impl StorageHandle {
32    /// Returns the size the handle is pointing to in memory.
33    ///
34    /// # Notes
35    ///
36    /// The result considers the offset.
37    pub fn size(&self) -> u64 {
38        self.utilization.size
39    }
40
41    /// Returns the offset of the handle.
42    pub fn offset(&self) -> u64 {
43        self.utilization.offset
44    }
45
46    /// Increase the current offset with the given value in bytes.
47    pub fn offset_start(&self, offset_bytes: u64) -> Self {
48        let utilization = StorageUtilization {
49            offset: self.offset() + offset_bytes,
50            size: self.size() - offset_bytes,
51        };
52
53        Self {
54            id: self.id,
55            utilization,
56        }
57    }
58
59    /// Reduce the size of the memory handle..
60    pub fn offset_end(&self, offset_bytes: u64) -> Self {
61        let utilization = StorageUtilization {
62            offset: self.offset(),
63            size: self.size() - offset_bytes,
64        };
65
66        Self {
67            id: self.id,
68            utilization,
69        }
70    }
71}
72
73/// Storage types are responsible for allocating and deallocating memory.
74pub trait ComputeStorage: Send {
75    /// The resource associated type determines the way data is implemented and how
76    /// it can be accessed by kernels.
77    type Resource: Send;
78
79    /// The alignment memory is allocated with in this storage.
80    fn alignment(&self) -> usize;
81
82    /// Returns the underlying resource for a specified storage handle.
83    ///
84    /// # Errors
85    ///
86    /// [`IoError::StorageHandleNotFound`] when this storage holds no
87    /// allocation for the handle's id — a handle that outlived its allocation,
88    /// or one whose id a deallocation retired.
89    fn get(&mut self, handle: &StorageHandle) -> Result<Self::Resource, IoError>;
90
91    /// Allocates `size` units of memory and returns a handle to it
92    fn alloc(&mut self, size: u64) -> Result<StorageHandle, IoError>;
93
94    /// Deallocates the memory pointed by the given storage id.
95    ///
96    /// These deallocations might need to be flushed with [`Self::flush`].
97    fn dealloc(&mut self, id: StorageId);
98
99    /// Flush deallocations when required.
100    fn flush(&mut self);
101}
102
103/// Access to the underlying resource.
104#[derive(new, Debug)]
105pub struct ManagedResource<Resource: Send> {
106    // This handle is here just to keep the underlying allocation alive.
107    // If the underlying allocation becomes invalid, someone else might
108    // allocate into this resource which could lead to bad behaviour.
109    #[allow(unused)]
110    binding: ManagedMemoryBinding,
111    resource: Resource,
112}
113
114impl<Resource: Send> ManagedResource<Resource> {
115    /// access the underlying resource.
116    ///
117    /// # Note
118    ///
119    /// The resource might be bigger than the part required.
120    /// (e.g. a big buffer where the handle only refers to a slice of it).
121    /// Only the part required by the handle is guaranteed to remain,
122    /// other parts of this resource *will* be re-used.
123    pub fn resource(&self) -> &Resource {
124        &self.resource
125    }
126
127    /// Mutable access to the underlying resource.
128    pub fn resource_mut(&mut self) -> &mut Resource {
129        &mut self.resource
130    }
131}