"vscode:/vscode.git/clone" did not exist on "6a826c41a6e4b9d8e6d2b8c768d769587cc85672"
ndarray.cc 17.3 KB
Newer Older
1
/**
2
 *  Copyright (c) 2017-2022 by Contributors
3
4
 * @file ndarray.cc
 * @brief NDArray container infratructure.
Minjie Wang's avatar
Minjie Wang committed
5
6
7
 */
#include <dgl/runtime/c_runtime_api.h>
#include <dgl/runtime/device_api.h>
8
#include <dgl/runtime/ndarray.h>
9
#include <dgl/runtime/shared_mem.h>
10
#include <dgl/runtime/tensordispatch.h>
11
12
13
14
#include <dgl/zerocopy_serializer.h>
#include <dmlc/logging.h>
#include <string.h>

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

17
namespace dgl {
18

19
constexpr DGLDataType DGLDataTypeTraits<int8_t>::dtype;
20
constexpr DGLDataType DGLDataTypeTraits<uint8_t>::dtype;
21
22
23
24
25
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;
26
#ifdef DGL_USE_CUDA
27
constexpr DGLDataType DGLDataTypeTraits<__half>::dtype;
28
29
30
31
#if BF16_ENABLED
constexpr DGLDataType DGLDataTypeTraits<__nv_bfloat16>::dtype;
#endif  // BF16_ENABLED
#endif  // DGL_USE_CUDA
32
33
constexpr DGLDataType DGLDataTypeTraits<float>::dtype;
constexpr DGLDataType DGLDataTypeTraits<double>::dtype;
34

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

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

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

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

62
63
64
65
66
67
68
69
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.
70
    if (ptr->pinned_by_dgl_) UnpinContainer(ptr);
71
72
73
74
75
76
77
78
79
80
    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
81
  }
82
83
84
  delete ptr;
}

85
86
NDArray NDArray::Internal::Create(
    std::vector<int64_t> shape, DGLDataType dtype, DGLContext ctx) {
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
  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) {
102
    data->stride_[i] = data->shape_[i + 1] * data->stride_[i + 1];
Minjie Wang's avatar
Minjie Wang committed
103
  }
104
105
106
107
108
109
110
111
112
113
114
115
116
117
  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
118

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

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

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

134
135
  // See https://github.com/dmlc/dgl/issues/2118 and PyTorch's
  // compute_contiguous() implementation
136
137
138
139
140
141
142
143
  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;
    }
144
  }
145
  return true;
146
147
}

148
149
NDArray NDArray::CreateView(
    std::vector<int64_t> shape, DGLDataType dtype, int64_t offset) {
Minjie Wang's avatar
Minjie Wang committed
150
  CHECK(data_ != nullptr);
151
  CHECK(IsContiguous()) << "Can only create view for compact tensor";
Minjie Wang's avatar
Minjie Wang committed
152
  NDArray ret = Internal::Create(shape, dtype, data_->dl_tensor.ctx);
153
  ret.data_->dl_tensor.byte_offset = this->data_->dl_tensor.byte_offset;
Minjie Wang's avatar
Minjie Wang committed
154
155
156
157
158
159
160
  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_;
161
  ret.data_->dl_tensor.data =
162
      static_cast<char*>(this->data_->dl_tensor.data) + offset;
Minjie Wang's avatar
Minjie Wang committed
163
164
165
  return ret;
}

166
167
168
NDArray NDArray::EmptyShared(
    const std::string& name, std::vector<int64_t> shape, DGLDataType dtype,
    DGLContext ctx, bool is_create) {
169
170
171
172
  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) {
173
    ret.data_->dl_tensor.data = mem->CreateNew(size);
174
  } else {
175
    ret.data_->dl_tensor.data = mem->Open(size);
176
177
178
179
180
181
  }

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

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

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

199
200
201
202
  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
203
204
205

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

208
  // default: local current cuda stream
Minjie Wang's avatar
Minjie Wang committed
209
  DeviceAPI::Get(ctx)->CopyDataFromTo(
210
211
212
      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
213
214
}

215
216
217
218
219
220
221
222
223
224
225
226
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
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.";

  CHECK(from->ctx.device_type == kDGLCUDA || to->ctx.device_type == kDGLCUDA)
      << "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;
}

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

262
263
264
265
266
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,
  // so we cannot unpin it with cudaHostUnregister.
  CHECK(ptr->pinned_by_dgl_ || !container_is_pinned)
267
      << "Cannot unpin a tensor that is pinned outside of DGL.";
268
269
270
  // 1. not pinned, do nothing
  if (!container_is_pinned) return;
  // 2. pinned by DGL, unpin it
271
  DeviceAPI::Get(kDGLCUDA)->UnpinData(ptr->dl_tensor.data);
272
  ptr->pinned_by_dgl_ = false;
273
274
}

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

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

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

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

305
// export specializations
306
307
308
309
310
311
312
313
314
315
316
317
318
319
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>
320
std::vector<T> NDArray::ToVector() const {
321
  const DGLDataType dtype = DGLDataTypeTraits<T>::dtype;
322
323
  CHECK(data_->dl_tensor.ndim == 1)
      << "ToVector() only supported for 1D arrays";
324
325
326
327
  CHECK(data_->dl_tensor.dtype == dtype) << "dtype mismatch";

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

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

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

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

bool NDArray::Load(dmlc::Stream* strm) {
367
  auto zc_strm = dynamic_cast<StreamWithBuffer*>(strm);
368
369
370
371
372
  if (zc_strm) {
    *this = zc_strm->PopNDArray();
    return true;
  }
  uint64_t header, reserved;
373
374
375
  CHECK(strm->Read(&header)) << "Invalid DGLArray file format";
  CHECK(strm->Read(&reserved)) << "Invalid DGLArray file format";
  CHECK(header == kDGLNDArrayMagic) << "Invalid DGLArray file format";
376
  DGLContext ctx;
377
  int ndim;
378
  DGLDataType dtype;
379
380
381
  CHECK(strm->Read(&ctx)) << "Invalid DGLArray file format";
  CHECK(strm->Read(&ndim)) << "Invalid DGLArray file format";
  CHECK(strm->Read(&dtype)) << "Invalid DGLArray file format";
382
383
  CHECK_EQ(ctx.device_type, kDGLCPU)
      << "Invalid DGLArray context: can only save as CPU tensor";
384
385
  std::vector<int64_t> shape(ndim);
  if (ndim != 0) {
386
    CHECK(strm->ReadArray(&shape[0], ndim)) << "Invalid DGLArray file format";
387
388
389
390
391
392
393
394
  }
  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;
395
  CHECK(strm->Read(&data_byte_size)) << "Invalid DGLArray file format";
396
  CHECK(data_byte_size == num_elems * elem_bytes)
397
      << "Invalid DGLArray file format";
398
  if (data_byte_size != 0) {
399
400
401
    // 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))
402
        << "Invalid DGLArray file format";
403
404
405
406
407
408
409
410
  }
  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
411
}  // namespace runtime
412
}  // namespace dgl
Minjie Wang's avatar
Minjie Wang committed
413

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

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

432
433
434
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) {
435
  API_BEGIN();
436
  DGLDataType dtype;
437
438
439
440
  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);
441
442
  NDArray arr = NDArray::EmptyShared(
      mem_name, shape_vec, dtype, DGLContext{kDGLCPU, 0}, is_create);
443
  *out = NDArray::Internal::MoveAsDGLArray(arr);
444
445
446
  API_END();
}

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

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

459
int DGLArrayCopyFromBytes(DGLArrayHandle handle, void* data, size_t nbytes) {
Minjie Wang's avatar
Minjie Wang committed
460
  API_BEGIN();
461
  DGLContext cpu_ctx;
462
  cpu_ctx.device_type = kDGLCPU;
Minjie Wang's avatar
Minjie Wang committed
463
464
  cpu_ctx.device_id = 0;
  size_t arr_size = GetDataSize(*handle);
465
466
467
468
469
  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
470
471
472
  API_END();
}

473
int DGLArrayCopyToBytes(DGLArrayHandle handle, void* data, size_t nbytes) {
Minjie Wang's avatar
Minjie Wang committed
474
  API_BEGIN();
475
  DGLContext cpu_ctx;
476
  cpu_ctx.device_type = kDGLCPU;
Minjie Wang's avatar
Minjie Wang committed
477
478
  cpu_ctx.device_id = 0;
  size_t arr_size = GetDataSize(*handle);
479
480
481
482
483
  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
484
485
  API_END();
}
486

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

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

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