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);
169  TVM_DLL static NDArray FromDLPack(DLManagedTensor* tensor);
176  TVM_DLL static void CopyFromTo(const DLTensor* from, DLTensor* to,
177  TVMStreamHandle stream = nullptr);
178 
179  TVM_DLL ShapeTuple Shape() const;
180  TVM_DLL runtime::DataType DataType() const;
181  // internal namespace
182  struct Internal;
183 
184  protected:
185  friend class TVMPODValue_;
186  friend class TVMRetValue;
187  friend class TVMArgsSetter;
192  inline Container* get_mutable() const;
193  // Helper functions for FFI handling.
203  inline static ObjectPtr<Object> FFIDataFromHandle(TVMArrayHandle handle);
208  inline static void FFIDecRef(TVMArrayHandle handle);
214  inline static TVMArrayHandle FFIGetHandle(const ObjectRef& nd);
215 };
216 
222 inline bool SaveDLTensor(dmlc::Stream* strm, const DLTensor* tensor);
223 
232  public:
239  DLTensor dl_tensor;
240 
247  void* manager_ctx{nullptr};
248 
249  protected:
255 };
256 
262  public:
265  // Initialize the type index.
266  type_index_ = Container::RuntimeTypeIndex();
267  dl_tensor.data = nullptr;
268  dl_tensor.ndim = 0;
269  dl_tensor.shape = nullptr;
270  dl_tensor.strides = nullptr;
271  dl_tensor.byte_offset = 0;
272  }
273 
274  Container(void* data, ShapeTuple shape, DLDataType dtype, Device dev) {
275  // Initialize the type index.
276  type_index_ = Container::RuntimeTypeIndex();
277  dl_tensor.data = data;
278  shape_ = std::move(shape);
279  dl_tensor.ndim = static_cast<int>(shape_.size());
280  dl_tensor.shape = const_cast<ShapeTuple::index_type*>(shape_.data());
281  dl_tensor.dtype = dtype;
282  dl_tensor.strides = nullptr;
283  dl_tensor.byte_offset = 0;
284  dl_tensor.device = dev;
285  }
290  void SetDeleter(FDeleter deleter) { deleter_ = deleter; }
291 
292  // Expose DecRef and IncRef as public function
293  // NOTE: they are only for developer purposes only.
294  using Object::DecRef;
295  using Object::IncRef;
296 
297  // Information for object protocol.
298  static constexpr const uint32_t _type_index = TypeIndex::kRuntimeNDArray;
299  static constexpr const uint32_t _type_child_slots = 0;
300  static constexpr const uint32_t _type_child_slots_can_overflow = true;
301  static constexpr const char* _type_key = "runtime.NDArray";
303 
304  protected:
305  friend class RPCWrappedFunc;
306  friend class NDArray;
307 };
308 
309 // implementations of inline functions
316 inline size_t GetDataSize(const DLTensor& arr) {
317  size_t size = 1;
318  for (tvm_index_t i = 0; i < arr.ndim; ++i) {
319  size *= static_cast<size_t>(arr.shape[i]);
320  }
321  size *= (arr.dtype.bits * arr.dtype.lanes + 7) / 8;
322  return size;
323 }
324 
330 inline bool IsContiguous(const DLTensor& arr) {
331  if (arr.strides == nullptr) return true;
332  int64_t expected_stride = 1;
333  for (int32_t i = arr.ndim; i != 0; --i) {
334  int32_t k = i - 1;
335  if (arr.strides[k] != expected_stride) return false;
336  expected_stride *= arr.shape[k];
337  }
338  return true;
339 }
340 
341 inline bool NDArray::IsContiguous() const {
343 }
344 
345 inline void NDArray::CopyFrom(const DLTensor* other) {
346  ICHECK(data_ != nullptr);
347  CopyFromTo(other, &(get_mutable()->dl_tensor));
348 }
349 
350 inline void NDArray::CopyFrom(const NDArray& other) {
351  ICHECK(data_ != nullptr);
352  ICHECK(other.data_ != nullptr);
354 }
355 
356 inline void NDArray::CopyTo(DLTensor* other) const {
357  ICHECK(data_ != nullptr);
358  CopyFromTo(&(get_mutable()->dl_tensor), other);
359 }
360 
361 inline void NDArray::CopyTo(const NDArray& other) const {
362  ICHECK(data_ != nullptr);
363  ICHECK(other.data_ != nullptr);
364  CopyFromTo(&(get_mutable()->dl_tensor), &(other.get_mutable()->dl_tensor));
365 }
366 
367 inline NDArray NDArray::CopyTo(const Device& dev) const {
368  ICHECK(data_ != nullptr);
369  const DLTensor* dptr = operator->();
370  NDArray ret = Empty(ShapeTuple(dptr->shape, dptr->shape + dptr->ndim), dptr->dtype, dev);
371  this->CopyTo(ret);
372  return ret;
373 }
374 
375 inline int NDArray::use_count() const { return data_.use_count(); }
376 
377 inline const DLTensor* NDArray::operator->() const { return &(get_mutable()->dl_tensor); }
378 
380  return static_cast<NDArray::Container*>(data_.get());
381 }
382 
384  return GetObjectPtr<Object>(
385  static_cast<NDArray::Container*>(reinterpret_cast<NDArray::ContainerBase*>(handle)));
386 }
387 
389  // NOTE: it is necessary to cast to container then to base
390  // so that the FFI handle uses the ContainerBase address.
391  auto ptr = reinterpret_cast<TVMArrayHandle>(static_cast<NDArray::ContainerBase*>(
392  static_cast<NDArray::Container*>(const_cast<Object*>(nd.get()))));
393  return ptr;
394 }
395 
396 inline void NDArray::FFIDecRef(TVMArrayHandle handle) {
397  static_cast<NDArray::Container*>(reinterpret_cast<NDArray::ContainerBase*>(handle))->DecRef();
398 }
399 
401  return static_cast<NDArray::Container*>(reinterpret_cast<NDArray::ContainerBase*>(handle));
402 }
403 
405 constexpr uint64_t kTVMNDArrayMagic = 0xDD5E40F096B4A13F;
406 
407 inline bool SaveDLTensor(dmlc::Stream* strm, const DLTensor* tensor) {
408  uint64_t header = kTVMNDArrayMagic, reserved = 0;
409  strm->Write(header);
410  strm->Write(reserved);
411  // Always save data as CPU context
412  //
413  // Parameters that get serialized should be in CPU by default.
414  // So even the array's context is GPU, it will be stored as CPU array.
415  // This is used to prevent case when another user loads the parameters
416  // back on machine that do not have GPU or related context.
417  //
418  // We can always do array.CopyTo(target_dev) to get a corresponding
419  // array in the target context.
420  Device cpu_dev;
421  cpu_dev.device_type = kDLCPU;
422  cpu_dev.device_id = 0;
423  strm->Write(cpu_dev);
424  strm->Write(tensor->ndim);
425  strm->Write(tensor->dtype);
426  int ndim = tensor->ndim;
427  strm->WriteArray(tensor->shape, ndim);
428  int type_bytes = (tensor->dtype.bits + 7) / 8;
429  int64_t num_elems = 1;
430  for (int i = 0; i < ndim; ++i) {
431  num_elems *= tensor->shape[i];
432  }
433  int64_t data_byte_size = type_bytes * num_elems;
434  strm->Write(data_byte_size);
435 
436  if (DMLC_IO_NO_ENDIAN_SWAP && tensor->device.device_type == kDLCPU &&
437  tensor->strides == nullptr && tensor->byte_offset == 0) {
438  // quick path
439  strm->Write(tensor->data, data_byte_size);
440  } else {
441  std::vector<uint8_t> bytes(data_byte_size);
442  ICHECK_EQ(
443  TVMArrayCopyToBytes(const_cast<DLTensor*>(tensor), dmlc::BeginPtr(bytes), data_byte_size),
444  0)
445  << TVMGetLastError();
446  if (!DMLC_IO_NO_ENDIAN_SWAP) {
447  dmlc::ByteSwap(dmlc::BeginPtr(bytes), type_bytes, num_elems);
448  }
449  strm->Write(dmlc::BeginPtr(bytes), data_byte_size);
450  }
451  return true;
452 }
453 
454 inline void NDArray::Save(dmlc::Stream* strm) const { SaveDLTensor(strm, operator->()); }
455 
456 inline bool NDArray::Load(dmlc::Stream* strm) {
457  uint64_t header, reserved;
458  ICHECK(strm->Read(&header)) << "Invalid DLTensor file format";
459  ICHECK(strm->Read(&reserved)) << "Invalid DLTensor file format";
460  ICHECK(header == kTVMNDArrayMagic) << "Invalid DLTensor file format";
461  Device dev;
462  int ndim;
463  DLDataType dtype;
464  ICHECK(strm->Read(&dev)) << "Invalid DLTensor file format";
465  ICHECK(strm->Read(&ndim)) << "Invalid DLTensor file format";
466  ICHECK(strm->Read(&dtype)) << "Invalid DLTensor file format";
467  ICHECK_EQ(dev.device_type, kDLCPU) << "Invalid DLTensor device: can only save as CPU tensor";
468  std::vector<int64_t> shape(ndim);
469  if (ndim != 0) {
470  ICHECK(strm->ReadArray(&shape[0], ndim)) << "Invalid DLTensor file format";
471  }
472  NDArray ret = NDArray::Empty(ShapeTuple(shape), dtype, dev);
473  int64_t num_elems = 1;
474  int elem_bytes = (ret->dtype.bits + 7) / 8;
475  for (int i = 0; i < ret->ndim; ++i) {
476  num_elems *= ret->shape[i];
477  }
478  int64_t data_byte_size;
479  ICHECK(strm->Read(&data_byte_size)) << "Invalid DLTensor file format";
480  ICHECK(data_byte_size == num_elems * elem_bytes) << "Invalid DLTensor file format";
481  auto read_ret = strm->Read(ret->data, data_byte_size);
482  // Only check non-empty data
483  if (ndim > 0 && shape[0] != 0) {
484  ICHECK(read_ret) << "Invalid DLTensor file format";
485  }
486  if (!DMLC_IO_NO_ENDIAN_SWAP) {
487  dmlc::ByteSwap(ret->data, elem_bytes, num_elems);
488  }
489  *this = ret;
490  return true;
491 }
492 
493 } // namespace runtime
494 } // namespace tvm
495 
496 namespace std {
497 template <>
498 struct hash<tvm::Device> {
499  std::size_t operator()(const tvm::Device& dev) const {
500  return ((dev.device_id << 8) | dev.device_type);
501  }
502 };
503 
504 template <>
505 struct equal_to<tvm::Device> {
506  bool operator()(const tvm::Device& lhs, const tvm::Device& rhs) const {
507  return (lhs.device_type == rhs.device_type && lhs.device_id == rhs.device_id);
508  }
509 };
510 } // namespace std
511 
512 #endif // TVM_RUNTIME_NDARRAY_H_
The container base structure contains all the fields except for the Object header.
Definition: ndarray.h:231
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:734
static void FFIDecRef(TVMArrayHandle handle)
DecRef resource managed by an FFI array handle.
Definition: ndarray.h:396
runtime::DataType DataType() const
A custom smart pointer for Object.
Definition: object.h:356
Runtime Optional container types.
bool Load(dmlc::Stream *stream)
Load NDArray from stream.
Definition: ndarray.h:456
Runtime String container types.
Internal base class to handle conversion to POD values.
Definition: packed_func.h:485
void CopyFrom(const DLTensor *other)
Copy data content from another array.
Definition: ndarray.h:345
Performance counters for profiling via the PAPI library.
Definition: analyzer.h:36
DLTensor * TVMArrayHandle
the array handle
Definition: c_runtime_api.h:138
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:792
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:356
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:165
bool IsContiguous() const
Definition: ndarray.h:341
Object * TVMArrayHandleToObjectHandle(TVMArrayHandle handle)
Definition: ndarray.h:400
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:172
void DecRef()
developer function, decrease reference counter.
Definition: object.h:794
static uint32_t RuntimeTypeIndex()
Definition: object.h:223
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:375
void Save(dmlc::Stream *stream) const
Save NDArray to stream.
Definition: ndarray.h:454
const DLTensor * operator->() const
Definition: ndarray.h:377
static ObjectPtr< Object > FFIDataFromHandle(TVMArrayHandle handle)
Construct NDArray&#39;s Data field from array handle in FFI.
Definition: ndarray.h:383
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:379
Object container class that backs NDArray.
Definition: ndarray.h:261
Container(void *data, ShapeTuple shape, DLDataType dtype, Device dev)
Definition: ndarray.h:274
bool IsContiguous(const DLTensor &arr)
check if a DLTensor is contiguous.
Definition: ndarray.h:330
ObjectPtr< Object > data_
Internal pointer that backs the reference.
Definition: object.h:567
DLTensor dl_tensor
The corresponding dl_tensor field.
Definition: ndarray.h:239
void SetDeleter(FDeleter deleter)
Set the deleter field.
Definition: ndarray.h:290
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:1608
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:539
Base class of all object reference.
Definition: object.h:504
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:388
Container()
default constructor
Definition: ndarray.h:264
constexpr uint64_t kTVMNDArrayMagic
Magic number for NDArray file.
Definition: ndarray.h:405
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:407
static NDArray FromDLPack(DLManagedTensor *tensor)
Create a NDArray backed by a dlpack tensor.
Definition: packed_func.h:1257
constexpr runtime::NullOptType NullOpt
Definition: optional.h:155
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:641
constexpr DLDeviceType kInvalidDeviceType
Definition: ndarray.h:51
ShapeTuple shape_
The shape container, can be used used for shape data.
Definition: ndarray.h:254
size_t GetDataSize(const DLTensor &arr)
return the size of data the DLTensor hold, in term of number of bytes
Definition: ndarray.h:316
Reference to shape tuple objects.
Definition: shape_tuple.h:81
runtime::NDArray.
Definition: object.h:64