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.
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.
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);
387  CopyFromTo(&(other.get_mutable()->dl_tensor), &(get_mutable()->dl_tensor));
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_
DLTensor * TVMArrayHandle
the array handle
Definition: c_runtime_api.h:202
const char * TVMGetLastError(void)
return str message of the last error all function in this file will return 0 when success and nonzero...
void * TVMStreamHandle
The stream that is specific to device can be NULL, which indicates the default one.
Definition: c_runtime_api.h:236
int64_t tvm_index_t
type of array index.
Definition: c_runtime_api.h:88
int TVMArrayCopyToBytes(TVMArrayHandle handle, void *data, size_t nbytes)
Copy array data to CPU byte array.
DataType dtype() const
Definition: expr.h:128
Runtime primitive data type.
Definition: data_type.h:42
int bits() const
Definition: data_type.h:89
The container base structure contains all the fields except for the Object header.
Definition: ndarray.h:256
DLTensor dl_tensor
The corresponding dl_tensor field.
Definition: ndarray.h:264
void * manager_ctx
additional context, reserved for recycling
Definition: ndarray.h:272
ShapeTuple shape_
The shape container, can be used used for shape data.
Definition: ndarray.h:279
Object container class that backs NDArray.
Definition: ndarray.h:286
static constexpr const uint32_t _type_index
Definition: ndarray.h:323
Container()
default constructor
Definition: ndarray.h:289
static constexpr const uint32_t _type_child_slots_can_overflow
Definition: ndarray.h:325
void SetDeleter(FDeleter deleter)
Set the deleter field.
Definition: ndarray.h:315
Container(void *data, ShapeTuple shape, DLDataType dtype, Device dev)
Definition: ndarray.h:299
static constexpr const char * _type_key
Definition: ndarray.h:326
static constexpr const uint32_t _type_child_slots
Definition: ndarray.h:324
friend class RPCWrappedFunc
Definition: ndarray.h:330
void DecRef()
developer function, decrease reference counter.
Definition: object.h:828
TVM_DECLARE_BASE_OBJECT_INFO(NDArray::Container, Object)
Managed NDArray. The array is backed by reference counted blocks.
Definition: ndarray.h:51
static TVMArrayHandle FFIGetHandle(const ObjectRef &nd)
Get FFI Array handle from ndarray.
Definition: ndarray.h:422
void CopyFrom(const DLTensor *other)
Copy data content from another array.
Definition: ndarray.h:379
bool IsContiguous() const
Definition: ndarray.h:375
static NDArray FromExternalDLTensor(const DLTensor &dl_tensor)
Create a NDArray backed by an external DLTensor without memory copying.
NDArray()
default constructor
Definition: ndarray.h:60
static NDArray Empty(ShapeTuple shape, DLDataType dtype, Device dev, Optional< String > mem_scope=NullOpt)
Create an empty NDArray.
static NDArray NewFromDLTensor(DLTensor *dl_tensor, const Device &dev)
Create new NDArray, data is copied from DLTensor.
void CopyTo(DLTensor *other) const
Copy data content into another array.
Definition: ndarray.h:390
runtime::DataType DataType() const
int use_count() const
Definition: ndarray.h:409
DLManagedTensor * ToDLPack() const
Create a reference view of NDArray that represents as DLManagedTensor.
static ObjectPtr< Object > FFIDataFromHandle(TVMArrayHandle handle)
Construct NDArray's Data field from array handle in FFI.
Definition: ndarray.h:417
void CopyToBytes(void *data, size_t nbytes) const
Copy data content into another array.
static NDArray FromDLPack(DLManagedTensor *tensor)
Create a NDArray backed by a dlpack tensor.
ShapeTuple Shape() const
const DLTensor * operator->() const
Definition: ndarray.h:411
Container * get_mutable() const
Get mutable internal container pointer.
Definition: ndarray.h:413
bool Load(dmlc::Stream *stream)
Load NDArray from stream.
Definition: ndarray.h:490
static void FFIDecRef(TVMArrayHandle handle)
DecRef resource managed by an FFI array handle.
Definition: ndarray.h:430
static bool AbilityOfZeroCopyForDLTensor(DLTensor *tensor, const Device &dev)
Check conditions for construction NDArray over DLTensor without copying. There are three conditions t...
NDArray CreateView(ShapeTuple shape, DLDataType dtype)
Create a NDArray that shares the data memory with the current one.
static void CopyFromTo(const DLTensor *from, DLTensor *to, TVMStreamHandle stream=nullptr)
Function to copy data from one array to another.
void Save(dmlc::Stream *stream) const
Save NDArray to stream.
Definition: ndarray.h:488
void reset()
reset the content of NDArray to be nullptr
void CopyFromBytes(const void *data, size_t nbytes)
Copy data content from a byte buffer.
NDArray(ObjectPtr< Object > data)
constructor.
Definition: ndarray.h:65
A custom smart pointer for Object.
Definition: object.h:358
Base class of all object reference.
Definition: object.h:515
const Object * get() const
Definition: object.h:550
ObjectPtr< Object > data_
Internal pointer that backs the reference.
Definition: object.h:601
base class of all object containers.
Definition: object.h:167
uint32_t type_index_
Type index(tag) that indicates the type of the object.
Definition: object.h:261
void DecRef()
developer function, decrease reference counter.
Definition: object.h:828
void(* FDeleter)(Object *self)
Object deleter.
Definition: object.h:173
void IncRef()
developer function, increases reference counter.
Definition: object.h:826
static uint32_t RuntimeTypeIndex()
Definition: object.h:225
FDeleter deleter_
deleter of this object to enable customized allocation. If the deleter is nullptr,...
Definition: object.h:269
Optional container that to represent to a Nullable variant of T.
Definition: optional.h:51
Reference to shape tuple objects.
Definition: shape_tuple.h:81
const index_type * data() const
Return the data pointer.
Definition: shape_tuple.h:121
size_t size() const
Return the size of the shape tuple.
Definition: shape_tuple.h:128
ShapeTupleObj::index_type index_type
The type of shape index element.
Definition: shape_tuple.h:84
Definition: packed_func.h:1517
Internal base class to handle conversion to POD values.
Definition: packed_func.h:541
Return Value container, Unlike TVMArgValue, which only holds reference and do not delete the underlyi...
Definition: packed_func.h:799
size_t GetDataSize(const DLTensor &arr)
return the size of data the DLTensor hold, in term of number of bytes
Definition: ndarray.h:341
bool SaveDLTensor(dmlc::Stream *strm, const DLTensor *tensor)
Save a DLTensor to stream.
Definition: ndarray.h:441
constexpr uint64_t kTVMNDArrayMagic
Magic number for NDArray file.
Definition: ndarray.h:439
Object * TVMArrayHandleToObjectHandle(TVMArrayHandle handle)
Definition: ndarray.h:434
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:1763
runtime implementation for LibTorch/TorchScript.
Definition: analyzer.h:36
PrimExpr ret(PrimExpr value, Span span=Span())
Return the value.
DLDevice Device
Definition: ndarray.h:43
constexpr runtime::NullOptType NullOpt
Definition: optional.h:169
A managed object in the TVM runtime.
Runtime Optional container types.
Serializer extension to support TVM data types Include this file to enable serialization of DLDataTyp...
Runtime ShapeTuple container types.
Runtime String container types.
@ kRuntimeNDArray
runtime::NDArray.
Definition: object.h:64