# Mojo functions, across a cluster

> Dista runs Mojo functions across a cluster: declare a task, call it, and the work spreads over the machines, with results staying where they were computed. Early, open source, and measured against Ray and Rust.

[magmalake](https://magmalake.dev/) [Dista](https://magmalake.dev/s/dista)

Dista is for developers who have written fast Mojo and now need more than one machine. A task is an ordinary Mojo function. The program that calls it runs unchanged on a laptop, where Dista starts its own workers, and on a Kubernetes cluster, where every node runs the same small image. There is no Python in the path and no second language to learn.

```
from dista import Runtime, Task

def square(x: Int) -> Int:
    return x * x

comptime Square = Task[Int, Int, square]

def main() raises:
    var rt = Runtime[Square](workers=4)
    var r = rt.remote[Square](7)   # an ObjectRef[Int]
    print(rt.get(r))               # 49
```

Results that are large stay on the worker that computed them. A task on the same host maps them from shared memory, and one elsewhere fetches them from that worker directly, without going through the head. A task can take other tasks' results as its input, so a pipeline is a graph of calls rather than a sequence of round trips to the driver.

### Who it's for

**Mojo developers** whose code has outgrown one machine, and who want to keep it compiled and typed rather than wrap it for a Python scheduler.

**Data engineers** scanning Parquet and Iceberg in object storage: Dista ships with magmalake's native Iceberg reader, and moves Arrow batches between tasks.

**Teams evaluating Ray** for compiled workloads, who want a smaller runtime: one binary, a 32 MB image, and a head whose state rebuilds itself after a restart.

[Read the docs](https://magmalake.dev/s/dista/docs) · [source on GitHub](https://github.com/magmalake/dista.mojo)

##  12 s a pass, against 28.7 s for Rust and 38.6 s for Ray

The same eight queries over 1.4 billion taxi trips in an Iceberg table in S3, on three worker nodes, with the answers checked against each other. A day earlier Dista took 56.7 s on the same run; rewriting how it reads from object storage made it the fastest of the three. The [performance page](https://magmalake.dev/s/dista/performance) has every query, the before-and-after timeline, the earlier runs where Dista lost, and how to reproduce each number.

Dista 12 s

Rust 28.7 s

Ray 38.6 s

Median pass of the eight queries, 3 × AWS c7g.4xlarge (16 Graviton3 vCPUs, 1 November 2026\. Lower is better. One run on one day: the comparison frameworks varied about 10% between runs.

## Early, and measured as it goes

Dista has passed its fourth phase's gate: the taxi queries on a Kubernetes cluster stay exact with a worker killed mid-run, and a worker stopped the way Kubernetes stops one drains without anything running twice. Before that came tasks, results, a head that restarts, and actors that survive their worker or the head restarting. What it doesn't do yet is listed here rather than left to be found out.

### Works today

- Tasks: a top-level Mojo function, declared once, called with rt.remote; Task2 and Task3 for two or three arguments
- Results that stay on the worker that computed them, mapped from shared memory on the same host or fetched from that worker directly
- Tasks on other tasks' results: one ObjectRef, or a list gathered into a Seq
- Placement by data location: a task goes to the node holding most of its input, when one does
- Tasks whose worker dies run again on another, up to DISTA\_TASK\_RETRIES times
- Draining: a worker that Kubernetes stops finishes its tasks, and its results and its actor move to other workers, so nothing fails or runs twice
- Spilling: past DISTA\_STORE\_BYTES, a worker's results go to disk, and readers find them there
- Backpressure: with too much queued, rt.remote waits for the workers to catch up
- Arrow record batches as task arguments and results
- Freeing: reference counts at the head, rt.release
- A head that restarts: workers rejoin with what they hold, drivers resume where they were
- Actors: stateful structs on a worker of their own, calls in order, handles passed to tasks and other actors
- Actors that survive their worker or the head restarting, restored from checkpoints in a directory or S3
- Shuffles: several results from one task, and a task taking a value beside other tasks' results; a shuffle benchmark against Ray
- A task timeline in Chrome's trace format, the driver's own work included
- Prometheus metrics on the head and every worker
- The dista command: one head for several programs; dista status, timeline and stop
- One container image for every role, 32 MB compressed, on Kubernetes
- A published package: pixi shelf add dista-mojo

### Not yet

- Refs nested inside a struct or list in a task's argument, which is a compile error for now (deferred, issue 68)
- Rebuilding results lost with a worker, as Ray's lineage does: the job fails, naming them, and orca runs it again (deferred, issue 69)
- A running job asking for more workers: orca sizes each run's cluster (deferred, issue 76)

A longer write-up, with the design and the measurements behind it, follows with the second benchmark, which spends its time in user code and a shuffle.

[A magmalake project](https://magmalake.dev/)

Apache 2.0 · © 2026 Marius Seritan
