Standalone · Kernel

Pallas, Mosaic, and where Triton fits

When the compiler's automatic decisions are not good enough, you write the kernel yourself. In JAX that door is Pallas, and it lowers through two very different backends: Triton on GPU and Mosaic on TPU.

XLA fuses at the granularity of HLO ops using a cost model you do not control, and some kernels, flash attention is the canonical one, cannot be expressed as a good fusion at that granularity. When you hit that wall you stop describing what to compute and start describing how. In JAX that door is Pallas, and it lowers through two very different backends: Triton on GPU and Mosaic on TPU.

You write to references, not values

A Pallas kernel does not look like normal JAX. Normal JAX is functional: values in, values out. A Pallas kernel is imperative and operates on references: you are handed input and output Refs and you load from and store to them explicitly.

def kernel(x_ref, o_ref):
    o_ref[...] = jnp.maximum(x_ref[...] * 2.0 + 1.0, 0.0)

n, blk = 1024, 256
def f(x):
    return pl.pallas_call(kernel,
        out_shape=jax.ShapeDtypeStruct((n,), jnp.float32),
        grid=(n // blk,),
        in_specs=[pl.BlockSpec((blk,), lambda i: (i,))],
        out_specs=pl.BlockSpec((blk,), lambda i: (i,)))(x)

The shape of the thing is the whole point: a pallas_call node in the jaxpr with a grid mapping, block mappings, and the kernel as a subcomputation. The grid is how many programs run, and the BlockSpecs say which slice of the input each program gets.

Two backends, one language

Pallas lowers the same kernel source through two very different backends:

This is the escape hatch from XLA's fusion decisions. When the compiler's cost model picks the wrong fusion granularity, you write the kernel yourself, and Pallas gives you one language for both GPU and TPU.

Mental model

Pallas is the "open the hood" button on the JAX/XLA car. Normal JAX drives: you say where to go, it handles the engine. Pallas is for when you need to adjust the valves yourself, and it speaks the same language whether the engine is a GPU or a TPU.

When you hit the fusion wall, you stop describing what to compute and start describing how.

Back to the blog