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 fn get(&mut self, handle: &StorageHandle) -> Self::Resource;
84
85 /// Allocates `size` units of memory and returns a handle to it
86 fn alloc(&mut self, size: u64) -> Result<StorageHandle, IoError>;
87
88 /// Deallocates the memory pointed by the given storage id.
89 ///
90 /// These deallocations might need to be flushed with [`Self::flush`].
91 fn dealloc(&mut self, id: StorageId);
92
93 /// Flush deallocations when required.
94 fn flush(&mut self);
95}
96
97/// Access to the underlying resource.
98#[derive(new, Debug)]
99pub struct ManagedResource<Resource: Send> {
100 // This handle is here just to keep the underlying allocation alive.
101 // If the underlying allocation becomes invalid, someone else might
102 // allocate into this resource which could lead to bad behaviour.
103 #[allow(unused)]
104 binding: ManagedMemoryBinding,
105 resource: Resource,
106}
107
108impl<Resource: Send> ManagedResource<Resource> {
109 /// access the underlying resource.
110 ///
111 /// # Note
112 ///
113 /// The resource might be bigger than the part required.
114 /// (e.g. a big buffer where the handle only refers to a slice of it).
115 /// Only the part required by the handle 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}