DLPack: the C header that lets PyTorch, JAX and NumPy hand each other tensors
common in-memory tensor structure
At a glance
- What is it?
- DLPack is not a tensor library. It is a small C struct plus a deleter callback that frameworks agree on, so a tensor can cross a library boundary without a copy. Here is what v1.3 ships, how to build the mock in contrib, and where the design stops being enough.
- Who is it for?
- Adopt DLPack if you are writing a framework, a device plugin, or a binding that must hand tensor memory to PyTorch, JAX, NumPy or Arrow without a copy, and you are prepared to own the deleter and the memory lifetime on your side of the boundary. Do not adopt it as a general array library: there are no ops, no broadcasting, no shape arithmetic, and the README states outright that the project does not intend to implement tensors and ops.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 49 days ago.
- What is it written in?
- Mainly C++, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 29, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The problem DLPack solves is a pointer handoff, not an array API
Every deep learning framework has its own tensor type, its own allocator, and its own idea of what a device is. When two of them want to cooperate on the same buffer, the usual answer is a copy: serialize on one side, allocate on the other, memcpy in between. DLPack exists to remove that copy. The README describes it as "an open in-memory tensor structure for sharing tensors among frameworks" and lists four uses: sharing operators between deep learning frameworks, wrapping vendor-level operator implementations, swapping backend implementations such as different BLAS versions, and letting end users mix frameworks. The audience is therefore not an application developer choosing an array library. It is the person writing the layer that sits between two frameworks, or the vendor shipping a kernel that has to be consumed by more than one runtime. The project is explicit that it is a bridge and not a product: "We do not intend to implement Tensor and Ops, but instead use this as common bridge to reuse tensor and ops across frameworks." That sentence is the whole scope statement, and it is worth taking literally before you plan anything on top of it.
Inside the repository: a stabilized header, an unstable contrib, and a mock
The README names two major components. include holds stabilized headers, and contrib holds in-progress unstable libraries. Everything else in the tree is scaffolding around those two: docs for the Doxygen build, tests with a lint script, cmake, apps, and a Makefile whose default target is bin/mock. The Makefile compiles every .cc and .c file under contrib with -std=c++11 and -Wall -O3, placing objects in build/ and linking them into bin/mock. That target name is a fair summary of the project's own posture: the reference artifact in contrib is a mock, not a runtime. The layout also tells you where the contract lives. If you are a consumer, the header under include is the thing you compile against. If you are a contributor, contrib is where implementations go, and the README's warning that it is in progress and unstable is not boilerplate. Governance runs through RFC issues, and the README states that major releases happen as a vote issue so participants agree on the changes. That is a slower process than a maintainer merging a patch, and it means protocol changes arrive on a release cadence rather than continuously. The releases given are v1.1 on 2025-03-11, v1.2 on 2025-10-11, and v1.3 on 2026-01-26, with the last push to main on 2026-08-11.
Building the contrib mock and checking the header compiles
There is no package to install. DLPack is a header you vendor or include, and the repository's own build produces a test binary rather than a library you link against at runtime. Clone the repository, then run the default target. The Makefile's first rule is all: bin/mock, so a bare make builds it.
git clone https://github.com/dmlc/dlpack
cd dlpack
makeThe compiler is invoked with -Wall -O3 -Iinclude -Icontrib and -std=c++11, and the link step produces bin/mock. If you see no output beyond the compile lines, that is the expected result: the target is a binary, not a test suite run. The Makefile also defines a lint target and a doc target, and show_docs serves the rendered documentation over a local HTTP server, which is the quickest way to read it without going to the homepage.
make lint
make doc
make show_docsThe show_docs rule starts python3 -m http.server pointed at docs/build/latest, so the docs are served from the build output, not from a checked-in site. For a first real use, the practical path is not to write your own producer. It is to take an existing framework that already speaks the protocol, ask it for a tensor in DLPack form, and hand that object to a second framework that consumes it. The repository does not ship that example; the README points to https://dmlc.github.io/dlpack/latest for documentation, and the ecosystem bindings live in the frameworks themselves, not here.
Where DLPack is the wrong tool
The failure mode is lifetime, not correctness of the numbers. A DLPack tensor is a view over memory that someone else allocated, plus a deleter callback. If the producer frees the buffer, or if the consumer holds the struct past the point where the deleter is valid, you get a dangling pointer with no runtime check to catch it. Nothing in the repository layout suggests a validation layer that would catch this for you. The second limitation is scope. There is no operator set, no shape manipulation, no dtype promotion, and no device management. If what you actually want is to compute on arrays in Python, DLPack gives you none of that and you should be using a framework. The third is version skew. Because changes go through RFC issues and land in numbered releases, a producer built against one version and a consumer built against another can disagree about the struct, and the README does not document a negotiation or rollback path for that case. The fourth is that contrib is explicitly unstable. Building against something in contrib means building against an interface the README does not promise to keep. Treat include as the contract and contrib as a sample.
How it differs from Arrow's C Data Interface
The closest thing to a real alternative is the Arrow C Data Interface, which solves a similar handoff problem for columnar data. The difference in approach is what the struct describes. Arrow's interface is built around arrays with a schema, validity bitmaps, and nested types, aimed at tabular and columnar interchange between query engines and dataframe libraries. DLPack is built around a single dense tensor with a device, a dtype, a shape and strides, aimed at numerical kernels and accelerators. A dataframe library cares about null counts and chunked layouts; a tensor library cares about whether the buffer is on a GPU and what its strides are. That is why the two coexist rather than compete: PyArrow appears in searches about DLPack because the two interfaces meet at the boundary between columnar data and numerical arrays, and a bridge between them has to translate a schema-aware array into a plain tensor. If your data is tabular and your consumers are query engines, Arrow's interface is the right shape. If your data is a dense tensor and your consumers are training runtimes, DLPack is.
Maintenance, licensing, and what a version bump costs you
The repository is not archived, and the last push to main was on 2026-08-11, which is recent enough that describing it as maintained is fair on the evidence given. The release cadence is roughly one major version every six to eight months across the three releases listed, and the README's vote-issue procedure means a breaking protocol change is a deliberate, announced event rather than a silent commit. That is good for consumers and slow for anyone waiting on a fix. Upgrading means re-reading NEWS.md, checking whether the stabilized header under include changed in a way that affects your struct usage, and rebuilding against the new include path. There is no package manager step to automate this, so the cost lands on your build system. The licence is Apache-2.0, which permits commercial use and modification and includes an explicit patent grant; it also requires that you preserve notices and state significant changes. The LICENSE file at the repository root is the authoritative text, and if you are redistributing a vendored copy of the header inside a proprietary product, that is a question for your own counsel rather than something the README answers.
Editorial conclusion
Adopt DLPack if you are writing a framework, a device plugin, or a binding that must hand tensor memory to PyTorch, JAX, NumPy or Arrow without a copy, and you are prepared to own the deleter and the memory lifetime on your side of the boundary. Do not adopt it as a general array library: there are no ops, no broadcasting, no shape arithmetic, and the README states outright that the project does not intend to implement tensors and ops. Before committing, read include/ for the stabilized header you are targeting, check NEWS.md for what v1.3 changed relative to v1.2, and confirm that every consumer you care about already supports the same version, because the version negotiation is the part of the protocol most likely to bite you.
Frequently asked questions
What is DLPack?
DLPack is an open in-memory tensor structure for sharing tensors among frameworks, with stabilized headers under include and in-progress unstable libraries under contrib. The README states that the project does not intend to implement tensors and ops, and instead acts as a common bridge so frameworks can reuse them.
How do I install DLPack?
There is no install step in the repository. You clone it and build the contrib mock with make, or you include the stabilized header from include in your own build. Distribution happens by vendoring the header or by using a framework that already implements the protocol.
Does DLPack provide tensor operations?
No. The README states that the project does not intend to implement Tensor and Ops, and positions DLPack as a common bridge so that tensors and operators can be reused across frameworks rather than defined here.
What does the DLPack Makefile build by default?
The default target is bin/mock. It compiles the .cc and .c files under contrib with -std=c++11 and -Wall -O3, and links them into bin/mock. Other targets are lint, doc, and show_docs.
How does DLPack handle changes to the protocol?
The README states that RFC proposals are opened as issues and that major releases happen as a vote issue so participants agree on the changes. The listed releases are v1.1, v1.2, and v1.3.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/dmlc-dlpack)