tvm
tensor.h
Go to the documentation of this file.
1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements. See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership. The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the
7  * "License"); you may not use this file except in compliance
8  * with the License. You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing,
13  * software distributed under the License is distributed on an
14  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15  * KIND, either express or implied. See the License for the
16  * specific language governing permissions and limitations
17  * under the License.
18  */
19 
24 #ifndef TVM_RUNTIME_TENSOR_H_
25 #define TVM_RUNTIME_TENSOR_H_
26 
27 #include <tvm/ffi/container/shape.h>
28 #include <tvm/ffi/container/tensor.h>
29 #include <tvm/ffi/optional.h>
30 #include <tvm/ffi/string.h>
31 #include <tvm/runtime/base.h>
32 #include <tvm/runtime/data_type.h>
33 #include <tvm/runtime/device_api.h>
34 #include <tvm/support/io.h>
35 #include <tvm/support/serializer.h>
36 
37 #include <atomic>
38 #include <functional>
39 #include <utility>
40 #include <vector>
41 
42 namespace tvm {
43 namespace runtime {
44 
49 class Tensor : public tvm::ffi::Tensor {
50  public:
51  Tensor() = default;
56  explicit Tensor(ffi::ObjectPtr<ffi::TensorObj> data) : tvm::ffi::Tensor(data) {}
57  explicit Tensor(ffi::UnsafeInit tag) : tvm::ffi::Tensor(tag) {}
58  Tensor(ffi::Tensor&& other) : tvm::ffi::Tensor(std::move(other)) {} // NOLINT(*)
59  Tensor(const ffi::Tensor& other) : tvm::ffi::Tensor(other) {} // NOLINT(*)
60 
61  ffi::ShapeView Shape() const { return this->shape(); }
62  runtime::DataType DataType() const { return runtime::DataType(this->dtype()); }
63 
64  // DLPack handling
65  static Tensor FromDLPack(DLManagedTensor* tensor) {
66  return tvm::ffi::Tensor::FromDLPack(tensor, kAllocAlignment, true);
67  }
68 
69  static Tensor FromDLPackVersioned(DLManagedTensorVersioned* tensor) {
70  return tvm::ffi::Tensor::FromDLPackVersioned(tensor, kAllocAlignment, true);
71  }
72  inline const DLTensor* operator->() const { return this->get(); }
79  inline void CopyFrom(const DLTensor* other);
80  inline void CopyFrom(const Tensor& other);
88  TVM_RUNTIME_DLL void CopyFromBytes(const void* data, size_t nbytes);
95  inline void CopyTo(DLTensor* other) const;
96  inline void CopyTo(const Tensor& other) const;
104  TVM_RUNTIME_DLL void CopyToBytes(void* data, size_t nbytes) const;
113  ffi::Optional<ffi::String> mem_scope = std::nullopt) const;
119  inline bool Load(support::Stream* stream);
124  inline void Save(support::Stream* stream) const;
125 
146  TVM_RUNTIME_DLL Tensor CreateView(ffi::Shape shape, DLDataType dtype,
147  uint64_t relative_byte_offset = 0) const;
156  TVM_RUNTIME_DLL static Tensor Empty(ffi::Shape shape, DLDataType dtype, Device dev,
157  ffi::Optional<ffi::String> mem_scope = std::nullopt);
164  TVM_RUNTIME_DLL static void CopyFromTo(const DLTensor* from, DLTensor* to,
165  TVMStreamHandle stream = nullptr);
166 
174  TVM_RUNTIME_DLL static void CopyToBytes(const DLTensor* from, void* to, size_t nbytes,
175  TVMStreamHandle stream = nullptr);
176 
184  TVM_RUNTIME_DLL static void CopyFromBytes(const DLTensor* to, void* from, size_t nbytes,
185  TVMStreamHandle stream = nullptr);
186 };
187 
193 inline bool SaveDLTensor(support::Stream* strm, const DLTensor* tensor);
194 
195 inline void Tensor::CopyFrom(const DLTensor* other) {
196  TVM_FFI_ICHECK(data_ != nullptr);
197  CopyFromTo(other, get_mutable());
198 }
199 
200 inline void Tensor::CopyFrom(const Tensor& other) {
201  TVM_FFI_ICHECK(data_ != nullptr);
202  TVM_FFI_ICHECK(other.data_ != nullptr);
203  CopyFromTo(other.get_mutable(), get_mutable());
204 }
205 
206 inline void Tensor::CopyTo(DLTensor* other) const {
207  TVM_FFI_ICHECK(data_ != nullptr);
208  CopyFromTo(get_mutable(), other);
209 }
210 
211 inline void Tensor::CopyTo(const Tensor& other) const {
212  TVM_FFI_ICHECK(data_ != nullptr);
213  TVM_FFI_ICHECK(other.data_ != nullptr);
214  CopyFromTo(get_mutable(), other.get_mutable());
215 }
216 
218 constexpr uint64_t kTVMTensorMagic = 0xDD5E40F096B4A13F;
219 
220 inline bool SaveDLTensor(support::Stream* strm, const DLTensor* tensor) {
221  uint64_t header = kTVMTensorMagic, reserved = 0;
222  strm->Write(header);
223  strm->Write(reserved);
224  // Always save data as CPU context
225  //
226  // Parameters that get serialized should be in CPU by default.
227  // So even the array's context is GPU, it will be stored as CPU array.
228  // This is used to prevent case when another user loads the parameters
229  // back on machine that do not have GPU or related context.
230  //
231  // We can always do array.CopyTo(target_dev) to get a corresponding
232  // array in the target context.
233  Device cpu_dev;
234  cpu_dev.device_type = kDLCPU;
235  cpu_dev.device_id = 0;
236  strm->Write(cpu_dev);
237  strm->Write(tensor->ndim);
238  strm->Write(tensor->dtype);
239  int ndim = tensor->ndim;
240  strm->WriteArray(tensor->shape, ndim);
241  int type_bytes = (tensor->dtype.bits + 7) / 8;
242  int64_t num_elems = 1;
243  for (int i = 0; i < ndim; ++i) {
244  num_elems *= tensor->shape[i];
245  }
246  int64_t data_byte_size = type_bytes * num_elems;
247  strm->Write(data_byte_size);
248 
249  if (TVM_FFI_IO_NO_ENDIAN_SWAP && tensor->device.device_type == kDLCPU &&
250  ffi::IsContiguous(*tensor) && tensor->byte_offset == 0) {
251  // quick path
252  strm->Write(tensor->data, data_byte_size);
253  } else {
254  std::vector<uint8_t> bytes(data_byte_size);
255  Tensor::CopyToBytes(const_cast<DLTensor*>(tensor), bytes.data(), data_byte_size);
256  if (!TVM_FFI_IO_NO_ENDIAN_SWAP) {
257  ffi::ByteSwap(bytes.data(), type_bytes, num_elems);
258  }
259  strm->Write(bytes.data(), data_byte_size);
260  }
261  return true;
262 }
263 
264 inline void Tensor::Save(support::Stream* strm) const { SaveDLTensor(strm, operator->()); }
265 
266 inline bool Tensor::Load(support::Stream* strm) {
267  uint64_t header, reserved;
268  TVM_FFI_ICHECK(strm->Read(&header)) << "Invalid DLTensor file format";
269  TVM_FFI_ICHECK(strm->Read(&reserved)) << "Invalid DLTensor file format";
270  TVM_FFI_ICHECK(header == kTVMTensorMagic) << "Invalid DLTensor file format";
271  Device dev;
272  int ndim;
273  DLDataType dtype;
274  TVM_FFI_ICHECK(strm->Read(&dev)) << "Invalid DLTensor file format";
275  TVM_FFI_ICHECK(strm->Read(&ndim)) << "Invalid DLTensor file format";
276  TVM_FFI_ICHECK(strm->Read(&dtype)) << "Invalid DLTensor file format";
277  TVM_FFI_ICHECK_EQ(dev.device_type, kDLCPU)
278  << "Invalid DLTensor device: can only save as CPU tensor";
279  std::vector<int64_t> shape(ndim);
280  if (ndim != 0) {
281  TVM_FFI_ICHECK(strm->ReadArray(&shape[0], ndim)) << "Invalid DLTensor file format";
282  }
283  Tensor ret = Tensor::Empty(ffi::Shape(shape), dtype, dev);
284  int64_t num_elems = 1;
285  int elem_bytes = (ret->dtype.bits + 7) / 8;
286  for (int i = 0; i < ret->ndim; ++i) {
287  num_elems *= ret->shape[i];
288  }
289  int64_t data_byte_size;
290  TVM_FFI_ICHECK(strm->Read(&data_byte_size)) << "Invalid DLTensor file format";
291  TVM_FFI_ICHECK(data_byte_size == num_elems * elem_bytes) << "Invalid DLTensor file format";
292  auto read_ret = strm->Read(ret->data, data_byte_size);
293  // Only check non-empty data
294  if (ndim > 0 && shape[0] != 0) {
295  TVM_FFI_ICHECK(read_ret) << "Invalid DLTensor file format";
296  }
297  if (!TVM_FFI_IO_NO_ENDIAN_SWAP) {
298  ffi::ByteSwap(ret->data, elem_bytes, num_elems);
299  }
300  *this = ret;
301  return true;
302 }
303 
311  if (device.device_type == DLDeviceType::kDLCUDA) {
312  return Device{DLDeviceType::kDLCUDAHost, 0};
313  } else if (device.device_type == DLDeviceType::kDLROCM) {
314  return Device{DLDeviceType::kDLROCMHost, 0};
315  } else {
316  // Fallback to CPU.
317  return Device{DLDeviceType::kDLCPU, 0};
318  }
319 }
320 
321 } // namespace runtime
322 } // namespace tvm
323 
324 namespace std {
325 template <>
326 struct hash<tvm::Device> {
327  std::size_t operator()(const tvm::Device& dev) const {
328  return ((dev.device_id << 8) | dev.device_type);
329  }
330 };
331 
332 template <>
333 struct equal_to<tvm::Device> {
334  bool operator()(const tvm::Device& lhs, const tvm::Device& rhs) const {
335  return (lhs.device_type == rhs.device_type && lhs.device_id == rhs.device_id);
336  }
337 };
338 } // namespace std
339 
340 #endif // TVM_RUNTIME_TENSOR_H_
DataType dtype() const
Definition: expr.h:140
Runtime primitive data type.
Definition: data_type.h:45
int bits() const
Definition: data_type.h:114
Managed Tensor. The array is backed by reference counted blocks.
Definition: tensor.h:49
const DLTensor * operator->() const
Definition: tensor.h:72
static TVM_RUNTIME_DLL void CopyToBytes(const DLTensor *from, void *to, size_t nbytes, TVMStreamHandle stream=nullptr)
Function to copy data from one array to a byte buffer.
static TVM_RUNTIME_DLL Tensor Empty(ffi::Shape shape, DLDataType dtype, Device dev, ffi::Optional< ffi::String > mem_scope=std::nullopt)
Create an empty Tensor.
static Tensor FromDLPackVersioned(DLManagedTensorVersioned *tensor)
Definition: tensor.h:69
TVM_RUNTIME_DLL void CopyToBytes(void *data, size_t nbytes) const
Copy data content into another array.
bool Load(support::Stream *stream)
Load Tensor from stream.
Definition: tensor.h:266
void CopyFrom(const DLTensor *other)
Copy data content from another array.
Definition: tensor.h:195
TVM_RUNTIME_DLL Tensor CopyTo(const Device &dev, ffi::Optional< ffi::String > mem_scope=std::nullopt) const
Copy the data to another device.
void Save(support::Stream *stream) const
Save Tensor to stream.
Definition: tensor.h:264
ffi::ShapeView Shape() const
Definition: tensor.h:61
Tensor(ffi::UnsafeInit tag)
Definition: tensor.h:57
void CopyTo(DLTensor *other) const
Copy data content into another array.
Definition: tensor.h:206
static TVM_RUNTIME_DLL void CopyFromTo(const DLTensor *from, DLTensor *to, TVMStreamHandle stream=nullptr)
Function to copy data from one array to another.
Tensor(const ffi::Tensor &other)
Definition: tensor.h:59
Tensor(ffi::Tensor &&other)
Definition: tensor.h:58
runtime::DataType DataType() const
Definition: tensor.h:62
Tensor(ffi::ObjectPtr< ffi::TensorObj > data)
constructor.
Definition: tensor.h:56
TVM_RUNTIME_DLL void CopyFromBytes(const void *data, size_t nbytes)
Copy data content from a byte buffer.
static Tensor FromDLPack(DLManagedTensor *tensor)
Definition: tensor.h:65
TVM_RUNTIME_DLL Tensor CreateView(ffi::Shape shape, DLDataType dtype, uint64_t relative_byte_offset=0) const
Create a Tensor that shares the data memory with the current one.
static TVM_RUNTIME_DLL void CopyFromBytes(const DLTensor *to, void *from, size_t nbytes, TVMStreamHandle stream=nullptr)
Function to copy data from one array to a byte buffer.
Abstract binary stream for serialization.
Definition: io.h:57
void WriteArray(const T *data, size_t num_elems)
Write an array of typed values element by element.
Definition: io.h:107
virtual size_t Read(void *ptr, size_t size)=0
Read raw bytes from the stream.
bool ReadArray(T *data, size_t num_elems)
Read an array of typed values element by element.
Definition: io.h:118
virtual size_t Write(const void *ptr, size_t size)=0
Write raw bytes to the stream.
Abstract device memory management API.
void * TVMStreamHandle
The stream that is specific to device can be NULL, which indicates the default one.
Definition: device_api.h:38
Binary stream I/O interface.
bool SaveDLTensor(support::Stream *strm, const DLTensor *tensor)
Save a DLTensor to stream.
Definition: tensor.h:220
Device GetPreferredHostDevice(Device device)
Get the preferred host device from the input device.
Definition: tensor.h:310
constexpr int kAllocAlignment
Number of bytes each allocation must align to.
Definition: device_api.h:112
constexpr uint64_t kTVMTensorMagic
Magic number for Tensor file.
Definition: tensor.h:218
Tensor shape(const Tensor &src, DataType dtype, const std::string name="T_shape", const std::string tag=kInjective)
Get the shape of input tensor.
Definition: transform.h:1981
An object that builds and maintains block scope and StmtSref mapping for Dependence analysis.
Definition: analyzer.h:37
PrimExpr ret(PrimExpr value, Span span=Span())
Return the value.
runtime::DataType DataType
Definition: data_type.h:457
DLDevice Device
Definition: device_api.h:43
#define TVM_RUNTIME_DLL
Definition: base.h:88
Serializer<T> specializations for tvm::support::Stream.