From 16c2d3bfffb15a8a52ba17f4512569fec8038d26 Mon Sep 17 00:00:00 2001 From: zachjweiner Date: Mon, 26 Aug 2019 15:16:59 -0500 Subject: [PATCH 1/6] adds get_async and doc, untested --- pyopencl/array.py | 76 ++++++++++++++++++++++++++++++++++++----------- 1 file changed, 58 insertions(+), 18 deletions(-) diff --git a/pyopencl/array.py b/pyopencl/array.py index 24c6ded6..7fc8c833 100644 --- a/pyopencl/array.py +++ b/pyopencl/array.py @@ -305,6 +305,7 @@ class Array(object): .. attribute :: T .. automethod :: set .. automethod :: get + .. automethod :: get_async .. automethod :: copy .. automethod :: __str__ @@ -375,6 +376,12 @@ class Array(object): care of its own operation ordering. The facilities in this section make this possible. + .. note:: + + Currently, read and write events are not distinguished. + As a result, e.g., read-only operations will needlessly wait on other + read-only operations (i.e., executed among asynchronous queues). + .. versionadded:: 2014.1.1 .. attribute:: events @@ -627,23 +634,7 @@ class Array(object): is_blocking=not async_) self.add_event(event1) - def get(self, queue=None, ary=None, async_=None, **kwargs): - """Transfer the contents of *self* into *ary* or a newly allocated - :mod:`numpy.ndarray`. If *ary* is given, it must have the same - shape and dtype. - - .. versionchanged:: 2015.2 - - *ary* with different shape was deprecated. - - .. versionchanged:: 2017.2.1 - - Python 3.7 makes ``async`` a reserved keyword. On older Pythons, - we will continue to accept *async* as a parameter, however this - should be considered deprecated. *async_* is the new, official - spelling. - """ - + def _get(self, queue=None, ary=None, async_=None, **kwargs): # {{{ handle 'async' deprecation async_arg = kwargs.pop("async", None) @@ -688,12 +679,61 @@ class Array(object): "to associate one.") if self.size: - cl.enqueue_copy(queue, ary, self.base_data, + event1 = cl.enqueue_copy(queue, ary, self.base_data, device_offset=self.offset, wait_for=self.events, is_blocking=not async_) + self.add_event(event1) + else: + event1 = None + + return ary, event1 + + def get(self, queue=None, ary=None, async_=None, **kwargs): + """Transfer the contents of *self* into *ary* or a newly allocated + :mod:`numpy.ndarray`. If *ary* is given, it must have the same + shape and dtype. + + .. versionchanged:: 2019.1 + + Calling with `async_=True` was deprecated and replaced by + :meth:`get_async`. + The event returned by :meth:`pyopencl.enqueue_copy` is now stored into + :attr:`events` to ensure data is not modified before the copy is + complete. + + .. versionchanged:: 2015.2 + + *ary* with different shape was deprecated. + + .. versionchanged:: 2017.2.1 + + Python 3.7 makes ``async`` a reserved keyword. On older Pythons, + we will continue to accept *async* as a parameter, however this + should be considered deprecated. *async_* is the new, official + spelling. + """ + + if async_: + from warnings import warn + warn("calling pyopencl.Array.get with `async_=True` is deprecated. " + "Please use pyopencl.Array.get_async for asynchronous " + "device-to-host transfers", + DeprecationWarning, 2) + + ary, event1 = self.get(queue=queue, ary=ary, async_=async_, **kwargs) return ary + def get_async(self, queue=None, ary=None, **kwargs): + """ + Asynchronous version of :meth:`get`, following the same calling convention + while returning a tuple ``(ary, event)`` containing the host array `ary` + and the :class:`pyopencl.NannyEvent` `event` returned by + :meth:`pyopencl.enqueue_copy`. + """ + + return self.get(queue=queue, ary=ary, async_=True, **kwargs) + def copy(self, queue=_copy_queue): """ :arg queue: The :class:`CommandQueue` for the returned array. -- GitLab From 0cb766c0805ba78a6730eebf8ad25d56f50235e3 Mon Sep 17 00:00:00 2001 From: zachjweiner Date: Mon, 26 Aug 2019 15:32:38 -0500 Subject: [PATCH 2/6] forgot to call private get method --- pyopencl/array.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyopencl/array.py b/pyopencl/array.py index 7fc8c833..c1b132cc 100644 --- a/pyopencl/array.py +++ b/pyopencl/array.py @@ -720,7 +720,7 @@ class Array(object): "device-to-host transfers", DeprecationWarning, 2) - ary, event1 = self.get(queue=queue, ary=ary, async_=async_, **kwargs) + ary, event1 = self._get(queue=queue, ary=ary, async_=async_, **kwargs) return ary @@ -732,7 +732,7 @@ class Array(object): :meth:`pyopencl.enqueue_copy`. """ - return self.get(queue=queue, ary=ary, async_=True, **kwargs) + return self._get(queue=queue, ary=ary, async_=True, **kwargs) def copy(self, queue=_copy_queue): """ -- GitLab From 51d03c14697b9d178d6246cc9233c591862363ba Mon Sep 17 00:00:00 2001 From: zachjweiner Date: Tue, 27 Aug 2019 10:03:23 -0500 Subject: [PATCH 3/6] add async tests --- pyopencl/array.py | 4 ++-- test/test_array.py | 26 ++++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/pyopencl/array.py b/pyopencl/array.py index c1b132cc..72d2a0cf 100644 --- a/pyopencl/array.py +++ b/pyopencl/array.py @@ -726,8 +726,8 @@ class Array(object): def get_async(self, queue=None, ary=None, **kwargs): """ - Asynchronous version of :meth:`get`, following the same calling convention - while returning a tuple ``(ary, event)`` containing the host array `ary` + Asynchronous version of :meth:`get` which returns a tuple ``(ary, event)`` + containing the host array `ary` and the :class:`pyopencl.NannyEvent` `event` returned by :meth:`pyopencl.enqueue_copy`. """ diff --git a/test/test_array.py b/test/test_array.py index 02e43e24..cf63fc14 100644 --- a/test/test_array.py +++ b/test/test_array.py @@ -1217,6 +1217,32 @@ def test_multi_put(ctx_factory): assert np.all(np.all(out_compare[i] == out_arrays[i].get()) for i in range(9)) +def test_get_async(ctx_factory): + context = ctx_factory() + queue = cl.CommandQueue(context) + + a = np.random.rand(10**6).astype(np.dtype('float32')) + a_gpu = cl_array.to_device(queue, a) + b = a + a**5 + 1 + b_gpu = a_gpu + a_gpu**5 + 1 + + # deprecated, but still test + b1 = b_gpu.get(async_=True) # testing that this waits for events + b_gpu.finish() + assert np.abs(b1 - b).mean() < 1e-5 + + b1 = b_gpu.get_async() # testing that this waits for events + b_gpu.finish() + assert np.abs(b1 - b).mean() < 1e-5 + + wait_event = cl.UserEvent(context) + b_gpu.add_event(wait_event) + b = b_gpu.get_async() # testing that this doesn't hang + wait_event.set_status(cl.command_execution_status.COMPLETE) + b_gpu.finish() + assert np.abs(b1 - b).mean() < 1e-5 + + def test_outoforderqueue_get(ctx_factory): context = ctx_factory() try: -- GitLab From c8a819cea914fcec7ff4c855926373f17a070bcf Mon Sep 17 00:00:00 2001 From: zachjweiner Date: Tue, 27 Aug 2019 10:10:59 -0500 Subject: [PATCH 4/6] correct get_async test --- test/test_array.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/test_array.py b/test/test_array.py index cf63fc14..e9fb2ddd 100644 --- a/test/test_array.py +++ b/test/test_array.py @@ -1231,15 +1231,15 @@ def test_get_async(ctx_factory): b_gpu.finish() assert np.abs(b1 - b).mean() < 1e-5 - b1 = b_gpu.get_async() # testing that this waits for events - b_gpu.finish() + b1, evt = b_gpu.get_async() # testing that this waits for events + evt.wait() assert np.abs(b1 - b).mean() < 1e-5 wait_event = cl.UserEvent(context) b_gpu.add_event(wait_event) - b = b_gpu.get_async() # testing that this doesn't hang + b, evt = b_gpu.get_async() # testing that this doesn't hang wait_event.set_status(cl.command_execution_status.COMPLETE) - b_gpu.finish() + evt.wait() assert np.abs(b1 - b).mean() < 1e-5 -- GitLab From ec5ca5d58fa5c2b7adc4e877870015ddfaf5ab31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20Kl=C3=B6ckner?= Date: Mon, 21 Oct 2019 16:48:18 +0200 Subject: [PATCH 5/6] Remove disputed note about read-after-read sync, gh-303 --- pyopencl/array.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/pyopencl/array.py b/pyopencl/array.py index 72d2a0cf..9f2cb675 100644 --- a/pyopencl/array.py +++ b/pyopencl/array.py @@ -376,12 +376,6 @@ class Array(object): care of its own operation ordering. The facilities in this section make this possible. - .. note:: - - Currently, read and write events are not distinguished. - As a result, e.g., read-only operations will needlessly wait on other - read-only operations (i.e., executed among asynchronous queues). - .. versionadded:: 2014.1.1 .. attribute:: events -- GitLab From 466c27488935553180840dc337e667ae4f2173db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20Kl=C3=B6ckner?= Date: Mon, 21 Oct 2019 16:51:21 +0200 Subject: [PATCH 6/6] Fix version change documentation for Array.get_async and related Array.get change --- pyopencl/array.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pyopencl/array.py b/pyopencl/array.py index 9f2cb675..3805d2d8 100644 --- a/pyopencl/array.py +++ b/pyopencl/array.py @@ -687,7 +687,7 @@ class Array(object): :mod:`numpy.ndarray`. If *ary* is given, it must have the same shape and dtype. - .. versionchanged:: 2019.1 + .. versionchanged:: 2019.1.2 Calling with `async_=True` was deprecated and replaced by :meth:`get_async`. @@ -724,6 +724,8 @@ class Array(object): containing the host array `ary` and the :class:`pyopencl.NannyEvent` `event` returned by :meth:`pyopencl.enqueue_copy`. + + .. versionadded:: 2019.1.2 """ return self._get(queue=queue, ary=ary, async_=True, **kwargs) -- GitLab