1#![allow(clippy::missing_safety_doc)]
2#![allow(clippy::missing_transmute_annotations)]
3
4mod lazy;
5mod owned;
6mod view;
7
8pub use lazy::*;
9pub use owned::*;
10pub use view::*;
11
12use num_traits::AsPrimitive;
13use std::ffi::c_void;
14use std::fmt::Display;
15use tract_core::internal::*;
16use tract_data::itertools::Itertools;
17
18use crate::device::{DeviceBuffer, get_context};
19use crate::utils::MAX_COPY_RANK;
20use crate::utils::merge_copy_axes;
21
22#[derive(Debug, Clone, Hash, PartialEq, Eq)]
24pub enum DeviceTensor {
25 Owned(Box<dyn OwnedDeviceTensor>),
26 View(DeviceView),
27}
28
29impl DeviceTensor {
30 pub const SUPPORTED_DT: [DatumType; 11] = [
31 DatumType::Bool,
32 DatumType::F32,
33 DatumType::F16,
34 DatumType::I8,
35 DatumType::U8,
36 DatumType::I16,
37 DatumType::U16,
38 DatumType::I32,
39 DatumType::U32,
40 DatumType::I64,
41 DatumType::U64,
42 ];
43
44 pub fn tname(dt: DatumType) -> TractResult<&'static str> {
45 Ok(match dt {
46 DatumType::F32 => "f32",
47 DatumType::F16 => "f16",
48 DatumType::U8 => "u8",
49 DatumType::U16 => "u16",
50 DatumType::U32 => "u32",
51 DatumType::U64 => "u64",
52 DatumType::I8 => "i8",
53 DatumType::I16 => "i16",
54 DatumType::I32 => "i32",
55 DatumType::I64 => "i64",
56 DatumType::Bool => "bool",
57 _ => bail!("Unsupported dt {:?} for GPU Tensor", dt),
58 })
59 }
60
61 pub fn uninitialized_dt(dt: DatumType, shape: &[usize]) -> TractResult<DeviceTensor> {
63 Ok(DeviceTensor::Owned(get_context()?.uninitialized_device_tensor(shape, dt)?))
64 }
65
66 pub fn uninitialized<T: Datum>(shape: &[usize]) -> TractResult<DeviceTensor> {
67 Self::uninitialized_dt(T::datum_type(), shape)
68 }
69
70 pub fn uninitialized_exotic(exotic_fact: Box<dyn ExoticFact>) -> TractResult<DeviceTensor> {
71 Ok(DeviceTensor::Owned(get_context()?.uninitialized_device_exotic_tensor(exotic_fact)?))
72 }
73 pub fn from_shape<T: Copy + Datum>(shape: &[usize], data: &[T]) -> TractResult<DeviceTensor> {
75 Tensor::from_shape(shape, data)?.into_device()
76 }
77
78 pub fn is_supported_dt(dt: DatumType) -> bool {
79 Self::SUPPORTED_DT.contains(&dt)
80 }
81
82 #[inline]
84 pub fn datum_type(&self) -> DatumType {
85 match self {
86 Self::Owned(owned) => owned.datum_type(),
87 Self::View(view) => view.datum_type(),
88 }
89 }
90
91 #[inline]
93 pub fn rank(&self) -> usize {
94 self.shape().len()
95 }
96
97 #[inline]
99 pub fn shape(&self) -> &[usize] {
100 match self {
101 Self::Owned(t) => t.shape(),
102 Self::View(t) => t.shape(),
103 }
104 }
105
106 #[inline]
108 #[allow(clippy::len_without_is_empty)]
109 pub fn len(&self) -> usize {
110 match self {
111 Self::Owned(t) => t.len(),
112 Self::View(t) => t.len(),
113 }
114 }
115
116 #[inline]
118 pub fn strides(&self) -> &[isize] {
119 match self {
120 Self::Owned(t) => t.strides(),
121 Self::View(t) => t.strides(),
122 }
123 }
124
125 pub fn device_buffer(&self) -> &dyn DeviceBuffer {
127 match self {
128 Self::Owned(t) => t.device_buffer(),
129 Self::View(t) => t.device_buffer(),
130 }
131 }
132
133 pub fn buffer_offset<I: Copy + 'static>(&self) -> I
135 where
136 usize: AsPrimitive<I>,
137 {
138 match self {
139 Self::Owned(_) => 0.as_(),
140 Self::View(t) => t.buffer_offset(),
141 }
142 }
143
144 pub fn device_buffer_ptr(&self) -> *const c_void {
145 match self {
146 Self::Owned(t) => t.device_buffer().ptr(),
147 Self::View(t) => t.device_buffer().ptr(),
148 }
149 }
150
151 pub fn description(&self) -> String {
153 format!("|{},{:?}|", self.shape().iter().join(","), self.datum_type(),)
154 }
155
156 pub fn reshaped(&self, shape: TVec<usize>) -> TractResult<Self> {
158 match self {
159 Self::Owned(t) => Ok(t.reshaped(shape)?),
160 Self::View(t) => Ok(Self::View(t.reshaped(shape)?)),
161 }
162 }
163
164 pub fn restrided(&self, strides: TVec<isize>) -> TractResult<Self> {
165 match self {
166 Self::Owned(t) => Ok(t.restrided(strides)?),
167 Self::View(t) => Ok(Self::View(t.restrided(strides)?)),
168 }
169 }
170
171 pub fn into_tensor(self) -> Tensor {
176 let dt = self.datum_type();
177 let shape: TVec<usize> = self.shape().into();
178 Tensor::from_storage(dt, &shape, self)
179 }
180
181 pub fn prefix_window(&self, axis: usize, len: usize) -> TractResult<DeviceTensor> {
191 ensure!(axis < self.rank(), "axis {axis} out of rank {}", self.rank());
192 ensure!(
193 len <= self.shape()[axis],
194 "window of {len} on axis {axis} of extent {}",
195 self.shape()[axis]
196 );
197 ensure!(!self.is_exotic(), "an exotic tensor carries no shape to window");
198 let mut shape: TVec<usize> = self.shape().into();
199 shape[axis] = len;
200 let (buffer, offset_bytes) = match self {
201 Self::Owned(owned) => (Arc::new(tract_core::dyn_clone::clone_box(&**owned)), 0),
202 Self::View(view) => (Arc::clone(&view.buffer), view.offset_bytes),
203 };
204 Ok(DeviceView {
205 buffer,
206 dt: self.datum_type(),
207 len: shape.iter().product(),
208 shape,
209 strides: self.strides().into(),
210 offset_bytes,
211 exotic_fact: None,
212 }
213 .into())
214 }
215
216 pub(crate) fn slice_on_device(
232 &self,
233 dt: DatumType,
234 shape: &[usize],
235 axis: usize,
236 start: usize,
237 end: usize,
238 ) -> TractResult<Option<DeviceTensor>> {
239 ensure!(
240 axis < shape.len() && start < end && end <= shape[axis],
241 "Invalid slicing range {start}..{end} on axis {axis} of {shape:?}"
242 );
243 let mut sliced: TVec<usize> = shape.into();
244 sliced[axis] = end - start;
245 let servable = dt == self.datum_type()
246 && shape == self.shape()
247 && !self.is_exotic()
248 && Self::is_supported_dt(dt)
249 && self.strides().iter().all(|s| *s > 0)
250 && (1..=MAX_COPY_RANK).contains(
251 &merge_copy_axes(self.strides(), &sliced, &Tensor::natural_strides(&sliced))
252 .shape
253 .len(),
254 );
255 if !servable {
256 return Ok(None);
257 }
258 let output = DeviceTensor::uninitialized_dt(dt, &sliced)?;
259 get_context()?.assign_slice(&output, 0..end - start, self, start..end, axis)?;
260 Ok(Some(output))
261 }
262
263 pub fn to_host(&self) -> TractResult<Arc<Tensor>> {
266 get_context()?.synchronize()?;
267
268 Ok(match self {
269 Self::Owned(o) => o.to_host()?,
270 Self::View(v) => v.to_host()?.into(),
271 })
272 }
273}
274
275impl Display for DeviceTensor {
276 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
277 match self {
278 Self::Owned(o) => o.fmt(f),
279 Self::View(v) => {
280 let content =
281 v.to_host().unwrap().dump(false).unwrap_or_else(|e| format!("Error : {e:?}"));
282 write!(f, "View: {{ {content} }}")
283 }
284 }
285 }
286}
287
288pub trait IntoDevice<T> {
289 fn into_device(self) -> TractResult<T>;
290}
291
292impl IntoDevice<DeviceTensor> for Tensor {
293 fn into_device(self) -> TractResult<DeviceTensor> {
294 Ok(DeviceTensor::Owned(get_context()?.tensor_to_device(self.into_tvalue())?))
295 }
296}
297
298impl IntoDevice<DeviceTensor> for Arc<Tensor> {
299 fn into_device(self) -> TractResult<DeviceTensor> {
300 Ok(DeviceTensor::Owned(get_context()?.tensor_to_device(self.into_tvalue())?))
301 }
302}
303
304impl TensorStorage for DeviceTensor {
305 fn byte_len(&self) -> usize {
306 self.len() * self.datum_type().size_of()
307 }
308
309 fn is_empty(&self) -> bool {
310 self.byte_len() == 0
311 }
312
313 fn deep_clone(&self) -> Box<dyn TensorStorage> {
314 Box::new(self.clone())
315 }
316
317 fn as_plain_ram(&self) -> Option<&PlainStorage> {
318 None
319 }
320
321 fn as_plain_ram_mut(&mut self) -> Option<&mut PlainStorage> {
322 None
323 }
324
325 fn into_plain_ram(self: Box<Self>) -> Option<PlainStorage> {
326 None
327 }
328
329 fn dyn_hash(&self, _state: &mut dyn std::hash::Hasher) {
330 }
332
333 fn is_exotic(&self) -> bool {
334 match self {
335 Self::Owned(owned) => owned.exotic_fact().is_some(),
336 Self::View(view) => view.exotic_fact().is_some(),
337 }
338 }
339
340 fn in_ram(&self) -> bool {
341 false
342 }
343
344 fn slice(
345 &self,
346 dt: DatumType,
347 shape: &[usize],
348 axis: usize,
349 start: usize,
350 end: usize,
351 ) -> TractResult<Option<Tensor>> {
352 Ok(self.slice_on_device(dt, shape, axis, start, end)?.map(|t| t.into_tensor()))
353 }
354
355 fn exotic_fact(&self, _shape: &[usize]) -> TractResult<Option<Box<dyn ExoticFact>>> {
356 bail!(
357 "DeviceTensor cannot reconstruct a DeviceFact: origin (FromHost/FromDevice) is not carried by storage"
358 )
359 }
360}
361
362impl From<DeviceView> for DeviceTensor {
363 fn from(view: DeviceView) -> Self {
364 Self::View(view)
365 }
366}
367
368pub trait DeviceTensorExt {
369 fn to_device_tensor(&self) -> TractResult<&DeviceTensor>;
370 fn as_device_tensor(&self) -> Option<&DeviceTensor>;
371}
372
373impl DeviceTensorExt for Tensor {
374 fn to_device_tensor(&self) -> TractResult<&DeviceTensor> {
375 self.try_storage_as::<DeviceTensor>()
376 }
377
378 fn as_device_tensor(&self) -> Option<&DeviceTensor> {
379 self.storage_as::<DeviceTensor>()
380 }
381}