Skip to content

Commit 8384937

Browse files
committed
support getting native handle in C++/Cython
1 parent 5408229 commit 8384937

File tree

11 files changed

+132
-7
lines changed

11 files changed

+132
-7
lines changed

.github/workflows/test-wheel-linux.yml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,19 @@ jobs:
223223
pytest -rxXs tests/
224224
popd
225225
226+
# It is a bit convoluted to run the Cython tests against CTK wheels,
227+
# so let's just skip them.
228+
if [[ "${{ inputs.local-ctk }}" == 1 ]]; then
229+
if [[ "${{ inputs.host-platform }}" == linux* ]]; then
230+
bash tests/cython/build_tests.sh
231+
elif [[ "${{ inputs.host-platform }}" == win* ]]; then
232+
# TODO: enable this once win-64 runners are up
233+
exit 1
234+
fi
235+
pytest -rxXs tests/cython
236+
popd
237+
fi
238+
226239
- name: Ensure cuda-python installable
227240
run: |
228241
if [[ "${{ inputs.local-ctk }}" == 1 ]]; then

cuda_core/README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,14 @@ for more details, including how to sign your commits.
4141
To run these tests:
4242
* `python -m pytest tests/` against editable installations
4343
* `pytest tests/` against installed packages
44+
45+
### Cython Unit Tests
46+
47+
Cython tests are located in `tests/cython` and need to be built. These builds have the same CUDA Toolkit header requirements as [those of cuda.bindings](https://nvidia.github.io/cuda-python/cuda-bindings/latest/install.html#requirements) where the major.minor version must match `cuda.bindings`. To build them:
48+
49+
1. Setup environment variable `CUDA_HOME` with the path to the CUDA Toolkit installation.
50+
2. Run `build_tests` script located in `test/cython` appropriate to your platform. This will both cythonize the tests and build them.
51+
52+
To run these tests:
53+
* `python -m pytest tests/cython/` against editable installations
54+
* `pytest tests/cython/` against installed packages

cuda_core/cuda/core/experimental/_event.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,6 @@ def is_done(self) -> bool:
130130
raise CUDAError(f"unexpected error: {result}")
131131

132132
@property
133-
def handle(self) -> int:
133+
def handle(self) -> "CUevent":
134134
"""Return the underlying cudaEvent_t pointer address as Python int."""
135-
return int(self._mnff.handle)
135+
return self._mnff.handle

cuda_core/cuda/core/experimental/_linker.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,10 @@ def _exception_manager(self):
323323
raise e
324324

325325

326+
nvJitLinkHandleT = int
327+
LinkerHandleT = Union[nvJitLinkHandleT, "CUlinkState"]
328+
329+
326330
class Linker:
327331
"""Represent a linking machinery to link one or multiple object codes into
328332
:obj:`~cuda.core.experimental._module.ObjectCode` with the specified options.
@@ -483,7 +487,7 @@ def _input_type_from_code_type(self, code_type: str):
483487
return input_type
484488

485489
@property
486-
def handle(self):
490+
def handle(self) -> LinkerHandleT:
487491
"""Return the underlying handle object."""
488492
return self._mnff.handle
489493

cuda_core/cuda/core/experimental/_program.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from warnings import warn
99

1010
from cuda.core.experimental._device import Device
11-
from cuda.core.experimental._linker import Linker, LinkerOptions
11+
from cuda.core.experimental._linker import Linker, LinkerHandleT, LinkerOptions
1212
from cuda.core.experimental._module import ObjectCode
1313
from cuda.core.experimental._utils import (
1414
_handle_boolean_option,
@@ -498,6 +498,6 @@ def backend(self) -> str:
498498
return self._backend
499499

500500
@property
501-
def handle(self):
501+
def handle(self) -> Union["nvrtcProgram", LinkerHandleT]:
502502
"""Return the underlying handle object."""
503503
return self._mnff.handle

cuda_core/cuda/core/experimental/_stream.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -147,9 +147,9 @@ def __cuda_stream__(self) -> Tuple[int, int]:
147147
return (0, self.handle)
148148

149149
@property
150-
def handle(self) -> int:
150+
def handle(self) -> "CUstream":
151151
"""Return the underlying cudaStream_t pointer address as Python int."""
152-
return int(self._mnff.handle)
152+
return self._mnff.handle
153153

154154
@property
155155
def is_nonblocking(self) -> bool:
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#include <type_traits>
2+
3+
// In cuda.bindings 12.8, the private member name was renamed from "_ptr" to "_pvt_ptr".
4+
// We want to have the C++ layer supporting all past 12.x versions, so some tricks are needed.
5+
// Since there's no std::has_member<T, member_name> so we use SFINAE to create the same effect.
6+
7+
template <typename T,
8+
std::enable_if_t<std::is_pointer_v<decltype(std::remove_pointer_t<T>::_pvt_ptr)>, bool> = true>
9+
inline auto& get_cuda_native_handle(const T& obj) {
10+
return *(obj->_pvt_ptr);
11+
}
12+
13+
template <typename T,
14+
std::enable_if_t<std::is_pointer_v<decltype(std::remove_pointer_t<T>::_ptr)>, bool> = true>
15+
inline auto& get_cuda_native_handle(const T& obj) {
16+
return *(obj->_ptr);
17+
}

cuda_core/tests/cython/build_tests.sh

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
#!/bin/bash
2+
3+
SCRIPTPATH=$(dirname $(realpath "$0"))
4+
CPLUS_INCLUDE_PATH=$SCRIPTPATH/../../cuda/core/experimental/include:$CUDA_HOME/include:$CPLUS_INCLUDE_PATH cythonize -3 -i $(dirname "$0")/test_*.pyx

cuda_core/tests/cython/test_cython.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# Copyright 2021-2024 NVIDIA Corporation. All rights reserved.
2+
#
3+
# Please refer to the NVIDIA end user license agreement (EULA) associated
4+
# with this source code for terms and conditions that govern your use of
5+
# this software. Any use, reproduction, disclosure, or distribution of
6+
# this software and related documentation outside the terms of the EULA
7+
# is strictly prohibited.
8+
import functools
9+
import importlib
10+
import sys
11+
12+
13+
def py_func(func):
14+
"""
15+
Wraps func in a plain Python function.
16+
"""
17+
18+
@functools.wraps(func)
19+
def wrapped(*args, **kwargs):
20+
return func(*args, **kwargs)
21+
22+
return wrapped
23+
24+
25+
cython_test_modules = [
26+
"test_get_cuda_native_handle",
27+
]
28+
29+
30+
for mod in cython_test_modules:
31+
try:
32+
# For each callable in `mod` with name `test_*`,
33+
# wrap the callable in a plain Python function
34+
# and set the result as an attribute of this module.
35+
mod = importlib.import_module(mod)
36+
for name in dir(mod):
37+
item = getattr(mod, name)
38+
if callable(item) and name.startswith("test_"):
39+
item = py_func(item)
40+
setattr(sys.modules[__name__], name, item)
41+
except ImportError:
42+
raise
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# distutils: language = c++
2+
# distutils: extra_compile_args = -std=c++17
3+
4+
from libc.stdint cimport intptr_t
5+
6+
from cuda.bindings.driver cimport (CUstream as pyCUstream,
7+
CUevent as pyCUevent)
8+
from cuda.bindings.cydriver cimport CUstream, CUevent
9+
10+
from cuda.core.experimental import Device
11+
12+
13+
cdef extern from "utility.hpp":
14+
void* get_cuda_native_handle[T](T)
15+
16+
17+
def test_get_cuda_native_handle():
18+
dev = Device(0)
19+
dev.set_current()
20+
21+
s = dev.create_stream()
22+
cdef pyCUstream s_py = s.handle
23+
cdef CUstream s_c = <CUstream>get_cuda_native_handle(s_py)
24+
assert <intptr_t>(s_c) == <intptr_t>(int(s_py))
25+
26+
e = s.record()
27+
cdef pyCUevent e_py = e.handle
28+
cdef CUevent e_c = <CUevent>get_cuda_native_handle(e_py)
29+
assert <intptr_t>(e_c) == <intptr_t>(int(e_py))
30+
31+
e.close()
32+
s.close()

cuda_core/tests/pytest.ini

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[pytest]
2+
norecursedirs = cython

0 commit comments

Comments
 (0)