From 8b120381c51d8bf1c9b475888a3b1f76e7b6353e Mon Sep 17 00:00:00 2001 From: Oleksandr Pavlyk Date: Wed, 15 Feb 2023 22:24:36 -0600 Subject: [PATCH 1/5] Fixes gh-1071 Whenever start of array decoded from its dlpack capsule data did not start from the beginning of the allocation, like with x0 = dpt.arrange(6); x1 = x0[::-2]; dpt.from_dlpack(x1) which failed, vs. x0 = dpt.arrange(7); x1 = x0[::-2]; dpt.from_dlpack(x1) which worked. --- dpctl/tensor/_dlpack.pyx | 50 +++++++++++++++++++++++++++------------- 1 file changed, 34 insertions(+), 16 deletions(-) diff --git a/dpctl/tensor/_dlpack.pyx b/dpctl/tensor/_dlpack.pyx index f8ed9d8ae8..3fcb7a8974 100644 --- a/dpctl/tensor/_dlpack.pyx +++ b/dpctl/tensor/_dlpack.pyx @@ -311,14 +311,17 @@ cpdef usm_ndarray from_dlpack_capsule(object py_caps) except +: cdef DLManagedTensor *dlm_tensor = NULL cdef bytes usm_type cdef size_t sz = 1 + cdef size_t alloc_sz = 1 cdef int i cdef int device_id = -1 cdef int element_bytesize = 0 cdef Py_ssize_t offset_min = 0 cdef Py_ssize_t offset_max = 0 - cdef int64_t stride_i cdef char *mem_ptr = NULL + cdef Py_ssize_t mem_ptr_delta = 0 cdef Py_ssize_t element_offset = 0 + cdef int64_t stride_i = -1 + cdef int64_t shape_i = -1 if not cpython.PyCapsule_IsValid(py_caps, 'dltensor'): if cpython.PyCapsule_IsValid(py_caps, 'used_dltensor'): @@ -370,22 +373,22 @@ cpdef usm_ndarray from_dlpack_capsule(object py_caps) except +: raise BufferError( "Can not import DLPack tensor with lanes != 1" ) + offset_min = 0 if dlm_tensor.dl_tensor.strides is NULL: for i in range(dlm_tensor.dl_tensor.ndim): sz = sz * dlm_tensor.dl_tensor.shape[i] + offset_max = sz - 1 else: - offset_min = 0 offset_max = 0 for i in range(dlm_tensor.dl_tensor.ndim): stride_i = dlm_tensor.dl_tensor.strides[i] - if stride_i > 0: - offset_max = offset_max + stride_i * ( - dlm_tensor.dl_tensor.shape[i] - 1 - ) - else: - offset_min = offset_min + stride_i * ( - dlm_tensor.dl_tensor.shape[i] - 1 - ) + shape_i = dlm_tensor.dl_tensor.shape[i] + if shape_i > 1: + shape_i -= 1 + if stride_i > 0: + offset_max = offset_max + stride_i * shape_i + else: + offset_min = offset_min + stride_i * shape_i sz = offset_max - offset_min + 1 if sz == 0: sz = 1 @@ -401,14 +404,29 @@ cpdef usm_ndarray from_dlpack_capsule(object py_caps) except +: if dlm_tensor.dl_tensor.data is NULL: usm_mem = dpmem.MemoryUSMDevice(sz, q) else: - mem_ptr = dlm_tensor.dl_tensor.data + dlm_tensor.dl_tensor.byte_offset - mem_ptr = mem_ptr - (element_offset * element_bytesize) - usm_mem = c_dpmem._Memory.create_from_usm_pointer_size_qref( + mem_ptr_delta = dlm_tensor.dl_tensor.byte_offset - ( + element_offset * element_bytesize + ) + mem_ptr = dlm_tensor.dl_tensor.data + alloc_sz = dlm_tensor.dl_tensor.byte_offset + ( + (offset_max + 1) * element_bytesize) + tmp = c_dpmem._Memory.create_from_usm_pointer_size_qref( mem_ptr, - sz, + max(alloc_sz, element_bytesize), (q).get_queue_ref(), memory_owner=dlm_holder ) + if mem_ptr_delta == 0: + usm_mem = tmp + else: + alloc_sz = dlm_tensor.dl_tensor.byte_offset + ( + (offset_max * element_bytesize + mem_ptr_delta)) + usm_mem = c_dpmem._Memory.create_from_usm_pointer_size_qref( + (mem_ptr + (element_bytesize - mem_ptr_delta)), + max(alloc_sz, element_bytesize), + (q).get_queue_ref(), + memory_owner=tmp + ) py_shape = list() for i in range(dlm_tensor.dl_tensor.ndim): py_shape.append(dlm_tensor.dl_tensor.shape[i]) @@ -427,7 +445,7 @@ cpdef usm_ndarray from_dlpack_capsule(object py_caps) except +: elif (dlm_tensor.dl_tensor.dtype.code == kDLComplex): ary_dt = np.dtype("c" + str(element_bytesize)) else: - raise ValueError( + raise BufferError( "Can not import DLPack tensor with type code {}.".format( dlm_tensor.dl_tensor.dtype.code ) @@ -441,7 +459,7 @@ cpdef usm_ndarray from_dlpack_capsule(object py_caps) except +: ) return res_ary else: - raise ValueError( + raise BufferError( "The DLPack tensor resides on unsupported device." ) From 11924c0f8a1d0483f260c8f436233ded77e9ba5e Mon Sep 17 00:00:00 2001 From: Oleksandr Pavlyk Date: Thu, 16 Feb 2023 06:51:15 -0600 Subject: [PATCH 2/5] Extended test_usm_ndarray_dlpack with example from gh-1071 --- dpctl/tests/test_usm_ndarray_dlpack.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/dpctl/tests/test_usm_ndarray_dlpack.py b/dpctl/tests/test_usm_ndarray_dlpack.py index 786f2f3398..b534e59328 100644 --- a/dpctl/tests/test_usm_ndarray_dlpack.py +++ b/dpctl/tests/test_usm_ndarray_dlpack.py @@ -113,6 +113,30 @@ def test_from_dlpack(shape, typestr, usm_type): assert V.strides == W.strides +@pytest.mark.parametrize("mod", [2, 5]) +def test_from_dlpack_strides(mod, typestr, usm_type): + all_root_devices = dpctl.get_devices() + for sycl_dev in all_root_devices: + skip_if_dtype_not_supported(typestr, sycl_dev) + X0 = dpt.empty( + 3 * mod, dtype=typestr, usm_type=usm_type, device=sycl_dev + ) + for start in range(mod): + X = X0[slice(-start - 1, None, -mod)] + Y = dpt.from_dlpack(X) + assert X.shape == Y.shape + assert X.dtype == Y.dtype or ( + str(X.dtype) == "bool" and str(Y.dtype) == "uint8" + ) + assert X.sycl_device == Y.sycl_device + assert X.usm_type == Y.usm_type + assert X._pointer == Y._pointer + if Y.ndim: + V = Y[::-1] + W = dpt.from_dlpack(V) + assert V.strides == W.strides + + def test_from_dlpack_input_validation(): vstr = dpt._dlpack.get_build_dlpack_version() assert type(vstr) is str From 7658010fccf314124f9653b6d826c695babddd6e Mon Sep 17 00:00:00 2001 From: Oleksandr Pavlyk Date: Thu, 16 Feb 2023 06:54:52 -0600 Subject: [PATCH 3/5] Updated vendored dlpack.h to v0.8 --- dpctl/tensor/include/dlpack/README.md | 2 +- dpctl/tensor/include/dlpack/dlpack.h | 11 +++++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/dpctl/tensor/include/dlpack/README.md b/dpctl/tensor/include/dlpack/README.md index a827fcd528..02c594c0fe 100644 --- a/dpctl/tensor/include/dlpack/README.md +++ b/dpctl/tensor/include/dlpack/README.md @@ -1,6 +1,6 @@ # DLPack header -The header `dlpack.h` downloaded from `https://github.com/dmlc/dlpack.git` remote at tag 0.7 commit [`e2bdd3bee8`](https://github.com/dmlc/dlpack/commit/e2bdd3bee8cb6501558042633fa59144cc8b7f5f). +The header `dlpack.h` downloaded from `https://github.com/dmlc/dlpack.git` remote at tag v0.8 commit [`365b823`](https://github.com/dmlc/dlpack/commit/365b823cedb281cd0240ca601aba9b78771f91a3). The file can also be viewed using github web interface at https://github.com/dmlc/dlpack/blob/e2bdd3bee8cb6501558042633fa59144cc8b7f5f/include/dlpack/dlpack.h diff --git a/dpctl/tensor/include/dlpack/dlpack.h b/dpctl/tensor/include/dlpack/dlpack.h index 06a3625dba..6d51801123 100644 --- a/dpctl/tensor/include/dlpack/dlpack.h +++ b/dpctl/tensor/include/dlpack/dlpack.h @@ -16,7 +16,7 @@ #endif /*! \brief The current version of dlpack */ -#define DLPACK_VERSION 70 +#define DLPACK_VERSION 80 /*! \brief The current ABI version of dlpack */ #define DLPACK_ABI_VERSION 1 @@ -126,6 +126,8 @@ typedef enum { * (C/C++/Python layout: compact struct per complex number) */ kDLComplex = 5U, + /*! \brief boolean */ + kDLBool = 6U, } DLDataTypeCode; /*! @@ -134,10 +136,11 @@ typedef enum { * export an array with non-native endianness * * Examples - * - float: type_code = 2, bits = 32, lanes=1 - * - float4(vectorized 4 float): type_code = 2, bits = 32, lanes=4 - * - int8: type_code = 0, bits = 8, lanes=1 + * - float: type_code = 2, bits = 32, lanes = 1 + * - float4(vectorized 4 float): type_code = 2, bits = 32, lanes = 4 + * - int8: type_code = 0, bits = 8, lanes = 1 * - std::complex: type_code = 5, bits = 64, lanes = 1 + * - bool: type_code = 6, bits = 8, lanes = 1 (as per common array library convention, the underlying storage size of bool is 8 bits) */ typedef struct { /*! From 0bc9bacad0ca592cbe646ffbb4df5539dd100b0c Mon Sep 17 00:00:00 2001 From: Oleksandr Pavlyk Date: Thu, 16 Feb 2023 07:44:31 -0600 Subject: [PATCH 4/5] Changes to support kDLBool type added in DLPack v0.8 --- dpctl/tensor/_dlpack.pyx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/dpctl/tensor/_dlpack.pyx b/dpctl/tensor/_dlpack.pyx index 3fcb7a8974..c93bfb1c1c 100644 --- a/dpctl/tensor/_dlpack.pyx +++ b/dpctl/tensor/_dlpack.pyx @@ -71,6 +71,7 @@ cdef extern from 'dlpack/dlpack.h' nogil: kDLFloat kDLBfloat kDLComplex + kDLBool ctypedef struct DLDataType: uint8_t code @@ -244,7 +245,7 @@ cpdef to_dlpack_capsule(usm_ndarray usm_ary) except+: dl_tensor.dtype.lanes = 1 dl_tensor.dtype.bits = (ary_dt.itemsize * 8) if (ary_dtk == "b"): - dl_tensor.dtype.code = kDLUInt + dl_tensor.dtype.code = kDLBool elif (ary_dtk == "u"): dl_tensor.dtype.code = kDLUInt elif (ary_dtk == "i"): @@ -444,6 +445,8 @@ cpdef usm_ndarray from_dlpack_capsule(object py_caps) except +: ary_dt = np.dtype("f" + str(element_bytesize)) elif (dlm_tensor.dl_tensor.dtype.code == kDLComplex): ary_dt = np.dtype("c" + str(element_bytesize)) + elif (dlm_tensor.dl_tensor.dtype.code == kDLBool): + ary_dt = np.dtype("?") else: raise BufferError( "Can not import DLPack tensor with type code {}.".format( From 84b0232bbf075aee819703067308fc0a1004eeda Mon Sep 17 00:00:00 2001 From: Oleksandr Pavlyk Date: Thu, 16 Feb 2023 07:46:36 -0600 Subject: [PATCH 5/5] Removed exceptions from tests now that bool is supported by DLPack --- dpctl/tests/test_usm_ndarray_dlpack.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/dpctl/tests/test_usm_ndarray_dlpack.py b/dpctl/tests/test_usm_ndarray_dlpack.py index b534e59328..baf870c036 100644 --- a/dpctl/tests/test_usm_ndarray_dlpack.py +++ b/dpctl/tests/test_usm_ndarray_dlpack.py @@ -101,9 +101,7 @@ def test_from_dlpack(shape, typestr, usm_type): X = dpt.empty(shape, dtype=typestr, usm_type=usm_type, device=sycl_dev) Y = dpt.from_dlpack(X) assert X.shape == Y.shape - assert X.dtype == Y.dtype or ( - str(X.dtype) == "bool" and str(Y.dtype) == "uint8" - ) + assert X.dtype == Y.dtype assert X.sycl_device == Y.sycl_device assert X.usm_type == Y.usm_type assert X._pointer == Y._pointer @@ -125,9 +123,7 @@ def test_from_dlpack_strides(mod, typestr, usm_type): X = X0[slice(-start - 1, None, -mod)] Y = dpt.from_dlpack(X) assert X.shape == Y.shape - assert X.dtype == Y.dtype or ( - str(X.dtype) == "bool" and str(Y.dtype) == "uint8" - ) + assert X.dtype == Y.dtype assert X.sycl_device == Y.sycl_device assert X.usm_type == Y.usm_type assert X._pointer == Y._pointer