diff --git a/CMakeLists.txt b/CMakeLists.txt index ea113e1..66dc8e1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,7 +8,7 @@ cmake_minimum_required(VERSION 3.2 FATAL_ERROR) # Set variables: # * PROJECT_NAME # * PROJECT_VERSION -project(dlpack VERSION 0.6 LANGUAGES C CXX) +project(dlpack VERSION 0.7.0 LANGUAGES C CXX) ##### # Change the default build type from Debug to Release, while still diff --git a/NEWS.md b/NEWS.md index 558204a..7c759cf 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,31 +1 @@ -DLPack Change Log -================= - -This file records the changes in DLPack in reverse chronological order. - -## v0.4 - -- OpaqueHandle type -- Complex support -- Rename DLContext -> DLDevice - - This requires dependent frameworks to upgrade the type name. - - The ABI is backward compatible, as it is only change of constant name. - -## v0.3 - -- Add bfloat16 -- Vulkan support - - -## v0.2 -- New device types - - kDLMetal for Apple Metal device - - kDLVPI for verilog simulator memory - - kDLROCM for AMD GPUs -- Add prefix DL to all enum constant values - - This requires dependent frameworks to upgrade their reference to these constant - - The ABI is compatible, as it is only change of constant name. -- Add DLManagedTensor structure for borrowing tensors - -## v0.1 -- Finalize DLTensor structure +This file has moved to [doc/source/release_notes.rst](/doc/source/release_notes.rst). diff --git a/docs/source/_static/images/DLPack_diagram.png b/docs/source/_static/images/DLPack_diagram.png index 3beadf5..07a7b70 100644 Binary files a/docs/source/_static/images/DLPack_diagram.png and b/docs/source/_static/images/DLPack_diagram.png differ diff --git a/docs/source/c_api.rst b/docs/source/c_api.rst index 9ef9f64..5b43e8d 100644 --- a/docs/source/c_api.rst +++ b/docs/source/c_api.rst @@ -10,6 +10,8 @@ Macros .. doxygendefine:: DLPACK_VERSION +.. doxygendefine:: DLPACK_ABI_VERSION + .. doxygendefine:: DLPACK_DLL Enumerations @@ -28,6 +30,22 @@ Structs .. doxygenstruct:: DLDataType :members: +.. doxygenstruct:: DLPackVersion + :members: + +.. doxygenstruct:: DLTensorVersioned + :members: + +.. doxygenstruct:: DLManagedTensorVersioned + :members: + +ABI v1 Structs +~~~~~~~~~~~~~~ + +DLTensor and DLManagedTensor don't contain any field to export version info. +Since ABI version 2, structs DLTensorVersioned and DLManagedTensorVersioned +have been added with version info and should be used instead. + .. doxygenstruct:: DLTensor :members: diff --git a/docs/source/conf.py b/docs/source/conf.py index af7f86b..03749d9 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -22,7 +22,7 @@ author = 'DLPack contributors' # The full version, including alpha/beta/rc tags -release = '0.6.0' +release = '0.7.0' # -- General configuration --------------------------------------------------- diff --git a/docs/source/index.rst b/docs/source/index.rst index ddd2c41..d521bdb 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -73,6 +73,7 @@ DLPack Documentation c_api python_spec + release_notes Indices and tables diff --git a/docs/source/python_spec.rst b/docs/source/python_spec.rst index 81df284..cbf4278 100644 --- a/docs/source/python_spec.rst +++ b/docs/source/python_spec.rst @@ -16,10 +16,12 @@ The array API will offer the following syntax for data interchange: 1. A ``from_dlpack(x)`` function, which accepts (array) objects with a ``__dlpack__`` method and uses that method to construct a new array containing the data from ``x``. -2. ``__dlpack__(self, stream=None)`` and ``__dlpack_device__`` methods on the - array object, which will be called from within ``from_dlpack``, to query - what device the array is on (may be needed to pass in the correct - stream, e.g. in the case of multiple GPUs) and to access the data. +2. ``__dlpack__(self, stream: int = None, version: int = None)``, + ``__dlpack_info__(self)``, and ``__dlpack_device__(self)`` methods + on the array object, which will be called from within ``from_dlpack``, + to access the data, to get the maximum supported DLPack version, and + to query what device the array is on (may be needed to pass in the + correct stream, e.g. in the case of multiple GPUs). Semantics @@ -48,6 +50,14 @@ producer; the producer must synchronize or wait on the stream when necessary. In the common case of the default stream being used, synchronization will be unnecessary so asynchronous execution is enabled. +A DLPack version can be requested by passing the ``version`` keyword. The +consumer should call the ``__dlpack_info__`` method to get the maximum +DLPack version supported by the producer and request for a version both +support e.g. ``min(producer_version, consumer_version)``. If the consumer +doesn't support any version below the producer's maximum version, a +``BufferError`` should be raised. Similarly, If the producer doesn't +support the requested version, it should raise a ``BufferError``. + Implementation ~~~~~~~~~~~~~~ @@ -65,25 +75,31 @@ struct members, gray text enum values of supported devices and data types.* The ``__dlpack__`` method will produce a ``PyCapsule`` containing a -``DLManagedTensor``, which will be consumed immediately within -``from_dlpack`` - therefore it is consumed exactly once, and it will not be -visible to users of the Python API. - -The producer must set the ``PyCapsule`` name to ``"dltensor"`` so that -it can be inspected by name, and set ``PyCapsule_Destructor`` that calls -the ``deleter`` of the ``DLManagedTensor`` when the ``"dltensor"``-named -capsule is no longer needed. - -The consumer must transer ownership of the ``DLManangedTensor`` from the -capsule to its own object. It does so by renaming the capsule to -``"used_dltensor"`` to ensure that ``PyCapsule_Destructor`` will not get -called (ensured if ``PyCapsule_Destructor`` calls ``deleter`` only for -capsules whose name is ``"dltensor"``), but the ``deleter`` of the -``DLManagedTensor`` will be called by the destructor of the consumer -library object created to own the ``DLManagerTensor`` obtained from the +``DLManagedTensorVersioned`` (or a ``DLManagedTensor``) that is +compatible with the DLPack and DLPack ABI version requested by the +consumer. It will be consumed immediately within ``from_dlpack`` - +therefore it is consumed exactly once, and it will not be visible +to users of the Python API. + +The producer must set the ``PyCapsule`` name to ``"dltensor"`` so +that it can be inspected by name, and set ``PyCapsule_Destructor`` +that calls the ``deleter`` of the ``DLManagedTensorVersioned`` (or +``DLManagedTensor``) when the ``"dltensor"``-named capsule is no +longer needed. + +The consumer must transfer ownership of the ``DLManangedTensorVersioned`` +(or ``DLManangedTensor``) from the capsule to its own object. It does so +by renaming the capsule to ``"used_dltensor"`` to ensure that +``PyCapsule_Destructor`` will not get called (ensured if +``PyCapsule_Destructor`` calls ``deleter`` only for capsules whose name +is ``"dltensor"``), but the ``deleter`` of the +``DLManagedTensorVersioned`` (or ``DLManagedTensor``) will be called by +the destructor of the consumer library object created to own the +``DLManagerTensorVersioned`` (or ``DLManagedTensor``) obtained from the capsule. Below is an example of the capsule deleter written in the Python C API which is called either when the refcount on the capsule named -``"dltensor"`` reaches zero or the consumer decides to deallocate its array: +``"dltensor"`` reaches zero or the consumer decides to deallocate its +array: .. code-block:: C @@ -96,7 +112,7 @@ C API which is called either when the refcount on the capsule named PyObject *type, *value, *traceback; PyErr_Fetch(&type, &value, &traceback); - DLManagedTensor *managed = (DLManagedTensor *)PyCapsule_GetPointer(self, "dltensor"); + DLManagedTensorVersioned *managed = (DLManagedTensorVersioned *)PyCapsule_GetPointer(self, "dltensor"); if (managed == NULL) { PyErr_WriteUnraisable(self); goto done; @@ -115,13 +131,14 @@ C API which is called either when the refcount on the capsule named Note: the capsule names ``"dltensor"`` and ``"used_dltensor"`` must be statically allocated. -When the ``strides`` field in the ``DLTensor`` struct is ``NULL``, it indicates a -row-major compact array. If the array is of size zero, the data pointer in -``DLTensor`` should be set to either ``NULL`` or ``0``. +When the ``strides`` field in the ``DLTensorVersioned`` (or ``DLTensor``) +struct is ``NULL``, it indicates a row-major compact array. If the array +is of size zero, the data pointer in ``DLTensorVersioned`` (or +``DLTensor``) should be set to either ``NULL`` or ``0``. -DLPack version used must be ``0.2 <= DLPACK_VERSION < 1.0``. For further -details on DLPack design and how to implement support for it, -refer to `github.com/dmlc/dlpack `_. +For further details on DLPack design and how to implement support for it, +refer to https://github.com/dmlc/dlpack. For details on ABI compatibility +and to upgrade to the new ABI (version 2), refer to :ref:`future-abi-compat`. .. warning:: DLPack contains a ``device_id``, which will be the device @@ -136,6 +153,45 @@ refer to `github.com/dmlc/dlpack `_. whether the ``.device`` attribute of the array returned from ``from_dlpack`` is guaranteed to be in a certain order or not. +.. _future-abi-compat: + +Future ABI Compatibility +~~~~~~~~~~~~~~~~~~~~~~~~ + +ABI version 1 did not provide any fields in the structs ``DLTensor`` or +``DLManagedTensor`` to export version info. Two equivalent structs, +``DLTensorVersioned`` and ``DLManagedTensorVersioned``, have been added +since ABI version 2 (DLPack version 0.7.0) and have a ``version`` field +that can be used to export version info and check if the producer's +DLPack version is compatible with the consumer's DLPack version. This +section gives a path for Python libraries to upgrade to the new ABI +(while preserving support for the old ABI): + +* ``__dlpack__`` should accept a ``version`` (int) keyword which is set to + ``None`` by default. Consumers can use this kwarg to request certain DLPack + versions. If ``version=None`` or ``version=60`` is requested: + + * a capsule named ``"dltensor"`` which uses the old ABI (``DLTensor`` and + ``DLManagedTensor``) should be returned (if the producer wants to keep + supporting it) or + * a ``BufferError`` should be raised (if the producer doesn't want to keep + support for the old ABI) + + Otherwise, a capsule named ``"dltensor"`` which uses the new ABI + (``DLTensorVersioned`` and ``DLManagedTensorVersioned``) should be returned. + If the requested version is not supported, ``__dlpack__`` should raise a + ``BufferError``. +* Producers should implement a ``__dlpack_info__`` method that returns the + maximum supported DLPack version. If this method does not exist, the consumer + must use the old ABI. +* Consumers should call the ``__dlpack_info__`` method to get the maximum DLPack + version supported by the producer. The consumer should then request a DLPack + version (by passing the ``version`` kwarg to the ``__dlpack__`` method) that + both support e.g. ``min(producer_version, consumer_version)`` or raise a + ``BufferError`` if no compatible version exist. If the ``__dlpack_info__`` + method can't be found (if the method doesn't exist), the consumer should + fallback to the old API i.e. passing no version keyword to the ``__dlpack__`` + method and expecting a capsule pointing to a ``DLManagedTensor`` (old ABI). Reference Implementations ~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/docs/source/release_notes.rst b/docs/source/release_notes.rst new file mode 100644 index 0000000..5a71cfd --- /dev/null +++ b/docs/source/release_notes.rst @@ -0,0 +1,67 @@ +.. _release-notes: + +DLPack Change Log +================= + +This file records the changes in DLPack in reverse chronological order. + +v0.7 +~~~~ + +Note: This release contains ABI breaking changes. + +- ABI version has been added as ``DLPACK_ABI_VERSION`` macro in the header file. +- Support for OneAPI (``kDLOneAPI``), WebGPU (``kDLWebGPU``), and Hexagon + (``kDLHexagon``) devices has been added. +- Two new structs with a field to export the version info and the readonly + flag have been added: ``DLTensorVersioned`` and ``DLManagedTensorVersioned``. + New implementations should use these structs over ``DLTensor`` and + ``DLManagedTensor``. If you have already added support for DLPack, it should + be updated to use the new structs instead (warning: this is an ABI breaking + change for C/C++ libraries. Python libraries should follow the + :ref:`future-abi-compat` section to upgrade to the new ABI without breaking + backward-compatibility). + +v0.6 +~~~~ + +- Support for ROCm host memory and CUDA managed memory has been added. + +v0.5 +~~~~ + +- Devices kDLGPU and kDLCPUPinned have been renamed to kDLCUDA and kDLCUDAHost + respectively. + +v0.4 +~~~~ + +- OpaqueHandle type +- Complex support +- Rename DLContext -> DLDevice + - This requires dependent frameworks to upgrade the type name. + - The ABI is backward compatible, as it is only change of constant name. + +v0.3 +~~~~ + +- Add bfloat16 +- Vulkan support + + +v0.2 +~~~~ + +- New device types + - kDLMetal for Apple Metal device + - kDLVPI for verilog simulator memory + - kDLROCM for AMD GPUs +- Add prefix DL to all enum constant values + - This requires dependent frameworks to upgrade their reference to these constant + - The ABI is compatible, as it is only change of constant name. +- Add DLManagedTensor structure for borrowing tensors + +v0.1 +~~~~ + +- Finalize DLTensor structure diff --git a/include/dlpack/dlpack.h b/include/dlpack/dlpack.h index 519b4e4..38fcef6 100644 --- a/include/dlpack/dlpack.h +++ b/include/dlpack/dlpack.h @@ -16,10 +16,10 @@ #endif /*! \brief The current version of dlpack */ -#define DLPACK_VERSION 60 +#define DLPACK_VERSION 70 /*! \brief The current ABI version of dlpack */ -#define DLPACK_ABI_VERSION 1 +#define DLPACK_ABI_VERSION 2 /*! \brief DLPACK_DLL prefix for windows */ #ifdef _WIN32 @@ -154,6 +154,16 @@ typedef struct { uint16_t lanes; } DLDataType; +/*! + * \brief The DLPack and DLPack ABI versions of the tensor. + */ +typedef struct { + /*! \brief DLPack version. */ + uint8_t dlpack; + /*! \brief DLPack ABI version. */ + uint8_t abi; +} DLPackVersion; + /*! * \brief Plain C Tensor object, does not manage memory. */ @@ -200,27 +210,60 @@ typedef struct { int64_t* strides; /*! \brief The offset in bytes to the beginning pointer to data */ uint64_t byte_offset; -} DLTensor; + /*! \brief The DLPack and DLPack ABI versions. The exporting library + * should set the DLPack version to DLPACK_VERSION and ABI version to + * DLPACK_ABI_VERSION. */ + DLPackVersion version; + /*! \brief Mark the data readonly. */ + uint8_t readonly; +} DLTensorVersioned; /*! - * \brief C Tensor object, manage memory of DLTensor. This data structure is - * intended to facilitate the borrowing of DLTensor by another framework. It is + * \brief C Tensor object, manage memory of DLTensorVersioned. This data structure is + * intended to facilitate the borrowing of DLTensorVersioned by another framework. It is * not meant to transfer the tensor. When the borrowing framework doesn't need * the tensor, it should call the deleter to notify the host that the resource * is no longer needed. */ -typedef struct DLManagedTensor { - /*! \brief DLTensor which is being memory managed */ - DLTensor dl_tensor; - /*! \brief the context of the original host framework of DLManagedTensor in - * which DLManagedTensor is used in the framework. It can also be NULL. +typedef struct DLManagedTensorVersioned { + /*! \brief DLTensorVersioned which is being memory managed */ + DLTensorVersioned dl_tensor; + /*! \brief the context of the original host framework of DLManagedTensorVersioned in + * which DLManagedTensorVersioned is used in the framework. It can also be NULL. */ void * manager_ctx; /*! \brief Destructor signature void (*)(void*) - this should be called - * to destruct manager_ctx which holds the DLManagedTensor. It can be NULL + * to destruct manager_ctx which holds the DLManagedTensorVersioned. It can be NULL * if there is no way for the caller to provide a reasonable destructor. * The destructors deletes the argument self as well. */ + void (*deleter)(struct DLManagedTensorVersioned * self); +} DLManagedTensorVersioned; + + +/* ABI v1 (no version info exported). Present for backward-compatibility. */ + +/*! + * \brief Plain C Tensor object, does not manage memory. This is an outdated + * struct without version info. Use the new DLTensorVersioned instead. + */ +typedef struct { + void* data; + DLDevice device; + int32_t ndim; + DLDataType dtype; + int64_t* shape; + int64_t* strides; + uint64_t byte_offset; +} DLTensor; + +/*! + * \brief C Tensor object, manage memory of DLTensor. This is an outdated + * struct without version info. Use the new DLManagedTensorVersioned instead. + */ +typedef struct DLManagedTensor { + DLTensor dl_tensor; + void * manager_ctx; void (*deleter)(struct DLManagedTensor * self); } DLManagedTensor; #ifdef __cplusplus