pybind11 headers supporting the __cuda_array_interface__
Project description
Pybind11 - CUDA Array Interface
Overview
pybind11_cuda_array_interface
is a plugin for pybind11 that facilitates effortless exchange of arrays between Python and C++ environments. It specifically targets objects or arrays that implement the __cuda_array_interface__
, ensuring smooth interaction with GPU data structures.
Features
-
Automatic Conversion:
No need for manual data type conversions. The plugin handles it seamlessly.
-
Lifecycle management:
Specifically designed to work with the
__cuda_array_interface__
, by defining acuda_array_t<T>
type for the C++ side managing the reference count intrinsically. -
Header-only:
Just include the header and you're set. No heavy installations or configurations required.
Usage and making bindings
#include "pybind11_cuda_array_interface/pybind11_cuda_array_interface.hpp"
namespace py = pybind11;
template <typename T>
cai::cuda_array_t<T> receive_and_return_cuda_array_interface(cai::cuda_array_t<T> s_cai)
{
return s_cai;
}
template <typename T>
cai::cuda_array_t<T> return_cuda_array_interface(std::vector<size_t> shape = {3, 4, 5},
bool readonly = false, int version = 3)
{
cai::cuda_array_t<T> s_cai(shape, readonly, version);
return s_cai;
}
Bindings:
PYBIND11_MODULE(pycai, module)
{
module.doc() = "Module for __cuda_array_interface__";
caiexcp::register_custom_cuda_array_interface_exceptions(module);
module.def("receive_and_return_cuda_array_interface_float",
&receive_and_return_cuda_array_interface<float>,
"Function accepts a cuda_array_t and immediately just sends it back",
py::arg("s"));
module.def(
"return_cuda_array_interface",
&return_cuda_array_interface<float>,
"Constructs a cuda_array_t in C++ and returns it to Python",
py::arg_v("shape", std::vector<size_t>({3, 4, 5}), "std::vector<size_t>({3, 4, 5})"),
py::arg("readonly") = false,
py::arg("version") = 3);
}
From python you can now use these functions to send and receive arrays implementing the __cuda_array_interface__
defined by Numba.
Features and the cuda_array_t<T>
class
Custom exceptions:
The plugin defines a number of internal exceptions and a utility function is provided for exporting them to Python via caiexcp::register_custom_cuda_array_interface_exceptions(module);
. For a complete list please refer to the source code here.
cuda_array_t<T>:
The following public utilities are provided by cuda_array_t<T>
:
cuda_array_t(std::vector<size_t> shape, const bool readonly = false, const int version = 3)
: shape(std::move(shape)), readonly(readonly), version(version)
, where shape
, readonly
and version
correspond to their counterparts of the __cuda_array_interface__
.
const std::vector<size_t> &get_shape() const
, get method to return the shape of the array stored as an std::vector
.
py::dtype get_dtype() const
, get method to return the dtype
corresponding to the provided type T
.
bool is_readonly() const
, get method to return the boolean state of the readonly
variable of __cuda_array_interface__
.
int get_version() const
, get method to return the version of __cuda_array_interface__
.
size_t size_of_shape() const
, get method to return the total number of elements in the pointed to cuda_array.
T *get_compatible_typed_pointer()
const T *get_compatible_typed_pointer() const
, get method to return a raw pointer of type T
as const
or non-const
depending on the value of readonly
.
Return type:
The __cuda_array_interface__
is represented by the cuda_array_t<T>
class in C++, but the integrity of the Python array implementing the __cuda_array_interface__
is respected when said object is returned to Python from C++. I.e. objects sent from Python to C++ and at a later stage returned to Python as a cuda_array_t<T>
, will still return the actual Python object sent in the first place. However, should a cuda_array_t<T>
instance be defined or originating from C++ it will be returned to Python as a types.SimpleNamespace
implementing the __cuda_array_interface__
and a py::capsule
object to handle the ownership transfer.
Using a CuPy array the following wrapper class might prove useful:
from typing import Any, Protocol
import cupy as cp
class ArrayCapsuleWrapperProtocol(Protocol):
def __init__(self, array: cp.ndarray, capsule: Any) -> None:
...
@property
def array(self) -> cp.ndarray:
...
@property
def capsule(self) -> Any: # expected to be py::capsule from pybind11
...
def cupy_asarray_with_capsule(obj: Any) -> ArrayCapsuleWrapperProtocol:
# Check if the object has both __cuda_array_interface__ and _capsule attributes
if not (hasattr(obj, "__cuda_array_interface__") and hasattr(obj, "_capsule")):
raise ValueError("Input object must have both __cuda_array_interface__ and _capsule attributes.")
# Convert the object to a CuPy array
array = cp.asarray(obj)
class ArrayCapsuleWrapper:
def __init__(self, array: cp.ndarray, capsule: Any) -> None:
self._array = array
self._capsule = capsule
@property
def array(self) -> cp.ndarray:
return self._array
@property
def capsule(self) -> Any: # expected to be py::capsule from pybind11
return self._capsule
# Create a wrapper to hold the array and capsule
wrapper = ArrayCapsuleWrapper(array, obj._capsule)
return wrapper
Installations
Depending on your preferred choice the package can be installed in several ways given that three main dependencies are met
- pybind11 >=2.11.1
- Python >=3.8
- CUDA Toolkit >=10.2
Install from PyPI
pip install pybind11-cuda-array-interface
which will install the header files such that CMake can find them.
Install from source
git clone git@github.com:mikaeltw/pybind11_cuda_array_interface.git
or
git clone https://github.com/mikaeltw/pybind11_cuda_array_interface.git
Then
python -m pip install .
or (Provided that you have CMake >=3.18 installed)
mkdir build
cd build
cmake ..
make install
Install by copying headers
You can also copy the headers manually from include/pybind11_cuda_array_interface/*
.
Linting and tests
Please note that the tests require a functional NVIDIA GPU of at least the Pascal generation.
Install dependencies and compile tests:
python tests/install_cupy.py
python linting/install_linters.py
BUILD_GTESTS=ON BUILD_PYTESTS=ON python -m pip install .[test]
Run tests:
python -m pytest
and
tests/gtest/run_gtest_cai
Run Python linting:
python linting/check_python_linting.py
Clang-tidy & Clang-format:
No utility command provided but implemented in .github/workflows/linting.yaml.
Contributing
Issues and PRs are welcomed. If opening a PR, please do so from a fork of the repository.
License
This project is licensed under the BSD-3-Clause License. See the LICENSE file for details.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Hashes for pybind11_cuda_array_interface-1.0.0.tar.gz
Algorithm | Hash digest | |
---|---|---|
SHA256 | 0b7c37bc06e9500f9317d16632004ad79bc7e9c892722ee2eb108ae4b3e7282b |
|
MD5 | 080f5e98544ed9c9681e19cc9a86a100 |
|
BLAKE2b-256 | a28ab431dd043f231a575ae2c6e413e677a58b98775812e66f73b5376942fa46 |