cubecl_runtime/storage/
base.rs

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