From b3a8226df4822888c5035ebf01e5254248cdfc59 Mon Sep 17 00:00:00 2001 From: Andreas Kloeckner Date: Sun, 14 Jul 2013 16:06:19 -0400 Subject: [PATCH] Add Array.map_to_host(), plus infrastructure --- doc/runtime.rst | 12 ++++++++++-- pyopencl/array.py | 35 +++++++++++++++++++++++++++++++++- src/wrapper/wrap_cl.hpp | 16 +++++++++++----- src/wrapper/wrap_cl_part_2.cpp | 2 ++ src/wrapper/wrap_helpers.hpp | 8 +++++++- test/test_array.py | 18 +++++++++++++++++ 6 files changed, 82 insertions(+), 9 deletions(-) diff --git a/doc/runtime.rst b/doc/runtime.rst index aae5b790..905f06f1 100644 --- a/doc/runtime.rst +++ b/doc/runtime.rst @@ -534,12 +534,13 @@ Mapping Memory into Host Address Space .. method:: release(queue=None, wait_for=None) -.. function:: enqueue_map_buffer(queue, buf, flags, offset, shape, dtype, order="C", wait_for=None, is_blocking=True) +.. function:: enqueue_map_buffer(queue, buf, flags, offset, shape, dtype, order="C", strides=None, wait_for=None, is_blocking=True) |explain-waitfor| *shape*, *dtype*, and *order* have the same meaning as in :func:`numpy.empty`. See :class:`map_flags` for possible values of *flags*. + *strides*, if given, overrides *order*. :return: a tuple *(array, event)*. *array* is a :class:`numpy.ndarray` representing the host side @@ -552,12 +553,16 @@ Mapping Memory into Host Address Space .. versionchanged:: 2013.1 *order* now defaults to "C". -.. function:: enqueue_map_image(queue, buf, flags, origin, region, shape, dtype, order="C", wait_for=None, is_blocking=True) + .. versionchanged:: 2013.2 + Added *strides* argument. + +.. function:: enqueue_map_image(queue, buf, flags, origin, region, shape, dtype, order="C", strides=None, wait_for=None, is_blocking=True) |explain-waitfor| *shape*, *dtype*, and *order* have the same meaning as in :func:`numpy.empty`. See :class:`map_flags` for possible values of *flags*. + *strides*, if given, overrides *order*. :return: a tuple *(array, event)*. *array* is a :class:`numpy.ndarray` representing the host side @@ -570,6 +575,9 @@ Mapping Memory into Host Address Space .. versionchanged:: 2013.1 *order* now defaults to "C". + .. versionchanged:: 2013.2 + Added *strides* argument. + Samplers ^^^^^^^^ diff --git a/pyopencl/array.py b/pyopencl/array.py index 69f81f6c..e1d7b40a 100644 --- a/pyopencl/array.py +++ b/pyopencl/array.py @@ -1179,7 +1179,7 @@ class Array(object): shape=shape, dtype=dtype, strides=strides) - # }} + # }}} def finish(self): # undoc @@ -1187,6 +1187,37 @@ class Array(object): cl.wait_for_events(self.events) del self.events[:] + def map_to_host(self, queue=None, flags=None, is_blocking=True, wait_for=None): + """If *is_blocking*, return a :class:`numpy.ndarray` corresponding to the + same memory as *self*. + + If *is_blocking* is not true, return a tuple ``(ary, evt)``, where + *ary* is the above-mentioned array. + + The host array is obtained using :func:`pyopencl.enqueue_map_buffer`. + See there for further details. + + :arg flags: A combination of :class:`pyopencl.map_flags`. + Defaults to read-write. + + .. versionadded :: 2013.2 + """ + + if flags is None: + flags = cl.map_flags.READ | cl.map_flags.WRITE + + ary, evt = cl.enqueue_map_buffer( + queue or self.queue, self.base_data, flags, self.offset, + self.shape, self.dtype, strides=self.strides, wait_for=wait_for, + is_blocking=is_blocking) + + if is_blocking: + return ary + else: + return ary, evt + + # {{{ getitem/setitem + def __getitem__(self, index): """ .. versionadded:: 2013.1 @@ -1348,6 +1379,8 @@ class Array(object): """ self.setitem(subscript, value) + # }}} + # }}} diff --git a/src/wrapper/wrap_cl.hpp b/src/wrapper/wrap_cl.hpp index cc7741e5..d246f150 100644 --- a/src/wrapper/wrap_cl.hpp +++ b/src/wrapper/wrap_cl.hpp @@ -2618,7 +2618,8 @@ namespace pyopencl memory_object_holder &buf, cl_map_flags flags, size_t offset, - py::object py_shape, py::object dtype, py::object order_py, + py::object py_shape, py::object dtype, + py::object py_order, py::object py_strides, py::object py_wait_for, bool is_blocking ) @@ -2656,12 +2657,14 @@ namespace pyopencl { result = py::handle<>(PyArray_NewFromDescr( &PyArray_Type, tp_descr, - shape.size(), shape.empty( ) ? NULL : &shape.front(), /*strides*/ NULL, + shape.size(), + shape.empty() ? NULL : &shape.front(), + strides.empty() ? NULL : &strides.front(), mapped, ary_flags, /*obj*/NULL)); if (size_in_bytes != (npy_uintp) PyArray_NBYTES(result.get())) throw pyopencl::error("enqueue_map_buffer", CL_INVALID_VALUE, - "miscalculated numpy array size"); + "miscalculated numpy array size (not contiguous?)"); map = std::auto_ptr(new memory_map(cq, buf, mapped)); } @@ -2691,7 +2694,8 @@ namespace pyopencl cl_map_flags flags, py::object py_origin, py::object py_region, - py::object py_shape, py::object dtype, py::object order_py, + py::object py_shape, py::object dtype, + py::object py_order, py::object py_strides, py::object py_wait_for, bool is_blocking ) @@ -2734,7 +2738,9 @@ namespace pyopencl py::handle<> result = py::handle<>(PyArray_NewFromDescr( &PyArray_Type, tp_descr, - shape.size(), shape.empty( ) ? NULL : &shape.front(), /*strides*/ NULL, + shape.size(), + shape.empty() ? NULL : &shape.front(), + strides.empty() ? NULL : &strides.front(), mapped, ary_flags, /*obj*/NULL)); py::handle<> map_py(handle_from_new_ptr(map.release())); diff --git a/src/wrapper/wrap_cl_part_2.cpp b/src/wrapper/wrap_cl_part_2.cpp index 6049c435..87b60800 100644 --- a/src/wrapper/wrap_cl_part_2.cpp +++ b/src/wrapper/wrap_cl_part_2.cpp @@ -153,6 +153,7 @@ void pyopencl_expose_part_2() "offset", "shape", "dtype"), py::arg("order")="C", + py::arg("strides")=py::object(), py::arg("wait_for")=py::object(), py::arg("is_blocking")=true)); py::def("enqueue_map_image", enqueue_map_image, @@ -160,6 +161,7 @@ void pyopencl_expose_part_2() "origin", "region", "shape", "dtype"), py::arg("order")="C", + py::arg("strides")=py::object(), py::arg("wait_for")=py::object(), py::arg("is_blocking")=true)); diff --git a/src/wrapper/wrap_helpers.hpp b/src/wrapper/wrap_helpers.hpp index 002f6d41..69e666f4 100644 --- a/src/wrapper/wrap_helpers.hpp +++ b/src/wrapper/wrap_helpers.hpp @@ -109,7 +109,7 @@ namespace py = boost::python; COPY_PY_LIST(npy_intp, shape); \ \ NPY_ORDER order = PyArray_CORDER; \ - PyArray_OrderConverter(order_py.ptr(), &order); \ + PyArray_OrderConverter(py_order.ptr(), &order); \ \ int ary_flags = 0; \ if (order == PyArray_FORTRANORDER) \ @@ -118,6 +118,12 @@ namespace py = boost::python; ary_flags |= NPY_CARRAY; \ else \ throw std::runtime_error("unrecognized order specifier"); \ + \ + std::vector strides; \ + if (py_strides.ptr() != Py_None) \ + { \ + COPY_PY_LIST(npy_intp, strides); \ + } #define PYOPENCL_RETURN_VECTOR(ITEMTYPE, NAME) \ { \ diff --git a/test/test_array.py b/test/test_array.py index 3ff5030b..62904dce 100644 --- a/test/test_array.py +++ b/test/test_array.py @@ -679,6 +679,24 @@ def test_any_all(ctx_factory): # }}} +@pytools.test.mark_test.opencl +def test_map_to_host(ctx_factory): + context = ctx_factory() + queue = cl.CommandQueue(context) + + a_dev = cl_array.zeros(queue, (5, 6, 7,), dtype=np.float32) + a_host = a_dev.map_to_host() + a_host[1, 2, 3] = 10 + a_dev[3, 2, 1] = 10 + + a_dev.finish() + + a_host_saved = a_host.copy() + + assert (a_host_saved == a_dev.get()).all() + assert (a_host == a_dev.get()).all() + + if __name__ == "__main__": # make sure that import failures get reported, instead of skipping the # tests. -- GitLab