diff --git a/docs/source/python_spec.rst b/docs/source/python_spec.rst index f4844f0..4e2d162 100644 --- a/docs/source/python_spec.rst +++ b/docs/source/python_spec.rst @@ -179,6 +179,42 @@ refer to `github.com/dmlc/dlpack `_. guaranteed to be in a certain order or not. +DLPack C Exchange API +~~~~~~~~~~~~~~~~~~~~~ + +Starting with DLPack 1.3, a new C Exchange API is introduced to enable faster +data exchange than the Python ``__dlpack__`` API at the C extension level. +Producer array frameworks may provide a ``__dlpack_c_exchange_api__`` +attribute on the array type. +The attribute should be a ``PyCapsule`` with name ``"dlpack_exchange_api"``. +The consumer can query whether this attribute exists and use it at the C extension level. +Notably, consumer frameworks can always start implementing by only using the Python ``__dlpack__`` API, +and then upgrade to the C Exchange API later when faster data exchange is needed. + +.. code-block:: C + + PyObject *api_obj = type(tensor_obj).__dlpack_c_exchange_api__; // as C code. + MyDLPackExchangeAPI *api = PyCapsule_GetPointer(api_obj, "dlpack_exchange_api"); + if (api == NULL && PyErr_Occurred()) { goto handle_error; } + + +.. note:: Implementation of the C Exchange API + + Producer framework should implement the C Exchange API in a static way either + through Cython, Python C extensions, or Python binding mechanism. Importantly, + because the DLPack C exchange API operates at the C extension level, we need + direct interaction between the array framework ``PyObject*`` and DLPack, + as a result it is harder to implement the C Exchange API through ctypes (because + ctypes releases GIL by default and sometimes in non-free-threading environment, + GIL is needed to interact with the Python C API). + +A reference implementations of the C Exchange API in frameworks: + + +* PyTorch: `C++ `__ +* Paddle: `C++ `__ + + Reference Implementations ~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -198,3 +234,4 @@ ctypes, cffi, etc: * mpi4py: `Cython `_ * Paddle: `C++ `__, `Python wrapper using Python C API `__ * Hidet: `ctypes `__ + diff --git a/include/dlpack/dlpack.h b/include/dlpack/dlpack.h index 9d7be97..d4d46fd 100644 --- a/include/dlpack/dlpack.h +++ b/include/dlpack/dlpack.h @@ -19,7 +19,7 @@ #define DLPACK_MAJOR_VERSION 1 /*! \brief The current minor version of dlpack */ -#define DLPACK_MINOR_VERSION 2 +#define DLPACK_MINOR_VERSION 3 /*! \brief DLPACK_DLL prefix for windows */ #ifdef _WIN32 @@ -375,7 +375,7 @@ typedef struct DLManagedTensorVersioned { } DLManagedTensorVersioned; //---------------------------------------------------------------------- -// DLPack `__c_dlpack_exchange_api__` fast exchange protocol definitions +// DLPack `__dlpack_c_exchange_api__` fast exchange protocol definitions //---------------------------------------------------------------------- /*! * \brief Request a producer library to create a new tensor. @@ -391,7 +391,7 @@ typedef struct DLManagedTensorVersioned { * \param error_ctx Context for `SetError`. * \param SetError The function to set the error. * \return The owning DLManagedTensorVersioned* or NULL on failure. - * SetError is called exactly when NULL is returned (the implementor + * SetError is called exactly when NULL is returned (the implementer * must ensure this). * \note - As a C function, must not thrown C++ exceptions. * - Error propagation via SetError to avoid any direct need @@ -432,11 +432,11 @@ typedef int (*DLPackManagedTensorFromPyObjectNoSync)( // * \brief Exports a PyObject* Tensor/NDArray to a provided DLTensor. * * This function provides a faster interface for temporary, non-owning, exchange. - * The producer (implementor) still owns the memory of data, strides, shape. + * The producer (implementer) still owns the memory of data, strides, shape. * The liveness of the DLTensor and the data it views is only guaranteed until * control is returned. * - * This function currently assumes that the producer (implementor) can fill + * This function currently assumes that the producer (implementer) can fill * in the DLTensor shape and strides without the need for temporary allocations. * * This function does not perform any stream synchronization. The consumer should query @@ -488,7 +488,7 @@ typedef int (*DLPackCurrentWorkStream)( // * \brief Imports a DLManagedTensorVersioned to a PyObject* Tensor/NDArray. * * Convert an owning DLManagedTensorVersioned* to the Python tensor of the - * producer (implementor) library with the correct type. + * producer (implementer) library with the correct type. * * This function does not perform any stream synchronization. * @@ -532,16 +532,17 @@ typedef struct DLPackExchangeAPIHeader { * \brief Framework-specific function pointers table for DLPack exchange. * * Additionally to `__dlpack__()` we define a C function table sharable by - * Python implementations via `__c_dlpack_exchange_api__`. - * This attribute must be set on the type as a Python integer compatible - * with `PyLong_FromVoidPtr`/`PyLong_AsVoidPtr`. + * + * Python implementations via `__dlpack_c_exchange_api__`. + * This attribute must be set on the type as a Python PyCapsule + * with name "dlpack_exchange_api". * * A consumer library may use a pattern such as: * * \code * - * PyObject *api_obj = type(tensor_obj).__c_dlpack_exchange_api__; // as C-code - * MyDLPackExchangeAPI *api = PyLong_AsVoidPtr(api_obj); + * PyObject *api_obj = type(tensor_obj).__dlpack_c_exchange_api__; // as C-code + * MyDLPackExchangeAPI *api = PyCapsule_GetPointer(api_obj, "dlpack_exchange_api"); * if (api == NULL && PyErr_Occurred()) { goto handle_error; } * * \endcode