Repository navigation
Conversation
This adds a minimal public C-API to fetch commonly used `ndarray` metadata mainly into a provided struct. The caller must keep the original alive for this to be correct. The API is "backported" in the C-header to work with old versions of CuPy, although filling the `itemsize` will go via a slow Python path. Summary of API: * A get_cupy_api() function which is somewhat more than PyCapsule_Import() because it adds backwards-compatibility to the table (for old cupy versions) * `ndarray_type` the cupy.ndarray type (for C/C++ convenience really) * `int get_ndarray_ptr(CuPyNDArray *arr, void **ptr)` * `get_ndarray_metadata(CuPyNDArray *arr, CuPyNDArrayMetadata *out)`. The metadata includes: * ptr * ndim, size, itemsize * shape, strides * dtype (Python object) * `get_current_stream_ptr(int device_id, void **ptr)` Things to consider: * Are there any missing fields, or should we split things up? (`nvmath-python` needs the `itemsize` for example). * Add a function to get the DLPack dtype or even include it? My main reservation is that fetching it may be relatively slow (especially for backports)? And of course is this approach of a capsule with functions what we want. It is very safe (so long users are careful as documented). And the capsule has the nice property that it can even be deprecated in the module level `__getattr__`. (I think the main point against this would be "we'll just use the new DLPack API always anyway.)
|
cc @shwina for vis |
|
One thing I would consider adding as well is a flags space with contiguity and maybe 32bit indexing 1 flags set. (future flags seem plausible too; another flag might be the memory kind, although checking it is currently very slightly more expensive maybe.) EDIT: If we are nervous about ABI safety (considering how small this should be I didn't worry too much), I would suggest to move "table" building to be user responsibility: That would allow targeted deprecation of API/enum values. And a Footnotes
|
| * defined in C++ and not compiling for the stable ABI (limited API is fine). | ||
| * | ||
| * When we can assume almost all downstream uses CuPy 14.3+ this fallback | ||
| * should be removed. |
There was a problem hiding this comment.
If this interface exposes C++ standard-library types across the binary boundary, users must compile against a compatible C++ ABI and standard-library configuration. Matching CuPy’s C++ language standard alone does not guarantee compatibility.
There was a problem hiding this comment.
Arg, thanks, you are right should have thought of that. That means that std::vector .data() call can't be "backported" :(.
Which means, I could still backport other API, but get_ndarray_metadata (or anything that accesses shape/strides) can't be...
To be clearer: The interface doesn't, but for the backport I wanted to abuse this...
| from cupy._version import __version__ as _cupy_version | ||
|
|
||
|
|
||
| cdef int _get_ndarray_ptr(ndarray arr, void **ptr) except -1: |
There was a problem hiding this comment.
Since cupy/public_c_api.pxd uses ctypedef class cupy.ndarray [object CuPyNDArray, check_size ignore]:, perhaps we need to fully qualify ndarray type here as well:
| cdef int _get_ndarray_ptr(ndarray arr, void **ptr) except -1: | |
| cdef int _get_ndarray_ptr(cupy.ndarray arr, void **ptr) except -1: |
The code is correct as written. The semantics of ctypedef class cupy.ndarray [object CuPyNDArray, check_size ignore]: is define Cython type in cupy.public_c_api namespace that corresponds to Python type cupy.ndarray.
Hence, when from cupy.public_c_api cimport * implies ndarray type is define in current namespace now.
| out.itemsize = (<cnp.dtype>a.dtype).itemsize | ||
| out.shape = a._shape.data() | ||
| out.strides = a._strides.data() | ||
| out.dtype = <PyObject *>a.dtype |
There was a problem hiding this comment.
Since we use PyObject here, I think we must cimport it at the top of this Cython file:
from cpython.ref cimport PyObject
I realize this is defined via transitive import, but it's best to be explicit.
| cdef CuPyAPI *cp_api = get_cupy_api() | ||
|
|
||
| def use_array(arr): | ||
| cdef void *ptr |
There was a problem hiding this comment.
| cdef void *ptr | |
| cdef void *ptr = NULL |
I would prefer to initialize the pointer in the example source code
| Cython type-checks the first form for you. `None` is the one exception: | ||
| it passes the check and is then cast like any other object, so declare | ||
| arguments as `ndarray not None` where you can. |
There was a problem hiding this comment.
Perhaps provide an example
| Cython type-checks the first form for you. `None` is the one exception: | |
| it passes the check and is then cast like any other object, so declare | |
| arguments as `ndarray not None` where you can. | |
| Cython type-checks the first form for you. `None` is the one exception: | |
| it passes the check and is then cast like any other object, so declare | |
| arguments as `ndarray not None` where you can. For example, | |
| cdef void * _get_ndarray_ptr(ndarray arr not None): | |
| cdef void *ptr = NULL | |
| cp_api.get_ndarray_ptr(<ndarray>arr, &ptr) | |
| return ptr |
This adds a minimal public C-API to fetch commonly used
ndarraymetadata mainly into a provided struct. This does not expose any internal ABI/API.The caller must keep the original alive for this to be correct.
The API is "backported" in the C-header to work with old versions of CuPy, although filling the
itemsizewill go via a slow Python path.Summary of API:
ndarray_typethe cupy.ndarray type (for C/C++ convenience really) for type-checking.int get_ndarray_ptr(CuPyNDArray *arr, void **ptr)get_ndarray_metadata(CuPyNDArray *arr, CuPyNDArrayMetadata *out). The metadata includes:get_current_stream_ptr(int device_id, void **ptr)The Cython API does check types (implicitly), but of course you can cast to avoid that.
Things to consider:
nvmath-pythonneeds theitemsizefor example).And of course is this approach of a capsule with functions what we want. It is very safe (so long users are careful as documented). And the capsule has the nice property that it can even be deprecated in the module level
__getattr__.(I think the main point against this would be "we'll just use the new DLPack API always anyway.)
Closes gh-9881, CC @NaderAlAwar the
.pxdand header file here are designed so that one can also just vendor them (otherwise you need abuild_requireswithcupy>=14.3).(The "backport" is a very safe hack to make this much more nice to adopt.)
From what I could tell
nvmath-pythonbasically needs everything (because it needs strides in itemsize). But to use this, you would have to Cythonize (or similar) yourTensorHolder/CupyTensor.