Skip to main content

tract_gpu/tensor/
view.rs

1use num_traits::AsPrimitive;
2use std::ffi::c_void;
3use std::fmt::Display;
4use tract_core::internal::*;
5use tract_core::tract_linalg::block_quant::{BlockQuantFact, BlockQuantStorage};
6
7use crate::device::{DeviceBuffer, get_context};
8use crate::utils::check_strides_validity;
9
10use super::{DeviceTensor, OwnedDeviceTensor};
11
12/// A window into a device buffer owned by someone else, described by shape,
13/// strides and a byte offset.
14///
15/// The buffer is kept alive by the `Arc`, but its *contents* are not: the turn
16/// arena hands out windows at offsets the memory schema deliberately reuses,
17/// and a state-owned buffer is written by the op that owns it. So a view is
18/// good for the turn that produced it and no longer -- it is read by the nodes
19/// downstream of its producer, and copied out at the model boundary rather
20/// than handed to the application.
21#[derive(Debug, Clone, Hash, PartialEq, Eq)]
22pub struct DeviceView {
23    pub(crate) buffer: Arc<Box<dyn OwnedDeviceTensor>>,
24    pub(crate) dt: DatumType,
25    pub(crate) len: usize,
26    pub(crate) shape: TVec<usize>,
27    pub(crate) strides: TVec<isize>,
28    pub(crate) offset_bytes: usize,
29    pub(crate) exotic_fact: Option<Box<dyn ExoticFact>>,
30}
31
32impl DeviceView {
33    #[inline]
34    pub fn shape(&self) -> &[usize] {
35        self.shape.as_slice()
36    }
37
38    /// Get the datum type of the tensor.
39    #[inline]
40    pub fn datum_type(&self) -> DatumType {
41        self.dt
42    }
43
44    #[inline]
45    pub fn strides(&self) -> &[isize] {
46        self.strides.as_slice()
47    }
48
49    /// Get underlying inner device buffer.
50    pub fn device_buffer(&self) -> &dyn DeviceBuffer {
51        self.buffer.device_buffer()
52    }
53
54    pub fn device_buffer_ptr(&self) -> *const c_void {
55        self.buffer.device_buffer().ptr()
56    }
57
58    /// Get underlying inner device buffer offset
59    pub fn buffer_offset<I: Copy + 'static>(&self) -> I
60    where
61        usize: AsPrimitive<I>,
62    {
63        self.offset_bytes.as_()
64    }
65
66    pub fn exotic_fact(&self) -> Option<&dyn ExoticFact> {
67        self.exotic_fact.as_deref()
68    }
69
70    /// Get the number of values in the tensor.
71    #[inline]
72    #[allow(clippy::len_without_is_empty)]
73    pub fn len(&self) -> usize {
74        self.len
75    }
76
77    pub fn as_bytes(&self) -> Vec<u8> {
78        let len = if let Some(of) = &self.exotic_fact {
79            of.mem_size().as_i64().unwrap() as usize
80        } else {
81            self.len() * self.dt.size_of()
82        };
83        self.buffer.get_bytes_slice(self.offset_bytes, len)
84    }
85
86    /// Reshaped tensor with given shape.
87    pub fn reshaped(&self, shape: impl Into<TVec<usize>>) -> TractResult<Self> {
88        ensure!(self.exotic_fact.is_none(), "Can't reshape exotic tensor");
89        let shape = shape.into();
90        if self.len() != shape.iter().product::<usize>() {
91            bail!("Invalid reshape {:?} to {:?}", self.shape(), shape);
92        }
93        if shape.as_slice() != self.shape() {
94            Ok(Self {
95                buffer: Arc::clone(&self.buffer),
96                dt: self.dt,
97                len: self.len,
98                strides: Tensor::natural_strides(&shape),
99                shape,
100                offset_bytes: self.offset_bytes,
101                exotic_fact: None,
102            })
103        } else {
104            Ok(self.clone())
105        }
106    }
107
108    pub fn restrided(&self, strides: impl Into<TVec<isize>>) -> TractResult<Self> {
109        ensure!(self.exotic_fact.is_none(), "Can't restride exotic tensor");
110        let strides = strides.into();
111        check_strides_validity(self.shape().into(), strides.clone())?;
112
113        if strides.as_slice() != self.strides() {
114            Ok(Self {
115                buffer: Arc::clone(&self.buffer),
116                dt: self.dt,
117                len: self.len,
118                strides,
119                shape: self.shape.clone(),
120                offset_bytes: self.offset_bytes,
121                exotic_fact: None,
122            })
123        } else {
124            Ok(self.clone())
125        }
126    }
127
128    /// True where the bytes of this view are the tensor, in order and with no
129    /// gap: what a reader working from a pointer and a length may assume.
130    pub fn is_packed(&self) -> bool {
131        self.strides.as_slice() == Tensor::natural_strides(&self.shape).as_slice()
132    }
133
134    pub fn to_host(&self) -> TractResult<Tensor> {
135        // A window with spare capacity is read by walking it, and `as_bytes`
136        // reads a flat run: pack it on the device first, where the copy is.
137        if !self.is_packed() {
138            ensure!(self.exotic_fact.is_none(), "an exotic tensor is never strided");
139            let packed = DeviceTensor::uninitialized_dt(self.dt, &self.shape)?;
140            let me: DeviceTensor = self.clone().into();
141            get_context()?.copy_nd(
142                &me,
143                0,
144                &self.strides,
145                &packed,
146                0,
147                &self.shape,
148                packed.strides(),
149            )?;
150            return Ok((*packed.to_host()?).clone());
151        }
152        get_context()?.synchronize()?;
153        let content = self.as_bytes();
154        unsafe {
155            if let Some(bqf) =
156                self.exotic_fact.as_ref().and_then(|of| of.downcast_ref::<BlockQuantFact>())
157            {
158                Ok(BlockQuantStorage::new(
159                    bqf.format.clone(),
160                    bqf.m(),
161                    bqf.k(),
162                    Arc::new(Blob::from_bytes(&content)?),
163                )?
164                .into_tensor_with_shape(self.dt, bqf.shape()))
165            } else {
166                Tensor::from_raw_dt(self.dt, &self.shape, &content)
167            }
168        }
169    }
170}
171
172impl Display for DeviceView {
173    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
174        let content = self
175            .clone()
176            .to_host()
177            .unwrap()
178            .dump(false)
179            .unwrap_or_else(|e| format!("Error : {e:?}"));
180        write!(f, "DeviceView: {{ {content} }}")
181    }
182}