From bbf8a1c0d8ae09253d7edb50ad16baa3be6db7bc Mon Sep 17 00:00:00 2001 From: Casper da Costa-Luis Date: Sat, 1 Apr 2023 00:57:06 +0100 Subject: [PATCH 1/8] build: add swig to requirements --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index e476125..9d9d6f9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [build-system] requires = ["setuptools>=42", "wheel", "setuptools_scm[toml]>=3.4", "miutil[cuda]>=0.4.0", - "scikit-build>=0.11.0", "cmake>=3.18", "ninja"] + "scikit-build>=0.11.0", "cmake>=3.18", "ninja", "swig"] build-backend = "setuptools.build_meta" [tool.setuptools_scm] From df21d9e1ccb8ab878cedf9bd87245e6ede7501b0 Mon Sep 17 00:00:00 2001 From: Casper da Costa-Luis Date: Tue, 16 May 2023 19:03:23 +0530 Subject: [PATCH 2/8] swigcuvec: add retarray convenience function --- cuvec/include/cuvec.i | 3 +-- cuvec/swigcuvec.py | 17 ++++++++++++++--- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/cuvec/include/cuvec.i b/cuvec/include/cuvec.i index 701c848..b60efce 100644 --- a/cuvec/include/cuvec.i +++ b/cuvec/include/cuvec.i @@ -3,11 +3,10 @@ * for external use via `%include "cuvec.i"`. */ %include "std_vector.i" - %{ #include "cuvec.cuh" // SwigCuVec %} - +/// expose definitions template struct SwigCuVec { CuVec vec; std::vector shape; diff --git a/cuvec/swigcuvec.py b/cuvec/swigcuvec.py index 7dd5f16..9f91ba2 100644 --- a/cuvec/swigcuvec.py +++ b/cuvec/swigcuvec.py @@ -181,9 +181,9 @@ def asarray(arr, dtype=None, order=None, ownership: str = 'warning') -> CuVec: >>> res = asarray(some_swig_api_func(..., output=getattr(out, 'cuvec', None))) `res.cuvec` and `out.cuvec` are now the same yet garbage collected separately (dangling ptr). - Instead, use: - >>> res = some_swig_api_func(..., output=getattr(out, 'cuvec', None)) - >>> res = out if hasattr(out, 'cuvec') else asarray(res) + Instead, use the `retarray` helper: + >>> raw = some_swig_api_func(..., output=getattr(out, 'cuvec', None)) + >>> res = retarray(raw, out) NB: `asarray()` is safe if the raw cuvec was created in C++/SWIG, e.g.: >>> res = asarray(some_swig_api_func(..., output=None), ownership='debug') """ @@ -198,3 +198,14 @@ def asarray(arr, dtype=None, order=None, ownership: str = 'warning') -> CuVec: if dtype is None or res.dtype == np.dtype(dtype): return CuVec(np.asanyarray(res, order=order)) return CuVec(np.asanyarray(arr, dtype=dtype, order=order)) + + +def retarray(raw, out: Optional[CuVec] = None): + """ + Returns `out if hasattr(out, 'cuvec') else asarray(raw, ownership='debug')`. + See `asarray` for explanation. + Args: + raw: a raw CuVec (returned by C++/SWIG function). + out: preallocated output array. + """ + return out if hasattr(out, 'cuvec') else asarray(raw, ownership='debug') From df1913af29a193f1f9157af95c094e9de8ae404a Mon Sep 17 00:00:00 2001 From: Casper da Costa-Luis Date: Tue, 16 May 2023 20:45:31 +0530 Subject: [PATCH 3/8] tests: increase coverage --- cuvec/swigcuvec.py | 4 ++-- tests/test_swigcuvec.py | 11 +++++++++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/cuvec/swigcuvec.py b/cuvec/swigcuvec.py index 9f91ba2..f2a45c6 100644 --- a/cuvec/swigcuvec.py +++ b/cuvec/swigcuvec.py @@ -184,8 +184,8 @@ def asarray(arr, dtype=None, order=None, ownership: str = 'warning') -> CuVec: Instead, use the `retarray` helper: >>> raw = some_swig_api_func(..., output=getattr(out, 'cuvec', None)) >>> res = retarray(raw, out) - NB: `asarray()` is safe if the raw cuvec was created in C++/SWIG, e.g.: - >>> res = asarray(some_swig_api_func(..., output=None), ownership='debug') + NB: `asarray()`/`retarray()` are safe if the raw cuvec was created in C++/SWIG, e.g.: + >>> res = retarray(some_swig_api_func(..., output=None)) """ if is_raw_cuvec(arr): ownership = ownership.lower() diff --git a/tests/test_swigcuvec.py b/tests/test_swigcuvec.py index 21cbec3..a2aee94 100644 --- a/tests/test_swigcuvec.py +++ b/tests/test_swigcuvec.py @@ -158,5 +158,12 @@ def test_increment(): a[:] = 0 assert (a == 0).all() - res = cu.asarray(increment2d_f(a.cuvec), ownership='debug') - assert (res == 1).all() + b = cu.retarray(increment2d_f(a.cuvec)) + assert (b == 1).all() + + c = cu.retarray(increment2d_f(b.cuvec, a.cuvec), a) + assert (a == 2).all() + assert c.cuvec == a.cuvec + assert (c == a).all() + assert str(c.swvec) == str(a.swvec) + assert np.asarray(c.swvec).data == np.asarray(a.swvec).data From 56c82a987a00c6ec4033314a87ec779e0d88f2df Mon Sep 17 00:00:00 2001 From: Casper da Costa-Luis Date: Tue, 16 May 2023 21:50:41 +0100 Subject: [PATCH 4/8] build: only find Python Development.Module --- cuvec/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cuvec/CMakeLists.txt b/cuvec/CMakeLists.txt index 198e58d..909acd4 100644 --- a/cuvec/CMakeLists.txt +++ b/cuvec/CMakeLists.txt @@ -9,7 +9,7 @@ option(CUVEC_CUDA_OPTIONAL "Make CUDA optional rather than forced" ON) cmake_policy(PUSH) cmake_policy(SET CMP0074 NEW) # _ROOT hints for find_package cmake_policy(SET CMP0104 NEW) # CMAKE_CUDA_ARCHITECTURES -find_package(Python3 COMPONENTS Interpreter Development REQUIRED) # NumPy +find_package(Python3 COMPONENTS Interpreter Development.Module REQUIRED) # NumPy if(NOT CUVEC_CUDA_OPTIONAL) find_package(CUDAToolkit REQUIRED) enable_language(CUDA) From 01a491c5f111cd70a69a73dee2a409a1ce4041c2 Mon Sep 17 00:00:00 2001 From: Casper da Costa-Luis Date: Wed, 17 May 2023 17:25:58 +0530 Subject: [PATCH 5/8] docs: SWIG retarray --- docs/index.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/index.md b/docs/index.md index 2e78c31..0a754fb 100644 --- a/docs/index.md +++ b/docs/index.md @@ -153,7 +153,7 @@ The following involve no memory copies. === "**SWIG API** to **Python**" ```py import cuvec, my_custom_lib - arr = cuvec.swigcuvec.asarray(my_custom_lib.some_swig_api_func()) + arr = cuvec.swigcuvec.retarray(my_custom_lib.some_swig_api_func()) ``` === "**SWIG API** to **C++**" @@ -191,7 +191,7 @@ Python: import cuvec.swigcuvec as cuvec, numpy, mymod arr = cuvec.zeros((1337, 42, 7), "float32") assert all(numpy.mean(arr, axis=(0, 1)) == 0) - print(cuvec.asarray(mymod.myfunc(arr.cuvec)).sum()) + print(cuvec.retarray(mymod.myfunc(arr.cuvec)).sum()) ``` C++: @@ -391,7 +391,7 @@ See also [NumCu](https://github.com/AMYPAD/NumCu), a minimal stand-alone Python ## External Projects === "Python" - Python objects (`arr`, returned by `cuvec.zeros()`, `cuvec.asarray()`, or `cuvec.copy()`) contain all the attributes of a `numpy.ndarray`. Additionally, `arr.cuvec` implements the [buffer protocol](https://docs.python.org/3/c-api/buffer.html), while `arr.__cuda_array_interface__` provides [compatibility with other libraries](https://numba.readthedocs.io/en/latest/cuda/cuda_array_interface.html) such as Numba, CuPy, PyTorch, PyArrow, and RAPIDS. + Python objects (`arr`, returned by `cuvec.zeros()`, `cuvec.asarray()`, or `cuvec.copy()`) contain all the attributes of a `numpy.ndarray`. Additionally, `arr.cuvec` implements the [buffer protocol](https://docs.python.org/3/c-api/buffer.html), while `arr.__cuda_array_interface__` (and `arr.__array_interface__`) provide [compatibility with other libraries](https://numba.readthedocs.io/en/latest/cuda/cuda_array_interface.html) such as Numba, CuPy, PyTorch, PyArrow, and RAPIDS. When using the SWIG alternative module, `arr.cuvec` is a wrapper around `SwigCuVec *`. From 68d599fbf61716fd45e5ec38c734ff3463f08e1b Mon Sep 17 00:00:00 2001 From: Casper da Costa-Luis Date: Wed, 17 May 2023 17:35:58 +0530 Subject: [PATCH 6/8] tests: suppress retarray warnings --- tests/test_perf.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/test_perf.py b/tests/test_perf.py index 865f295..ef4f3e8 100644 --- a/tests/test_perf.py +++ b/tests/test_perf.py @@ -11,7 +11,7 @@ try: # alternative to `cu` # `example_swig` is defined in ../cuvec/src/example_swig/ - from cuvec import example_swig + from cuvec import example_swig # type: ignore # yapf: disable from cuvec import swigcuvec as sw except ImportError: sw, example_swig = None, None # type: ignore # yapf: disable @@ -57,6 +57,7 @@ def test_inner(*args, **kwargs): def test_perf(cu, ex, shape=(1337, 42), quiet=False, return_time=False): if cu is None: skip("SWIG not available") + retarray = getattr(cu, 'retarray', cu.asarray) overhead = np.mean([_time_overhead() for _ in range(100)]) t = {} t['create src'], src = timer(cu.zeros)(shape, "float32") @@ -68,10 +69,10 @@ def test_perf(cu, ex, shape=(1337, 42), quiet=False, return_time=False): if not quiet: t['warmup'], res = timer(ex.increment2d_f)(src.cuvec, None, True) - t['> create dst'], t['> kernel'] = cu.asarray(res)[0, :2] + t['> create dst'], t['> kernel'] = retarray(res)[0, :2] t['call ext'], res = timer(ex.increment2d_f)(src.cuvec, None, True) t['- create dst'], t['- kernel'] = None, None - t['view'], dst = timer(cu.asarray)(res) + t['view'], dst = timer(retarray)(res) t['- create dst'], t['- kernel'] = dst[0, :2] if not quiet: From bc3261cef9f6bddee74369196af3706bd88966f9 Mon Sep 17 00:00:00 2001 From: Casper da Costa-Luis Date: Wed, 17 May 2023 17:46:37 +0530 Subject: [PATCH 7/8] drop redundant typecast --- cuvec/include/pycuvec.cuh | 4 ++-- cuvec/src/example_mod/example_mod.cu | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cuvec/include/pycuvec.cuh b/cuvec/include/pycuvec.cuh index f308b1e..d7843aa 100644 --- a/cuvec/include/pycuvec.cuh +++ b/cuvec/include/pycuvec.cuh @@ -116,7 +116,7 @@ template void PyCuVec_dealloc(PyCuVec *self) { self->shape.shrink_to_fit(); self->strides.clear(); self->strides.shrink_to_fit(); - Py_TYPE(self)->tp_free((PyObject *)self); + Py_TYPE(self)->tp_free(self); } /// __name__ template const char *PyCuVec_t_str() { @@ -300,7 +300,7 @@ template PyCuVec *asPyCuVec(PyCuVec *o) { // return cuvec o = (PyCuVec *)PyObject_GetAttrString((PyObject *)o, "cuvec"); if (!o) return NULL; - Py_DECREF((PyObject *)o); + Py_DECREF(o); } return o; } diff --git a/cuvec/src/example_mod/example_mod.cu b/cuvec/src/example_mod/example_mod.cu index ba81694..fc59f05 100644 --- a/cuvec/src/example_mod/example_mod.cu +++ b/cuvec/src/example_mod/example_mod.cu @@ -49,7 +49,7 @@ static PyObject *increment2d_f(PyObject *self, PyObject *args, PyObject *kwargs) PyErr_SetString(PyExc_IndexError, "`output` must be same shape as `src`"); return NULL; } - Py_INCREF((PyObject *)dst); // anticipating returning + Py_INCREF(dst); // anticipating returning } else { dst = PyCuVec_zeros_like(src); if (!dst) return NULL; From 9d30135df26f5e172e92088872551219da9fa290 Mon Sep 17 00:00:00 2001 From: Casper da Costa-Luis Date: Sun, 21 May 2023 14:04:37 +0530 Subject: [PATCH 8/8] build: drop redundant Python ROOT_DIR --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index a9ddd87..8b307c9 100644 --- a/setup.py +++ b/setup.py @@ -14,7 +14,7 @@ log = logging.getLogger("cuvec.setup") build_ver = ".".join(__version__.split('.')[:3]).split(".dev")[0] -cmake_args = [f"-DCUVEC_BUILD_VERSION={build_ver}", f"-DPython3_ROOT_DIR={sys.prefix}"] +cmake_args = [f"-DCUVEC_BUILD_VERSION={build_ver}"] try: from miutil import cuinfo nvcc_arch_raw = map(cuinfo.compute_capability, range(cuinfo.num_devices()))