# Legion keeps the algorithm separate from the machine in parallel C++ programs

> Legion is a C++ programming model and runtime system for distributed heterogeneous machines, where programmers describe data as logical regions and hand placement decisions to a mapper. It suits people writing multi-node or GPU code who want the runtime to catch data races, and it costs a real learning curve in exchange.

**StanfordLegion/legion** — The Legion Parallel Programming System

- Repository: https://github.com/StanfordLegion/legion
- Website: https://legion.stanford.edu
- Stars: 773 · Forks: 152
- Language: C++
- License: Apache-2.0
- Published: 2026-10-21 · Updated: 2026-10-21 · Language: en
- Canonical page: https://hysenlabs.com/projects/stanfordlegion-legion

## Legion exists to make the machine invisible, not the parallelism

Legion is a programming model and runtime system written in C++ for distributed, heterogeneous machines. The problem it addresses is stated in the project documentation as decoupling the specification of parallel algorithms from their mapping onto the target architecture.

That split is the whole idea. A program says what the tasks are and what data they touch; a separate mapper decides which processor runs a task and in which memory a physical instance of the data is created. The runtime then handles the parts that usually go wrong: working out when two tasks can run at the same time without a data race, and inserting the copies and constraints needed when a task in one location needs data that lives in another.

The audience is scientific application developers on clusters with mixed hardware, plus the compiler and runtime engineers around the Regent language that ships in the same repository. The project homepage points at legion.stanford.edu, and the tutorials directory holds the source code for the tutorial content linked from the documentation.

## Logical regions and subregions are how you describe data

Running on a distributed machine means distributing data, not just computation, so Legion needs an abstraction for program data that does not depend on the machine. That abstraction is the logical region. A logical region describes the structure of program data in a machine independent way.

The programmer then partitions a logical region into subregions, and the part that matters is what that partitioning carries. It expresses both the independence of the data and its locality. When two tasks touch different subregions, the runtime has what it needs to reason about whether they can run concurrently, and when they need the same subregion on different nodes, the runtime knows a copy is required and can insert it.

The alternative to this style of programming is to hand the system a description of independence that it cannot check. Legion's answer is to derive the answer from the data structure the programmer already has to write, and to report potential races when the programmer asserts a parallel launch that the data does not support.

## The mapping interface is declarative, and the project says so on purpose

Mapping a Legion program involves two kinds of decisions. For each task, select a processor to run it on. For each logical region a task needs, select a memory in which to create a physical instance for that task.

The interface for expressing those decisions is a set of runtime calls that the mapper implements, and one stated non-goal is worth quoting because it explains the design: the project set out not to build a system that was magically capable of making intelligent mapping decisions. Instead the interface is declarative, so the programmer communicates a decision without writing the code that performs the copy or the synchronization.

Because the interface is dynamic, decisions can depend on information only available at run time. The documentation lists three kinds: program data, such as whether an irregular graph is sparse or dense in the number of edges; system data, such as which nodes are up or down or which are running fast to conserve power; and execution data, such as profiling feedback about how a previous mapping performed, or which processors are currently overloaded.

One property makes all of this safe to experiment with. No mapping decision can affect program correctness, so every decision is only a performance decision. A team can enumerate mapping choices and try them without risking a wrong answer.

## Implicit and explicit launches decide what the runtime checks

The runtime's correctness role comes in two flavours, and the difference matters when you write code against it.

With implicit constructs, Legion discovers the parallelism itself. You describe tasks and their data and the runtime works out what can overlap. With explicit constructs, where the programmer states that two tasks should run in parallel, Legion notifies the programmer about potential data races between them.

That notification is the practical payoff of the region model. A race between tasks that the programmer believed were independent is reported rather than silently corrupting data, which matters most on a machine where a race can appear only under a particular placement, on a particular node, at a particular moment.

A default mapper ships with the system so that a program works before any mapping work has been done. The documentation is candid that these heuristic decisions are good in general but certain to be sub-optimal across all applications and architectures. Every call in the mapping interface is a C++ virtual function, so the intended path is to extend the default mapper and override only the calls that affect performance, or to implement the interface from scratch when a workload needs it.

## The branch you clone decides which features you actually get

The repository layout is part of the installation decision, because this is a source tree rather than a package you install. The root contains a CMakeLists.txt, a Dockerfile, a doc directory, the tutorial, examples, apps, and a language directory that holds the Regent compiler and its examples. The core runtime components sit under runtime, with legion for the Legion runtime itself, realm for the Realm low-level runtime, and mappers for the mapper implementations including the default one. A tools directory holds utilities including legion_spy.py, a visualization tool linked from the debugging documentation.

The branch policy is the part most people get wrong. The default branch when you clone is stable, which the project describes as generally about a month behind master so that larger changes get mileage on real workloads first. Updates move to stable roughly monthly, each batch gets a version number, and CHANGES.txt lists the major changes. The master branch is the mainline with fixes that may not have reached stable yet, and is what you want if you are after functionality that has just landed. Other feature branches exist for larger work and are not intended for general use.

Recent version numbers follow a date-like scheme, with releases 26.09.1 on 2026-10-04, 26.09.0 on 2026-09-30 and 26.06.0 on 2026-07-02.

## The container image builds Regent, and it deliberately leaves out CUDA and GASNet

A Dockerfile in the repository root builds the Regent compiler rather than the Legion runtime, and its header comment states the exclusion directly: the version does not include CUDA or GASNet. The base image is Ubuntu 14.04, and the dependency install step is a single apt-get invocation:

```dockerfile
    RUN apt-get update && \
        apt-get install -y build-essential clang-3.5 git libclang-3.5-dev libncurses5-dev llvm-3.5-dev wget zlib1g-dev && \
        apt-get clean
```

The source tree is copied into place and the compiler is installed with a pinned LLVM configuration, after which the regent script is linked into the path:

```dockerfile
    RUN LLVM_CONFIG=llvm-config-3.5 /usr/local/legion/language/install.py --rdir=auto && \
        ln -s /usr/local/legion/language/regent.py /usr/local/bin/regent
```

Two consequences follow for anyone planning around containers. The image gives you Regent, which compiles to Legion programs, and it is not a path to a CUDA-enabled or GASNet-enabled Legion build. The pinned clang 3.5 and llvm 3.5 toolchain also dates from the same era as the Ubuntu 14.04 base, so the image is a specific historical configuration rather than a current toolchain snapshot.

## Realm, MPI and NCCL show the layers you can work at instead

The most concrete alternative to working in Legion's model is to work at a different layer of the same stack, and the repository makes that choice visible.

Realm is the low-level runtime that Legion sits on, with its own header realm.h. A program that does not need logical regions, automatic race discovery and the mapper can be written against Realm directly and skip the Legion abstraction entirely. That is a different approach in kind: you lose the automatic discovery of parallelism and the data-driven placement, and you keep a lower-level runtime that Legion itself uses.

The examples directory also shows that Legion is expected to coexist with existing software rather than replace it. Directories named mpi_interop, mpi_with_ctrl_repl and nccl cover interoperation with MPI and with NCCL, and kokkos_saxpy and omp_saxpy cover work alongside other execution models. There are also examples for ghost cells, collective writes, future instances and machine configuration.

The limit of the whole approach is worth stating plainly. Legion automates race discovery and copy insertion, but the mapper, the partition of regions and the placement of data remain your problem, and the documentation is explicit that the default heuristics will not be optimal for your application and your hardware.

## Conclusion

Legion earns its place in a codebase that runs on mixed CPU and GPU nodes and where data race freedom matters more than a quick start, and it is the wrong choice for a single-node job or for a team unwilling to own a mapper. Verify first that the branch you clone matches the features you need, since stable trails master by about a month, and confirm that the version you build produces a runtime your existing build system can launch.

## FAQ

### What is Legion used for?

Legion is a parallel programming model and runtime system for distributed, heterogeneous machines, written in C++. It decouples the specification of a parallel algorithm from its mapping onto the architecture, using logical regions to describe program data and a mapper to place tasks and data.

### Which Legion branch should I clone?

Clone stable, which is the default branch, unless you need functionality that has just landed on master. Stable is generally about a month behind master, updates move to it roughly monthly, and CHANGES.txt lists the major changes in each batch. Other feature branches are intended for larger in-progress work.

### Does Legion detect data races in my program?

Yes, through its task launch and region model. With implicit constructs Legion discovers the parallelism itself, and with explicit parallel launches it notifies the programmer about potential data races between the tasks. Copies and constraints are inserted automatically when a task needs data located elsewhere.

### How do I change how Legion maps tasks onto processors?

Every call in the mapping interface is a C++ virtual function, so you extend the default mapper and override the mapping calls that affect performance, or implement the interface from scratch. The default mapper uses heuristics that the documentation describes as good in general but sub-optimal across all applications and architectures.

## Sources

- [License: Apache-2.0](https://github.com/StanfordLegion/legion/blob/stable/LICENSE)
- [Project website](https://legion.stanford.edu)
- [README](https://github.com/StanfordLegion/legion/blob/stable/README.md)
- [Releases](https://github.com/StanfordLegion/legion/releases)
- [StanfordLegion/legion on GitHub](https://github.com/StanfordLegion/legion)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/stanfordlegion-legion
