Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions onedal/datatypes/sycl_usm/data_conversion.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,11 @@ dal::table convert_to_homogen_impl(py::object obj) {
}

// Get `__sycl_usm_array_interface__['data'][0]`, the first element of data entry,
// which is a Python integer encoding USM pointer value.
const auto* const ptr = reinterpret_cast<const Type*>(get_sua_ptr(sua_iface_dict));
// which is a Python integer encoding USM pointer value. The `offset` field (in
// elements) is applied because sliced views keep the base allocation pointer in
// `data[0]` and encode their start position via the offset instead.
const auto offset = get_sua_offset(sua_iface_dict);
const auto* const ptr = reinterpret_cast<const Type*>(get_sua_ptr(sua_iface_dict)) + offset;

// Get SYCL object from `__sycl_usm_array_interface__["syclobj"]`.
// syclobj: Python object from which SYCL context to which represented USM
Expand Down
11 changes: 11 additions & 0 deletions onedal/datatypes/sycl_usm/sycl_usm_utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,17 @@ bool is_sua_readonly(const py::dict& sua) {
return data[1ul].cast<bool>();
}

// Get `__sycl_usm_array_interface__['offset']`, the offset in elements from the start
// of the USM allocation to the first array element. The field is optional and defaults
// to zero; sliced views (e.g. dpnp X[a:b]) report a non-zero offset while keeping the
// same base pointer in `data[0]`, so it must be applied to avoid reading the wrong rows.
std::int64_t get_sua_offset(const py::dict& sua) {
if (!sua.contains("offset") || sua["offset"].is_none()) {
return 0l;
}
return sua["offset"].cast<std::int64_t>();
}
Comment on lines +96 to +101

// Get `__sycl_usm_array_interface__['shape']`.
// shape : a tuple of integers describing dimensions of an N-dimensional array.
py::tuple get_sua_shape(const py::dict& sua) {
Expand Down
2 changes: 2 additions & 0 deletions onedal/datatypes/sycl_usm/sycl_usm_utils.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ std::uintptr_t get_sua_ptr(const py::dict& sua);

bool is_sua_readonly(const py::dict& sua);

std::int64_t get_sua_offset(const py::dict& sua);

py::tuple get_sua_shape(const py::dict& sua);

void report_problem_for_sua_iface(const char* clarification);
Expand Down
27 changes: 27 additions & 0 deletions onedal/datatypes/tests/test_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
from onedal.cluster.dbscan import DBSCAN
from onedal.primitives import linear_kernel
from onedal.tests.utils._dataframes_support import (
_as_numpy,
_convert_to_dataframe,
array_api_modules,
get_dataframes_and_queues,
Expand Down Expand Up @@ -228,6 +229,32 @@ def test_input_zero_copy_sycl_usm(dataframe, queue, order, dtype):
_assert_tensor_attr(X_dp, X_dp_from_table, order)


@pytest.mark.skipif(
not dpctl_available,
reason="dpctl is required for checks.",
)
@pytest.mark.skipif(
not backend.is_dpc,
reason="__sycl_usm_array_interface__ support requires DPC backend.",
)
@pytest.mark.parametrize("dataframe,queue", get_dataframes_and_queues("dpnp", "cpu,gpu"))
@pytest.mark.parametrize("start", [1, 3])
@pytest.mark.parametrize("dtype", [np.float32, np.float64])
def test_sliced_view_offset_sycl_usm(dataframe, queue, start, dtype):
"""A sliced view (e.g. X[start:]) keeps the base allocation pointer in
`__sycl_usm_array_interface__['data'][0]` and encodes its start via the
`offset` field. The table conversion must apply that offset, otherwise the
view reads from row 0 (regression test for the SUA offset bug).
"""
X_np = np.arange(50, dtype=dtype).reshape(10, 5)
X_dp = _convert_to_dataframe(X_np, sycl_queue=queue, target_df=dataframe)

X_view = X_dp[start:]
Comment thread
david-cortes-intel marked this conversation as resolved.
Outdated
X_roundtrip = from_table(to_table(X_view), like=X_dp)

assert_allclose(_as_numpy(X_roundtrip), X_np[start:])


@pytest.mark.skipif(
not dpctl_available,
reason="dpctl is required for checks.",
Expand Down
Loading