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 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;
117  TVM_DLL NDArray CopyTo(const Device& dev, Optional<String> mem_scope = NullOpt) const;
123  inline bool Load(dmlc::Stream* stream);
128  inline void Save(dmlc::Stream* stream) const;
129 
150  TVM_DLL NDArray CreateView(ShapeTuple shape, DLDataType dtype, uint64_t relative_byte_offset = 0);
151 
157  TVM_DLL DLManagedTensor* ToDLPack() const;
166  TVM_DLL static NDArray Empty(ShapeTuple shape, DLDataType dtype, Device dev,
167  Optional<String> mem_scope = NullOpt);
178  TVM_DLL static NDArray FromExternalDLTensor(const DLTensor& dl_tensor);
186  TVM_DLL static NDArray NewFromDLTensor(DLTensor* dl_tensor, const Device& dev);
198  TVM_DLL static NDArray FromDLPack(DLManagedTensor* tensor);
205  TVM_DLL static void CopyFromTo(const DLTensor* from, DLTensor* to,
206  TVMStreamHandle stream = nullptr);
207 
208  TVM_DLL ShapeTuple Shape() const;
209  TVM_DLL runtime::DataType DataType() const;
220  TVM_DLL static bool AbilityOfZeroCopyForDLTensor(DLTensor* tensor, const Device& dev);
221  // internal namespace
222  struct Internal;
223 
224  private:
225  TVM_DLL static bool IsAligned(const DLTensor& tensor);
226 
227  protected:
228  friend class TVMPODValue_;
229  friend class TVMRetValue;
230  friend class TVMArgsSetter;
235  inline Container* get_mutable() const;
236  // Helper functions for FFI handling.
246  inline static ObjectPtr<Object> FFIDataFromHandle(TVMArrayHandle handle);
251  inline static void FFIDecRef(TVMArrayHandle handle);
257  inline static TVMArrayHandle FFIGetHandle(const ObjectRef& nd);
258 };
259 
265 inline bool SaveDLTensor(dmlc::Stream* strm, const DLTensor* tensor);
266 
275  public:
282  DLTensor dl_tensor;
283 
290  void* manager_ctx{nullptr};
291 
292  protected:
298 };
299 
305  public:
308  // Initialize the type index.
310  dl_tensor.data = nullptr;
311  dl_tensor.ndim = 0;
312  dl_tensor.shape = nullptr;
313  dl_tensor.strides = nullptr;
314  dl_tensor.byte_offset = 0;
315  }
316 
317  Container(void* data, ShapeTuple shape, DLDataType dtype, Device dev) {
318  // Initialize the type index.
320  dl_tensor.data = data;
321  shape_ = std::move(shape);
322  dl_tensor.ndim = static_cast<int>(shape_.size());
323  dl_tensor.shape = const_cast<ShapeTuple::index_type*>(shape_.data());
324  dl_tensor.dtype = dtype;
325  dl_tensor.strides = nullptr;
326  dl_tensor.byte_offset = 0;
327  dl_tensor.device = dev;
328  }
333  void SetDeleter(FDeleter deleter) { deleter_ = deleter; }
334 
335  // Expose DecRef and IncRef as public function
336  // NOTE: they are only for developer purposes only.
337  using Object::DecRef;
338  using Object::IncRef;
339 
340  // Information for object protocol.
341  static constexpr const uint32_t _type_index = TypeIndex::kRuntimeNDArray;
342  static constexpr const uint32_t _type_child_slots = 0;
343  static constexpr const uint32_t _type_child_slots_can_overflow = true;
344  static constexpr const char* _type_key = "runtime.NDArray";
346 
347  protected:
348  friend class RPCWrappedFunc;
349  friend class NDArray;
350 };
351 
352 // implementations of inline functions
359 inline size_t GetDataSize(const DLTensor& arr) {
360  size_t size = 1;
361  for (tvm_index_t i = 0; i < arr.ndim; ++i) {
362  size *= static_cast<size_t>(arr.shape[i]);
363  }
364  size *= (arr.dtype.bits * arr.dtype.lanes + 7) / 8;
365  return size;
366 }
367 
373 static inline bool IsContiguous(const DLTensor& arr) {
374  if (arr.strides == nullptr) return true;
375  int64_t expected_stride = 1;
376  for (int32_t i = arr.ndim; i != 0; --i) {
377  int32_t k = i - 1;
378  if (arr.shape[k] == 1) {
379  // Skip stride check if shape[k] is 1, where the dimension is contiguous
380  // regardless of the value of stride.
381  //
382  // For example, PyTorch will normalize stride to 1 if shape is 1 when exporting
383  // to DLPack.
384  // More context: https://github.com/pytorch/pytorch/pull/83158
385  continue;
386  }
387  if (arr.strides[k] != expected_stride) return false;
388  expected_stride *= arr.shape[k];
389  }
390  return true;
391 }
392 
393 inline bool NDArray::IsContiguous() const {
394  return ::tvm::runtime::IsContiguous(get_mutable()->dl_tensor);
395 }
396 
397 inline void NDArray::CopyFrom(const DLTensor* other) {
398  ICHECK(data_ != nullptr);
399  CopyFromTo(other, &(get_mutable()->dl_tensor));
400 }
401 
402 inline void NDArray::CopyFrom(const NDArray& other) {
403  ICHECK(data_ != nullptr);
404  ICHECK(other.data_ != nullptr);
405  CopyFromTo(&(other.get_mutable()->dl_tensor), &(get_mutable()->dl_tensor));
406 }
407 
408 inline void NDArray::CopyTo(DLTensor* other) const {
409  ICHECK(data_ != nullptr);
410  CopyFromTo(&(get_mutable()->dl_tensor), other);
411 }
412 
413 inline void NDArray::CopyTo(const NDArray& other) const {
414  ICHECK(data_ != nullptr);
415  ICHECK(other.data_ != nullptr);
416  CopyFromTo(&(get_mutable()->dl_tensor), &(other.get_mutable()->dl_tensor));
417 }
418 
419 inline int NDArray::use_count() const { return data_.use_count(); }
420 
421 inline const DLTensor* NDArray::operator->() const { return &(get_mutable()->dl_tensor); }
422 
424  return static_cast<NDArray::Container*>(data_.get());
425 }
426 
428  return GetObjectPtr<Object>(
429  static_cast<NDArray::Container*>(reinterpret_cast<NDArray::ContainerBase*>(handle)));
430 }
431 
433  // NOTE: it is necessary to cast to container then to base
434  // so that the FFI handle uses the ContainerBase address.
435  auto ptr = reinterpret_cast<TVMArrayHandle>(static_cast<NDArray::ContainerBase*>(
436  static_cast<NDArray::Container*>(const_cast<Object*>(nd.get()))));
437  return ptr;
438 }
439 
440 inline void NDArray::FFIDecRef(TVMArrayHandle handle) {
441  static_cast<NDArray::Container*>(reinterpret_cast<NDArray::ContainerBase*>(handle))->DecRef();
442 }
443 
445  return static_cast<NDArray::Container*>(reinterpret_cast<NDArray::ContainerBase*>(handle));
446 }
447 
449 constexpr uint64_t kTVMNDArrayMagic = 0xDD5E40F096B4A13F;
450 
451 inline bool SaveDLTensor(dmlc::Stream* strm, const DLTensor* tensor) {
452  uint64_t header = kTVMNDArrayMagic, reserved = 0;
453  strm->Write(header);
454  strm->Write(reserved);
455  // Always save data as CPU context
456  //
457  // Parameters that get serialized should be in CPU by default.
458  // So even the array's context is GPU, it will be stored as CPU array.
459  // This is used to prevent case when another user loads the parameters
460  // back on machine that do not have GPU or related context.
461  //
462  // We can always do array.CopyTo(target_dev) to get a corresponding
463  // array in the target context.
464  Device cpu_dev;
465  cpu_dev.device_type = kDLCPU;
466  cpu_dev.device_id = 0;
467  strm->Write(cpu_dev);
468  strm->Write(tensor->ndim);
469  strm->Write(tensor->dtype);
470  int ndim = tensor->ndim;
471  strm->WriteArray(tensor->shape, ndim);
472  int type_bytes = (tensor->dtype.bits + 7) / 8;
473  int64_t num_elems = 1;
474  for (int i = 0; i < ndim; ++i) {
475  num_elems *= tensor->shape[i];
476  }
477  int64_t data_byte_size = type_bytes * num_elems;
478  strm->Write(data_byte_size);
479 
480  if (DMLC_IO_NO_ENDIAN_SWAP && tensor->device.device_type == kDLCPU &&
481  tensor->strides == nullptr && tensor->byte_offset == 0) {
482  // quick path
483  strm->Write(tensor->data, data_byte_size);
484  } else {
485  std::vector<uint8_t> bytes(data_byte_size);
486  ICHECK_EQ(
487  TVMArrayCopyToBytes(const_cast<DLTensor*>(tensor), dmlc::BeginPtr(bytes), data_byte_size),
488  0)
489  << TVMGetLastError();
490  if (!DMLC_IO_NO_ENDIAN_SWAP) {
491  dmlc::ByteSwap(dmlc::BeginPtr(bytes), type_bytes, num_elems);
492  }
493  strm->Write(dmlc::BeginPtr(bytes), data_byte_size);
494  }
495  return true;
496 }
497 
498 inline void NDArray::Save(dmlc::Stream* strm) const { SaveDLTensor(strm, operator->()); }
499 
500 inline bool NDArray::Load(dmlc::Stream* strm) {
501  uint64_t header, reserved;
502  ICHECK(strm->Read(&header)) << "Invalid DLTensor file format";
503  ICHECK(strm->Read(&reserved)) << "Invalid DLTensor file format";
504  ICHECK(header == kTVMNDArrayMagic) << "Invalid DLTensor file format";
505  Device dev;
506  int ndim;
507  DLDataType dtype;
508  ICHECK(strm->Read(&dev)) << "Invalid DLTensor file format";
509  ICHECK(strm->Read(&ndim)) << "Invalid DLTensor file format";
510  ICHECK(strm->Read(&dtype)) << "Invalid DLTensor file format";
511  ICHECK_EQ(dev.device_type, kDLCPU) << "Invalid DLTensor device: can only save as CPU tensor";
512  std::vector<int64_t> shape(ndim);
513  if (ndim != 0) {
514  ICHECK(strm->ReadArray(&shape[0], ndim)) << "Invalid DLTensor file format";
515  }
516  NDArray ret = NDArray::Empty(ShapeTuple(shape), dtype, dev);
517  int64_t num_elems = 1;
518  int elem_bytes = (ret->dtype.bits + 7) / 8;
519  for (int i = 0; i < ret->ndim; ++i) {
520  num_elems *= ret->shape[i];
521  }
522  int64_t data_byte_size;
523  ICHECK(strm->Read(&data_byte_size)) << "Invalid DLTensor file format";
524  ICHECK(data_byte_size == num_elems * elem_bytes) << "Invalid DLTensor file format";
525  auto read_ret = strm->Read(ret->data, data_byte_size);
526  // Only check non-empty data
527  if (ndim > 0 && shape[0] != 0) {
528  ICHECK(read_ret) << "Invalid DLTensor file format";
529  }
530  if (!DMLC_IO_NO_ENDIAN_SWAP) {
531  dmlc::ByteSwap(ret->data, elem_bytes, num_elems);
532  }
533  *this = ret;
534  return true;
535 }
536 
537 } // namespace runtime
538 } // namespace tvm
539 
540 namespace std {
541 template <>
542 struct hash<tvm::Device> {
543  std::size_t operator()(const tvm::Device& dev) const {
544  return ((dev.device_id << 8) | dev.device_type);
545  }
546 };
547 
548 template <>
549 struct equal_to<tvm::Device> {
550  bool operator()(const tvm::Device& lhs, const tvm::Device& rhs) const {
551  return (lhs.device_type == rhs.device_type && lhs.device_id == rhs.device_id);
552  }
553 };
554 } // namespace std
555 
556 #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:129
Runtime primitive data type.
Definition: data_type.h:43
int bits() const
Definition: data_type.h:94
The container base structure contains all the fields except for the Object header.
Definition: ndarray.h:274
DLTensor dl_tensor
The corresponding dl_tensor field.
Definition: ndarray.h:282
void * manager_ctx
additional context, reserved for recycling
Definition: ndarray.h:290
ShapeTuple shape_
The shape container, can be used for shape data.
Definition: ndarray.h:297
Object container class that backs NDArray.
Definition: ndarray.h:304
static constexpr const uint32_t _type_index
Definition: ndarray.h:341
Container()
default constructor
Definition: ndarray.h:307
static constexpr const uint32_t _type_child_slots_can_overflow
Definition: ndarray.h:343
void SetDeleter(FDeleter deleter)
Set the deleter field.
Definition: ndarray.h:333
Container(void *data, ShapeTuple shape, DLDataType dtype, Device dev)
Definition: ndarray.h:317
static constexpr const char * _type_key
Definition: ndarray.h:344
static constexpr const uint32_t _type_child_slots
Definition: ndarray.h:342
friend class RPCWrappedFunc
Definition: ndarray.h:348
void DecRef()
developer function, decrease reference counter.
Definition: object.h:842
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:432
void CopyFrom(const DLTensor *other)
Copy data content from another array.
Definition: ndarray.h:397
bool IsContiguous() const
Definition: ndarray.h:393
NDArray CreateView(ShapeTuple shape, DLDataType dtype, uint64_t relative_byte_offset=0)
Create a NDArray that shares the data memory with the current one.
static NDArray FromExternalDLTensor(const DLTensor &dl_tensor)
Create a NDArray backed by an external DLTensor without memory copying.
NDArray CopyTo(const Device &dev, Optional< String > mem_scope=NullOpt) const
Copy the data to another device.
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:408
runtime::DataType DataType() const
int use_count() const
Definition: ndarray.h:419
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:427
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:421
Container * get_mutable() const
Get mutable internal container pointer.
Definition: ndarray.h:423
bool Load(dmlc::Stream *stream)
Load NDArray from stream.
Definition: ndarray.h:500
static void FFIDecRef(TVMArrayHandle handle)
DecRef resource managed by an FFI array handle.
Definition: ndarray.h:440
static bool AbilityOfZeroCopyForDLTensor(DLTensor *tensor, const Device &dev)
Check conditions for construction NDArray over DLTensor without copying. There are three conditions t...
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:498
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:362
Base class of all object reference.
Definition: object.h:519
const Object * get() const
Definition: object.h:554
ObjectPtr< Object > data_
Internal pointer that backs the reference.
Definition: object.h:605
base class of all object containers.
Definition: object.h:171
uint32_t type_index_
Type index(tag) that indicates the type of the object.
Definition: object.h:265
void DecRef()
developer function, decrease reference counter.
Definition: object.h:842
void(* FDeleter)(Object *self)
Object deleter.
Definition: object.h:177
void IncRef()
developer function, increases reference counter.
Definition: object.h:840
static uint32_t RuntimeTypeIndex()
Definition: object.h:229
FDeleter deleter_
deleter of this object to enable customized allocation. If the deleter is nullptr,...
Definition: object.h:273
Optional container that to represent to a Nullable variant of T.
Definition: optional.h:51
Reference to shape tuple objects.
Definition: shape_tuple.h:85
const index_type * data() const
Return the data pointer.
Definition: shape_tuple.h:125
size_t size() const
Return the size of the shape tuple.
Definition: shape_tuple.h:132
ShapeTupleObj::index_type index_type
The type of shape index element.
Definition: shape_tuple.h:88
Definition: packed_func.h:1680
Internal base class to handle conversion to POD values.
Definition: packed_func.h:552
Return Value container, Unlike TVMArgValue, which only holds reference and do not delete the underlyi...
Definition: packed_func.h:807
size_t GetDataSize(const DLTensor &arr)
return the size of data the DLTensor hold, in term of number of bytes
Definition: ndarray.h:359
bool SaveDLTensor(dmlc::Stream *strm, const DLTensor *tensor)
Save a DLTensor to stream.
Definition: ndarray.h:451
constexpr uint64_t kTVMNDArrayMagic
Magic number for NDArray file.
Definition: ndarray.h:449
Object * TVMArrayHandleToObjectHandle(TVMArrayHandle handle)
Definition: ndarray.h:444
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:1853
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