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 
27 #include <tvm/ffi/container/ndarray.h>
28 #include <tvm/ffi/container/shape.h>
29 #include <tvm/ffi/optional.h>
30 #include <tvm/ffi/string.h>
31 #include <tvm/runtime/base.h>
32 #include <tvm/runtime/data_type.h>
33 #include <tvm/runtime/device_api.h>
34 #include <tvm/runtime/object.h>
35 #include <tvm/runtime/serializer.h>
36 
37 #include <atomic>
38 #include <functional>
39 #include <utility>
40 #include <vector>
41 
42 namespace tvm {
43 namespace runtime {
44 
45 using ffi::GetDataSize;
46 using ffi::IsAligned;
47 using ffi::IsContiguous;
48 
53 class NDArray : public tvm::ffi::NDArray {
54  public:
55  using Container = ffi::NDArrayObj;
56  NDArray() = default;
61  explicit NDArray(ObjectPtr<Object> data) : tvm::ffi::NDArray(data) {}
62  NDArray(ffi::NDArray&& other) : tvm::ffi::NDArray(std::move(other)) {} // NOLINT(*)
63  NDArray(const ffi::NDArray& other) : tvm::ffi::NDArray(other) {} // NOLINT(*)
64 
65  ffi::Shape Shape() const { return this->shape(); }
66  runtime::DataType DataType() const { return runtime::DataType(this->dtype()); }
67 
68  // DLPack handling
69  static NDArray FromDLPack(DLManagedTensor* tensor) {
70  return tvm::ffi::NDArray::FromDLPack(tensor, kAllocAlignment, true);
71  }
72 
73  static NDArray FromDLPackVersioned(DLManagedTensorVersioned* tensor) {
74  return tvm::ffi::NDArray::FromDLPackVersioned(tensor, kAllocAlignment, true);
75  }
82  inline void CopyFrom(const DLTensor* other);
83  inline void CopyFrom(const NDArray& other);
91  TVM_DLL void CopyFromBytes(const void* data, size_t nbytes);
98  inline void CopyTo(DLTensor* other) const;
99  inline void CopyTo(const NDArray& other) const;
107  TVM_DLL void CopyToBytes(void* data, size_t nbytes) const;
115  TVM_DLL NDArray CopyTo(const Device& dev, Optional<String> mem_scope = std::nullopt) const;
121  inline bool Load(dmlc::Stream* stream);
126  inline void Save(dmlc::Stream* stream) const;
127 
148  TVM_DLL NDArray CreateView(ffi::Shape shape, DLDataType dtype,
149  uint64_t relative_byte_offset = 0) const;
158  TVM_DLL static NDArray Empty(ffi::Shape shape, DLDataType dtype, Device dev,
159  Optional<String> mem_scope = std::nullopt);
166  TVM_DLL static void CopyFromTo(const DLTensor* from, DLTensor* to,
167  TVMStreamHandle stream = nullptr);
168 
176  TVM_DLL static void CopyToBytes(const DLTensor* from, void* to, size_t nbytes,
177  TVMStreamHandle stream = nullptr);
178 };
179 
185 inline bool SaveDLTensor(dmlc::Stream* strm, const DLTensor* tensor);
186 
187 inline void NDArray::CopyFrom(const DLTensor* other) {
188  ICHECK(data_ != nullptr);
189  CopyFromTo(other, get_mutable());
190 }
191 
192 inline void NDArray::CopyFrom(const NDArray& other) {
193  ICHECK(data_ != nullptr);
194  ICHECK(other.data_ != nullptr);
195  CopyFromTo(other.get_mutable(), get_mutable());
196 }
197 
198 inline void NDArray::CopyTo(DLTensor* other) const {
199  ICHECK(data_ != nullptr);
200  CopyFromTo(get_mutable(), other);
201 }
202 
203 inline void NDArray::CopyTo(const NDArray& other) const {
204  ICHECK(data_ != nullptr);
205  ICHECK(other.data_ != nullptr);
206  CopyFromTo(get_mutable(), other.get_mutable());
207 }
208 
210 constexpr uint64_t kTVMNDArrayMagic = 0xDD5E40F096B4A13F;
211 
212 inline bool SaveDLTensor(dmlc::Stream* strm, const DLTensor* tensor) {
213  uint64_t header = kTVMNDArrayMagic, reserved = 0;
214  strm->Write(header);
215  strm->Write(reserved);
216  // Always save data as CPU context
217  //
218  // Parameters that get serialized should be in CPU by default.
219  // So even the array's context is GPU, it will be stored as CPU array.
220  // This is used to prevent case when another user loads the parameters
221  // back on machine that do not have GPU or related context.
222  //
223  // We can always do array.CopyTo(target_dev) to get a corresponding
224  // array in the target context.
225  Device cpu_dev;
226  cpu_dev.device_type = kDLCPU;
227  cpu_dev.device_id = 0;
228  strm->Write(cpu_dev);
229  strm->Write(tensor->ndim);
230  strm->Write(tensor->dtype);
231  int ndim = tensor->ndim;
232  strm->WriteArray(tensor->shape, ndim);
233  int type_bytes = (tensor->dtype.bits + 7) / 8;
234  int64_t num_elems = 1;
235  for (int i = 0; i < ndim; ++i) {
236  num_elems *= tensor->shape[i];
237  }
238  int64_t data_byte_size = type_bytes * num_elems;
239  strm->Write(data_byte_size);
240 
241  if (DMLC_IO_NO_ENDIAN_SWAP && tensor->device.device_type == kDLCPU &&
242  tensor->strides == nullptr && tensor->byte_offset == 0) {
243  // quick path
244  strm->Write(tensor->data, data_byte_size);
245  } else {
246  std::vector<uint8_t> bytes(data_byte_size);
247  NDArray::CopyToBytes(const_cast<DLTensor*>(tensor), dmlc::BeginPtr(bytes), data_byte_size);
248  if (!DMLC_IO_NO_ENDIAN_SWAP) {
249  dmlc::ByteSwap(dmlc::BeginPtr(bytes), type_bytes, num_elems);
250  }
251  strm->Write(dmlc::BeginPtr(bytes), data_byte_size);
252  }
253  return true;
254 }
255 
256 inline void NDArray::Save(dmlc::Stream* strm) const { SaveDLTensor(strm, operator->()); }
257 
258 inline bool NDArray::Load(dmlc::Stream* strm) {
259  uint64_t header, reserved;
260  ICHECK(strm->Read(&header)) << "Invalid DLTensor file format";
261  ICHECK(strm->Read(&reserved)) << "Invalid DLTensor file format";
262  ICHECK(header == kTVMNDArrayMagic) << "Invalid DLTensor file format";
263  Device dev;
264  int ndim;
265  DLDataType dtype;
266  ICHECK(strm->Read(&dev)) << "Invalid DLTensor file format";
267  ICHECK(strm->Read(&ndim)) << "Invalid DLTensor file format";
268  ICHECK(strm->Read(&dtype)) << "Invalid DLTensor file format";
269  ICHECK_EQ(dev.device_type, kDLCPU) << "Invalid DLTensor device: can only save as CPU tensor";
270  std::vector<int64_t> shape(ndim);
271  if (ndim != 0) {
272  ICHECK(strm->ReadArray(&shape[0], ndim)) << "Invalid DLTensor file format";
273  }
274  NDArray ret = NDArray::Empty(ffi::Shape(shape), dtype, dev);
275  int64_t num_elems = 1;
276  int elem_bytes = (ret->dtype.bits + 7) / 8;
277  for (int i = 0; i < ret->ndim; ++i) {
278  num_elems *= ret->shape[i];
279  }
280  int64_t data_byte_size;
281  ICHECK(strm->Read(&data_byte_size)) << "Invalid DLTensor file format";
282  ICHECK(data_byte_size == num_elems * elem_bytes) << "Invalid DLTensor file format";
283  auto read_ret = strm->Read(ret->data, data_byte_size);
284  // Only check non-empty data
285  if (ndim > 0 && shape[0] != 0) {
286  ICHECK(read_ret) << "Invalid DLTensor file format";
287  }
288  if (!DMLC_IO_NO_ENDIAN_SWAP) {
289  dmlc::ByteSwap(ret->data, elem_bytes, num_elems);
290  }
291  *this = ret;
292  return true;
293 }
294 
302  if (device.device_type == DLDeviceType::kDLCUDA) {
303  return Device{DLDeviceType::kDLCUDAHost, 0};
304  } else if (device.device_type == DLDeviceType::kDLROCM) {
305  return Device{DLDeviceType::kDLROCMHost, 0};
306  } else {
307  // Fallback to CPU.
308  return Device{DLDeviceType::kDLCPU, 0};
309  }
310 }
311 
312 } // namespace runtime
313 } // namespace tvm
314 
315 namespace std {
316 template <>
317 struct hash<tvm::Device> {
318  std::size_t operator()(const tvm::Device& dev) const {
319  return ((dev.device_id << 8) | dev.device_type);
320  }
321 };
322 
323 template <>
324 struct equal_to<tvm::Device> {
325  bool operator()(const tvm::Device& lhs, const tvm::Device& rhs) const {
326  return (lhs.device_type == rhs.device_type && lhs.device_id == rhs.device_id);
327  }
328 };
329 } // namespace std
330 
331 #endif // TVM_RUNTIME_NDARRAY_H_
DataType dtype() const
Definition: expr.h:143
Runtime primitive data type.
Definition: data_type.h:47
int bits() const
Definition: data_type.h:115
Managed NDArray. The array is backed by reference counted blocks.
Definition: ndarray.h:53
void CopyFrom(const DLTensor *other)
Copy data content from another array.
Definition: ndarray.h:187
static void CopyToBytes(const DLTensor *from, void *to, size_t nbytes, TVMStreamHandle stream=nullptr)
Function to copy data from one array to a byte buffer.
ffi::Shape Shape() const
Definition: ndarray.h:65
NDArray CreateView(ffi::Shape shape, DLDataType dtype, uint64_t relative_byte_offset=0) const
Create a NDArray that shares the data memory with the current one.
ffi::NDArrayObj Container
Definition: ndarray.h:55
NDArray CopyTo(const Device &dev, Optional< String > mem_scope=std::nullopt) const
Copy the data to another device.
NDArray(ffi::NDArray &&other)
Definition: ndarray.h:62
void CopyTo(DLTensor *other) const
Copy data content into another array.
Definition: ndarray.h:198
runtime::DataType DataType() const
Definition: ndarray.h:66
static NDArray FromDLPackVersioned(DLManagedTensorVersioned *tensor)
Definition: ndarray.h:73
void CopyToBytes(void *data, size_t nbytes) const
Copy data content into another array.
static NDArray FromDLPack(DLManagedTensor *tensor)
Definition: ndarray.h:69
bool Load(dmlc::Stream *stream)
Load NDArray from stream.
Definition: ndarray.h:258
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:256
static NDArray Empty(ffi::Shape shape, DLDataType dtype, Device dev, Optional< String > mem_scope=std::nullopt)
Create an empty NDArray.
void CopyFromBytes(const void *data, size_t nbytes)
Copy data content from a byte buffer.
NDArray(ObjectPtr< Object > data)
constructor.
Definition: ndarray.h:61
NDArray(const ffi::NDArray &other)
Definition: ndarray.h:63
Abstract device memory management API.
void * TVMStreamHandle
The stream that is specific to device can be NULL, which indicates the default one.
Definition: device_api.h:37
Device GetPreferredHostDevice(Device device)
Get the preferred host device from the input device.
Definition: ndarray.h:301
bool SaveDLTensor(dmlc::Stream *strm, const DLTensor *tensor)
Save a DLTensor to stream.
Definition: ndarray.h:212
constexpr int kAllocAlignment
Number of bytes each allocation must align to.
Definition: device_api.h:111
constexpr uint64_t kTVMNDArrayMagic
Magic number for NDArray file.
Definition: ndarray.h:210
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:1945
Performance counters for profiling via the PAPI library.
Definition: analyzer.h:37
PrimExpr ret(PrimExpr value, Span span=Span())
Return the value.
runtime::DataType DataType
Definition: data_type.h:458
DLDevice Device
Definition: device_api.h:42
A managed object in the TVM runtime.
Serializer extension to support TVM data types Include this file to enable serialization of DLDataTyp...