[Other]Add dlpack (#556)

add dlpack
This commit is contained in:
heliqi
2022-11-10 16:00:08 +08:00
committed by GitHub
parent d4995e5468
commit 6bad97351f
38 changed files with 4181 additions and 0 deletions

View File

@@ -638,6 +638,7 @@ if(BUILD_FASTDEPLOY_PYTHON)
${PYTHON_INCLUDE_DIR})
target_include_directories(${PY_LIBRARY_NAME} PUBLIC ${PROJECT_SOURCE_DIR}/third_party/pybind11/include)
target_include_directories(${PY_LIBRARY_NAME} PUBLIC ${PROJECT_SOURCE_DIR}/third_party/dlpack/include)
if(APPLE)
set_target_properties(${PY_LIBRARY_NAME}

View File

@@ -0,0 +1,39 @@
name: CI
on:
push:
branches:
- main
jobs:
test_linux:
name: Deploy Docs
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
submodules: recursive
- name: Configuring Test Environment
run: |
sudo apt-get update
sudo apt-get -y install build-essential doxygen ghp-import
python3 -m pip install -U pip wheel
- name: Installing dependencies
run: |
python3 -m pip install -r doc_requirements.txt
- name: Generating Docs
run: |
make doc
- name: Deploying on GitHub Pages
run: |
touch docs/build/.nojekyll
git remote set-url origin https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/$GITHUB_REPOSITORY
git config --global user.email "dlpack-gh-actions-bot@nomail"
git config --global user.name "dlpack-gh-actions-bot"
ghp-import -m "Generate DLPack website" -b gh-pages docs/build
git push origin gh-pages -f

View File

@@ -0,0 +1,49 @@
name: Build Doc
on:
push:
branches:
- main
pull_request:
branches:
- main
jobs:
test_linux:
name: Linux
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
submodules: recursive
- name: Configuring Test Environment
run: |
sudo apt-get update
sudo apt-get -y install build-essential doxygen
python3 -m pip install -U pip wheel
python3 -m pip install cmake ninja
python3 --version
python3 -m pip --version
doxygen --version
make --version
cmake --version
ninja --version
- name: Installing dependencies
run: |
python3 -m pip install -r doc_requirements.txt
- name: Testing CMakeLists.txt
run: |
mkdir build
cd build
cmake .. -G Ninja -DCMAKE_INSTALL_PREFIX=./install -DBUILD_DOCS=ON
ninja
ninja install
- name: Testing Makefile
run: |
make doc

View File

@@ -0,0 +1,35 @@
name: CI
on:
push:
branches:
- main
pull_request:
branches:
- main
jobs:
Build:
strategy:
matrix:
os: [ubuntu-latest, macOS-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v2
- name: Setup Python
run: |
python3 -m pip install cpplint
- name: Setup@Ubuntu
if: startsWith(matrix.os, 'ubuntu')
run: |
sudo apt-get install -y doxygen wget graphviz unzip
- name: Lint
if: startsWith(matrix.os, 'ubuntu')
run: |
./tests/scripts/task_lint.sh
- name: Test
run: |
./tests/scripts/task_build.sh

32
third_party/dlpack/.gitignore vendored Normal file
View File

@@ -0,0 +1,32 @@
# Compiled Object files
*.slo
*.lo
*.o
*.obj
# Precompiled Headers
*.gch
*.pch
# Compiled Dynamic libraries
*.so
*.dylib
*.dll
# Fortran module files
*.mod
*.smod
# Compiled Static libraries
*.lai
*.la
*.a
*.lib
# Executables
*.exe
*.out
*.app
*~
build
bin

127
third_party/dlpack/CMakeLists.txt vendored Normal file
View File

@@ -0,0 +1,127 @@
###
# Set minimum version of CMake. Since command 'project' use
# VERSION sub-option we need at least 3.0.
# Note: If you use 2.6 or 2.4, God kills a kitten. Seriously.
cmake_minimum_required(VERSION 3.2 FATAL_ERROR)
####
# Set variables:
# * PROJECT_NAME
# * PROJECT_VERSION
project(dlpack VERSION 0.6 LANGUAGES C CXX)
#####
# Change the default build type from Debug to Release, while still
# supporting overriding the build type.
#
# The CACHE STRING logic here and elsewhere is needed to force CMake
# to pay attention to the value of these variables.
if(NOT CMAKE_BUILD_TYPE)
message(STATUS "No build type specified; defaulting to CMAKE_BUILD_TYPE=Release.")
set(CMAKE_BUILD_TYPE Release CACHE STRING
"Choose the type of build, options are: None Debug Release RelWithDebInfo MinSizeRel."
FORCE)
else(NOT CMAKE_BUILD_TYPE)
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
message("==========================================================================================")
message(STATUS "Build type: Debug. Performance will be terrible!")
message(STATUS "Add -DCMAKE_BUILD_TYPE=Release to the CMake command line to get an optimized build.")
message("==========================================================================================")
endif(CMAKE_BUILD_TYPE STREQUAL "Debug")
endif(NOT CMAKE_BUILD_TYPE)
####
# Setup the compiler options
# set c++ standard to c++11.
# Note: not working on CMake 2.8. We assume that user has
# a compiler with C++11 support.
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
message(STATUS "C++11 support has been enabled by default.")
option(BUILD_DOCS "Set to ON to build documentation" OFF)
option(BUILD_MOCK "Build mock executable" ON)
if(BUILD_DOCS)
add_subdirectory(docs)
endif(BUILD_DOCS)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/bin)
if(BUILD_MOCK)
set(DLPACK_MOCK_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/contrib/mock_main.cc
${CMAKE_CURRENT_SOURCE_DIR}/contrib/mock_c.c)
add_executable(mock ${DLPACK_MOCK_SRCS})
endif()
add_library(dlpack INTERFACE)
add_library(${PROJECT_NAME}::dlpack ALIAS dlpack)
target_include_directories(
dlpack
INTERFACE
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/contrib>
)
if(BUILD_MOCK)
target_link_libraries(mock PRIVATE dlpack)
endif()
# Installation (https://github.com/forexample/package-example) {
# Introduce variables:
# * CMAKE_INSTALL_LIBDIR
# * CMAKE_INSTALL_BINDIR
# * CMAKE_INSTALL_INCLUDEDIR
include(GNUInstallDirs)
set(config_install_dir "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}")
set(generated_dir "${CMAKE_CURRENT_BINARY_DIR}/generated")
set(version_config "${generated_dir}/${PROJECT_NAME}ConfigVersion.cmake")
set(project_config "${generated_dir}/${PROJECT_NAME}Config.cmake")
set(TARGETS_EXPORT_NAME "${PROJECT_NAME}Targets")
set(namespace "${PROJECT_NAME}::")
include(CMakePackageConfigHelpers)
# Use:
# * PROJECT_VERSION
write_basic_package_version_file(
"${version_config}" COMPATIBILITY SameMajorVersion
)
# Use:
# * TARGETS_EXPORT_NAME
# * PROJECT_NAME
configure_package_config_file(
"cmake/template/Config.cmake.in"
"${project_config}"
INSTALL_DESTINATION "${config_install_dir}"
)
install(
TARGETS dlpack
EXPORT "${TARGETS_EXPORT_NAME}"
INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
)
install(
DIRECTORY include/
DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
)
install(
FILES "${project_config}" "${version_config}"
DESTINATION "${config_install_dir}"
)
install(
EXPORT "${TARGETS_EXPORT_NAME}"
NAMESPACE "${namespace}"
DESTINATION "${config_install_dir}"
)
# }

201
third_party/dlpack/LICENSE vendored Normal file
View File

@@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2017 by Contributors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

39
third_party/dlpack/Makefile vendored Normal file
View File

@@ -0,0 +1,39 @@
.PHONY: clean all test doc lint show_docs
all: bin/mock
LDFLAGS =
CFLAGS = -Wall -O3 -Iinclude -Icontrib
CXXFLAGS = -std=c++11 $(CFLAGS)
SRC = $(wildcard contrib/*.cc contrib/*.c)
ALL_CXX_OBJ = $(patsubst contrib/%.cc, build/%.o, $(SRC))
ALL_C_OBJ = $(patsubst contrib/%.c, build/%.o, $(SRC))
ALL_OBJ = $(ALL_CC_OBJ) $(ALL_CXX_OBJ)
doc:
doxygen docs/Doxyfile
$(MAKE) -C docs html
show_docs:
@python3 -m http.server --directory docs/build/latest
lint:
./tests/scripts/task_lint.sh
build/%.o: contrib/%.cc
@mkdir -p $(@D)
$(CXX) $(CXXFLAGS) -MM -MT build/$*.o $< >build/$*.d
$(CXX) -c $(CXXFLAGS) -c $< -o $@
build/%.o: contrib/%.c
@mkdir -p $(@D)
$(CC) $(CFLAGS) -MM -MT build/$*.o $< >build/$*.d
$(CC) -c $(CFLAGS) -c $< -o $@
bin/mock: $(ALL_OBJ)
@mkdir -p $(@D)
$(CXX) $(CXXFLAGS) -o $@ $(filter %.o %.a, $^) $(LDFLAGS)
clean:
$(RM) -rf build */*/*/*~ */*.o */*/*.o */*/*/*.o */*.d */*/*.d */*/*/*.d docs/build docs/doxygen

53
third_party/dlpack/NEWS.md vendored Normal file
View File

@@ -0,0 +1,53 @@
DLPack Change Log
=================
This file records the changes in DLPack in reverse chronological order.
## v0.7
- Add kDLHexagon
- Add kDLOneAPI
- Add DLPACK_VERSION and DLPACK_ABI_VERSION
## v0.6
- Add kDLROCMHost
- Add kDLCUDAManaged
## v0.5
Rename enum values
- kDLGPU -> kDLCUDA
- kDLCPUPinned -> kDLCUDAHost
The ABI is backward compatible, as it is only change of constant name,
exchange can still happen between the new version and old version.
## 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

29
third_party/dlpack/README.md vendored Normal file
View File

@@ -0,0 +1,29 @@
# DLPack: Open In Memory Tensor Structure
[![Build Status](https://github.com/dmlc/dlpack/actions/workflows/main.yaml/badge.svg?branch=main)](https://github.com/dmlc/dlpack/actions/workflows/main.yaml)
Documentation: [https://dmlc.github.io/dlpack/latest](https://dmlc.github.io/dlpack/latest)
DLPack is an open in-memory tensor structure for sharing tensors among frameworks. DLPack enables
- Easier sharing of operators between deep learning frameworks.
- Easier wrapping of vendor level operator implementations, allowing collaboration when introducing new devices/ops.
- Quick swapping of backend implementations, like different version of BLAS
- For final users, this could bring more operators, and possibility of mixing usage between frameworks.
We do not intend to implement Tensor and Ops, but instead use this as common bridge
to reuse tensor and ops across frameworks.
## Proposal Procedure
RFC proposals are opened as issues. The major release will happen as a vote issue to make
sure the participants agree on the changes.
## Project Structure
There are two major components so far
- include: stabilized headers
- contrib: in progress unstable libraries
## People
Here are list of people who have been involved in DLPack RFC design proposals:
@soumith @piiswrong @Yangqing @naibaf7 @bhack @edgarriba @tqchen @prigoyal @zdevito

View File

@@ -0,0 +1 @@
__pycache__

View File

@@ -0,0 +1,23 @@
# Numpy DLPack Array Conversion Example
This example demonstrates how a underlying array memory can be handed off between
two DLPack compatible frameworks without requiring any copies. In this case,
we demonstrate how to convert numpy to TVM's NDArray and vice-versa with proper
memory handling. We hope that not only is this directly useful for TVM users, but
also a solid example for how similar efficient copies can be implemented in other
array frameworks.
## File Breakdown
[dlpack.py](dlpack/dlpack.py): Contains the definition of common DLPack structures shared between frameworks. Mirrors the official C++ definitions.
[from_numpy.py](dlpack/from_numpy.py): Demonstrates how to convert a numpy array into a PyCapsule containing a DLPack Tensor.
[to_numpy.py](dlpack/to_numpy.py): Demonstrates how to take a PyCapsule with a DLPack Tensor and convert it into a numpy array.
[test.py](dlpack/test.py): Shows how to_numpy and from_numpy can be used to convert tensor formats without copies.
## Authors
[Josh Fromm](https://github.com/jwfromm)
[Junru Shao](https://github.com/junrushao1994)

View File

@@ -0,0 +1,2 @@
from .from_numpy import from_numpy
from .to_numpy import to_numpy

View File

@@ -0,0 +1,137 @@
import ctypes
_c_str_dltensor = b"dltensor"
class DLDeviceType(ctypes.c_int):
"""The enum that encodes the type of the device where
DLTensor memory is allocated.
"""
kDLCPU = 1
kDLCUDA = 2
kDLCUDAHost = 3
kDLOpenCL = 4
kDLVulkan = 7
kDLMetal = 8
kDLVPI = 9
kDLROCM = 10
kDLROCMHost = 11
kDLCUDAManaged = 13
kDLOneAPI = 14
def __str__(self):
return {
self.kDLCPU: "CPU",
self.kDLCUDA: "CUDA",
self.kDLCUDAHost: "CUDAHost",
self.kDLOpenCL: "OpenCL",
self.kDLVulkan: "Vulkan",
self.kDLMetal: "Metal",
self.kDLVPI: "VPI",
self.kDLROCM: "ROCM",
self.kDLROCMHost: "ROMCHost",
self.kDLCUDAManaged: "CUDAManaged",
self.kDLOneAPI: "oneAPI",
}[self.value]
class DLDevice(ctypes.Structure):
"""Represents the device where DLTensor memory is allocated.
The device is represented by the pair of fields:
device_type: DLDeviceType
device_id: c_int
"""
_fields_ = [
("device_type", DLDeviceType),
("device_id", ctypes.c_int),
]
class DLDataTypeCode(ctypes.c_uint8):
"""An integer that encodes the category of DLTensor elements' data type."""
kDLInt = 0
kDLUInt = 1
kDLFloat = 2
kDLOpaquePointer = 3
kDLBfloat = 4
kDLComplex = 5
def __str__(self):
return {
self.kDLInt: "int",
self.kDLUInt: "uint",
self.kDLFloat: "float",
self.kDLBfloat: "bfloat",
self.kDLComplex: "complex",
self.kDLOpaquePointer: "void_p"
}[self.value]
class DLDataType(ctypes.Structure):
"""Descriptor of data type for elements of DLTensor.
The data type is described by a triple, `DLDataType.type_code`,
`DLDataType.bits`, and `DLDataType.lanes`.
The element is understood as packed `lanes` repetitions of
elements from `type_code` data-category of width `bits`.
"""
_fields_ = [
("type_code", DLDataTypeCode),
("bits", ctypes.c_uint8),
("lanes", ctypes.c_uint16),
]
TYPE_MAP = {
"bool": (DLDataTypeCode.kDLUInt, 1, 1),
"int8": (DLDataTypeCode.kDLInt, 8, 1),
"int16": (DLDataTypeCode.kDLInt, 16, 1),
"int32": (DLDataTypeCode.kDLInt, 32, 1),
"int64": (DLDataTypeCode.kDLInt, 64, 1),
"uint8": (DLDataTypeCode.kDLUInt, 8, 1),
"uint16": (DLDataTypeCode.kDLUInt, 16, 1),
"uint32": (DLDataTypeCode.kDLUInt, 32, 1),
"uint64": (DLDataTypeCode.kDLUInt, 64, 1),
"float16": (DLDataTypeCode.kDLFloat, 16, 1),
"float32": (DLDataTypeCode.kDLFloat, 32, 1),
"float64": (DLDataTypeCode.kDLFloat, 64, 1),
"complex64": (DLDataTypeCode.kDLComplex, 64, 1),
"complex128": (DLDataTypeCode.kDLComplex, 128, 1)
}
class DLTensor(ctypes.Structure):
"""Structure describing strided layout of DLTensor.
Fields are:
data: void pointer
device: DLDevice
ndim: number of indices needed to reference an
element of the tensor
dtype: data type descriptor
shape: tuple with lengths of the corresponding
tensor dimensions
strides: tuple of numbers of array elements to
step in each dimension when traversing
the tensor
byte_offset: data + byte_offset gives the address of
tensor element with index (0,) * ndim
"""
_fields_ = [
("data", ctypes.c_void_p),
("device", DLDevice),
("ndim", ctypes.c_int),
("dtype", DLDataType),
("shape", ctypes.POINTER(ctypes.c_int64)),
("strides", ctypes.POINTER(ctypes.c_int64)),
("byte_offset", ctypes.c_uint64),
]
class DLManagedTensor(ctypes.Structure):
"""Structure storing the pointer to the tensor descriptor,
deleter callable for the tensor descriptor, and pointer to
some additional data. These are stored in fields `dl_tensor`,
`deleter`, and `manager_ctx`."""
_fields_ = [
("dl_tensor", DLTensor),
("manager_ctx", ctypes.c_void_p),
("deleter", ctypes.CFUNCTYPE(None, ctypes.c_void_p)),
]

View File

@@ -0,0 +1,96 @@
from typing import Callable
import numpy as np
import ctypes
from .dlpack import DLManagedTensor, DLDevice, DLDataType, _c_str_dltensor
ctypes.pythonapi.PyMem_RawMalloc.restype = ctypes.c_void_p
ctypes.pythonapi.PyMem_RawFree.argtypes = [ctypes.c_void_p]
ctypes.pythonapi.PyCapsule_New.restype = ctypes.py_object
ctypes.pythonapi.PyCapsule_New.argtypes = [
ctypes.c_void_p, ctypes.c_char_p, ctypes.c_void_p
]
class _Holder:
"""A wrapper around a numpy array to keep track of references to the underlying memory.
Parameters
----------
np_array : np.ndarray
The numpy array that will be converted to a DLPack tensor and must be managed.
"""
def __init__(self, np_array: np.ndarray) -> None:
self.np_array = np_array
self.data = np_array.ctypes.data_as(ctypes.c_void_p)
self.shape = np_array.ctypes.shape_as(ctypes.c_int64)
self.strides = np_array.ctypes.strides_as(ctypes.c_int64)
for i in range(np_array.ndim):
self.strides[i] //= np_array.itemsize
def _as_manager_ctx(self) -> ctypes.c_void_p:
py_obj = ctypes.py_object(self)
py_obj_ptr = ctypes.pointer(py_obj)
ctypes.pythonapi.Py_IncRef(py_obj)
ctypes.pythonapi.Py_IncRef(ctypes.py_object(py_obj_ptr))
return ctypes.cast(py_obj_ptr, ctypes.c_void_p)
@ctypes.CFUNCTYPE(None, ctypes.c_void_p)
def _numpy_array_deleter(handle: ctypes.c_void_p) -> None:
"""A function to deallocate the memory of a numpy array."""
dl_managed_tensor = DLManagedTensor.from_address(handle)
py_obj_ptr = ctypes.cast(dl_managed_tensor.manager_ctx,
ctypes.POINTER(ctypes.py_object))
py_obj = py_obj_ptr.contents
ctypes.pythonapi.Py_DecRef(py_obj)
ctypes.pythonapi.Py_DecRef(ctypes.py_object(py_obj_ptr))
ctypes.pythonapi.PyMem_RawFree(handle)
@ctypes.CFUNCTYPE(None, ctypes.c_void_p)
def _numpy_pycapsule_deleter(handle: ctypes.c_void_p) -> None:
"""A function to deallocate a pycapsule that wraps a numpy array."""
pycapsule: ctypes.py_object = ctypes.cast(handle, ctypes.py_object)
if ctypes.pythonapi.PyCapsule_IsValid(pycapsule, _c_str_dltensor):
dl_managed_tensor = ctypes.pythonapi.PyCapsule_GetPointer(
pycapsule, _c_str_dltensor)
_numpy_array_deleter(dl_managed_tensor)
ctypes.pythonapi.PyCapsule_SetDestructor(pycapsule, None)
def from_numpy(np_array: np.ndarray):
"""Convert a numpy array to another type of dlpack compatible array.
Parameters
----------
np_array : np.ndarray
The source numpy array that will be converted.
Returns
-------
pycapsule : PyCapsule
A pycapsule containing a DLManagedTensor that can be converted
to other array formats without copying the underlying memory.
"""
holder = _Holder(np_array)
size = ctypes.c_size_t(ctypes.sizeof(DLManagedTensor))
dl_managed_tensor = DLManagedTensor.from_address(
ctypes.pythonapi.PyMem_RawMalloc(size))
dl_managed_tensor.dl_tensor.data = holder.data
dl_managed_tensor.dl_tensor.device = DLDevice(1, 0)
dl_managed_tensor.dl_tensor.ndim = np_array.ndim
dl_managed_tensor.dl_tensor.dtype = DLDataType.TYPE_MAP[str(
np_array.dtype)]
dl_managed_tensor.dl_tensor.shape = holder.shape
dl_managed_tensor.dl_tensor.strides = holder.strides
dl_managed_tensor.dl_tensor.byte_offset = 0
dl_managed_tensor.manager_ctx = holder._as_manager_ctx()
dl_managed_tensor.deleter = _numpy_array_deleter
pycapsule = ctypes.pythonapi.PyCapsule_New(
ctypes.byref(dl_managed_tensor),
_c_str_dltensor,
_numpy_pycapsule_deleter, )
return pycapsule

View File

@@ -0,0 +1,83 @@
import ctypes
import numpy as np
from .dlpack import _c_str_dltensor, DLManagedTensor, DLTensor
ctypes.pythonapi.PyCapsule_IsValid.restype = ctypes.c_int
ctypes.pythonapi.PyCapsule_IsValid.argtypes = [
ctypes.py_object, ctypes.c_char_p
]
ctypes.pythonapi.PyCapsule_GetPointer.restype = ctypes.c_void_p
ctypes.pythonapi.PyCapsule_GetPointer.argtypes = [
ctypes.py_object, ctypes.c_char_p
]
def _array_interface_from_dl_tensor(dlt):
"""Constructs NumPy's array_interface dictionary
from `dlpack.DLTensor` descriptor."""
assert isinstance(dlt, DLTensor)
shape = tuple(dlt.shape[dim] for dim in range(dlt.ndim))
itemsize = dlt.dtype.lanes * dlt.dtype.bits // 8
if dlt.strides:
strides = tuple(dlt.strides[dim] * itemsize for dim in range(dlt.ndim))
else:
# Array is compact, make it numpy compatible.
strides = []
for i, s in enumerate(shape):
cumulative = 1
for e in range(i + 1, dlt.ndim):
cumulative *= shape[e]
strides.append(cumulative * itemsize)
strides = tuple(strides)
typestr = "|" + str(dlt.dtype.type_code)[0] + str(itemsize)
return dict(
version=3,
shape=shape,
strides=strides,
data=(dlt.data, True),
offset=dlt.byte_offset,
typestr=typestr, )
class _Holder:
"""A wrapper that combines a pycapsule and array_interface for consumption by numpy.
Parameters
----------
array_interface : dict
A description of the underlying memory.
pycapsule : PyCapsule
A wrapper around the dlpack tensor that will be converted to numpy.
"""
def __init__(self, array_interface, pycapsule) -> None:
self.__array_interface__ = array_interface
self._pycapsule = pycapsule
def to_numpy(pycapsule) -> np.ndarray:
"""Convert a dlpack tensor into a numpy array without copying.
Parameters
----------
pycapsule : PyCapsule
A pycapsule wrapping a dlpack tensor that will be converted.
Returns
-------
np_array : np.ndarray
A new numpy array that uses the same underlying memory as the input
pycapsule.
"""
assert ctypes.pythonapi.PyCapsule_IsValid(pycapsule, _c_str_dltensor)
dl_managed_tensor = ctypes.pythonapi.PyCapsule_GetPointer(pycapsule,
_c_str_dltensor)
dl_managed_tensor_ptr = ctypes.cast(dl_managed_tensor,
ctypes.POINTER(DLManagedTensor))
dl_managed_tensor = dl_managed_tensor_ptr.contents
holder = _Holder(
_array_interface_from_dl_tensor(dl_managed_tensor.dl_tensor),
pycapsule)
return np.ctypeslib.as_array(holder)

View File

@@ -0,0 +1,36 @@
import tvm
import numpy as np
from dlpack import from_numpy, to_numpy
def test_from_numpy():
"""Test the copy free conversion of numpy to a tvm ndarray."""
np_array = np.random.normal(size=[10, 10])
np_array_ref = np_array.copy()
tvm_array = tvm.nd.from_dlpack(from_numpy(np_array))
del np_array
np.testing.assert_equal(actual=tvm_array.numpy(), desired=np_array_ref)
del tvm_array
def test_to_numpy():
"""Test the copy free conversion of a tvm ndarray to a numpy array"""
tvm_array = tvm.nd.array(np.random.normal(size=[10, 10]))
np_array_ref = tvm_array.numpy()
np_array = to_numpy(tvm_array.__dlpack__())
del tvm_array
np.testing.assert_equal(actual=np_array, desired=np_array_ref)
del np_array
if __name__ == "__main__":
"""
Run both tests a bunch of times to make
sure the conversions and memory management are stable.
"""
print("### Testing from_numpy")
for i in range(10000):
test_from_numpy()
print("### Testing to_numpy")
for i in range(10000):
test_to_numpy()

View File

@@ -0,0 +1,38 @@
import numpy as np
from dlpack import from_numpy, to_numpy
def test_to_from_numpy_zero_copy():
"""Test the copy free conversion of numpy array via DLPack."""
np_ary = np.random.normal(size=[10, 10])
np_ary_big = np.random.normal(size=[12, 10])
dlpack_capsule = from_numpy(np_ary_big)
reconstructed_ary = to_numpy(dlpack_capsule)
del dlpack_capsule
np_ary_big[1:11] = np_ary
del np_ary_big
np.testing.assert_equal(actual=reconstructed_ary[1:11], desired=np_ary)
def test_to_from_numpy_memory():
"""Test that DLPack capsule keeps the source array alive"""
source_array = np.random.normal(size=[10, 10])
np_array_ref = source_array.copy()
dlpack_capsule = from_numpy(source_array)
del source_array
reconstructed_array = to_numpy(dlpack_capsule)
del dlpack_capsule
np.testing.assert_equal(actual=reconstructed_array, desired=np_array_ref)
if __name__ == "__main__":
"""
Run both tests a bunch of times to make
sure the conversions and memory management are stable.
"""
print("### Running `test_to_from_numpy_zero_copy`")
for i in range(10000):
test_to_from_numpy_zero_copy()
print("### Running `test_to_from_numpy_memory`")
for i in range(10000):
test_to_from_numpy_memory()

View File

@@ -0,0 +1,4 @@
@PACKAGE_INIT@
include("${CMAKE_CURRENT_LIST_DIR}/@TARGETS_EXPORT_NAME@.cmake")
check_required_components("@PROJECT_NAME@")

View File

@@ -0,0 +1,65 @@
/*!
* Copyright (c) 2017 by Contributors
* \file dlpackcpp.h
* \brief Example C++ wrapper of DLPack
*/
#ifndef DLPACK_DLPACKCPP_H_
#define DLPACK_DLPACKCPP_H_
#include <dlpack/dlpack.h>
#include <cstdint> // for int64_t etc
#include <cstdlib> // for free()
#include <functional> // for std::multiplies
#include <memory>
#include <numeric>
#include <vector>
namespace dlpack {
// Example container wrapping of DLTensor.
class DLTContainer {
public:
DLTContainer() {
// default to float32
handle_.data = nullptr;
handle_.dtype.code = kDLFloat;
handle_.dtype.bits = 32U;
handle_.dtype.lanes = 1U;
handle_.device.device_type = kDLCPU;
handle_.device.device_id = 0;
handle_.shape = nullptr;
handle_.strides = nullptr;
handle_.byte_offset = 0;
}
~DLTContainer() {
if (origin_ == nullptr) {
free(handle_.data);
}
}
operator DLTensor() {
return handle_;
}
operator DLTensor*() {
return &(handle_);
}
void Reshape(const std::vector<int64_t>& shape) {
shape_ = shape;
int64_t sz = std::accumulate(std::begin(shape), std::end(shape),
int64_t(1), std::multiplies<int64_t>());
int ret = posix_memalign(&handle_.data, 256, sz);
if (ret != 0) throw std::bad_alloc();
handle_.shape = &shape_[0];
handle_.ndim = static_cast<uint32_t>(shape.size());
}
private:
DLTensor handle_;
std::vector<int64_t> shape_;
std::vector<int64_t> strides_;
// original space container, if
std::shared_ptr<DLTContainer> origin_;
};
} // namespace dlpack
#endif // DLPACK_DLPACKCPP_H_

7
third_party/dlpack/contrib/mock_c.c vendored Normal file
View File

@@ -0,0 +1,7 @@
// Copyright by contributors
// This file is used to make sure the package is C compatible
#include <dlpack/dlpack.h>
int GetNDim(DLTensor *t) {
return t->ndim;
}

View File

@@ -0,0 +1,8 @@
// Copyright by contributors
#include <dlpack/dlpack.h>
#include <dlpack/dlpackcpp.h>
int main() {
dlpack::DLTContainer c;
return 0;
}

View File

@@ -0,0 +1,3 @@
Sphinx==4.4.0
pydata-sphinx-theme==0.7.1
breathe==4.31.0

1
third_party/dlpack/docs/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
doxygen

37
third_party/dlpack/docs/CMakeLists.txt vendored Normal file
View File

@@ -0,0 +1,37 @@
find_package(Doxygen QUIET)
if(NOT DOXYGEN_FOUND)
message(FATAL_ERROR "Doxygen is needed to build the documentation.")
endif()
# TODO: add config file
set(doxyfile_in ${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in)
set(doxyfile ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile)
set(doxygen_output_dir ${CMAKE_CURRENT_BINARY_DIR}/doxygen)
configure_file(${doxyfile_in} ${doxyfile} @ONLY)
file(MAKE_DIRECTORY ${doxygen_output_dir})
add_custom_target(Doxygen ALL
COMMAND ${DOXYGEN_EXECUTABLE} ${doxyfile}
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
COMMENT "Generating API documentation with Doxygen"
VERBATIM)
find_program(SPHINX_EXECUTABLE
NAMES sphinx-build
DOC "Path to sphinx-build executable")
set(sphinx_source ${CMAKE_CURRENT_SOURCE_DIR}/source)
set(sphinx_build ${CMAKE_CURRENT_BINARY_DIR}/build/latest)
set(doxygen_xml_builddir ${doxygen_output_dir}/xml)
add_custom_target(Sphinx ALL
COMMAND ${SPHINX_EXECUTABLE} -b html
-Dbreathe_projects.dlpack=${doxygen_xml_builddir}
${sphinx_source} ${sphinx_build} -WT --keep-going
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
COMMENT "Generating documentation with Sphinx"
VERBATIM)
install(DIRECTORY ${sphinx_build} DESTINATION share/${PROJECT_NAME}/docs)

2339
third_party/dlpack/docs/Doxyfile vendored Normal file

File diff suppressed because it is too large Load Diff

18
third_party/dlpack/docs/Doxyfile.in vendored Normal file
View File

@@ -0,0 +1,18 @@
PROJECT_NAME = @PROJECT_NAME@
PROJECT_NUMBER = @PROJECT_VERSION@
PROJECT_BRIEF = "Common in-memory tensor structure and operator interface for deep learning and other systems"
STRIP_FROM_PATH = @PROJECT_SOURCE_DIR@ \
@PROJECT_BINARY_DIR@
OUTPUT_LANGUAGE = English
FILE_PATTERNS = *.h *.md
RECURSIVE = YES
INPUT = @CMAKE_SOURCE_DIR@/include/dlpack
IMAGE_PATH = @CMAKE_SOURCE_DIR@/docs
USE_MDFILE_AS_MAINPAGE = @CMAKE_SOURCE_DIR@/docs/readme.md
JAVADOC_AUTOBRIEF = YES
GENERATE_HTML = NO
GENERATE_LATEX = NO
GENERATE_XML = YES
XML_OUTPUT = xml
XML_PROGRAMLISTING = YES
OUTPUT_DIRECTORY = @CMAKE_BINARY_DIR@/docs/doxygen

20
third_party/dlpack/docs/Makefile vendored Normal file
View File

@@ -0,0 +1,20 @@
# Minimal makefile for Sphinx documentation
#
# You can set these variables from the command line, and also
# from the environment for the first two.
SPHINXOPTS ?= -Dbreathe_projects.dlpack=../doxygen/xml -WT --keep-going
SPHINXBUILD ?= sphinx-build
SOURCEDIR = source
BUILDDIR = build/latest
# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
.PHONY: help Makefile
# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -b $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

13
third_party/dlpack/docs/README.md vendored Normal file
View File

@@ -0,0 +1,13 @@
# Documentation for DLPack
## Building Locally
The following dependencies must be downloaded in order to build docs:
- Doxygen (On debian distros, simply run `sudo apt -y install doxygen`)
- Python dependencies present in the `doc_requirements.txt` file in the root directory of the project. Run `python3 -m pip install -r doc_requirements.txt` to install them.
Once the dependencies are installed, docs can be built using either CMake or the Makefile from the root directory of the project.
- Using Makefile: Run `make doc` to build the HTML pages. Run `make show_docs` to serve the website locally.
- Using CMake: Build with `BUILD_DOCS` option `ON`: `mkdir -p build && cd build && cmake .. -DBUILD_DOCS=ON && make`

36
third_party/dlpack/docs/make.bat vendored Normal file
View File

@@ -0,0 +1,36 @@
@ECHO OFF
pushd %~dp0
REM Command file for Sphinx documentation
if "%SPHINXBUILD%" == "" (
set SPHINXBUILD=sphinx-build
)
set SOURCEDIR=source
set SPHINXOPTS=-Dbreathe_projects.dlpack=../doxygen/xml -WT --keep-going
set BUILDDIR=build/latest
if "%1" == "" goto help
%SPHINXBUILD% >NUL 2>NUL
if errorlevel 9009 (
echo.
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
echo.installed, then set the SPHINXBUILD environment variable to point
echo.to the full path of the 'sphinx-build' executable. Alternatively you
echo.may add the Sphinx directory to PATH.
echo.
echo.If you don't have Sphinx installed, grab it from
echo.https://www.sphinx-doc.org/
exit /b 1
)
%SPHINXBUILD% -b %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
goto end
:help
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
:end
popd

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

View File

@@ -0,0 +1,35 @@
.. _c_api:
C API (``dlpack.h``)
====================
Macros
~~~~~~
.. doxygendefine:: DLPACK_EXTERN_C
.. doxygendefine:: DLPACK_VERSION
.. doxygendefine:: DLPACK_DLL
Enumerations
~~~~~~~~~~~~
.. doxygenenum:: DLDeviceType
.. doxygenenum:: DLDataTypeCode
Structs
~~~~~~~
.. doxygenstruct:: DLDevice
:members:
.. doxygenstruct:: DLDataType
:members:
.. doxygenstruct:: DLTensor
:members:
.. doxygenstruct:: DLManagedTensor
:members:

78
third_party/dlpack/docs/source/conf.py vendored Normal file
View File

@@ -0,0 +1,78 @@
# Configuration file for the Sphinx documentation builder.
#
# This file only contains a selection of the most common options. For a full
# list see the documentation:
# https://www.sphinx-doc.org/en/master/usage/configuration.html
# -- Path setup --------------------------------------------------------------
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
# import os
# import sys
# sys.path.insert(0, os.path.abspath('.'))
# -- Project information -----------------------------------------------------
project = 'DLPack'
copyright = '2022, DLPack contributors'
author = 'DLPack contributors'
# The full version, including alpha/beta/rc tags
release = '0.6.0'
# -- General configuration ---------------------------------------------------
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = ['sphinx.ext.intersphinx', 'breathe']
# Add any paths that contain templates here, relative to this directory.
# templates_path = ['_templates']
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This pattern also affects html_static_path and html_extra_path.
exclude_patterns = []
intersphinx_mapping = {
"array_api": ("https://data-apis.org/array-api/latest", None),
}
html_use_index = True
html_domain_indices = True
html_theme_options = {
"github_url": "https://github.com/dmlc/dlpack",
"use_edit_page_button": True,
"show_toc_level": 1,
}
html_context = {
"github_user": "dmlc",
"github_repo": "dlpack",
"github_version": "main",
"doc_path": "docs/source",
}
# -- Breathe configuration ---------------------------------------------------
# Tell breathe what to call the project. This is used to link XML files
# generated by Doxygen by passing a command line option:
# -Dbreathe_projects.<breathe_default_project>=</path/to/xml/index/directory>
breathe_default_project = "dlpack"
# -- Options for HTML output -------------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
html_theme = 'pydata_sphinx_theme' # 'alabaster'
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']

View File

@@ -0,0 +1,82 @@
Welcome to DLPack's documentation!
==================================
Purpose
~~~~~~~
In order for an ndarray system to interact with a variety of frameworks, a
stable in-memory data structure is needed.
DLPack is one such data structure that allows exchange between major
frameworks. It is developed with inputs from many deep learning system core
developers. Highlights include:
* Minimum and stable: :ref:`simple header <c_api>`
* Designed for cross hardware: CPU, CUDA, OpenCL, Vulkan, Metal, VPI, ROCm,
WebGPU, Hexagon
* Already a standard with wide community adoption and support:
* `NumPy <https://numpy.org/doc/stable/release/1.22.0-notes.html#add-nep-47-compatible-dlpack-support>`_
* `CuPy <https://docs.cupy.dev/en/stable/reference/generated/cupy.fromDlpack.html>`_
* `PyTorch <https://pytorch.org/docs/stable/dlpack.html>`_
* `Tensorflow <https://www.tensorflow.org/api_docs/python/tf/experimental/dlpack/from_dlpack>`_
* `MXNet <https://mxnet.apache.org/versions/master/api/python/docs/_modules/mxnet/dlpack.html>`_
* `TVM <https://tvm.apache.org/docs/reference/api/python/contrib.html#module-tvm.contrib.dlpack>`_
* `mpi4py <https://mpi4py.readthedocs.io/en/stable/overview.html#support-for-gpu-aware-mpi>`_
* Clean C ABI compatible.
* Means you can create and access it from any language.
* It is also essential for building JIT and AOT compilers to support these
data types.
Scope
~~~~~
The main design rationale of DLPack is the minimalism. DLPack drops the
consideration of allocator, device API and focus on the minimum data
structure. While still considering the need for cross hardware support
(e.g. the data field is opaque for platforms that does not support normal
addressing).
It also simplifies some of the design to remove legacy issues (e.g. everything
assumes to be row major, strides can be used to support other case, and avoid
the complexity to consider more layouts).
Roadmap
~~~~~~~
* C API that could be exposed as a new Python attribute ``__dlpack_info__``
for returning API and ABI versions. (see `#34 <https://github.com/dmlc/dlpack/issues/34>`_,
`#72 <https://github.com/dmlc/dlpack/pull/72>`_)
* Clarify alignment requirements. (see
`data-apis/array-api#293 <https://github.com/data-apis/array-api/issues/293>`_,
`numpy/numpy#20338 <https://github.com/numpy/numpy/issues/20338>`_,
`data-apis/array-api#293 (comment) <https://github.com/data-apis/array-api/issues/293#issuecomment-964434449>`_)
* Adding support for boolean data type (see `#75 <https://github.com/dmlc/dlpack/issues/75>`_)
* Adding a read-only flag (ABI break) or making it a hard requirement in the spec that
imported arrays should be treated as read-only. (see
`data-apis/consortium-feedback#1 (comment) <https://github.com/data-apis/consortium-feedback/issues/1#issuecomment-675857753>`_,
`data-apis/array-api#191 <https://github.com/data-apis/array-api/issues/191>`_)
* Standardize C interface for stream exchange. (see `#74 <https://github.com/dmlc/dlpack/issues/74>`_,
`#65 <https://github.com/dmlc/dlpack/issues/65>`_)
DLPack Documentation
~~~~~~~~~~~~~~~~~~~~
.. toctree::
:maxdepth: 2
c_api
python_spec
Indices and tables
==================
* :ref:`genindex`
* :ref:`search`

View File

@@ -0,0 +1,156 @@
.. _python-spec:
Python Specification for DLPack
===============================
The Python specification for DLPack is a part of the
`Python array API standard <https://data-apis.org/array-api/latest/index.html>`_.
More details about the spec can be found under the :ref:`data-interchange` page.
Syntax for data interchange with DLPack
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
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.
Semantics
~~~~~~~~~
DLPack describes the memory layout of strided, n-dimensional arrays.
When a user calls ``y = from_dlpack(x)``, the library implementing ``x`` (the
"producer") will provide access to the data from ``x`` to the library
containing ``from_dlpack`` (the "consumer"). If possible, this must be
zero-copy (i.e. ``y`` will be a *view* on ``x``). If not possible, that library
may make a copy of the data. In both cases:
- The producer keeps owning the memory
- ``y`` may or may not be a view, therefore the user must keep the recommendation to
avoid mutating ``y`` in mind - see :ref:`copyview-mutability`.
- Both ``x`` and ``y`` may continue to be used just like arrays created in other ways.
If an array that is accessed via the interchange protocol lives on a
device that the requesting library does not support, it is recommended to
raise a ``TypeError``.
Stream handling through the ``stream`` keyword applies to CUDA and ROCm (perhaps
to other devices that have a stream concept as well, however those haven't been
considered in detail). The consumer must pass the stream it will use to the
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.
Implementation
~~~~~~~~~~~~~~
*Note that while this API standard largely tries to avoid discussing
implementation details, some discussion and requirements are needed
here because data interchange requires coordination between
implementers on, e.g., memory management.*
.. image:: /_static/images/DLPack_diagram.png
:alt: Diagram of DLPack structs
*DLPack diagram. Dark blue are the structs it defines, light blue
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
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:
.. code-block:: C
static void dlpack_capsule_deleter(PyObject *self){
if (PyCapsule_IsValid(self, "used_dltensor")) {
return; /* Do nothing if the capsule has been consumed. */
}
/* an exception may be in-flight, we must save it in case we create another one */
PyObject *type, *value, *traceback;
PyErr_Fetch(&type, &value, &traceback);
DLManagedTensor *managed = (DLManagedTensor *)PyCapsule_GetPointer(self, "dltensor");
if (managed == NULL) {
PyErr_WriteUnraisable(self);
goto done;
}
/* the spec says the deleter can be NULL if there is no way for the caller to provide a reasonable destructor. */
if (managed->deleter) {
managed->deleter(managed);
/* TODO: is the deleter allowed to set a python exception? */
assert(!PyErr_Occurred());
}
done:
PyErr_Restore(type, value, traceback);
}
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``.
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 <https://github.com/dmlc/dlpack>`_.
.. warning::
DLPack contains a ``device_id``, which will be the device
ID (an integer, ``0, 1, ...``) which the producer library uses. In
practice this will likely be the same numbering as that of the
consumer, however that is not guaranteed. Depending on the hardware
type, it may be possible for the consumer library implementation to
look up the actual device from the pointer to the data - this is
possible for example for CUDA device pointers.
It is recommended that implementers of this array API consider and document
whether the ``.device`` attribute of the array returned from ``from_dlpack`` is
guaranteed to be in a certain order or not.
Reference Implementations
~~~~~~~~~~~~~~~~~~~~~~~~~
Several Python libraries have adopted this standard using Python C API, C++, Cython,
ctypes, cffi, etc:
* NumPy: `Python C API <https://github.com/numpy/numpy/blob/main/numpy/core/src/multiarray/dlpack.c>`__
* CuPy: `Cython <https://github.com/cupy/cupy/blob/master/cupy/_core/dlpack.pyx>`__
* Tensorflow: `C++ <https://github.com/tensorflow/tensorflow/blob/master/tensorflow/c/eager/dlpack.cc>`__,
`Python wrapper using Python C API <https://github.com/tensorflow/tensorflow/blob/a97b01a4ff009ed84a571c138837130a311e74a7/tensorflow/python/tfe_wrapper.cc#L1562>`__,
`XLA <https://github.com/tensorflow/tensorflow/blob/master/tensorflow/compiler/xla/python/dlpack.cc>`__
* PyTorch: `C++ <https://github.com/pytorch/pytorch/blob/master/aten/src/ATen/DLConvertor.cpp>`__,
`Python wrapper using Python C API <https://github.com/pytorch/pytorch/blob/c22b8a42e6038ed2f6a161114cf3d8faac3f6e9a/torch/csrc/Module.cpp#L355>`__
* MXNet: `ctypes <https://github.com/apache/incubator-mxnet/blob/master/python/mxnet/dlpack.py>`__
* TVM: `ctypes <https://github.com/apache/tvm/blob/main/python/tvm/_ffi/_ctypes/ndarray.py>`__,
`Cython <https://github.com/apache/tvm/blob/main/python/tvm/_ffi/_cython/ndarray.pxi>`__
* mpi4py: `Cython <https://github.com/mpi4py/mpi4py/blob/master/src/mpi4py/MPI/asdlpack.pxi>`_

View File

@@ -0,0 +1,229 @@
/*!
* Copyright (c) 2017 by Contributors
* \file dlpack.h
* \brief The common header of DLPack.
*/
#ifndef DLPACK_DLPACK_H_
#define DLPACK_DLPACK_H_
/**
* \brief Compatibility with C++
*/
#ifdef __cplusplus
#define DLPACK_EXTERN_C extern "C"
#else
#define DLPACK_EXTERN_C
#endif
/*! \brief The current version of dlpack */
#define DLPACK_VERSION 70
/*! \brief The current ABI version of dlpack */
#define DLPACK_ABI_VERSION 1
/*! \brief DLPACK_DLL prefix for windows */
#ifdef _WIN32
#ifdef DLPACK_EXPORTS
#define DLPACK_DLL __declspec(dllexport)
#else
#define DLPACK_DLL __declspec(dllimport)
#endif
#else
#define DLPACK_DLL
#endif
#include <stdint.h>
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
/*!
* \brief The device type in DLDevice.
*/
#ifdef __cplusplus
typedef enum : int32_t {
#else
typedef enum {
#endif
/*! \brief CPU device */
kDLCPU = 1,
/*! \brief CUDA GPU device */
kDLCUDA = 2,
/*!
* \brief Pinned CUDA CPU memory by cudaMallocHost
*/
kDLCUDAHost = 3,
/*! \brief OpenCL devices. */
kDLOpenCL = 4,
/*! \brief Vulkan buffer for next generation graphics. */
kDLVulkan = 7,
/*! \brief Metal for Apple GPU. */
kDLMetal = 8,
/*! \brief Verilog simulator buffer */
kDLVPI = 9,
/*! \brief ROCm GPUs for AMD GPUs */
kDLROCM = 10,
/*!
* \brief Pinned ROCm CPU memory allocated by hipMallocHost
*/
kDLROCMHost = 11,
/*!
* \brief Reserved extension device type,
* used for quickly test extension device
* The semantics can differ depending on the implementation.
*/
kDLExtDev = 12,
/*!
* \brief CUDA managed/unified memory allocated by cudaMallocManaged
*/
kDLCUDAManaged = 13,
/*!
* \brief Unified shared memory allocated on a oneAPI non-partititioned
* device. Call to oneAPI runtime is required to determine the device
* type, the USM allocation type and the sycl context it is bound to.
*
*/
kDLOneAPI = 14,
/*! \brief GPU support for next generation WebGPU standard. */
kDLWebGPU = 15,
/*! \brief Qualcomm Hexagon DSP */
kDLHexagon = 16,
} DLDeviceType;
/*!
* \brief A Device for Tensor and operator.
*/
typedef struct {
/*! \brief The device type used in the device. */
DLDeviceType device_type;
/*!
* \brief The device index.
* For vanilla CPU memory, pinned memory, or managed memory, this is set to 0.
*/
int32_t device_id;
} DLDevice;
/*!
* \brief The type code options DLDataType.
*/
typedef enum {
/*! \brief signed integer */
kDLInt = 0U,
/*! \brief unsigned integer */
kDLUInt = 1U,
/*! \brief IEEE floating point */
kDLFloat = 2U,
/*!
* \brief Opaque handle type, reserved for testing purposes.
* Frameworks need to agree on the handle data type for the exchange to be well-defined.
*/
kDLOpaqueHandle = 3U,
/*! \brief bfloat16 */
kDLBfloat = 4U,
/*!
* \brief complex number
* (C/C++/Python layout: compact struct per complex number)
*/
kDLComplex = 5U,
} DLDataTypeCode;
/*!
* \brief The data type the tensor can hold. The data type is assumed to follow the
* native endian-ness. An explicit error message should be raised when attempting to
* export an array with non-native endianness
*
* Examples
* - float: type_code = 2, bits = 32, lanes=1
* - float4(vectorized 4 float): type_code = 2, bits = 32, lanes=4
* - int8: type_code = 0, bits = 8, lanes=1
* - std::complex<float>: type_code = 5, bits = 64, lanes = 1
*/
typedef struct {
/*!
* \brief Type code of base types.
* We keep it uint8_t instead of DLDataTypeCode for minimal memory
* footprint, but the value should be one of DLDataTypeCode enum values.
* */
uint8_t code;
/*!
* \brief Number of bits, common choices are 8, 16, 32.
*/
uint8_t bits;
/*! \brief Number of lanes in the type, used for vector types. */
uint16_t lanes;
} DLDataType;
/*!
* \brief Plain C Tensor object, does not manage memory.
*/
typedef struct {
/*!
* \brief The data pointer points to the allocated data. This will be CUDA
* device pointer or cl_mem handle in OpenCL. It may be opaque on some device
* types. This pointer is always aligned to 256 bytes as in CUDA. The
* `byte_offset` field should be used to point to the beginning of the data.
*
* Note that as of Nov 2021, multiply libraries (CuPy, PyTorch, TensorFlow,
* TVM, perhaps others) do not adhere to this 256 byte aligment requirement
* on CPU/CUDA/ROCm, and always use `byte_offset=0`. This must be fixed
* (after which this note will be updated); at the moment it is recommended
* to not rely on the data pointer being correctly aligned.
*
* For given DLTensor, the size of memory required to store the contents of
* data is calculated as follows:
*
* \code{.c}
* static inline size_t GetDataSize(const DLTensor* t) {
* size_t size = 1;
* for (tvm_index_t i = 0; i < t->ndim; ++i) {
* size *= t->shape[i];
* }
* size *= (t->dtype.bits * t->dtype.lanes + 7) / 8;
* return size;
* }
* \endcode
*/
void* data;
/*! \brief The device of the tensor */
DLDevice device;
/*! \brief Number of dimensions */
int32_t ndim;
/*! \brief The data type of the pointer*/
DLDataType dtype;
/*! \brief The shape of the tensor */
int64_t* shape;
/*!
* \brief strides of the tensor (in number of elements, not bytes)
* can be NULL, indicating tensor is compact and row-majored.
*/
int64_t* strides;
/*! \brief The offset in bytes to the beginning pointer to data */
uint64_t byte_offset;
} DLTensor;
/*!
* \brief C Tensor object, manage memory of DLTensor. This data structure is
* intended to facilitate the borrowing of DLTensor 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.
*/
void * manager_ctx;
/*! \brief Destructor signature void (*)(void*) - this should be called
* to destruct manager_ctx which holds the DLManagedTensor. 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 DLManagedTensor * self);
} DLManagedTensor;
#ifdef __cplusplus
} // DLPACK_EXTERN_C
#endif
#endif // DLPACK_DLPACK_H_

View File

@@ -0,0 +1,6 @@
#!/bin/bash
make || exit -1
mkdir -p build
cd build
cmake .. || exit -1
make || exit -1

23
third_party/dlpack/tests/scripts/task_lint.sh vendored Executable file
View File

@@ -0,0 +1,23 @@
#!/bin/bash
mkdir -p bin
if [ ! -f bin/lint.py ]; then
echo "Grab linter ..."
wget https://raw.githubusercontent.com/dmlc/dmlc-core/main/scripts/lint.py
mv lint.py bin/lint.py
fi
echo "Check codestyle of c++ code..."
python bin/lint.py dlpack cpp include contrib
echo "Check doxygen generation..."
make doc 2>log.txt
(cat log.txt| grep -v ENABLE_PREPROCESSING |grep -v "unsupported tag") > logclean.txt
echo "---------Error Log----------"
cat logclean.txt
echo "----------------------------"
(cat logclean.txt|grep warning) && exit -1
(cat logclean.txt|grep error) && exit -1
rm logclean.txt
rm log.txt
echo "All checks passed..."