cubecl_std/tensor/
handle.rs1use cubecl_core::zspace::metadata::Metadata;
2use cubecl_core::{calculate_cube_count_elemwise, server::MemoryLayout};
3use cubecl_core::{prelude::*, server::CopyDescriptor};
4use cubecl_core::{server, zspace::strides};
5use cubecl_core::{
6 tensor_vector_size_parallel,
7 zspace::{Shape, Strides},
8};
9use cubecl_runtime::server::Handle;
10
11pub struct TensorHandle {
13 pub handle: server::Handle,
15 pub metadata: Box<Metadata>,
16 pub dtype: ElemType,
18}
19
20impl core::fmt::Debug for TensorHandle {
21 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
22 f.write_fmt(format_args!(
23 "Tensor {{ shape: {:?}, strides: {:?}, dtype: {}}}",
24 self.shape(),
25 self.strides(),
26 self.dtype,
27 ))
28 }
29}
30
31impl Clone for TensorHandle {
32 fn clone(&self) -> Self {
33 Self {
34 handle: self.handle.clone(),
35 metadata: self.metadata.clone(),
36 dtype: self.dtype,
37 }
38 }
39}
40
41impl TensorHandle {
42 pub fn new(
44 handle: server::Handle,
45 shape: impl Into<Shape>,
46 strides: impl Into<Strides>,
47 storage: impl Into<Type>,
48 ) -> Self {
49 Self {
50 handle,
51 metadata: Box::new(Metadata::new(shape, strides)),
52 dtype: storage.into().elem_type(),
53 }
54 }
55
56 pub fn from_metadata(
60 handle: server::Handle,
61 metadata: Metadata,
62 storage: impl Into<Type>,
63 ) -> Self {
64 Self {
65 handle,
66 metadata: Box::new(metadata),
67 dtype: storage.into().elem_type(),
68 }
69 }
70
71 pub fn empty(client: &Client, shape: impl Into<Shape>, storage: impl Into<Type>) -> Self {
72 let storage = storage.into();
73 let shape: Shape = shape.into();
74 let elem_size = storage.elem_type().size();
75 let MemoryLayout {
76 memory: handle,
77 strides,
78 } = client.empty_tensor(shape.clone(), elem_size);
79
80 Self::new(handle, shape, strides, storage)
81 }
82
83 pub fn new_contiguous(shape: impl Into<Shape>, handle: Handle, storage: ElemType) -> Self {
85 let shape = shape.into();
86 let strides = Self::contiguous_strides(&shape);
87
88 Self {
89 handle,
90 metadata: Box::new(Metadata::new(shape, strides)),
91 dtype: storage,
92 }
93 }
94
95 pub fn can_mut(&self) -> bool {
97 self.handle.can_mut()
98 }
99
100 pub fn binding(self) -> TensorBinding {
101 let Metadata {
102 shape,
103 strides,
104 tiling,
105 } = *self.metadata;
106 let mut binding = unsafe { TensorBinding::from_raw_parts(self.handle, strides, shape) };
107 binding.tiling = tiling;
110 binding
111 }
112
113 pub fn into_arg(self) -> TensorArg {
115 self.binding().into_tensor_arg()
116 }
117
118 pub fn into_copy_descriptor(self) -> CopyDescriptor {
119 CopyDescriptor {
120 handle: self.handle.binding(),
121 shape: self.metadata.shape,
122 strides: self.metadata.strides,
123 elem_size: self.dtype.size(),
124 }
125 }
126
127 pub fn required_address_type(&self) -> AddressType {
128 let len = self.handle.size() / self.dtype.size() as u64;
129 AddressType::from_len(len as usize)
130 }
131
132 pub fn shape(&self) -> &Shape {
133 self.metadata.shape()
134 }
135
136 pub fn strides(&self) -> &Strides {
137 self.metadata.strides()
138 }
139
140 fn contiguous_strides(shape: &[usize]) -> Strides {
141 let mut strides = strides![1; shape.len()];
142
143 let mut current = 1;
144 shape.iter().rev().enumerate().for_each(|(i, val)| {
145 strides[i] = current;
146 current *= val;
147 });
148 strides.reverse();
149 strides
150 }
151}
152impl TensorHandle {
153 pub fn zeros(client: &Client, shape: impl Into<Shape>, dtype: impl Into<Type>) -> Self {
154 let dtype = dtype.into();
155 let shape = shape.into();
156 let num_elements: usize = shape.iter().product();
157 let rank = shape.len();
158 let output = Self::empty(client, shape, dtype);
159 let dtype = dtype.elem_type();
160
161 let vector_size = tensor_vector_size_parallel(
162 client.io_optimized_vector_sizes(dtype.size()),
163 output.shape(),
164 output.strides(),
165 rank - 1,
166 );
167
168 let working_units = num_elements / vector_size as usize;
169 let cube_dim = CubeDim::new(client, working_units);
170 let cube_count = calculate_cube_count_elemwise(client, working_units, cube_dim);
171 let array_len = output.handle.size_in_used() as usize / dtype.size();
172
173 unsafe {
174 init::zeros_array::launch_unchecked(
175 client,
176 cube_count,
177 cube_dim,
178 output.required_address_type(),
179 vector_size,
180 BufferArg::from_raw_parts(output.handle.clone(), array_len),
181 dtype,
182 )
183 };
184
185 output
186 }
187}
188
189pub(crate) mod init {
190 use cubecl::prelude::*;
191 use cubecl_core::{self as cubecl};
192
193 #[cube(launch_unchecked, address_type = "dynamic")]
194 pub fn zeros_array<C: Numeric, N: Size>(
195 output: &mut [Vector<C, N>],
196 #[define(C)] _elem: ElemType,
197 ) {
198 if ABSOLUTE_POS < output.len() {
199 output[ABSOLUTE_POS] = Vector::cast_from(C::from_int(0));
200 }
201 }
202}