HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
tensor.h
Go to the documentation of this file.
1 // Copyright (c) Microsoft Corporation. All rights reserved.
2 // Licensed under the MIT License.
3 
4 #pragma once
5 
6 #include <cstddef>
7 #include <iostream>
8 #include <memory>
9 #include <string>
10 #include <vector>
11 
12 #include <gsl/gsl>
13 #include "core/common/common.h"
17 #include "onnxruntime_config.h"
20 
21 struct OrtValue;
22 
23 namespace onnxruntime {
24 
25 // TODO:ensure dtype_!=nullptr
26 #ifdef __GNUC__
27 #pragma GCC diagnostic push
28 #ifdef HAS_NULL_DEREFERENCE
29 #pragma GCC diagnostic ignored "-Wnull-dereference"
30 #endif
31 #endif
32 /*
33  We want to keep tensor as simple as possible, it is just a placeholder
34  for a piece of memory, with additional shape information.
35  Memory is owned and managed by Executor / Workspace, so Tensor just uses
36  it, and won't do any allocation / release.
37 */
38 
39 class Tensor final {
40  public:
41  // NB! Removing Create() methods returning unique_ptr<Tensor>.
42  // Still available in other EPs that are dynamically linked.
43  // Strive not to allocate Tensor with new/delete as it is a shallow class and using it by value is just fine.
44  // Use InitOrtValue() methods to allocate for OrtValue.
45 
46  Tensor() = default; // to allow creating vector<Tensor> to support seq(tensor)
47 
48  /**
49  * Create tensor with given type, shape, pre-allocated memory and allocator info.
50  * This function does not check if the preallocated buffer(p_data) has enough room for the shape.
51  * \param elt_type Data type of the tensor elements.
52  * \param shape Shape of the tensor
53  * \param p_data A preallocated buffer. Can be NULL if the shape is empty.
54  * Tensor does not own the data and will not delete it
55  * \param location Memory info for location of p_data.
56  * \param offset Offset in bytes to start of Tensor within p_data.
57  * \param strides Strides span. Can be empty if the tensor is contiguous.
58  */
59  Tensor(MLDataType elt_type, const TensorShape& shape, void* p_data, const OrtMemoryInfo& location,
60  ptrdiff_t offset = 0, gsl::span<const int64_t> strides = {});
61 
62  /**
63  * Create tensor with given type, shape, pre-allocated memory and allocator which will be used to free the
64  * pre-allocated memory. The Tensor will take over ownership of p_data.
65  * This function does not check if the preallocated buffer(p_data) has enough room for the shape.
66  * \param elt_type Data type of the tensor elements.
67  * \param shape Shape of the tensor
68  * \param p_data A preallocated buffer. Can be NULL if the shape is empty.
69  * Tensor will own the memory and will delete it when the tensor instance is destructed.
70  * \param deleter Allocator used to free the pre-allocated memory
71  * \param offset Offset in bytes to start of Tensor within p_data.
72  * \param strides Strides span. Can be empty if the tensor is contiguous.
73  */
74  Tensor(MLDataType elt_type, const TensorShape& shape, void* p_data, std::shared_ptr<IAllocator> deleter,
75  ptrdiff_t offset = 0, gsl::span<const int64_t> strides = {});
76 
77  /// <summary>
78  /// Create a Tensor that allocates and owns the buffer required for the specified shape.
79  /// </summary>
80  /// <param name="elt_type">Data type of the tensor elements.</param>
81  /// <param name="shape">Tensor shape.</param>
82  /// <param name="allocator">Allocator to use to create and free buffer.</param>
83  Tensor(MLDataType elt_type, const TensorShape& shape, std::shared_ptr<IAllocator> allocator);
84 
85  ~Tensor();
86 
87  // Move is allowed
89 
90  Tensor(Tensor&& other) noexcept;
91  Tensor& operator=(Tensor&& other) noexcept;
92 
93  /// <summary>
94  /// Creates an instance of Tensor on the heap and initializes OrtValue with it.
95  /// </summary>
96  /// <param name="elt_type">Data type of the tensor elements.</param>
97  /// <param name="shape">Tensor shape.</param>
98  /// <param name="p_data">Tensor data.</param>
99  /// <param name="location">Memory info for location of p_data.</param>
100  /// <param name="ort_value">OrtValue to populate with Tensor.</param>
101  /// <param name="offset">Optional offset if Tensor refers to a subset of p_data.</param>
102  /// <param name="strides">Optional strides if Tensor refers to a subset of p_data.</param>
103  static void InitOrtValue(MLDataType elt_type, const TensorShape& shape, void* p_data, const OrtMemoryInfo& location,
104  OrtValue& ort_value,
105  ptrdiff_t offset = 0, gsl::span<const int64_t> strides = {});
106 
107  /// <summary>
108  /// Creates an instance of Tensor on the heap which will take over ownership of the pre-allocated buffer.
109  /// </summary>
110  /// <param name="elt_type">Data type of the tensor elements.</param>
111  /// <param name="shape"Tensor shape.</param>
112  /// <param name="p_data">Tensor data.</param>
113  /// <param name="allocator">Allocator that was used to create p_data and will be used to free it.</param>
114  /// <param name="ort_value">OrtValue to populate with Tensor.</param>
115  /// <param name="offset">Optional offset if Tensor refers to a subset of p_data.</param>
116  /// <param name="strides">Optional strides if Tensor refers to a subset of p_data.</param>
117  static void InitOrtValue(MLDataType elt_type, const TensorShape& shape, void* p_data,
118  std::shared_ptr<IAllocator> allocator,
119  OrtValue& ort_value,
120  ptrdiff_t offset = 0, gsl::span<const int64_t> strides = {});
121 
122  /// <summary>
123  /// Creates an instance of Tensor on the heap and initializes OrtValue with it.
124  /// The Tensor instance will allocate and own the data required for `shape`.
125  /// </summary>
126  /// <param name="elt_type">Data type of the tensor elements.</param>
127  /// <param name="shape">Tensor shape.</param>
128  /// <param name="allocator">Allocator that was used to create p_data and will be used to free it.</param>
129  /// <param name="ort_value">OrtValue to populate with Tensor.</param>
130  static void InitOrtValue(MLDataType elt_type, const TensorShape& shape, std::shared_ptr<IAllocator> allocator,
131  OrtValue& ort_value);
132 
133  /// <summary>
134  /// Initializes OrtValue with an existing Tensor.
135  /// </summary>
136  /// <param name="tensor">Tensor.</param>
137  /// <param name="ort_value">OrtValue to populate with Tensor.</param>
138  static void InitOrtValue(Tensor&& tensor, OrtValue& ort_value);
139 
140  /// <summary>
141  /// Calculate the required storage for the tensor.
142  /// </summary>
143  /// <param name="elt_type">Data type of the tensor elements.</param>
144  /// <param name="shape">Tensor shape.</param>
145  /// <returns>Bytes required.</returns>
146  static size_t CalculateTensorStorageSize(MLDataType elt_type, const TensorShape& shape);
147 
148  /// <summary>
149  /// Calculate the required storage for the tensor.
150  /// </summary>
151  /// <param name="elt_type">Data type of the tensor elements.</param>
152  /// <param name="shape">Tensor shape.</param>
153  /// <param name="alignment">Power of 2 alignment to include in calculation.
154  /// Bumps up result to the nearest multiple of alignment. Set to 0 to ignore.</param>
155  /// <param name="storage_size">The resulting storage size.</param>
156  /// <returns>Status indicating success or failure.</returns>
157  static Status CalculateTensorStorageSize(MLDataType elt_type, const TensorShape& shape, size_t alignment,
158  size_t& storage_size);
159  /**
160  Returns the data type.
161  */
162  MLDataType DataType() const { return dtype_; }
163 
164  /**
165  Returns the data type enum constant
166  @remarks Use utils::ToTensorProtoElementType<T> for comparison.
167  */
168  int32_t GetElementType() const {
169  return dtype_->GetDataType();
170  }
171 
172  // Check if contains string data. This is a separate
173  // interface bc it is frequently used.
174  bool IsDataTypeString() const {
175  return utils::IsPrimitiveDataType<std::string>(dtype_);
176  }
177 
178  // Checks if the Tensor contains data type T
179  template <class T>
180  bool IsDataType() const {
181  return utils::IsPrimitiveDataType<T>(dtype_);
182  }
183 
184  /**
185  Returns the shape of the tensor.
186  */
187  const TensorShape& Shape() const noexcept { return shape_; }
188 
189  /**
190  Returns the location of the tensor's memory
191  */
192  const OrtMemoryInfo& Location() const { return alloc_info_; }
193 
194  /**
195  May return nullptr if tensor size is zero
196  */
197  template <typename T>
199  // Type check
200  ORT_ENFORCE(utils::IsPrimitiveDataType<T>(dtype_), "Tensor type mismatch. ",
201  "T ", "!=", dtype_);
202  return reinterpret_cast<T*>(static_cast<char*>(p_data_) + byte_offset_);
203  }
204 
205  /**
206  May return nullptr if tensor size is zero
207  */
208  template <typename T>
209  gsl::span<T> MutableDataAsSpan() {
210  // Type check
211  ORT_ENFORCE(utils::IsPrimitiveDataType<T>(dtype_), "Tensor type mismatch. ",
212  "T ", "!=", dtype_);
213  T* data = reinterpret_cast<T*>(static_cast<char*>(p_data_) + byte_offset_);
214  return gsl::make_span(data, static_cast<size_t>(NumStorageElements()));
215  }
216 
217  template <typename T>
218  const T* Data() const {
219  // Type check
220  ORT_ENFORCE(utils::IsPrimitiveDataType<T>(dtype_), "Tensor type mismatch. ",
221  "T ", "!=", dtype_);
222  return reinterpret_cast<const T*>(static_cast<char*>(p_data_) + byte_offset_);
223  }
224 
225  template <typename T>
226  gsl::span<const T> DataAsSpan() const {
227  // Type check
228  ORT_ENFORCE(utils::IsPrimitiveDataType<T>(dtype_), "Tensor type mismatch. ",
229  "T ", "!=", dtype_);
230  const T* data = reinterpret_cast<const T*>(static_cast<char*>(p_data_) + byte_offset_);
231  return gsl::make_span(data, static_cast<typename gsl::span<T>::size_type>(NumStorageElements()));
232  }
233 
235  ORT_ENFORCE(type == dtype_, "Tensor type mismatch.", type, "!=", dtype_);
236  return static_cast<char*>(p_data_) + byte_offset_;
237  }
238 
239  const void* DataRaw(MLDataType type) const {
240  ORT_ENFORCE(type == dtype_, "Tensor type mismatch.", type, "!=", dtype_);
241  return static_cast<char*>(p_data_) + byte_offset_;
242  }
243 
244  void* MutableDataRaw() noexcept {
245  return static_cast<char*>(p_data_) + byte_offset_;
246  }
247 
248  const void* DataRaw() const noexcept {
249  return static_cast<char*>(p_data_) + byte_offset_;
250  }
251 
252  bool OwnsBuffer() const noexcept {
253  return buffer_deleter_ != nullptr;
254  }
255 
256  /**
257  * Resizes the tensor without touching underlying storage.
258  * This requires the total size of the tensor to remains constant.
259  * @warning this function is NOT thread-safe.
260  */
261  inline void Reshape(const TensorShape& new_shape) {
262  ORT_ENFORCE(shape_.Size() == new_shape.Size(),
263  "Tensor size (" + std::to_string(shape_.Size()) +
264  ") != new size (" + std::to_string(new_shape.Size()) + ")");
265  shape_ = new_shape;
266  }
267 
268  /**
269  * Get the byte offset with respect to the p_data
270  * @warning this is a temporary solution for reusing the buffer bigger than needed.
271  * @warning use with caution - make sure you do boundary check before calling this method (see view.cc)
272  */
273  inline ptrdiff_t ByteOffset() const {
274  return byte_offset_;
275  }
276 
277  /**
278  * Set the byte offset with respect to the p_data
279  * @warning this is a temporary solution for reusing the buffer bigger than needed.
280  */
281  inline void SetByteOffset(ptrdiff_t byte_offset) {
282  byte_offset_ = byte_offset;
283  }
284 
285  /// <summary>
286  /// The number of Tensor "storage" elements. A single storage element may contain multiple sub-elements for
287  /// sub-byte data types (e.g., int4).
288  ///
289  /// For element types smaller than 1 byte (e.g., int4), a single storage element stores multiple sub-byte elements.
290  /// Example: Tensor<int4> of shape (4,) has 2 storage elements.
291  ///
292  /// For element types >= 1 byte, this function returns the product of the shape.
293  /// Example: Tensor<int8> of shape (4,) has 4 storage elements.
294  /// </summary>
295  /// <returns>Number of tensor storage elements</returns>
296  int64_t NumStorageElements() const;
297 
298  /**
299  The number of bytes of data.
300  */
301  size_t SizeInBytes() const;
302 
303 #ifdef ENABLE_STRIDED_TENSORS
304  /**
305  * Get the strides of the tensor.
306  */
307  gsl::span<const int64_t> Strides() const;
308 
309  /**
310  * Return if the tensor is contiguous.
311  */
312  bool IsContiguous() const noexcept { return is_contiguous_; }
313 
314  /**
315  * Set strides.
316  */
317  void SetShapeAndStrides(const TensorShape& new_shape, gsl::span<const int64_t> new_strides);
318 #endif
319 
320  // More API methods.
321  private:
322  void Init(MLDataType elt_type,
323  const TensorShape& shape,
324  void* p_raw_data,
325  AllocatorPtr deleter,
326  ptrdiff_t offset = 0,
327  gsl::span<const int64_t> strides = {});
328 
329  void ReleaseBuffer();
330 
331 #ifdef ENABLE_STRIDED_TENSORS
332  bool CheckIsContiguous() const;
333 #endif
334 
335  void* p_data_;
336  /**
337  if buffer_deleter_ is null, it means tensor does not own the buffer.
338  otherwise tensor will use the deleter to release the buffer when
339  tensor is released.
340  */
341  AllocatorPtr buffer_deleter_;
342 
343  TensorShape shape_;
344 #ifdef ENABLE_STRIDED_TENSORS
345  mutable TensorShapeVector strides_;
346  bool is_contiguous_ = true;
347 #endif
348 
349  const PrimitiveDataTypeBase* dtype_;
350  OrtMemoryInfo alloc_info_;
351  ptrdiff_t byte_offset_;
352 };
353 #ifdef __GNUC__
354 #pragma GCC diagnostic pop
355 #endif
356 } // namespace onnxruntime
bool OwnsBuffer() const noexcept
Definition: tensor.h:252
MLDataType DataType() const
Definition: tensor.h:162
auto to_string(const T &value) -> std::string
Definition: format.h:4527
Base class for MLDataType.
Definition: data_types.h:77
int64_t Size() const
constexpr span< ElementType, Extent > make_span(span< ElementType, Extent > s) noexcept
Definition: UT_Span.h:559
size_t SizeInBytes() const
#define ORT_ENFORCE(condition,...)
Definition: common.h:172
ORT_DISALLOW_COPY_AND_ASSIGNMENT(Tensor)
void * MutableDataRaw() noexcept
Definition: tensor.h:244
const TensorShape & Shape() const noexcept
Definition: tensor.h:187
static size_t CalculateTensorStorageSize(MLDataType elt_type, const TensorShape &shape)
Calculate the required storage for the tensor.
InlinedVector< int64_t > TensorShapeVector
Definition: tensor_shape.h:30
GLint GLint GLsizei GLint GLenum GLenum type
Definition: glcorearb.h:108
GLintptr offset
Definition: glcorearb.h:665
bool IsDataType() const
Definition: tensor.h:180
const void * DataRaw() const noexcept
Definition: tensor.h:248
int32_t GetElementType() const
Definition: tensor.h:168
Tensor & operator=(Tensor &&other) noexcept
GLint location
Definition: glcorearb.h:805
void SetByteOffset(ptrdiff_t byte_offset)
Definition: tensor.h:281
int64_t NumStorageElements() const
The number of Tensor "storage" elements. A single storage element may contain multiple sub-elements f...
const DataTypeImpl * MLDataType
Definition: data_types.h:68
gsl::span< T > MutableDataAsSpan()
Definition: tensor.h:209
const OrtMemoryInfo & Location() const
Definition: tensor.h:192
std::shared_ptr< IAllocator > AllocatorPtr
Definition: allocator.h:263
const void * DataRaw(MLDataType type) const
Definition: tensor.h:239
const T * Data() const
Definition: tensor.h:218
void Reshape(const TensorShape &new_shape)
Definition: tensor.h:261
static void InitOrtValue(MLDataType elt_type, const TensorShape &shape, void *p_data, const OrtMemoryInfo &location, OrtValue &ort_value, ptrdiff_t offset=0, gsl::span< const int64_t > strides={})
Creates an instance of Tensor on the heap and initializes OrtValue with it.
void * MutableDataRaw(MLDataType type)
Definition: tensor.h:234
gsl::span< const T > DataAsSpan() const
Definition: tensor.h:226
ptrdiff_t ByteOffset() const
Definition: tensor.h:273
Definition: format.h:1821
bool IsDataTypeString() const
Definition: tensor.h:174
GLsizei const GLuint const GLintptr const GLsizei * strides
Definition: glcorearb.h:2625