DaCe: mapping Python and NumPy code to dataflow programs for CPU, GPU and FPGA
DaCe - Data Centric Parallel Programming
At a glance
- What is it?
- DaCe is a data-centric parallel programming framework that turns Python and NumPy code into a Stateful DataFlow multigraph and then into optimised programs for CPUs, GPUs and FPGAs, with transformations you can apply from a notebook or the command line. It suits performance engineers and kernel developers who need the same source to target very different hardware.
- Who is it for?
- DaCe is a strong fit for a team with a computational kernel that has to run on more than one kind of accelerator, or for a performance engineer who wants a single place to hold transformations, instrumentation and code generation rather than a separate implementation per backend.
- Can I use it commercially?
- Yes. BSD-3-Clause 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 received new commits within the last day.
- What is it written in?
- Mainly Python, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
One source, several accelerators, and an intermediate representation in between
DaCe is a parallel programming framework from SPCL at ETH Zurich, and its stated purpose is decoupling domain science from performance optimization. It takes code in Python and NumPy, along with other languages, and maps it to high performance programs for CPUs, GPUs and FPGAs.
The interesting part is the step in the middle. Internally, DaCe uses the Stateful DataFlow multiGraph, an SDFG, as a data-centric intermediate representation. The README describes it as a transformable, interactive representation of code based on data movement. The input code and the SDFG are separate artifacts, which is what makes the rest of the design possible.
That separation gives DaCe two properties at once. A program can be optimized without changing its source, so the source stays readable and reviewable. And transformations are customizable and user extensible, so a transformation written once can be reused across many applications. The README frames this as direct knowledge transfer of performance optimization, regardless of the application or the target processor, which is the actual claim of the project rather than a raw speed number.
The audience is therefore narrow but real: a team with a computational kernel that must run on a CPU today and on an FPGA or a GPU later, or a performance engineer who wants the optimisation work separated from the science. The publication for the SDFG IR is linked in the README, and the citation is provided so that papers built on the framework can credit it.
The SDFG is a graph of states and data movement
An SDFG is not a control flow graph, and the distinction is the whole point of the project.
In a control flow graph, the structure of the program is a sequence of instructions and branches. In a data-centric representation, the structure is a set of states connected by edges that carry data, and the scheduling freedom of the underlying hardware, which can reorder and parallelise, is what the representation is designed to expose. The name Stateful DataFlow multiGraph says both halves: data flows along the edges, and the nodes carry state.
The practical consequence is that DaCe's optimisation work happens on the graph rather than on the source. A transformation can change how data is moved, which is the thing that dominates performance on a GPU or an FPGA, and the Python source is untouched. Because the graph is interactive, the same transformation can be applied in a notebook, from the command line, or by hand on a graph that was loaded from a file.
DaCe is not limited to Python for input. The README says the framework takes code in Python and NumPy and other programming languages, and the repository has a `samples/` directory with an `explicit/` example for explicit dataflow, plus `sdfg_api/`, `codegen/`, `optimization/`, `instrumentation/` and `distributed/` examples. There is also a `snitch/` sample directory, which suggests integration with a tracing or profiling tool for the distributed case, though the README does not describe it.
The framework also separates the concerns of describing the computation from the concerns of emitting it. The codegen sample directory and the tutorial on extending the code generator show that the back ends are meant to be replaceable and extendable, which is the mechanism behind the CPU, GPU and FPGA targets.
A first program: decorate a NumPy loop and call it
The quick start is short, and the entry point is a decorator.
The README says to install DaCe with pip, using `pip install dace`, and points to a full installation and troubleshooting guide for the cases where that is not enough. Using the framework in Python is described as as simple as adding a `@dace` decorator, and the example is a loop that adds an index to an array and then sums it.
@dace
def myprogram(a):
for i in range(a.shape[0]):
a[i] += i
return np.sum(a)The surrounding example imports `dace` and `numpy` first, and that is the entire setup for a first program. There is no separate build script and no separate description of the computation.
What happens on the call is described in the README. Calling `myprogram` with any NumPy array, or with a GPU array such as a PyTorch, Numba or CuPy array, generates the data-centric code, compiles it and runs it. From that point the program can be optimized, interactively or automatically, instrumented and distributed.
The last of those three is worth pausing on, because it is the part most frameworks leave out. Instrumentation, which in this context means measuring inside the generated program, and distribution, which means running the computation across machines, are described as operations you apply to the same artifact you already have. Both appear as dedicated sample directories, instrumentation and distributed, alongside a benchmarking tutorial in the list of notebooks.
The tutorials linked in the README are the real documentation here: a getting started notebook, one on benchmarking and comparison with other Python compilers, one on explicit dataflow, the NumPy frontend reference, the SDFG API, a tutorial on using and creating transformations and one on extending the code generator.
Generated code leaves the Python world as a shared library
There is a detail in the README that changes how you think about the output. The code creates a shared library, a DLL or an SO file, that can readily be used in any C ABI compatible language, with C, C++ and FORTRAN given as examples.
So DaCe is not only a Python to Python compiler. The Python is the authoring language, and the artefact is a native library with a C ABI. That makes the framework usable in a broader setting than a pure Python tool, since a host application in another language can call the generated kernel through the ABI, and it makes the generated code usable from an existing C or Fortran codebase without porting the whole program.
The same point appears in the topic list of the repository, where CUDA, FPGA, high-level synthesis, Vivado HLS and high performance computing all appear. The FPGA support itself lives in a separate repository, dace-fpga, and the README links it rather than describing it inline. The same split applies to the generated code, which for a CPU target is C or C++, for an NVIDIA or AMD GPU is CUDA or HIP, and for an FPGA is high level synthesis input.
That separation is worth understanding before you adopt the project. The DaCe repository is the core framework and the data-centric intermediate representation, the SDFG and the transformations. The FPGA path is a downstream project that consumes the same representation, and it is maintained separately.
For the GPU path, the framework is more self contained. The optional dependency groups in the package metadata include a gpu group with cupy and a cutensor build, and the README states that NVIDIA GPUs and AMD GPUs are supported, with AMD through HIP.
Transformations: notebook, command line or a Visual Studio Code extension
DaCe can be written inline in Python and transformed in the command line or in Jupyter notebooks, and SDFGs can also be modified interactively through a Visual Studio Code extension. The README links the extension in the marketplace, and the SDFG API tutorial is one of the listed notebooks.
Three access points for the same graph is a deliberate design choice, and each suits a different person. The notebook path is for exploration, where you load or build an SDFG, apply a transformation, look at the result and rerun. The command line path is for the same operations in a script or a build pipeline. The editor extension is for the case where the graph is large enough that reading it in a notebook is not productive.
The transformation model is where the reuse claim lives. Transformations are customizable and user extensible, so they can be written once and reused in many applications, and there is a dedicated tutorial on using and creating transformations, plus a `samples/optimization/` directory of worked examples. If you are migrating an existing hand-optimised kernel, the likely workflow is to express what you did as a transformation and then apply it to other kernels in the same form.
The scope of the transformation library is not described in the README, so a buyer should not assume that a specific optimisation they need already exists. What the framework guarantees is the mechanism, a graph representation where such a transformation can be written, and the infrastructure to apply and reuse it.
One more capability is listed in the features rather than the tutorials. The framework can be instrumented, and the samples directory has an instrumentation example, which is how you get timing data from inside a generated kernel rather than from outside it. For a data-centric program, where the interesting question is where the data movement is, that is a capability you would otherwise have to write by hand.
Dependencies, Python range and the 2.0 alpha line
The packaging tells you what DaCe is built on and what it will ask of your environment.
The Python floor is stated in the project metadata as requiring Python 3.10 and above but below 3.15. That upper bound is the sort of detail that matters in a scientific environment, where the cluster image and the local laptop often differ, and it means a DaCe installation cannot be dropped into an environment on 3.15 without checking.
The runtime dependencies are a mix of numeric libraries and, more unusually, parsing and build tools.
dependencies = [
"numpy",
"networkx>=2.5",
"astunparse",
"sympy>=1.9",
"pyyaml",
"ply",
"fparser>=0.1.3,!=0.2.3",
"dill",`numpy` and `sympy` are there for the numeric frontend, `networkx` for the graph structures that the multigraph is built on, `ply` and `astunparse` for Python parsing, and `fparser` for parsing Fortran, which is why a Fortran parser sits in the dependency list of a framework whose main input language is Python. `dill` is there to serialise the objects, and the list continues with `scikit-build`, `cmake` and `ninja`, which tell you that compiling the generated code is not optional and that a working C and CMake toolchain is part of the installation.
The optional groups are equally informative. There is an ml group with onnx, torch and related packages, an mpi group with mpi4py for the distributed case, and a gpu group with a CUDA 13 build of cupy and cutensor.
[project.optional-dependencies]
ml = ["onnx", "torch", "onnxsim", "onnxscript", "onnxruntime", "protobuf", "ninja"]
mpi = ["mpi4py", "pytest-mpi"]
gpu = ["cupy-cuda13x", "cutensor-cu13"]The release line is the part to weigh before adopting. The three most recent releases are v2.0.0-alpha8, alpha9 and alpha10, published on 2026-09-01, 2026-09-17 and 2026-10-03, and the last push to the repository was on 2026-10-20. A 2.0 line that exists only as alpha builds is a moving target, and the project is under active development, so the API is not yet frozen. The licence is the New BSD licence, and the repository carries a CITATION.cff file, an AUTHORS file and a code of conduct.
Editorial conclusion
DaCe is a strong fit for a team with a computational kernel that has to run on more than one kind of accelerator, or for a performance engineer who wants a single place to hold transformations, instrumentation and code generation rather than a separate implementation per backend. It is the wrong tool for a script that NumPy already runs fast enough, for a team unwilling to deal with a compiler toolchain, and for production use on the 2.0 line while it is still published as alpha builds. Verify first which Python versions you need, since the package requires Python 3.10 up to but not including 3.15, and confirm that your target backend and the optional dependency group it needs are actually covered before you plan around it.
Frequently asked questions
What is DaCe and what problem does it solve?
DaCe is a data-centric parallel programming framework that maps Python and NumPy code to high performance CPU, GPU and FPGA programs through the Stateful DataFlow multiGraph intermediate representation, so that performance optimization can happen without changing the source.
How do I install DaCe?
The README's quick start says to install DaCe with pip using `pip install dace`, and points to a full installation and troubleshooting guide for cases where the simple path is not enough.
What is an SDFG in DaCe?
The Stateful DataFlow multiGraph is DaCe's data-centric intermediate representation, a transformable and interactive representation of code based on data movement, with the input code and the graph kept separate so a program can be optimized without touching its source.
Which hardware does DaCe target?
The README lists multi-core CPUs tested on Intel, IBM POWER9 and ARM with SVE, NVIDIA and AMD GPUs with HIP, and Xilinx and Intel FPGAs, where the FPGA support lives in the separate dace-fpga repository.
Can DaCe generated code be used from other languages?
Yes. The README states that the code creates a shared library, a DLL or an SO file, that can readily be used in any C ABI compatible language, with C, C++ and FORTRAN given as examples.
What Python versions does DaCe support?
The package metadata requires Python 3.10 and above but below 3.15, and the runtime dependencies include scikit-build, cmake and ninja, so a working compiler toolchain is part of a working installation.
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/spcl-dace)Was this useful?