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;
116  inline NDArray CopyTo(const Device& dev, Optional<String> mem_scope = NullOpt) const;
122  inline bool Load(dmlc::Stream* stream);
127  inline void Save(dmlc::Stream* stream) const;
134  TVM_DLL NDArray CreateView(ShapeTuple shape, DLDataType dtype);
140  TVM_DLL DLManagedTensor* ToDLPack() const;
149  TVM_DLL static NDArray Empty(ShapeTuple shape, DLDataType dtype, Device dev,
150  Optional<String> mem_scope = NullOpt);
161  TVM_DLL static NDArray FromExternalDLTensor(const DLTensor& dl_tensor);
169  TVM_DLL static NDArray NewFromDLTensor(DLTensor* dl_tensor, const Device& dev);
181  TVM_DLL static NDArray FromDLPack(DLManagedTensor* tensor);
188  TVM_DLL static void CopyFromTo(const DLTensor* from, DLTensor* to,
189  TVMStreamHandle stream = nullptr);
190 
191  TVM_DLL ShapeTuple Shape() const;
192  TVM_DLL runtime::DataType DataType() const;
203  TVM_DLL static bool AbilityOfZeroCopyForDLTensor(DLTensor* tensor, const Device& dev);
204  // internal namespace
205  struct Internal;
206 
207  private:
208  TVM_DLL static bool IsAligned(const DLTensor& tensor);
209 
210  protected:
211  friend class TVMPODValue_;
212  friend class TVMRetValue;
213  friend class TVMArgsSetter;
218  inline Container* get_mutable() const;
219  // Helper functions for FFI handling.
229  inline static ObjectPtr<Object> FFIDataFromHandle(TVMArrayHandle handle);
234  inline static void FFIDecRef(TVMArrayHandle handle);
240  inline static TVMArrayHandle FFIGetHandle(const ObjectRef& nd);
241 };
242 
248 inline bool SaveDLTensor(dmlc::Stream* strm, const DLTensor* tensor);
249 
258  public:
265  DLTensor dl_tensor;
266 
273  void* manager_ctx{nullptr};
274 
275  protected:
281 };
282 
288  public:
291  // Initialize the type index.
293  dl_tensor.data = nullptr;
294  dl_tensor.ndim = 0;
295  dl_tensor.shape = nullptr;
296  dl_tensor.strides = nullptr;
297  dl_tensor.byte_offset = 0;
298  }
299 
300  Container(void* data, ShapeTuple shape, DLDataType dtype, Device dev) {
301  // Initialize the type index.
303  dl_tensor.data = data;
304  shape_ = std::move(shape);
305  dl_tensor.ndim = static_cast<int>(shape_.size());
306  dl_tensor.shape = const_cast<ShapeTuple::index_type*>(shape_.data());
307  dl_tensor.dtype = dtype;
308  dl_tensor.strides = nullptr;
309  dl_tensor.byte_offset = 0;
310  dl_tensor.device = dev;
311  }
316  void SetDeleter(FDeleter deleter) { deleter_ = deleter; }
317 
318  // Expose DecRef and IncRef as public function
319  // NOTE: they are only for developer purposes only.
320  using Object::DecRef;
321  using Object::IncRef;
322 
323  // Information for object protocol.
324  static constexpr const uint32_t _type_index = TypeIndex::kRuntimeNDArray;
325  static constexpr const uint32_t _type_child_slots = 0;
326  static constexpr const uint32_t _type_child_slots_can_overflow = true;
327  static constexpr const char* _type_key = "runtime.NDArray";
329 
330  protected:
331  friend class RPCWrappedFunc;
332  friend class NDArray;
333 };
334 
335 // implementations of inline functions
342 inline size_t GetDataSize(const DLTensor& arr) {
343  size_t size = 1;
344  for (tvm_index_t i = 0; i < arr.ndim; ++i) {
345  size *= static_cast<size_t>(arr.shape[i]);
346  }
347  size *= (arr.dtype.bits * arr.dtype.lanes + 7) / 8;
348  return size;
349 }
350 
356 static inline bool IsContiguous(const DLTensor& arr) {
357  if (arr.strides == nullptr) return true;
358  int64_t expected_stride = 1;
359  for (int32_t i = arr.ndim; i != 0; --i) {
360  int32_t k = i - 1;
361  if (arr.shape[k] == 1) {
362  // Skip stride check if shape[k] is 1, where the dimension is contiguous
363  // regardless of the value of stride.
364  //
365  // For example, PyTorch will normalize stride to 1 if shape is 1 when exporting
366  // to DLPack.
367  // More context: https://github.com/pytorch/pytorch/pull/83158
368  continue;
369  }
370  if (arr.strides[k] != expected_stride) return false;
371  expected_stride *= arr.shape[k];
372  }
373  return true;
374 }
375 
376 inline bool NDArray::IsContiguous() const {
377  return ::tvm::runtime::IsContiguous(get_mutable()->dl_tensor);
378 }
379 
380 inline void NDArray::CopyFrom(const DLTensor* other) {
381  ICHECK(data_ != nullptr);
382  CopyFromTo(other, &(get_mutable()->dl_tensor));
383 }
384 
385 inline void NDArray::CopyFrom(const NDArray& other) {
386  ICHECK(data_ != nullptr);
387  ICHECK(other.data_ != nullptr);
388  CopyFromTo(&(other.get_mutable()->dl_tensor), &(get_mutable()->dl_tensor));
389 }
390 
391 inline void NDArray::CopyTo(DLTensor* other) const {
392  ICHECK(data_ != nullptr);
393  CopyFromTo(&(get_mutable()->dl_tensor), other);
394 }
395 
396 inline void NDArray::CopyTo(const NDArray& other) const {
397  ICHECK(data_ != nullptr);
398  ICHECK(other.data_ != nullptr);
399  CopyFromTo(&(get_mutable()->dl_tensor), &(other.get_mutable()->dl_tensor));
400 }
401 
402 inline NDArray NDArray::CopyTo(const Device& dev, Optional<String> mem_scope) const {
403  ICHECK(data_ != nullptr);
404  const DLTensor* dptr = operator->();
405  NDArray ret =
406  Empty(ShapeTuple(dptr->shape, dptr->shape + dptr->ndim), dptr->dtype, dev, mem_scope);
407  this->CopyTo(ret);
408  return ret;
409 }
410 
411 inline int NDArray::use_count() const { return data_.use_count(); }
412 
413 inline const DLTensor* NDArray::operator->() const { return &(get_mutable()->dl_tensor); }
414 
416  return static_cast<NDArray::Container*>(data_.get());
417 }
418 
420  return GetObjectPtr<Object>(
421  static_cast<NDArray::Container*>(reinterpret_cast<NDArray::ContainerBase*>(handle)));
422 }
423 
425  // NOTE: it is necessary to cast to container then to base
426  // so that the FFI handle uses the ContainerBase address.
427  auto ptr = reinterpret_cast<TVMArrayHandle>(static_cast<NDArray::ContainerBase*>(
428  static_cast<NDArray::Container*>(const_cast<Object*>(nd.get()))));
429  return ptr;
430 }
431 
432 inline void NDArray::FFIDecRef(TVMArrayHandle handle) {
433  static_cast<NDArray::Container*>(reinterpret_cast<NDArray::ContainerBase*>(handle))->DecRef();
434 }
435 
437  return static_cast<NDArray::Container*>(reinterpret_cast<NDArray::ContainerBase*>(handle));
438 }
439 
441 constexpr uint64_t kTVMNDArrayMagic = 0xDD5E40F096B4A13F;
442 
443 inline bool SaveDLTensor(dmlc::Stream* strm, const DLTensor* tensor) {
444  uint64_t header = kTVMNDArrayMagic, reserved = 0;
445  strm->Write(header);
446  strm->Write(reserved);
447  // Always save data as CPU context
448  //
449  // Parameters that get serialized should be in CPU by default.
450  // So even the array's context is GPU, it will be stored as CPU array.
451  // This is used to prevent case when another user loads the parameters
452  // back on machine that do not have GPU or related context.
453  //
454  // We can always do array.CopyTo(target_dev) to get a corresponding
455  // array in the target context.
456  Device cpu_dev;
457  cpu_dev.device_type = kDLCPU;
458  cpu_dev.device_id = 0;
459  strm->Write(cpu_dev);
460  strm->Write(tensor->ndim);
461  strm->Write(tensor->dtype);
462  int ndim = tensor->ndim;
463  strm->WriteArray(tensor->shape, ndim);
464  int type_bytes = (tensor->dtype.bits + 7) / 8;
465  int64_t num_elems = 1;
466  for (int i = 0; i < ndim; ++i) {
467  num_elems *= tensor->shape[i];
468  }
469  int64_t data_byte_size = type_bytes * num_elems;
470  strm->Write(data_byte_size);
471 
472  if (DMLC_IO_NO_ENDIAN_SWAP && tensor->device.device_type == kDLCPU &&
473  tensor->strides == nullptr && tensor->byte_offset == 0) {
474  // quick path
475  strm->Write(tensor->data, data_byte_size);
476  } else {
477  std::vector<uint8_t> bytes(data_byte_size);
478  ICHECK_EQ(
479  TVMArrayCopyToBytes(const_cast<DLTensor*>(tensor), dmlc::BeginPtr(bytes), data_byte_size),
480  0)
481  << TVMGetLastError();
482  if (!DMLC_IO_NO_ENDIAN_SWAP) {
483  dmlc::ByteSwap(dmlc::BeginPtr(bytes), type_bytes, num_elems);
484  }
485  strm->Write(dmlc::BeginPtr(bytes), data_byte_size);
486  }
487  return true;
488 }
489 
490 inline void NDArray::Save(dmlc::Stream* strm) const { SaveDLTensor(strm, operator->()); }
491 
492 inline bool NDArray::Load(dmlc::Stream* strm) {
493  uint64_t header, reserved;
494  ICHECK(strm->Read(&header)) << "Invalid DLTensor file format";
495  ICHECK(strm->Read(&reserved)) << "Invalid DLTensor file format";
496  ICHECK(header == kTVMNDArrayMagic) << "Invalid DLTensor file format";
497  Device dev;
498  int ndim;
499  DLDataType dtype;
500  ICHECK(strm->Read(&dev)) << "Invalid DLTensor file format";
501  ICHECK(strm->Read(&ndim)) << "Invalid DLTensor file format";
502  ICHECK(strm->Read(&dtype)) << "Invalid DLTensor file format";
503  ICHECK_EQ(dev.device_type, kDLCPU) << "Invalid DLTensor device: can only save as CPU tensor";
504  std::vector<int64_t> shape(ndim);
505  if (ndim != 0) {
506  ICHECK(strm->ReadArray(&shape[0], ndim)) << "Invalid DLTensor file format";
507  }
508  NDArray ret = NDArray::Empty(ShapeTuple(shape), dtype, dev);
509  int64_t num_elems = 1;
510  int elem_bytes = (ret->dtype.bits + 7) / 8;
511  for (int i = 0; i < ret->ndim; ++i) {
512  num_elems *= ret->shape[i];
513  }
514  int64_t data_byte_size;
515  ICHECK(strm->Read(&data_byte_size)) << "Invalid DLTensor file format";
516  ICHECK(data_byte_size == num_elems * elem_bytes) << "Invalid DLTensor file format";
517  auto read_ret = strm->Read(ret->data, data_byte_size);
518  // Only check non-empty data
519  if (ndim > 0 && shape[0] != 0) {
520  ICHECK(read_ret) << "Invalid DLTensor file format";
521  }
522  if (!DMLC_IO_NO_ENDIAN_SWAP) {
523  dmlc::ByteSwap(ret->data, elem_bytes, num_elems);
524  }
525  *this = ret;
526  return true;
527 }
528 
529 } // namespace runtime
530 } // namespace tvm
531 
532 namespace std {
533 template <>
534 struct hash<tvm::Device> {
535  std::size_t operator()(const tvm::Device& dev) const {
536  return ((dev.device_id << 8) | dev.device_type);
537  }
538 };
539 
540 template <>
541 struct equal_to<tvm::Device> {
542  bool operator()(const tvm::Device& lhs, const tvm::Device& rhs) const {
543  return (lhs.device_type == rhs.device_type && lhs.device_id == rhs.device_id);
544  }
545 };
546 } // namespace std
547 
548 #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:257
DLTensor dl_tensor
The corresponding dl_tensor field.
Definition: ndarray.h:265
void * manager_ctx
additional context, reserved for recycling
Definition: ndarray.h:273
ShapeTuple shape_
The shape container, can be used for shape data.
Definition: ndarray.h:280
Object container class that backs NDArray.
Definition: ndarray.h:287
static constexpr const uint32_t _type_index
Definition: ndarray.h:324
Container()
default constructor
Definition: ndarray.h:290
static constexpr const uint32_t _type_child_slots_can_overflow
Definition: ndarray.h:326
void SetDeleter(FDeleter deleter)
Set the deleter field.
Definition: ndarray.h:316
Container(void *data, ShapeTuple shape, DLDataType dtype, Device dev)
Definition: ndarray.h:300
static constexpr const char * _type_key
Definition: ndarray.h:327
static constexpr const uint32_t _type_child_slots
Definition: ndarray.h:325
friend class RPCWrappedFunc
Definition: ndarray.h:331
void DecRef()
developer function, decrease reference counter.
Definition: object.h:830
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:424
void CopyFrom(const DLTensor *other)
Copy data content from another array.
Definition: ndarray.h:380
bool IsContiguous() const
Definition: ndarray.h:376
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:391
runtime::DataType DataType() const
int use_count() const
Definition: ndarray.h:411
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:419
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:413
Container * get_mutable() const
Get mutable internal container pointer.
Definition: ndarray.h:415
bool Load(dmlc::Stream *stream)
Load NDArray from stream.
Definition: ndarray.h:492
static void FFIDecRef(TVMArrayHandle handle)
DecRef resource managed by an FFI array handle.
Definition: ndarray.h:432
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:490
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:360
Base class of all object reference.
Definition: object.h:517
const Object * get() const
Definition: object.h:552
ObjectPtr< Object > data_
Internal pointer that backs the reference.
Definition: object.h:603
base class of all object containers.
Definition: object.h:169
uint32_t type_index_
Type index(tag) that indicates the type of the object.
Definition: object.h:263
void DecRef()
developer function, decrease reference counter.
Definition: object.h:830
void(* FDeleter)(Object *self)
Object deleter.
Definition: object.h:175
void IncRef()
developer function, increases reference counter.
Definition: object.h:828
static uint32_t RuntimeTypeIndex()
Definition: object.h:227
FDeleter deleter_
deleter of this object to enable customized allocation. If the deleter is nullptr,...
Definition: object.h:271
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:1666
Internal base class to handle conversion to POD values.
Definition: packed_func.h:544
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:342
bool SaveDLTensor(dmlc::Stream *strm, const DLTensor *tensor)
Save a DLTensor to stream.
Definition: ndarray.h:443
constexpr uint64_t kTVMNDArrayMagic
Magic number for NDArray file.
Definition: ndarray.h:441
Object * TVMArrayHandleToObjectHandle(TVMArrayHandle handle)
Definition: ndarray.h:436
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:1766
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