tvm
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 // A 'null' device type, does not correspond to any DLDeviceType enum.
46 // TODO(mbs): This is to help us as we transition away from representing the 'homogenous' case
47 // as a singleton target map indexed by the invalid DLDeviceType '0'.
48 constexpr DLDeviceType kNullDeviceType = static_cast<DLDeviceType>(0);
49 
50 // An 'invalid' device type, does not correspond to any DLDeviceType enum.
51 constexpr DLDeviceType kInvalidDeviceType = static_cast<DLDeviceType>(-1);
52 
53 namespace runtime {
54 
59 class NDArray : public ObjectRef {
60  public:
62  class ContainerBase;
64  class Container;
68  NDArray() {}
73  explicit NDArray(ObjectPtr<Object> data) : ObjectRef(data) {}
74 
76  inline void reset();
81  inline int use_count() const;
83  inline const DLTensor* operator->() const;
85  inline bool IsContiguous() const;
92  inline void CopyFrom(const DLTensor* other);
93  inline void CopyFrom(const NDArray& other);
101  TVM_DLL void CopyFromBytes(const void* data, size_t nbytes);
108  inline void CopyTo(DLTensor* other) const;
109  inline void CopyTo(const NDArray& other) const;
117  TVM_DLL void CopyToBytes(void* data, size_t nbytes) const;
123  inline NDArray CopyTo(const Device& dev) const;
129  inline bool Load(dmlc::Stream* stream);
134  inline void Save(dmlc::Stream* stream) const;
141  TVM_DLL NDArray CreateView(ShapeTuple shape, DLDataType dtype);
147  TVM_DLL DLManagedTensor* ToDLPack() const;
156  TVM_DLL static NDArray Empty(ShapeTuple shape, DLDataType dtype, Device dev,
157  Optional<String> mem_scope = NullOpt);
168  TVM_DLL static NDArray FromExternalDLTensor(const DLTensor& dl_tensor);
176  TVM_DLL static NDArray NewFromDLTensor(DLTensor* dl_tensor, const Device& dev);
188  TVM_DLL static NDArray FromDLPack(DLManagedTensor* tensor);
195  TVM_DLL static void CopyFromTo(const DLTensor* from, DLTensor* to,
196  TVMStreamHandle stream = nullptr);
197 
198  TVM_DLL ShapeTuple Shape() const;
199  TVM_DLL runtime::DataType DataType() const;
210  TVM_DLL static bool AbilityOfZeroCopyForDLTensor(DLTensor* tensor, const Device& dev);
211  // internal namespace
212  struct Internal;
213 
214  private:
215  TVM_DLL static bool IsAligned(const DLTensor& tensor);
216 
217  protected:
218  friend class TVMPODValue_;
219  friend class TVMRetValue;
220  friend class TVMArgsSetter;
225  inline Container* get_mutable() const;
226  // Helper functions for FFI handling.
236  inline static ObjectPtr<Object> FFIDataFromHandle(TVMArrayHandle handle);
241  inline static void FFIDecRef(TVMArrayHandle handle);
247  inline static TVMArrayHandle FFIGetHandle(const ObjectRef& nd);
248 };
249 
255 inline bool SaveDLTensor(dmlc::Stream* strm, const DLTensor* tensor);
256 
265  public:
272  DLTensor dl_tensor;
273 
280  void* manager_ctx{nullptr};
281 
282  protected:
288 };
289 
295  public:
298  // Initialize the type index.
299  type_index_ = Container::RuntimeTypeIndex();
300  dl_tensor.data = nullptr;
301  dl_tensor.ndim = 0;
302  dl_tensor.shape = nullptr;
303  dl_tensor.strides = nullptr;
304  dl_tensor.byte_offset = 0;
305  }
306 
307  Container(void* data, ShapeTuple shape, DLDataType dtype, Device dev) {
308  // Initialize the type index.
309  type_index_ = Container::RuntimeTypeIndex();
310  dl_tensor.data = data;
311  shape_ = std::move(shape);
312  dl_tensor.ndim = static_cast<int>(shape_.size());
313  dl_tensor.shape = const_cast<ShapeTuple::index_type*>(shape_.data());
314  dl_tensor.dtype = dtype;
315  dl_tensor.strides = nullptr;
316  dl_tensor.byte_offset = 0;
317  dl_tensor.device = dev;
318  }
323  void SetDeleter(FDeleter deleter) { deleter_ = deleter; }
324 
325  // Expose DecRef and IncRef as public function
326  // NOTE: they are only for developer purposes only.
327  using Object::DecRef;
328  using Object::IncRef;
329 
330  // Information for object protocol.
331  static constexpr const uint32_t _type_index = TypeIndex::kRuntimeNDArray;
332  static constexpr const uint32_t _type_child_slots = 0;
333  static constexpr const uint32_t _type_child_slots_can_overflow = true;
334  static constexpr const char* _type_key = "runtime.NDArray";
336 
337  protected:
338  friend class RPCWrappedFunc;
339  friend class NDArray;
340 };
341 
342 // implementations of inline functions
349 inline size_t GetDataSize(const DLTensor& arr) {
350  size_t size = 1;
351  for (tvm_index_t i = 0; i < arr.ndim; ++i) {
352  size *= static_cast<size_t>(arr.shape[i]);
353  }
354  size *= (arr.dtype.bits * arr.dtype.lanes + 7) / 8;
355  return size;
356 }
357 
363 static inline bool IsContiguous(const DLTensor& arr) {
364  if (arr.strides == nullptr) return true;
365  int64_t expected_stride = 1;
366  for (int32_t i = arr.ndim; i != 0; --i) {
367  int32_t k = i - 1;
368  if (arr.shape[k] == 1) {
369  // Skip stride check if shape[k] is 1, where the dimension is contiguous
370  // regardless of the value of stride.
371  //
372  // For example, PyTorch will normalize stride to 1 if shape is 1 when exporting
373  // to DLPack.
374  // More context: https://github.com/pytorch/pytorch/pull/83158
375  continue;
376  }
377  if (arr.strides[k] != expected_stride) return false;
378  expected_stride *= arr.shape[k];
379  }
380  return true;
381 }
382 
383 inline bool NDArray::IsContiguous() const {
384  return ::tvm::runtime::IsContiguous(get_mutable()->dl_tensor);
385 }
386 
387 inline void NDArray::CopyFrom(const DLTensor* other) {
388  ICHECK(data_ != nullptr);
389  CopyFromTo(other, &(get_mutable()->dl_tensor));
390 }
391 
392 inline void NDArray::CopyFrom(const NDArray& other) {
393  ICHECK(data_ != nullptr);
394  ICHECK(other.data_ != nullptr);
396 }
397 
398 inline void NDArray::CopyTo(DLTensor* other) const {
399  ICHECK(data_ != nullptr);
400  CopyFromTo(&(get_mutable()->dl_tensor), other);
401 }
402 
403 inline void NDArray::CopyTo(const NDArray& other) const {
404  ICHECK(data_ != nullptr);
405  ICHECK(other.data_ != nullptr);
406  CopyFromTo(&(get_mutable()->dl_tensor), &(other.get_mutable()->dl_tensor));
407 }
408 
409 inline NDArray NDArray::CopyTo(const Device& dev) const {
410  ICHECK(data_ != nullptr);
411  const DLTensor* dptr = operator->();
412  NDArray ret = Empty(ShapeTuple(dptr->shape, dptr->shape + dptr->ndim), dptr->dtype, dev);
413  this->CopyTo(ret);
414  return ret;
415 }
416 
417 inline int NDArray::use_count() const { return data_.use_count(); }
418 
419 inline const DLTensor* NDArray::operator->() const { return &(get_mutable()->dl_tensor); }
420 
422  return static_cast<NDArray::Container*>(data_.get());
423 }
424 
426  return GetObjectPtr<Object>(
427  static_cast<NDArray::Container*>(reinterpret_cast<NDArray::ContainerBase*>(handle)));
428 }
429 
431  // NOTE: it is necessary to cast to container then to base
432  // so that the FFI handle uses the ContainerBase address.
433  auto ptr = reinterpret_cast<TVMArrayHandle>(static_cast<NDArray::ContainerBase*>(
434  static_cast<NDArray::Container*>(const_cast<Object*>(nd.get()))));
435  return ptr;
436 }
437 
438 inline void NDArray::FFIDecRef(TVMArrayHandle handle) {
439  static_cast<NDArray::Container*>(reinterpret_cast<NDArray::ContainerBase*>(handle))->DecRef();
440 }
441 
443  return static_cast<NDArray::Container*>(reinterpret_cast<NDArray::ContainerBase*>(handle));
444 }
445 
447 constexpr uint64_t kTVMNDArrayMagic = 0xDD5E40F096B4A13F;
448 
449 inline bool SaveDLTensor(dmlc::Stream* strm, const DLTensor* tensor) {
450  uint64_t header = kTVMNDArrayMagic, reserved = 0;
451  strm->Write(header);
452  strm->Write(reserved);
453  // Always save data as CPU context
454  //
455  // Parameters that get serialized should be in CPU by default.
456  // So even the array's context is GPU, it will be stored as CPU array.
457  // This is used to prevent case when another user loads the parameters
458  // back on machine that do not have GPU or related context.
459  //
460  // We can always do array.CopyTo(target_dev) to get a corresponding
461  // array in the target context.
462  Device cpu_dev;
463  cpu_dev.device_type = kDLCPU;
464  cpu_dev.device_id = 0;
465  strm->Write(cpu_dev);
466  strm->Write(tensor->ndim);
467  strm->Write(tensor->dtype);
468  int ndim = tensor->ndim;
469  strm->WriteArray(tensor->shape, ndim);
470  int type_bytes = (tensor->dtype.bits + 7) / 8;
471  int64_t num_elems = 1;
472  for (int i = 0; i < ndim; ++i) {
473  num_elems *= tensor->shape[i];
474  }
475  int64_t data_byte_size = type_bytes * num_elems;
476  strm->Write(data_byte_size);
477 
478  if (DMLC_IO_NO_ENDIAN_SWAP && tensor->device.device_type == kDLCPU &&
479  tensor->strides == nullptr && tensor->byte_offset == 0) {
480  // quick path
481  strm->Write(tensor->data, data_byte_size);
482  } else {
483  std::vector<uint8_t> bytes(data_byte_size);
484  ICHECK_EQ(
485  TVMArrayCopyToBytes(const_cast<DLTensor*>(tensor), dmlc::BeginPtr(bytes), data_byte_size),
486  0)
487  << TVMGetLastError();
488  if (!DMLC_IO_NO_ENDIAN_SWAP) {
489  dmlc::ByteSwap(dmlc::BeginPtr(bytes), type_bytes, num_elems);
490  }
491  strm->Write(dmlc::BeginPtr(bytes), data_byte_size);
492  }
493  return true;
494 }
495 
496 inline void NDArray::Save(dmlc::Stream* strm) const { SaveDLTensor(strm, operator->()); }
497 
498 inline bool NDArray::Load(dmlc::Stream* strm) {
499  uint64_t header, reserved;
500  ICHECK(strm->Read(&header)) << "Invalid DLTensor file format";
501  ICHECK(strm->Read(&reserved)) << "Invalid DLTensor file format";
502  ICHECK(header == kTVMNDArrayMagic) << "Invalid DLTensor file format";
503  Device dev;
504  int ndim;
505  DLDataType dtype;
506  ICHECK(strm->Read(&dev)) << "Invalid DLTensor file format";
507  ICHECK(strm->Read(&ndim)) << "Invalid DLTensor file format";
508  ICHECK(strm->Read(&dtype)) << "Invalid DLTensor file format";
509  ICHECK_EQ(dev.device_type, kDLCPU) << "Invalid DLTensor device: can only save as CPU tensor";
510  std::vector<int64_t> shape(ndim);
511  if (ndim != 0) {
512  ICHECK(strm->ReadArray(&shape[0], ndim)) << "Invalid DLTensor file format";
513  }
514  NDArray ret = NDArray::Empty(ShapeTuple(shape), dtype, dev);
515  int64_t num_elems = 1;
516  int elem_bytes = (ret->dtype.bits + 7) / 8;
517  for (int i = 0; i < ret->ndim; ++i) {
518  num_elems *= ret->shape[i];
519  }
520  int64_t data_byte_size;
521  ICHECK(strm->Read(&data_byte_size)) << "Invalid DLTensor file format";
522  ICHECK(data_byte_size == num_elems * elem_bytes) << "Invalid DLTensor file format";
523  auto read_ret = strm->Read(ret->data, data_byte_size);
524  // Only check non-empty data
525  if (ndim > 0 && shape[0] != 0) {
526  ICHECK(read_ret) << "Invalid DLTensor file format";
527  }
528  if (!DMLC_IO_NO_ENDIAN_SWAP) {
529  dmlc::ByteSwap(ret->data, elem_bytes, num_elems);
530  }
531  *this = ret;
532  return true;
533 }
534 
535 } // namespace runtime
536 } // namespace tvm
537 
538 namespace std {
539 template <>
540 struct hash<tvm::Device> {
541  std::size_t operator()(const tvm::Device& dev) const {
542  return ((dev.device_id << 8) | dev.device_type);
543  }
544 };
545 
546 template <>
547 struct equal_to<tvm::Device> {
548  bool operator()(const tvm::Device& lhs, const tvm::Device& rhs) const {
549  return (lhs.device_type == rhs.device_type && lhs.device_id == rhs.device_id);
550  }
551 };
552 } // namespace std
553 
554 #endif // TVM_RUNTIME_NDARRAY_H_
The container base structure contains all the fields except for the Object header.
Definition: ndarray.h:264
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:438
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:498
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:387
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:195
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:398
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:383
Object * TVMArrayHandleToObjectHandle(TVMArrayHandle handle)
Definition: ndarray.h:442
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:59
void * TVMStreamHandle
The stream that is specific to device can be NULL, which indicates the default one.
Definition: c_runtime_api.h:229
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:417
void Save(dmlc::Stream *stream) const
Save NDArray to stream.
Definition: ndarray.h:496
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:419
static ObjectPtr< Object > FFIDataFromHandle(TVMArrayHandle handle)
Construct NDArray&#39;s Data field from array handle in FFI.
Definition: ndarray.h:425
NDArray(ObjectPtr< Object > data)
constructor.
Definition: ndarray.h:73
Runtime primitive data type.
Definition: data_type.h:41
ShapeTuple Shape() const
Container * get_mutable() const
Get mutable internal container pointer.
Definition: ndarray.h:421
Object container class that backs NDArray.
Definition: ndarray.h:294
Container(void *data, ShapeTuple shape, DLDataType dtype, Device dev)
Definition: ndarray.h:307
ObjectPtr< Object > data_
Internal pointer that backs the reference.
Definition: object.h:574
DLTensor dl_tensor
The corresponding dl_tensor field.
Definition: ndarray.h:272
void SetDeleter(FDeleter deleter)
Set the deleter field.
Definition: ndarray.h:323
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:1758
int64_t tvm_index_t
type of array index.
Definition: c_runtime_api.h:81
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:430
Container()
default constructor
Definition: ndarray.h:297
constexpr uint64_t kTVMNDArrayMagic
Magic number for NDArray file.
Definition: ndarray.h:447
NDArray()
default constructor
Definition: ndarray.h:68
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:449
static NDArray FromDLPack(DLManagedTensor *tensor)
Create a NDArray backed by a dlpack tensor.
Definition: packed_func.h:1513
constexpr runtime::NullOptType NullOpt
Definition: optional.h:160
void CopyToBytes(void *data, size_t nbytes) const
Copy data content into another array.
constexpr DLDeviceType kNullDeviceType
Definition: ndarray.h:48
#define TVM_DECLARE_BASE_OBJECT_INFO(TypeName, ParentType)
helper macro to declare a base object type that can be inherited.
Definition: object.h:648
constexpr DLDeviceType kInvalidDeviceType
Definition: ndarray.h:51
ShapeTuple shape_
The shape container, can be used used for shape data.
Definition: ndarray.h:287
size_t GetDataSize(const DLTensor &arr)
return the size of data the DLTensor hold, in term of number of bytes
Definition: ndarray.h:349
Reference to shape tuple objects.
Definition: shape_tuple.h:81
runtime::NDArray.
Definition: object.h:64