Skip to content

Commit 6cc2348

Browse files
authored
Add missing stream keyword argument to dpnp.ndarray.to_device method (#2263)
The PR proposes to align `dpnp.ndarray.to_device` method with python array API and to add support of `stream` keyword argument. The tests coverage is extended to cover new argument. Previously muted array-api test is enabled back. Note, tests for `dpnp.__dlpack__` are updated due to recent changes done in [dpctl#1969](IntelPython/dpctl#1969).
1 parent 952a798 commit 6cc2348

File tree

5 files changed

+120
-29
lines changed

5 files changed

+120
-29
lines changed

.github/workflows/array-api-skips.txt

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,6 @@ array_api_tests/test_sorting_functions.py::test_sort
5757
array_api_tests/test_signatures.py::test_func_signature[std]
5858
array_api_tests/test_signatures.py::test_func_signature[var]
5959

60-
# missing 'stream' keyword argument
61-
array_api_tests/test_signatures.py::test_array_method_signature[to_device]
62-
6360
# wrong shape is returned
6461
array_api_tests/test_linalg.py::test_vecdot
6562
array_api_tests/test_linalg.py::test_linalg_vecdot

dpnp/dpnp_array.py

Lines changed: 42 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -153,13 +153,6 @@ def mT(self):
153153

154154
return dpnp_array._create_from_usm_ndarray(self._array_obj.mT)
155155

156-
def to_device(self, target_device):
157-
"""Transfer array to target device."""
158-
159-
return dpnp_array(
160-
shape=self.shape, buffer=self.get_array().to_device(target_device)
161-
)
162-
163156
@property
164157
def sycl_queue(self):
165158
return self._array_obj.sycl_queue
@@ -1693,6 +1686,48 @@ def take(self, indices, axis=None, out=None, mode="wrap"):
16931686

16941687
return dpnp.take(self, indices, axis=axis, out=out, mode=mode)
16951688

1689+
def to_device(self, device, /, *, stream=None):
1690+
"""
1691+
Transfers this array to specified target device.
1692+
1693+
Parameters
1694+
----------
1695+
device : {string, SyclDevice, SyclQueue}
1696+
Array API concept of target device. It can be an OneAPI filter
1697+
selector string, an instance of :class:`dpctl.SyclDevice`
1698+
corresponding to a non-partitioned SYCL device, an instance of
1699+
:class:`dpctl.SyclQueue`, or a :class:`dpctl.tensor.Device` object
1700+
returned by :obj:`dpnp.dpnp_array.dpnp_array.device` property.
1701+
stream : {SyclQueue, None}, optional
1702+
Execution queue to synchronize with. If ``None``, synchronization
1703+
is not performed.
1704+
Default: ``None``.
1705+
1706+
Returns
1707+
-------
1708+
out : dpnp.ndarray
1709+
A view if data copy is not required, and a copy otherwise.
1710+
If copying is required, it is done by copying from the original
1711+
allocation device to the host, followed by copying from host
1712+
to the target device.
1713+
1714+
Examples
1715+
--------
1716+
>>> import dpnp as np, dpctl
1717+
>>> x = np.full(100, 2, dtype=np.int64)
1718+
>>> q_prof = dpctl.SyclQueue(x.sycl_device, property="enable_profiling")
1719+
>>> # return a view with profile-enabled queue
1720+
>>> y = x.to_device(q_prof)
1721+
>>> timer = dpctl.SyclTimer()
1722+
>>> with timer(q_prof):
1723+
... z = y * y
1724+
>>> print(timer.dt)
1725+
1726+
"""
1727+
1728+
usm_res = self._array_obj.to_device(device, stream=stream)
1729+
return dpnp_array._create_from_usm_ndarray(usm_res)
1730+
16961731
# 'tobytes',
16971732
# 'tofile',
16981733
# 'tolist',

dpnp/tests/test_dlpack.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1+
import dpctl
12
import numpy
23
import pytest
3-
from numpy.testing import assert_array_equal
4+
from numpy.testing import assert_array_equal, assert_raises
45

56
import dpnp
67

@@ -10,11 +11,20 @@
1011

1112

1213
class TestDLPack:
13-
@pytest.mark.parametrize("stream", [None, 1])
14+
@pytest.mark.parametrize("stream", [None, dpctl.SyclQueue()])
1415
def test_stream(self, stream):
1516
x = dpnp.arange(5)
1617
x.__dlpack__(stream=stream)
1718

19+
@pytest.mark.parametrize(
20+
"stream",
21+
[1, dict(), dpctl.SyclDevice()],
22+
ids=["scalar", "dictionary", "device"],
23+
)
24+
def test_invaid_stream(self, stream):
25+
x = dpnp.arange(5)
26+
assert_raises(TypeError, x.__dlpack__, stream=stream)
27+
1828
@pytest.mark.parametrize("copy", [True, None, False])
1929
def test_copy(self, copy):
2030
x = dpnp.arange(5)

dpnp/tests/test_sycl_queue.py

Lines changed: 59 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1932,23 +1932,66 @@ def test_svd(shape, full_matrices, compute_uv, device):
19321932
assert_sycl_queue_equal(dpnp_s_queue, expected_queue)
19331933

19341934

1935-
@pytest.mark.parametrize(
1936-
"device_from",
1937-
valid_devices,
1938-
ids=[device.filter_string for device in valid_devices],
1939-
)
1940-
@pytest.mark.parametrize(
1941-
"device_to",
1942-
valid_devices,
1943-
ids=[device.filter_string for device in valid_devices],
1944-
)
1945-
def test_to_device(device_from, device_to):
1946-
data = [1.0, 1.0, 1.0, 1.0, 1.0]
1947-
1948-
x = dpnp.array(data, dtype=dpnp.float32, device=device_from)
1949-
y = x.to_device(device_to)
1935+
class TestToDevice:
1936+
@pytest.mark.parametrize(
1937+
"device_from",
1938+
valid_devices,
1939+
ids=[device.filter_string for device in valid_devices],
1940+
)
1941+
@pytest.mark.parametrize(
1942+
"device_to",
1943+
valid_devices,
1944+
ids=[device.filter_string for device in valid_devices],
1945+
)
1946+
def test_basic(self, device_from, device_to):
1947+
data = [1.0, 1.0, 1.0, 1.0, 1.0]
1948+
x = dpnp.array(data, dtype=dpnp.float32, device=device_from)
1949+
1950+
y = x.to_device(device_to)
1951+
assert y.sycl_device == device_to
1952+
assert (x.asnumpy() == y.asnumpy()).all()
1953+
1954+
def test_to_queue(self):
1955+
x = dpnp.full(100, 2, dtype=dpnp.int64)
1956+
q_prof = dpctl.SyclQueue(x.sycl_device, property="enable_profiling")
1957+
1958+
y = x.to_device(q_prof)
1959+
assert (x.asnumpy() == y.asnumpy()).all()
1960+
assert_sycl_queue_equal(y.sycl_queue, q_prof)
1961+
1962+
def test_stream(self):
1963+
x = dpnp.full(100, 2, dtype=dpnp.int64)
1964+
q_prof = dpctl.SyclQueue(x.sycl_device, property="enable_profiling")
1965+
q_exec = dpctl.SyclQueue(x.sycl_device)
1966+
1967+
y = x.to_device(q_prof, stream=q_exec)
1968+
assert (x.asnumpy() == y.asnumpy()).all()
1969+
assert_sycl_queue_equal(y.sycl_queue, q_prof)
1970+
1971+
q_exec = dpctl.SyclQueue(x.sycl_device)
1972+
_ = dpnp.linspace(0, 20, num=10**5, sycl_queue=q_exec)
1973+
y = x.to_device(q_prof, stream=q_exec)
1974+
assert (x.asnumpy() == y.asnumpy()).all()
1975+
assert_sycl_queue_equal(y.sycl_queue, q_prof)
1976+
1977+
def test_stream_no_sync(self):
1978+
x = dpnp.full(100, 2, dtype=dpnp.int64)
1979+
q_prof = dpctl.SyclQueue(x.sycl_device, property="enable_profiling")
1980+
1981+
for stream in [None, x.sycl_queue]:
1982+
y = x.to_device(q_prof, stream=stream)
1983+
assert (x.asnumpy() == y.asnumpy()).all()
1984+
assert_sycl_queue_equal(y.sycl_queue, q_prof)
19501985

1951-
assert y.sycl_device == device_to
1986+
@pytest.mark.parametrize(
1987+
"stream",
1988+
[1, dict(), dpctl.SyclDevice()],
1989+
ids=["scalar", "dictionary", "device"],
1990+
)
1991+
def test_invalid_stream(self, stream):
1992+
x = dpnp.ones(2, dtype=dpnp.int64)
1993+
q_prof = dpctl.SyclQueue(x.sycl_device, property="enable_profiling")
1994+
assert_raises(TypeError, x.to_device, q_prof, stream=stream)
19521995

19531996

19541997
@pytest.mark.parametrize(

dpnp/tests/third_party/cupy/core_tests/test_dlpack.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,13 @@ def test_stream(self):
201201
for src_s in [self._get_stream(s) for s in allowed_streams]:
202202
for dst_s in [self._get_stream(s) for s in allowed_streams]:
203203
orig_array = _gen_array(cupy.float32, alloc_q=src_s)
204-
dltensor = orig_array.__dlpack__(stream=orig_array)
204+
205+
q = dpctl.SyclQueue(
206+
orig_array.sycl_context,
207+
orig_array.sycl_device,
208+
property="enable_profiling",
209+
)
210+
dltensor = orig_array.__dlpack__(stream=q)
205211

206212
out_array = dlp.from_dlpack_capsule(dltensor)
207213
out_array = cupy.from_dlpack(out_array, device=dst_s)

0 commit comments

Comments
 (0)