ndarray.cc 17.5 KB
Newer Older
sangwzh's avatar
sangwzh committed
1
// !!! This is a file automatically generated by hipify!!!
2
/**
3
 *  Copyright (c) 2017-2022 by Contributors
4
5
 * @file ndarray.cc
 * @brief NDArray container infratructure.
Minjie Wang's avatar
Minjie Wang committed
6
7
8
 */
#include <dgl/runtime/c_runtime_api.h>
#include <dgl/runtime/device_api.h>
9
#include <dgl/runtime/ndarray.h>
10
#include <dgl/runtime/shared_mem.h>
11
#include <dgl/runtime/tensordispatch.h>
12
13
14
15
#include <dgl/zerocopy_serializer.h>
#include <dmlc/logging.h>
#include <string.h>

Minjie Wang's avatar
Minjie Wang committed
16
17
#include "runtime_base.h"

18
namespace dgl {
19

20
constexpr DGLDataType DGLDataTypeTraits<int8_t>::dtype;
21
constexpr DGLDataType DGLDataTypeTraits<uint8_t>::dtype;
22
23
24
25
26
constexpr DGLDataType DGLDataTypeTraits<int16_t>::dtype;
constexpr DGLDataType DGLDataTypeTraits<int32_t>::dtype;
constexpr DGLDataType DGLDataTypeTraits<int64_t>::dtype;
constexpr DGLDataType DGLDataTypeTraits<uint32_t>::dtype;
constexpr DGLDataType DGLDataTypeTraits<uint64_t>::dtype;
27
#ifdef DGL_USE_CUDA
28
constexpr DGLDataType DGLDataTypeTraits<__half>::dtype;
29
#if BF16_ENABLED
sangwzh's avatar
sangwzh committed
30
constexpr DGLDataType DGLDataTypeTraits<__hip_bfloat16>::dtype;
31
32
#endif  // BF16_ENABLED
#endif  // DGL_USE_CUDA
33
34
constexpr DGLDataType DGLDataTypeTraits<float>::dtype;
constexpr DGLDataType DGLDataTypeTraits<double>::dtype;
35

Minjie Wang's avatar
Minjie Wang committed
36
37
namespace runtime {

38
inline void VerifyDataType(DGLDataType dtype) {
Minjie Wang's avatar
Minjie Wang committed
39
  CHECK_GE(dtype.lanes, 1);
40
  if (dtype.code == kDGLFloat) {
Minjie Wang's avatar
Minjie Wang committed
41
42
43
44
45
46
47
    CHECK_EQ(dtype.bits % 8, 0);
  } else {
    CHECK_EQ(dtype.bits % 8, 0);
  }
  CHECK_EQ(dtype.bits & (dtype.bits - 1), 0);
}

48
inline size_t GetDataSize(const DGLArray& arr) {
Minjie Wang's avatar
Minjie Wang committed
49
  size_t size = 1;
50
  for (dgl_index_t i = 0; i < arr.ndim; ++i) {
Minjie Wang's avatar
Minjie Wang committed
51
52
53
54
55
56
    size *= arr.shape[i];
  }
  size *= (arr.dtype.bits * arr.dtype.lanes + 7) / 8;
  return size;
}

57
inline size_t GetDataAlignment(const DGLArray& arr) {
Minjie Wang's avatar
Minjie Wang committed
58
59
60
61
62
  size_t align = (arr.dtype.bits / 8) * arr.dtype.lanes;
  if (align < kAllocAlignment) return kAllocAlignment;
  return align;
}

63
64
65
66
67
68
69
70
void NDArray::Internal::DefaultDeleter(NDArray::Container* ptr) {
  using dgl::runtime::NDArray;
  if (ptr->manager_ctx != nullptr) {
    static_cast<NDArray::Container*>(ptr->manager_ctx)->DecRef();
  } else if (ptr->mem) {
    ptr->mem = nullptr;
  } else if (ptr->dl_tensor.data != nullptr) {
    // if the array is still pinned before freeing, unpin it.
71
    if (ptr->pinned_by_dgl_) UnpinContainer(ptr);
72
73
74
75
76
77
78
79
80
81
    if (ptr->pinned_by_pytorch_) {
      DeviceAPI::Get(kDGLCUDA)->FreePinnedDataSpace(
          &(ptr->pytorch_raw_deleter_));
      CHECK(ptr->pytorch_raw_deleter_ == nullptr);
      ptr->pinned_by_pytorch_ = false;
      ptr->pytorch_ctx_ = nullptr;
    } else {
      dgl::runtime::DeviceAPI::Get(ptr->dl_tensor.ctx)
          ->FreeDataSpace(ptr->dl_tensor.ctx, ptr->dl_tensor.data);
    }
Minjie Wang's avatar
Minjie Wang committed
82
  }
83
84
85
  delete ptr;
}

86
87
NDArray NDArray::Internal::Create(
    std::vector<int64_t> shape, DGLDataType dtype, DGLContext ctx) {
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
  VerifyDataType(dtype);
  // critical zone
  NDArray::Container* data = new NDArray::Container();
  data->deleter = DefaultDeleter;
  NDArray ret(data);
  ret.data_ = data;
  // RAII now in effect
  // setup shape
  data->shape_ = std::move(shape);
  data->dl_tensor.shape = dmlc::BeginPtr(data->shape_);
  data->dl_tensor.ndim = static_cast<int>(data->shape_.size());
  // setup stride (this should be optional, but some framework
  //   does not support NULL stride and thus will crash the program).
  data->stride_.resize(data->dl_tensor.ndim, 1);
  for (int i = data->dl_tensor.ndim - 2; i >= 0; --i) {
103
    data->stride_[i] = data->shape_[i + 1] * data->stride_[i + 1];
Minjie Wang's avatar
Minjie Wang committed
104
  }
105
106
107
108
109
110
111
112
113
114
115
116
117
118
  data->dl_tensor.strides = dmlc::BeginPtr(data->stride_);
  // setup dtype
  data->dl_tensor.dtype = dtype;
  // setup ctx
  data->dl_tensor.ctx = ctx;
  return ret;
}

DGLArray* NDArray::Internal::MoveAsDGLArray(NDArray arr) {
  DGLArray* tensor = reinterpret_cast<DGLArray*>(arr.data_);
  CHECK(tensor == const_cast<DGLArray*>(arr.operator->()));
  arr.data_ = nullptr;
  return tensor;
}
Minjie Wang's avatar
Minjie Wang committed
119

120
size_t NDArray::GetSize() const { return GetDataSize(data_->dl_tensor); }
121

122
int64_t NDArray::NumElements() const {
123
  if (data_->dl_tensor.ndim == 0) return 0;
124
125
126
127
128
129
130
  int64_t size = 1;
  for (int i = 0; i < data_->dl_tensor.ndim; ++i) {
    size *= data_->dl_tensor.shape[i];
  }
  return size;
}

131
132
bool NDArray::IsContiguous() const {
  CHECK(data_ != nullptr);
133
  if (data_->dl_tensor.strides == nullptr) return true;
134

135
136
  // See https://github.com/dmlc/dgl/issues/2118 and PyTorch's
  // compute_contiguous() implementation
137
138
139
140
141
142
143
144
  int64_t z = 1;
  for (int64_t i = data_->dl_tensor.ndim - 1; i >= 0; --i) {
    if (data_->dl_tensor.shape[i] != 1) {
      if (data_->dl_tensor.strides[i] == z)
        z *= data_->dl_tensor.shape[i];
      else
        return false;
    }
145
  }
146
  return true;
147
148
}

149
150
NDArray NDArray::CreateView(
    std::vector<int64_t> shape, DGLDataType dtype, int64_t offset) {
Minjie Wang's avatar
Minjie Wang committed
151
  CHECK(data_ != nullptr);
152
  CHECK(IsContiguous()) << "Can only create view for compact tensor";
Minjie Wang's avatar
Minjie Wang committed
153
  NDArray ret = Internal::Create(shape, dtype, data_->dl_tensor.ctx);
154
  ret.data_->dl_tensor.byte_offset = this->data_->dl_tensor.byte_offset;
Minjie Wang's avatar
Minjie Wang committed
155
156
157
158
159
160
161
  size_t curr_size = GetDataSize(this->data_->dl_tensor);
  size_t view_size = GetDataSize(ret.data_->dl_tensor);
  CHECK_LE(view_size, curr_size)
      << "Tries to create a view that has bigger memory than current one";
  // increase ref count
  this->data_->IncRef();
  ret.data_->manager_ctx = this->data_;
162
  ret.data_->dl_tensor.data =
163
      static_cast<char*>(this->data_->dl_tensor.data) + offset;
Minjie Wang's avatar
Minjie Wang committed
164
165
166
  return ret;
}

167
168
169
NDArray NDArray::EmptyShared(
    const std::string& name, std::vector<int64_t> shape, DGLDataType dtype,
    DGLContext ctx, bool is_create) {
170
171
172
173
  NDArray ret = Internal::Create(shape, dtype, ctx);
  size_t size = GetDataSize(ret.data_->dl_tensor);
  auto mem = std::make_shared<SharedMemory>(name);
  if (is_create) {
174
    ret.data_->dl_tensor.data = mem->CreateNew(size);
175
  } else {
176
    ret.data_->dl_tensor.data = mem->Open(size);
177
178
179
180
181
182
  }

  ret.data_->mem = mem;
  return ret;
}

183
184
NDArray NDArray::Empty(
    std::vector<int64_t> shape, DGLDataType dtype, DGLContext ctx) {
185
  NDArray ret = Internal::Create(shape, dtype, ctx);
Minjie Wang's avatar
Minjie Wang committed
186
187
  size_t size = GetDataSize(ret.data_->dl_tensor);
  size_t alignment = GetDataAlignment(ret.data_->dl_tensor);
188
  if (size > 0)
189
190
    ret.data_->dl_tensor.data = DeviceAPI::Get(ret->ctx)->AllocDataSpace(
        ret->ctx, size, alignment, ret->dtype);
Minjie Wang's avatar
Minjie Wang committed
191
192
193
  return ret;
}

194
void NDArray::CopyFromTo(DGLArray* from, DGLArray* to) {
Minjie Wang's avatar
Minjie Wang committed
195
196
197
  size_t from_size = GetDataSize(*from);
  size_t to_size = GetDataSize(*to);
  CHECK_EQ(from_size, to_size)
198
      << "DGLArrayCopyFromTo: The size must exactly match";
Minjie Wang's avatar
Minjie Wang committed
199

200
201
202
203
  CHECK(
      from->ctx.device_type == to->ctx.device_type ||
      from->ctx.device_type == kDGLCPU || to->ctx.device_type == kDGLCPU)
      << "Can not copy across different ctx types directly";
Minjie Wang's avatar
Minjie Wang committed
204
205
206

  // Use the context that is *not* a cpu context to get the correct device
  // api manager.
207
  DGLContext ctx = from->ctx.device_type != kDGLCPU ? from->ctx : to->ctx;
Minjie Wang's avatar
Minjie Wang committed
208

209
  // default: local current cuda stream
Minjie Wang's avatar
Minjie Wang committed
210
  DeviceAPI::Get(ctx)->CopyDataFromTo(
211
212
213
      from->data, static_cast<size_t>(from->byte_offset), to->data,
      static_cast<size_t>(to->byte_offset), from_size, from->ctx, to->ctx,
      from->dtype);
Minjie Wang's avatar
Minjie Wang committed
214
215
}

216
217
218
219
220
221
222
223
224
225
void NDArray::RecordedCopyFromTo(
    DGLArray* from, DGLArray* to, void* pytorch_ctx) {
  size_t from_size = GetDataSize(*from);
  size_t to_size = GetDataSize(*to);
  CHECK_EQ(from_size, to_size)
      << "DGLArrayCopyFromTo: The size must exactly match.";

  CHECK(from->ctx.device_type != to->ctx.device_type)
      << "Recoding event is only called for the copy between CPU and GPU.";

226
  CHECK(from->ctx.device_type == kDGLCUDA || to->ctx.device_type == kDGLROCM ||from->ctx.device_type == kDGLROCM||to->ctx.device_type == kDGLCUDA)
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
      << "At least one CUDA ctx needs to be involved.";

  DeviceAPI::Get(kDGLCUDA)->RecordedCopyDataFromTo(
      from->data, static_cast<size_t>(from->byte_offset), to->data,
      static_cast<size_t>(to->byte_offset), from_size, from->ctx, to->ctx,
      from->dtype, pytorch_ctx);
}

NDArray NDArray::PinnedEmpty(
    std::vector<int64_t> shape, DGLDataType dtype, DGLContext ctx) {
  CHECK_EQ(ctx.device_type, kDGLCPU) << "Only NDArray on CPU can be pinned";
  NDArray ret = Internal::Create(shape, dtype, ctx);
  size_t size = GetDataSize(ret.data_->dl_tensor);
  if (size > 0) {
    ret.data_->dl_tensor.data = DeviceAPI::Get(kDGLCUDA)->AllocPinnedDataSpace(
        size, &(ret.data_->pytorch_ctx_), &(ret.data_->pytorch_raw_deleter_));
    CHECK(
        ret.data_->pytorch_ctx_ != nullptr &&
        ret.data_->pytorch_raw_deleter_ != nullptr)
        << "The allocation failed in PyTorch's CachingHostAllocator. "
        << "The returned context pointer is " << ret.data_->pytorch_ctx_
        << " and the function deleter is " << ret.data_->pytorch_raw_deleter_;
    ret.data_->pinned_by_pytorch_ = true;
  }
  return ret;
}

254
255
256
void NDArray::PinContainer(NDArray::Container* ptr) {
  if (IsContainerPinned(ptr)) return;
  auto* tensor = &(ptr->dl_tensor);
257
  CHECK_EQ(tensor->ctx.device_type, kDGLCPU)
258
      << "Only NDArray on CPU can be pinned";
259
260
  ptr->pinned_by_dgl_ =
      DeviceAPI::Get(kDGLCUDA)->PinData(tensor->data, GetDataSize(*tensor));
261
262
}

263
264
265
void NDArray::UnpinContainer(NDArray::Container* ptr) {
  auto container_is_pinned = IsContainerPinned(ptr);
  // The tensor may be pinned outside of DGL via a different CUDA API,
sangwzh's avatar
sangwzh committed
266
  // so we cannot unpin it with hipHostUnregister.
267
  CHECK(ptr->pinned_by_dgl_ || !container_is_pinned)
268
      << "Cannot unpin a tensor that is pinned outside of DGL.";
269
270
271
  // 1. not pinned, do nothing
  if (!container_is_pinned) return;
  // 2. pinned by DGL, unpin it
272
  DeviceAPI::Get(kDGLCUDA)->UnpinData(ptr->dl_tensor.data);
273
  ptr->pinned_by_dgl_ = false;
274
275
}

276
void NDArray::RecordStream(DGLArray* tensor, DGLStreamHandle stream) {
277
278
279
  TensorDispatcher* tensor_dispatcher = TensorDispatcher::Global();
  CHECK(tensor_dispatcher->IsAvailable())
      << "RecordStream only works when TensorAdapter is available.";
280
  CHECK_EQ(tensor->ctx.device_type, kDGLCUDA)
281
      << "RecordStream only works with GPU tensors.";
282

283
  tensor_dispatcher->RecordStream(tensor->data, stream, tensor->ctx.device_id);
284
285
}

286
template <typename T>
287
288
NDArray NDArray::FromVector(const std::vector<T>& vec, DGLContext ctx) {
  const DGLDataType dtype = DGLDataTypeTraits<T>::dtype;
289
  int64_t size = static_cast<int64_t>(vec.size());
290
  NDArray ret = NDArray::Empty({size}, dtype, ctx);
291
  DeviceAPI::Get(ctx)->CopyDataFromTo(
292
293
      vec.data(), 0, static_cast<T*>(ret->data), 0, size * sizeof(T),
      DGLContext{kDGLCPU, 0}, ctx, dtype);
294
295
296
  return ret;
}

297
298
299
NDArray NDArray::CreateFromRaw(
    const std::vector<int64_t>& shape, DGLDataType dtype, DGLContext ctx,
    void* raw, bool auto_free) {
300
301
  NDArray ret = Internal::Create(shape, dtype, ctx);
  ret.data_->dl_tensor.data = raw;
302
  if (!auto_free) ret.data_->deleter = nullptr;
303
304
305
  return ret;
}

306
// export specializations
307
308
309
310
311
312
313
314
315
316
317
318
319
320
template NDArray NDArray::FromVector<int32_t>(
    const std::vector<int32_t>&, DGLContext);
template NDArray NDArray::FromVector<int64_t>(
    const std::vector<int64_t>&, DGLContext);
template NDArray NDArray::FromVector<uint32_t>(
    const std::vector<uint32_t>&, DGLContext);
template NDArray NDArray::FromVector<uint64_t>(
    const std::vector<uint64_t>&, DGLContext);
template NDArray NDArray::FromVector<float>(
    const std::vector<float>&, DGLContext);
template NDArray NDArray::FromVector<double>(
    const std::vector<double>&, DGLContext);

template <typename T>
321
std::vector<T> NDArray::ToVector() const {
322
  const DGLDataType dtype = DGLDataTypeTraits<T>::dtype;
323
324
  CHECK(data_->dl_tensor.ndim == 1)
      << "ToVector() only supported for 1D arrays";
325
326
327
328
  CHECK(data_->dl_tensor.dtype == dtype) << "dtype mismatch";

  int64_t size = data_->dl_tensor.shape[0];
  std::vector<T> vec(size);
329
  const DGLContext& ctx = data_->dl_tensor.ctx;
330
  DeviceAPI::Get(ctx)->CopyDataFromTo(
331
332
      static_cast<T*>(data_->dl_tensor.data), 0, vec.data(), 0,
      size * sizeof(T), ctx, DGLContext{kDGLCPU, 0}, dtype);
333
334
335
336
337
338
339
340
341
  return vec;
}

template std::vector<int32_t> NDArray::ToVector<int32_t>() const;
template std::vector<int64_t> NDArray::ToVector<int64_t>() const;
template std::vector<uint32_t> NDArray::ToVector<uint32_t>() const;
template std::vector<uint64_t> NDArray::ToVector<uint64_t>() const;
template std::vector<float> NDArray::ToVector<float>() const;
template std::vector<double> NDArray::ToVector<double>() const;
342

343
344
345
346
std::shared_ptr<SharedMemory> NDArray::GetSharedMem() const {
  return this->data_->mem;
}

347
bool NDArray::IsContainerPinned(NDArray::Container* ptr) {
348
  if (ptr->pinned_by_dgl_ || ptr->pinned_by_pytorch_) return true;
349
  auto* tensor = &(ptr->dl_tensor);
350
  // Can only be pinned if on CPU...
351
352
353
  if (tensor->ctx.device_type != kDGLCPU) return false;
  // ... and CUDA device API is enabled, and the tensor is indeed in pinned
  // memory.
354
  auto device = DeviceAPI::Get(kDGLCUDA, true);
355
356
  return device && device->IsPinned(tensor->data);
}
357
358

void NDArray::Save(dmlc::Stream* strm) const {
359
  auto zc_strm = dynamic_cast<StreamWithBuffer*>(strm);
360
361
362
363
  if (zc_strm) {
    zc_strm->PushNDArray(*this);
    return;
  }
364
  SaveDGLArray(strm, const_cast<DGLArray*>(operator->()));
365
366
367
}

bool NDArray::Load(dmlc::Stream* strm) {
368
  auto zc_strm = dynamic_cast<StreamWithBuffer*>(strm);
369
370
371
372
373
  if (zc_strm) {
    *this = zc_strm->PopNDArray();
    return true;
  }
  uint64_t header, reserved;
374
375
376
  CHECK(strm->Read(&header)) << "Invalid DGLArray file format";
  CHECK(strm->Read(&reserved)) << "Invalid DGLArray file format";
  CHECK(header == kDGLNDArrayMagic) << "Invalid DGLArray file format";
377
  DGLContext ctx;
378
  int ndim;
379
  DGLDataType dtype;
380
381
382
  CHECK(strm->Read(&ctx)) << "Invalid DGLArray file format";
  CHECK(strm->Read(&ndim)) << "Invalid DGLArray file format";
  CHECK(strm->Read(&dtype)) << "Invalid DGLArray file format";
383
384
  CHECK_EQ(ctx.device_type, kDGLCPU)
      << "Invalid DGLArray context: can only save as CPU tensor";
385
386
  std::vector<int64_t> shape(ndim);
  if (ndim != 0) {
387
    CHECK(strm->ReadArray(&shape[0], ndim)) << "Invalid DGLArray file format";
388
389
390
391
392
393
394
395
  }
  NDArray ret = NDArray::Empty(shape, dtype, ctx);
  int64_t num_elems = 1;
  int elem_bytes = (ret->dtype.bits + 7) / 8;
  for (int i = 0; i < ret->ndim; ++i) {
    num_elems *= ret->shape[i];
  }
  int64_t data_byte_size;
396
  CHECK(strm->Read(&data_byte_size)) << "Invalid DGLArray file format";
397
  CHECK(data_byte_size == num_elems * elem_bytes)
398
      << "Invalid DGLArray file format";
399
  if (data_byte_size != 0) {
400
401
402
    // strm->Read will return the total number of elements successfully read.
    // Therefore if data_byte_size is zero, the CHECK below would fail.
    CHECK(strm->Read(ret->data, data_byte_size))
403
        << "Invalid DGLArray file format";
404
405
406
407
408
409
410
411
  }
  if (!DMLC_IO_NO_ENDIAN_SWAP) {
    dmlc::ByteSwap(ret->data, elem_bytes, num_elems);
  }
  *this = ret;
  return true;
}

Minjie Wang's avatar
Minjie Wang committed
412
}  // namespace runtime
413
}  // namespace dgl
Minjie Wang's avatar
Minjie Wang committed
414

415
using namespace dgl::runtime;
Minjie Wang's avatar
Minjie Wang committed
416

417
418
419
int DGLArrayAlloc(
    const dgl_index_t* shape, int ndim, int dtype_code, int dtype_bits,
    int dtype_lanes, int device_type, int device_id, DGLArrayHandle* out) {
Minjie Wang's avatar
Minjie Wang committed
420
  API_BEGIN();
421
  DGLDataType dtype;
Minjie Wang's avatar
Minjie Wang committed
422
423
424
  dtype.code = static_cast<uint8_t>(dtype_code);
  dtype.bits = static_cast<uint8_t>(dtype_bits);
  dtype.lanes = static_cast<uint16_t>(dtype_lanes);
425
426
  DGLContext ctx;
  ctx.device_type = static_cast<DGLDeviceType>(device_type);
Minjie Wang's avatar
Minjie Wang committed
427
  ctx.device_id = device_id;
428
  *out = NDArray::Internal::MoveAsDGLArray(
Minjie Wang's avatar
Minjie Wang committed
429
430
431
432
      NDArray::Empty(std::vector<int64_t>(shape, shape + ndim), dtype, ctx));
  API_END();
}

433
434
435
int DGLArrayAllocSharedMem(
    const char* mem_name, const dgl_index_t* shape, int ndim, int dtype_code,
    int dtype_bits, int dtype_lanes, bool is_create, DGLArrayHandle* out) {
436
  API_BEGIN();
437
  DGLDataType dtype;
438
439
440
441
  dtype.code = static_cast<uint8_t>(dtype_code);
  dtype.bits = static_cast<uint8_t>(dtype_bits);
  dtype.lanes = static_cast<uint16_t>(dtype_lanes);
  std::vector<int64_t> shape_vec(shape, shape + ndim);
442
443
  NDArray arr = NDArray::EmptyShared(
      mem_name, shape_vec, dtype, DGLContext{kDGLCPU, 0}, is_create);
444
  *out = NDArray::Internal::MoveAsDGLArray(arr);
445
446
447
  API_END();
}

448
int DGLArrayFree(DGLArrayHandle handle) {
Minjie Wang's avatar
Minjie Wang committed
449
450
451
452
453
  API_BEGIN();
  reinterpret_cast<NDArray::Container*>(handle)->DecRef();
  API_END();
}

454
int DGLArrayCopyFromTo(DGLArrayHandle from, DGLArrayHandle to) {
Minjie Wang's avatar
Minjie Wang committed
455
  API_BEGIN();
456
  NDArray::CopyFromTo(from, to);
Minjie Wang's avatar
Minjie Wang committed
457
458
459
  API_END();
}

460
int DGLArrayCopyFromBytes(DGLArrayHandle handle, void* data, size_t nbytes) {
Minjie Wang's avatar
Minjie Wang committed
461
  API_BEGIN();
462
  DGLContext cpu_ctx;
463
  cpu_ctx.device_type = kDGLCPU;
Minjie Wang's avatar
Minjie Wang committed
464
465
  cpu_ctx.device_id = 0;
  size_t arr_size = GetDataSize(*handle);
466
467
468
469
470
  CHECK_EQ(arr_size, nbytes) << "DGLArrayCopyFromBytes: size mismatch";
  DeviceAPI::Get(handle->ctx)
      ->CopyDataFromTo(
          data, 0, handle->data, static_cast<size_t>(handle->byte_offset),
          nbytes, cpu_ctx, handle->ctx, handle->dtype);
Minjie Wang's avatar
Minjie Wang committed
471
472
473
  API_END();
}

474
int DGLArrayCopyToBytes(DGLArrayHandle handle, void* data, size_t nbytes) {
Minjie Wang's avatar
Minjie Wang committed
475
  API_BEGIN();
476
  DGLContext cpu_ctx;
477
  cpu_ctx.device_type = kDGLCPU;
Minjie Wang's avatar
Minjie Wang committed
478
479
  cpu_ctx.device_id = 0;
  size_t arr_size = GetDataSize(*handle);
480
481
482
483
484
  CHECK_EQ(arr_size, nbytes) << "DGLArrayCopyToBytes: size mismatch";
  DeviceAPI::Get(handle->ctx)
      ->CopyDataFromTo(
          handle->data, static_cast<size_t>(handle->byte_offset), data, 0,
          nbytes, handle->ctx, cpu_ctx, handle->dtype);
Minjie Wang's avatar
Minjie Wang committed
485
486
  API_END();
}
487

488
int DGLArrayPinData(DGLArrayHandle handle, DGLContext ctx) {
489
  API_BEGIN();
490
491
  auto* nd_container = reinterpret_cast<NDArray::Container*>(handle);
  NDArray::PinContainer(nd_container);
492
493
494
  API_END();
}

495
int DGLArrayUnpinData(DGLArrayHandle handle, DGLContext ctx) {
496
  API_BEGIN();
497
498
  auto* nd_container = reinterpret_cast<NDArray::Container*>(handle);
  NDArray::UnpinContainer(nd_container);
499
500
  API_END();
}
501
502
503
504
505
506

int DGLArrayRecordStream(DGLArrayHandle handle, DGLStreamHandle stream) {
  API_BEGIN();
  NDArray::RecordStream(handle, stream);
  API_END();
}