Enceladus v0.1.0 · alpha
QuickstartAll pages
OverviewQuickstartProgramming modelMemory and synchronizationLanguage referenceDebuggingFramework interopPerformanceBenchmarksPorting from Triton

Get started

Quickstart

Install Enceladus, and then run a vector add, a row softmax, a matrix multiplication, and flash attention on your Mac's GPU.

Install Enceladus

To use Enceladus, you need the following:

  • A Mac with Apple silicon (M1 or later).
  • macOS 15 or later.
  • Python 3.11 or later.
  • uv, the Python package and project manager.

To add Enceladus to your uv project, run the following command:

shell
uv add enceladus

If you plan to pass PyTorch tensors or MLX arrays to kernels, install the matching extra, for example uv add "enceladus[torch]" or uv add "enceladus[mlx]".

To check that Enceladus finds your GPU, run the following command:

shell
uv run python -c "import enceladus; print(enceladus.get_device())"

The output is similar to the following:

<enceladus.Device Apple M4 Pro (applegpu_g16s)>

To build from source, clone the repository, run uv sync, and then run the tests with uv run pytest -q.

Run four kernels

Each of the following programs is complete. Save one to a file and run it with uv run python FILE.py, where FILE is the name you chose.

import numpy as np

import enceladus
import enceladus.language as tl


@enceladus.jit
def add_kernel(x_ptr, y_ptr, out_ptr, n, BLOCK: tl.constexpr):
pid = tl.program_id(0)
offs = pid * BLOCK + tl.arange(0, BLOCK)
mask = offs < n
x = tl.load(x_ptr + offs, mask=mask)
y = tl.load(y_ptr + offs, mask=mask)
tl.store(out_ptr + offs, x + y, mask=mask)


n = 98_432
x = enceladus.randn(n, seed=0)
y = enceladus.randn(n, seed=1)
out = enceladus.empty_like(x)
grid = (enceladus.cdiv(n, 1024),)
add_kernel[grid](x, y, out, n, BLOCK=1024)

np.testing.assert_allclose(out.numpy(), x.numpy() + y.numpy())
print("vector add matches NumPy")
The grid launches cdiv(98_432, 1024) = 97 programs. The last one covers only 128 valid elements, and mask keeps it in bounds.

How the vector add works

The vector add shows the core ideas:

  • tl.program_id(0) returns the index of the current program. The grid launches enough programs to cover all n elements.
  • tl.arange(0, BLOCK) creates a tile of 1,024 indices. x_ptr + offs is a tile of pointers.
  • mask keeps the last program from reading or writing past the end of the arrays.
  • BLOCK: tl.constexpr makes the block size a compile-time constant. Enceladus compiles one version of the kernel for each value you pass.

The launch returns before the GPU finishes when every array argument is an enceladus.Tensor. out.numpy() waits for the result. A launch with NumPy arrays waits for the GPU before it returns, so the arrays hold the result right away.

Run a kernel on the CPU

Enceladus includes an interpreter that runs kernels on the CPU with NumPy, one program at a time. In the interpreter, you can use print() and pdb inside a kernel. To run the vector add in the interpreter, run the following command:

shell
ENCELADUS_INTERPRET=1 uv run python add.py

The interpreter is much slower than the GPU. Use it to debug a kernel and to check the compiled kernel's results.

What's next