cubecl_runtime/server/handle.rs
1use cubecl_common::device::ServiceId;
2use cubecl_environment::stream::StreamId;
3use cubecl_zspace::{Shape, Strides};
4
5use crate::{
6 memory_management::{ManagedMemoryBinding, ManagedMemoryHandle, ManagedMemoryId},
7 server::{CopyDescriptor, TensorMapBinding},
8};
9
10/// Server handle containing the [memory handle](crate::server::Handle).
11pub struct Handle {
12 /// Memory handle.
13 pub memory: ManagedMemoryHandle,
14 /// The service whose memory this handle addresses. A client checks it
15 /// before handing the handle to its device: memory coordinates mean
16 /// nothing on another device, and reading them there is not an error
17 /// but garbage.
18 pub service: ServiceId,
19 /// Memory offset in bytes.
20 pub offset_start: Option<u64>,
21 /// Memory offset in bytes.
22 pub offset_end: Option<u64>,
23 /// The stream where the data was created.
24 pub stream: StreamId,
25 /// Length of the underlying buffer ignoring offsets
26 pub(crate) size: u64,
27}
28
29impl core::fmt::Debug for Handle {
30 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
31 f.debug_struct("Handle")
32 .field("id", &self.memory)
33 .field("service", &self.service)
34 .field("offset_start", &self.offset_start)
35 .field("offset_end", &self.offset_end)
36 .field("stream", &self.stream)
37 .field("size", &self.size)
38 .finish()
39 }
40}
41
42impl Clone for Handle {
43 fn clone(&self) -> Self {
44 Self {
45 memory: self.memory.clone(),
46 service: self.service,
47 offset_start: self.offset_start,
48 offset_end: self.offset_end,
49 stream: self.stream,
50 size: self.size,
51 }
52 }
53}
54
55impl Handle {
56 /// Creates a new handle of the given size.
57 pub fn from_memory(
58 id: ManagedMemoryHandle,
59 service: ServiceId,
60 stream: StreamId,
61 size: u64,
62 ) -> Self {
63 Self {
64 memory: id,
65 service,
66 offset_start: None,
67 offset_end: None,
68 stream,
69 size,
70 }
71 }
72 /// Creates a new handle of the given size.
73 pub fn new(service: ServiceId, stream: StreamId, size: u64) -> Self {
74 Self {
75 memory: ManagedMemoryHandle::new(),
76 service,
77 offset_start: None,
78 offset_end: None,
79 stream,
80 size,
81 }
82 }
83 /// Checks whether the handle can be mutated in-place without affecting other computation.
84 pub fn can_mut(&self) -> bool {
85 self.memory.can_mut()
86 }
87
88 /// Returns the [`BufferBinding`] corresponding to the current handle.
89 pub fn binding(self) -> BufferBinding {
90 BufferBinding {
91 memory: self.memory.binding(),
92 service: self.service,
93 offset_start: self.offset_start,
94 offset_end: self.offset_end,
95 stream: self.stream,
96 size: self.size,
97 }
98 }
99
100 /// Add to the current offset in bytes.
101 pub fn offset_start(mut self, offset: u64) -> Self {
102 if let Some(val) = &mut self.offset_start {
103 *val += offset;
104 } else {
105 self.offset_start = Some(offset);
106 }
107
108 self
109 }
110 /// Add to the current offset in bytes.
111 pub fn offset_end(mut self, offset: u64) -> Self {
112 if let Some(val) = &mut self.offset_end {
113 *val += offset;
114 } else {
115 self.offset_end = Some(offset);
116 }
117
118 self
119 }
120
121 /// Convert the [handle](Handle) into a [binding](Binding) with shape and stride metadata.
122 pub fn copy_descriptor(
123 self,
124 shape: Shape,
125 strides: Strides,
126 elem_size: usize,
127 ) -> CopyDescriptor {
128 CopyDescriptor {
129 shape,
130 strides,
131 elem_size,
132 handle: self.binding(),
133 }
134 }
135 /// Get the size of the handle, in bytes, accounting for offsets
136 pub fn size_in_used(&self) -> u64 {
137 self.size - self.offset_start.unwrap_or(0) - self.offset_end.unwrap_or(0)
138 }
139 /// Get the total size of the handle, in bytes.
140 pub fn size(&self) -> u64 {
141 self.size
142 }
143}
144
145/// A resource passed to a kernel function
146#[allow(clippy::large_enum_variant)]
147#[derive(Clone, Debug)]
148pub enum KernelResource {
149 /// Buffer resource
150 Buffer(BufferBinding),
151 /// Tensor map resource for CUDA
152 TensorMap(TensorMapBinding),
153}
154
155/// A buffer binding represents a [Handle] that is bound to managed memory.
156///
157/// The memory used is known by the compute server.
158/// A buffer binding is only valid after being initlized with [`super::Server::initialize_bindings`]
159///
160/// # Notes
161///
162/// A buffer binding is detached from a [`Handle`], meaning that is won't affect [`Handle::can_mut`].
163#[derive(Clone, Debug)]
164pub struct BufferBinding {
165 /// The id of the handle the binding is bound to.
166 pub memory: ManagedMemoryBinding,
167 /// The service whose memory this binding addresses; see [`Handle::service`].
168 pub service: ServiceId,
169 /// Memory offset in bytes.
170 pub offset_start: Option<u64>,
171 /// Memory offset in bytes.
172 pub offset_end: Option<u64>,
173 /// The stream where the data was created.
174 pub stream: StreamId,
175 /// Length of the underlying buffer ignoring offsets
176 pub size: u64,
177}
178
179impl BufferBinding {
180 /// Get the size of the handle, in bytes, accounting for offsets
181 pub fn size_in_used(&self) -> u64 {
182 self.size - self.offset_start.unwrap_or(0) - self.offset_end.unwrap_or(0)
183 }
184
185 /// The byte range of the allocation this binding names: what the offsets
186 /// leave of the buffer. This is the region the taint bookkeeping claims
187 /// when work writing through this binding fails, and releases when work
188 /// writing through it lands.
189 pub fn range(&self) -> core::ops::Range<u64> {
190 self.offset_start.unwrap_or(0)..self.size - self.offset_end.unwrap_or(0)
191 }
192
193 /// The identity a claim on this binding is stored under: the allocation
194 /// and the byte range of it, exactly as [`range`](Self::range) computes
195 /// it.
196 ///
197 /// Anything that deduplicates or compares claims — a capture's write set,
198 /// above all — must key by this and nothing coarser. Two tensors carved
199 /// from one batched allocation share a memory id and nothing else; keyed
200 /// by the id alone, one sibling's claim swallows the others'.
201 pub fn claim_key(&self) -> (ManagedMemoryId, u64, u64) {
202 let range = self.range();
203 (self.memory.id(), range.start, range.end)
204 }
205 /// Get the total size of the handle, in bytes.
206 pub fn size(&self) -> u64 {
207 self.size
208 }
209}