tvm
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
ndarray.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_NDARRAY_H_
25 #define TVM_RUNTIME_NDARRAY_H_
26 
31 #include <tvm/runtime/data_type.h>
32 #include <tvm/runtime/object.h>
33 #include <tvm/runtime/serializer.h>
34 
35 #include <atomic>
36 #include <functional>
37 #include <utility>
38 #include <vector>
39 
40 namespace tvm {
41 
42 // alias DLDevice
43 using Device = DLDevice;
44 
45 namespace runtime {
46 
51 class NDArray : public ObjectRef {
52  public:
54  class ContainerBase;
56  class Container;
60  NDArray() {}
65  explicit NDArray(ObjectPtr<Object> data) : ObjectRef(data) {}
66 
68  inline void reset();
73  inline int use_count() const;
75  inline const DLTensor* operator->() const;
77  inline bool IsContiguous() const;
84  inline void CopyFrom(const DLTensor* other);
85  inline void CopyFrom(const NDArray& other);
93  TVM_DLL void CopyFromBytes(const void* data, size_t nbytes);
100  inline void CopyTo(DLTensor* other) const;
101  inline void CopyTo(const NDArray& other) const;
109  TVM_DLL void CopyToBytes(void* data, size_t nbytes) const;
115  inline NDArray CopyTo(const Device& dev) const;
121  inline bool Load(dmlc::Stream* stream);
126  inline void Save(dmlc::Stream* stream) const;
133  TVM_DLL NDArray CreateView(ShapeTuple shape, DLDataType dtype);
139  TVM_DLL DLManagedTensor* ToDLPack() const;
148  TVM_DLL static NDArray Empty(ShapeTuple shape, DLDataType dtype, Device dev,
149  Optional<String> mem_scope = NullOpt);
160  TVM_DLL static NDArray FromExternalDLTensor(const DLTensor& dl_tensor);
168  TVM_DLL static NDArray NewFromDLTensor(DLTensor* dl_tensor, const Device& dev);
180  TVM_DLL static NDArray FromDLPack(DLManagedTensor* tensor);
187  TVM_DLL static void CopyFromTo(const DLTensor* from, DLTensor* to,
188  TVMStreamHandle stream = nullptr);
189 
190  TVM_DLL ShapeTuple Shape() const;
191  TVM_DLL runtime::DataType DataType() const;
202  TVM_DLL static bool AbilityOfZeroCopyForDLTensor(DLTensor* tensor, const Device& dev);
203  // internal namespace
204  struct Internal;
205 
206  private:
207  TVM_DLL static bool IsAligned(const DLTensor& tensor);
208 
209  protected:
210  friend class TVMPODValue_;
211  friend class TVMRetValue;
212  friend class TVMArgsSetter;
217  inline Container* get_mutable() const;
218  // Helper functions for FFI handling.
228  inline static ObjectPtr<Object> FFIDataFromHandle(TVMArrayHandle handle);
233  inline static void FFIDecRef(TVMArrayHandle handle);
239  inline static TVMArrayHandle FFIGetHandle(const ObjectRef& nd);
240 };
241 
247 inline bool SaveDLTensor(dmlc::Stream* strm, const DLTensor* tensor);
248 
257  public:
264  DLTensor dl_tensor;
265 
272  void* manager_ctx{nullptr};
273 
274  protected:
280 };
281 
287  public:
290  // Initialize the type index.
291  type_index_ = Container::RuntimeTypeIndex();
292  dl_tensor.data = nullptr;
293  dl_tensor.ndim = 0;
294  dl_tensor.shape = nullptr;
295  dl_tensor.strides = nullptr;
296  dl_tensor.byte_offset = 0;
297  }
298 
299  Container(void* data, ShapeTuple shape, DLDataType dtype, Device dev) {
300  // Initialize the type index.
301  type_index_ = Container::RuntimeTypeIndex();
302  dl_tensor.data = data;
303  shape_ = std::move(shape);
304  dl_tensor.ndim = static_cast<int>(shape_.size());
305  dl_tensor.shape = const_cast<ShapeTuple::index_type*>(shape_.data());
306  dl_tensor.dtype = dtype;
307  dl_tensor.strides = nullptr;
308  dl_tensor.byte_offset = 0;
309  dl_tensor.device = dev;
310  }
315  void SetDeleter(FDeleter deleter) { deleter_ = deleter; }
316 
317  // Expose DecRef and IncRef as public function
318  // NOTE: they are only for developer purposes only.
319  using Object::DecRef;
320  using Object::IncRef;
321 
322  // Information for object protocol.
323  static constexpr const uint32_t _type_index = TypeIndex::kRuntimeNDArray;
324  static constexpr const uint32_t _type_child_slots = 0;
325  static constexpr const uint32_t _type_child_slots_can_overflow = true;
326  static constexpr const char* _type_key = "runtime.NDArray";
328 
329  protected:
330  friend class RPCWrappedFunc;
331  friend class NDArray;
332 };
333 
334 // implementations of inline functions
341 inline size_t GetDataSize(const DLTensor& arr) {
342  size_t size = 1;
343  for (tvm_index_t i = 0; i < arr.ndim; ++i) {
344  size *= static_cast<size_t>(arr.shape[i]);
345  }
346  size *= (arr.dtype.bits * arr.dtype.lanes + 7) / 8;
347  return size;
348 }
349 
355 static inline bool IsContiguous(const DLTensor& arr) {
356  if (arr.strides == nullptr) return true;
357  int64_t expected_stride = 1;
358  for (int32_t i = arr.ndim; i != 0; --i) {
359  int32_t k = i - 1;
360  if (arr.shape[k] == 1) {
361  // Skip stride check if shape[k] is 1, where the dimension is contiguous
362  // regardless of the value of stride.
363  //
364  // For example, PyTorch will normalize stride to 1 if shape is 1 when exporting
365  // to DLPack.
366  // More context: https://github.com/pytorch/pytorch/pull/83158
367  continue;
368  }
369  if (arr.strides[k] != expected_stride) return false;
370  expected_stride *= arr.shape[k];
371  }
372  return true;
373 }
374 
375 inline bool NDArray::IsContiguous() const {
376  return ::tvm::runtime::IsContiguous(get_mutable()->dl_tensor);
377 }
378 
379 inline void NDArray::CopyFrom(const DLTensor* other) {
380  ICHECK(data_ != nullptr);
381  CopyFromTo(other, &(get_mutable()->dl_tensor));
382 }
383 
384 inline void NDArray::CopyFrom(const NDArray& other) {
385  ICHECK(data_ != nullptr);
386  ICHECK(other.data_ != nullptr);
388 }
389 
390 inline void NDArray::CopyTo(DLTensor* other) const {
391  ICHECK(data_ != nullptr);
392  CopyFromTo(&(get_mutable()->dl_tensor), other);
393 }
394 
395 inline void NDArray::CopyTo(const NDArray& other) const {
396  ICHECK(data_ != nullptr);
397  ICHECK(other.data_ != nullptr);
398  CopyFromTo(&(get_mutable()->dl_tensor), &(other.get_mutable()->dl_tensor));
399 }
400 
401 inline NDArray NDArray::CopyTo(const Device& dev) const {
402  ICHECK(data_ != nullptr);
403  const DLTensor* dptr = operator->();
404  NDArray ret = Empty(ShapeTuple(dptr->shape, dptr->shape + dptr->ndim), dptr->dtype, dev);
405  this->CopyTo(ret);
406  return ret;
407 }
408 
409 inline int NDArray::use_count() const { return data_.use_count(); }
410 
411 inline const DLTensor* NDArray::operator->() const { return &(get_mutable()->dl_tensor); }
412 
414  return static_cast<NDArray::Container*>(data_.get());
415 }
416 
418  return GetObjectPtr<Object>(
419  static_cast<NDArray::Container*>(reinterpret_cast<NDArray::ContainerBase*>(handle)));
420 }
421 
423  // NOTE: it is necessary to cast to container then to base
424  // so that the FFI handle uses the ContainerBase address.
425  auto ptr = reinterpret_cast<TVMArrayHandle>(static_cast<NDArray::ContainerBase*>(
426  static_cast<NDArray::Container*>(const_cast<Object*>(nd.get()))));
427  return ptr;
428 }
429 
430 inline void NDArray::FFIDecRef(TVMArrayHandle handle) {
431  static_cast<NDArray::Container*>(reinterpret_cast<NDArray::ContainerBase*>(handle))->DecRef();
432 }
433 
435  return static_cast<NDArray::Container*>(reinterpret_cast<NDArray::ContainerBase*>(handle));
436 }
437 
439 constexpr uint64_t kTVMNDArrayMagic = 0xDD5E40F096B4A13F;
440 
441 inline bool SaveDLTensor(dmlc::Stream* strm, const DLTensor* tensor) {
442  uint64_t header = kTVMNDArrayMagic, reserved = 0;
443  strm->Write(header);
444  strm->Write(reserved);
445  // Always save data as CPU context
446  //
447  // Parameters that get serialized should be in CPU by default.
448  // So even the array's context is GPU, it will be stored as CPU array.
449  // This is used to prevent case when another user loads the parameters
450  // back on machine that do not have GPU or related context.
451  //
452  // We can always do array.CopyTo(target_dev) to get a corresponding
453  // array in the target context.
454  Device cpu_dev;
455  cpu_dev.device_type = kDLCPU;
456  cpu_dev.device_id = 0;
457  strm->Write(cpu_dev);
458  strm->Write(tensor->ndim);
459  strm->Write(tensor->dtype);
460  int ndim = tensor->ndim;
461  strm->WriteArray(tensor->shape, ndim);
462  int type_bytes = (tensor->dtype.bits + 7) / 8;
463  int64_t num_elems = 1;
464  for (int i = 0; i < ndim; ++i) {
465  num_elems *= tensor->shape[i];
466  }
467  int64_t data_byte_size = type_bytes * num_elems;
468  strm->Write(data_byte_size);
469 
470  if (DMLC_IO_NO_ENDIAN_SWAP && tensor->device.device_type == kDLCPU &&
471  tensor->strides == nullptr && tensor->byte_offset == 0) {
472  // quick path
473  strm->Write(tensor->data, data_byte_size);
474  } else {
475  std::vector<uint8_t> bytes(data_byte_size);
476  ICHECK_EQ(
477  TVMArrayCopyToBytes(const_cast<DLTensor*>(tensor), dmlc::BeginPtr(bytes), data_byte_size),
478  0)
479  << TVMGetLastError();
480  if (!DMLC_IO_NO_ENDIAN_SWAP) {
481  dmlc::ByteSwap(dmlc::BeginPtr(bytes), type_bytes, num_elems);
482  }
483  strm->Write(dmlc::BeginPtr(bytes), data_byte_size);
484  }
485  return true;
486 }
487 
488 inline void NDArray::Save(dmlc::Stream* strm) const { SaveDLTensor(strm, operator->()); }
489 
490 inline bool NDArray::Load(dmlc::Stream* strm) {
491  uint64_t header, reserved;
492  ICHECK(strm->Read(&header)) << "Invalid DLTensor file format";
493  ICHECK(strm->Read(&reserved)) << "Invalid DLTensor file format";
494  ICHECK(header == kTVMNDArrayMagic) << "Invalid DLTensor file format";
495  Device dev;
496  int ndim;
497  DLDataType dtype;
498  ICHECK(strm->Read(&dev)) << "Invalid DLTensor file format";
499  ICHECK(strm->Read(&ndim)) << "Invalid DLTensor file format";
500  ICHECK(strm->Read(&dtype)) << "Invalid DLTensor file format";
501  ICHECK_EQ(dev.device_type, kDLCPU) << "Invalid DLTensor device: can only save as CPU tensor";
502  std::vector<int64_t> shape(ndim);
503  if (ndim != 0) {
504  ICHECK(strm->ReadArray(&shape[0], ndim)) << "Invalid DLTensor file format";
505  }
506  NDArray ret = NDArray::Empty(ShapeTuple(shape), dtype, dev);
507  int64_t num_elems = 1;
508  int elem_bytes = (ret->dtype.bits + 7) / 8;
509  for (int i = 0; i < ret->ndim; ++i) {
510  num_elems *= ret->shape[i];
511  }
512  int64_t data_byte_size;
513  ICHECK(strm->Read(&data_byte_size)) << "Invalid DLTensor file format";
514  ICHECK(data_byte_size == num_elems * elem_bytes) << "Invalid DLTensor file format";
515  auto read_ret = strm->Read(ret->data, data_byte_size);
516  // Only check non-empty data
517  if (ndim > 0 && shape[0] != 0) {
518  ICHECK(read_ret) << "Invalid DLTensor file format";
519  }
520  if (!DMLC_IO_NO_ENDIAN_SWAP) {
521  dmlc::ByteSwap(ret->data, elem_bytes, num_elems);
522  }
523  *this = ret;
524  return true;
525 }
526 
527 } // namespace runtime
528 } // namespace tvm
529 
530 namespace std {
531 template <>
532 struct hash<tvm::Device> {
533  std::size_t operator()(const tvm::Device& dev) const {
534  return ((dev.device_id << 8) | dev.device_type);
535  }
536 };
537 
538 template <>
539 struct equal_to<tvm::Device> {
540  bool operator()(const tvm::Device& lhs, const tvm::Device& rhs) const {
541  return (lhs.device_type == rhs.device_type && lhs.device_id == rhs.device_id);
542  }
543 };
544 } // namespace std
545 
546 #endif // TVM_RUNTIME_NDARRAY_H_
The container base structure contains all the fields except for the Object header.
Definition: ndarray.h:256
DLManagedTensor * ToDLPack() const
Create a reference view of NDArray that represents as DLManagedTensor.
Return Value container, Unlike TVMArgValue, which only holds reference and do not delete the underlyi...
Definition: packed_func.h:799
static void FFIDecRef(TVMArrayHandle handle)
DecRef resource managed by an FFI array handle.
Definition: ndarray.h:430
runtime::DataType DataType() const
A custom smart pointer for Object.
Definition: object.h:358
Runtime Optional container types.
bool Load(dmlc::Stream *stream)
Load NDArray from stream.
Definition: ndarray.h:490
Runtime String container types.
Internal base class to handle conversion to POD values.
Definition: packed_func.h:541
void CopyFrom(const DLTensor *other)
Copy data content from another array.
Definition: ndarray.h:379
runtime implementation for LibTorch/TorchScript.
Definition: analyzer.h:36
static NDArray NewFromDLTensor(DLTensor *dl_tensor, const Device &dev)
Create new NDArray, data is copied from DLTensor.
DLTensor * TVMArrayHandle
the array handle
Definition: c_runtime_api.h:202
Serializer extension to support TVM data types Include this file to enable serialization of DLDataTyp...
NDArray CreateView(ShapeTuple shape, DLDataType dtype)
Create a NDArray that shares the data memory with the current one.
void IncRef()
developer function, increases reference counter.
Definition: object.h:799
Definition: loop_state.h:456
int TVMArrayCopyToBytes(TVMArrayHandle handle, void *data, size_t nbytes)
Copy array data to CPU byte array.
void CopyTo(DLTensor *other) const
Copy data content into another array.
Definition: ndarray.h:390
static NDArray Empty(ShapeTuple shape, DLDataType dtype, Device dev, Optional< String > mem_scope=NullOpt)
Create an empty NDArray.
base class of all object containers.
Definition: object.h:167
bool IsContiguous() const
Definition: ndarray.h:375
Object * TVMArrayHandleToObjectHandle(TVMArrayHandle handle)
Definition: ndarray.h:434
const char * TVMGetLastError(void)
return str message of the last error all function in this file will return 0 when success and nonzero...
void reset()
reset the content of NDArray to be nullptr
Managed NDArray. The array is backed by reference counted blocks.
Definition: ndarray.h:51
void * TVMStreamHandle
The stream that is specific to device can be NULL, which indicates the default one.
Definition: c_runtime_api.h:236
void DecRef()
developer function, decrease reference counter.
Definition: object.h:801
static uint32_t RuntimeTypeIndex()
Definition: object.h:225
static void CopyFromTo(const DLTensor *from, DLTensor *to, TVMStreamHandle stream=nullptr)
Function to copy data from one array to another.
int use_count() const
Definition: ndarray.h:409
void Save(dmlc::Stream *stream) const
Save NDArray to stream.
Definition: ndarray.h:488
static bool AbilityOfZeroCopyForDLTensor(DLTensor *tensor, const Device &dev)
Check conditions for construction NDArray over DLTensor without copying. There are three conditions t...
const DLTensor * operator->() const
Definition: ndarray.h:411
static ObjectPtr< Object > FFIDataFromHandle(TVMArrayHandle handle)
Construct NDArray&#39;s Data field from array handle in FFI.
Definition: ndarray.h:417
NDArray(ObjectPtr< Object > data)
constructor.
Definition: ndarray.h:65
Runtime primitive data type.
Definition: data_type.h:41
ShapeTuple Shape() const
Container * get_mutable() const
Get mutable internal container pointer.
Definition: ndarray.h:413
Object container class that backs NDArray.
Definition: ndarray.h:286
Container(void *data, ShapeTuple shape, DLDataType dtype, Device dev)
Definition: ndarray.h:299
ObjectPtr< Object > data_
Internal pointer that backs the reference.
Definition: object.h:574
DLTensor dl_tensor
The corresponding dl_tensor field.
Definition: ndarray.h:264
void SetDeleter(FDeleter deleter)
Set the deleter field.
Definition: ndarray.h:315
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:1768
int64_t tvm_index_t
type of array index.
Definition: c_runtime_api.h:88
DLDevice Device
Definition: ndarray.h:43
const Object * get() const
Definition: object.h:546
Base class of all object reference.
Definition: object.h:511
static NDArray FromExternalDLTensor(const DLTensor &dl_tensor)
Create a NDArray backed by an external DLTensor without memory copying.
Runtime ShapeTuple container types.
A managed object in the TVM runtime.
static TVMArrayHandle FFIGetHandle(const ObjectRef &nd)
Get FFI Array handle from ndarray.
Definition: ndarray.h:422
Container()
default constructor
Definition: ndarray.h:289
constexpr uint64_t kTVMNDArrayMagic
Magic number for NDArray file.
Definition: ndarray.h:439
NDArray()
default constructor
Definition: ndarray.h:60
ShapeTupleObj::index_type index_type
The type of shape index element.
Definition: shape_tuple.h:84
void CopyFromBytes(const void *data, size_t nbytes)
Copy data content from a byte buffer.
PrimExpr ret(PrimExpr value, Span span=Span())
Return the value.
Optional container that to represent to a Nullable variant of T.
Definition: optional.h:51
bool SaveDLTensor(dmlc::Stream *strm, const DLTensor *tensor)
Save a DLTensor to stream.
Definition: ndarray.h:441
static NDArray FromDLPack(DLManagedTensor *tensor)
Create a NDArray backed by a dlpack tensor.
Definition: packed_func.h:1517
constexpr runtime::NullOptType NullOpt
Definition: optional.h:160
void CopyToBytes(void *data, size_t nbytes) const
Copy data content into another array.
#define TVM_DECLARE_BASE_OBJECT_INFO(TypeName, ParentType)
helper macro to declare a base object type that can be inherited.
Definition: object.h:648
ShapeTuple shape_
The shape container, can be used used for shape data.
Definition: ndarray.h:279
size_t GetDataSize(const DLTensor &arr)
return the size of data the DLTensor hold, in term of number of bytes
Definition: ndarray.h:341
Reference to shape tuple objects.
Definition: shape_tuple.h:81
runtime::NDArray.
Definition: object.h:64