the path · chapter 14 of 15 · part ii, the track

stage 04
title Distributed Pallas
schedule weeks 9-10
gate QUEUED

Ground collectives knowledge in the mechanism: remote DMA and semaphores, compute hiding communication.

mastery work · this chapter0/4
  1. go →
  2. go →auto
  3. auto
manual items are your word; auto items complete from your streaks, labs, and can-you ticks · stored in your browser only
the chapter

Collectives are kernels

A collective is not a primitive; it is a kernel somebody wrote. On a TPU pod, chips along a mesh axis form physical rings, and an all-gather is what it looks like: N−1 hops where every chip pushes one shard to its neighbor. The push is the TPU's native distributed move: an async remote DMA that writes directly into the neighbor's VMEM and signals a semaphore, leaving the MXU free the whole time. Once you have built one collective from these parts, the opaque names (all-gather, reduce-scatter, all-to-all) resolve into loop shapes you can reason about like any other schedule.

the chapter

Overlap is the economics

The economics that make this stage matter at scale: at frontier size, the dominant tax is not slow kernels but exposed communication, the fraction of each step where compute units idle waiting on the interconnect. The cure is overlap: transfer shard t+1 while computing on shard t, which is exactly the double-buffering pattern from stage 1 with the arrows crossing chips. EX·04 shows it: the same streaming attention schedule as EX·03 with the KV transport swapped from HBM DMA to remote DMA. Same algebra, longer arrows.

the chapter

Deadlock, met safely

With semaphores comes the one genuinely new failure mode of this stage: deadlock. A send waiting on a recv that waits on the send. LAB·4.1 has you build a deadlock on purpose in interpret mode, because meeting this failure for the first time in a real training run is a bad week, and meeting it in a toy is an afternoon.

the chapter

Composition closes the loop

The composition payoff closes the loop on the whole track: ring attention is LAB·3.1's monoid folded over shards arriving via LAB·4.1's ring, and nothing new needs deriving because associativity was proven once. When compositions keep coming out correct because one theorem was proven early, you have learned the track's deepest lesson: the famous kernels are few theorems wearing many schedules.

The famous kernels are few theorems wearing many schedules.
hands on the machine

Instruments

EX·04 ring attention · same schedule, longer arrows
HBMQQK · ICIK0K1K2K3V · ICIV0V1V2V3VMEMqemptykAemptyvAemptykBemptyvBemptyCARRIED STATEm : −infl : 0acc : 0MXU · idle

01/06prologue · Q pins resident in VMEM; K0, V0 stream in from the neighbor chip

identical frames to EX·03 with KV transport swapped from HBM DMA to remote DMA over ICI · state formulas unchanged
EX·10 the semaphore timeline · a deadlock, stepped into
chip 2: hop-1 wait issued before hop-1 send
HOP 1HOP 2HOP 3chip 01 / ← 3sendwaitt0t1t2t3t4t5chip 12 / ← 0sendwaitt0t1t2t3t4t5chip 23 / ← 1sendwaitt0t1t2t3t4t5chip 30 / ← 2sendwaitt0t1t2t3t4t5

00/05tick 0: chip 0 send (hop 1) fires · chip 1 send (hop 1) fires · chip 2 send (hop 1) fires · chip 3 send (hop 1) fires

send fired wait fired blocked (never resolves)
ordering computed from an explicit dependency model of a 4-chip ring all-gather · toggle one wait and walk into the cycle
EX·11 the mesh, hop by hop
hop 0 of 3
45.0 GB/s45.0 GB/s45.0 GB/s45.0 GB/s45.0 GB/s45.0 GB/s45.0 GB/s45.0 GB/s45.0 GB/s45.0 GB/s45.0 GB/s45.0 GB/sc0c1c2c3c4c5c6c7
c0
c1
c2
c3
c4
c5
c6
c7

all-gather, ring over x: at hop 0, each chip holds 1 of 4 shards. Fixed example: a 32 MB shard (32,000,000 bytes) moves over every active link each hop.

link bandwidth
45.0 GB/s one-way (TPU v5e ICI, chips.json's only ICI field, used for every link shown)
bytes this hop
0 MB, initial state
time this hop
n/a, no hop taken yet
elapsed
0.000 ms (0 MB moved / link)
full collective, 3 hops
2.133 ms
link bandwidths from the chip constants · per-hop bytes and times computed for a stated 32 MB shard
deliverables

What gets built

  • Ring all-gather from raw remote copies, validated against the collective
  • A deliberate deadlock, observed and explained
  • A toy ring attention step: the streaming algebra is indifferent to where a block came from
sources

Readings

the plan

The work, in order

week 9 · the ring

  • LAB·4.1: ring all-gather as ppermute hops, validated exactly against lax.all_gather
  • On a real slice: the same loop as Pallas remote DMAs, bitwise validated, overlap found in the profile
  • Break it on purpose: reorder one semaphore wait under interpret mode and study the deadlock

week 10 · the composition

  • LAB·4.2: ring attention from the stage 3 monoid plus the ring; verify equal to full attention
  • On a slice: swap hops for remote DMAs, put the flash kernel inside the loop, profile the hop hiding under compute
  • Note what you did not do: re-derive anything; write the paragraph explaining why causal ring attention is free
run it yourself

Labs

single source of truth

From the notebook

LAB·4.1Ring all-gather from remote DMAs · extracted from the notebook
# the ring algorithm: after step t, every device holds t+1 shards
mesh = Mesh(np.array(devs), ("x",))
AXIS = len(devs)

@partial(shard_map, mesh=mesh, in_specs=P("x", None), out_specs=P("x", None))
def ring_all_gather(shard):
    n = AXIS  # static mesh size from the closure
    idx = jax.lax.axis_index("x")
    out = jnp.zeros((n, *shard.shape[1:]), shard.dtype)
    out = out.at[idx].set(shard[0])
    recv = shard[0]
    def hop(t, carry):
        out, recv = carry
        recv = jax.lax.ppermute(recv, "x", [(i, (i + 1) % n) for i in range(n)])
        src = (idx - t - 1) % n
        return out.at[src].set(recv), recv
    out, _ = jax.lax.fori_loop(0, n - 1, hop, (out, recv))
    return out[None]

x = jax.random.normal(jax.random.key(0), (AXIS, 4, 128))
got = ring_all_gather(x).reshape(AXIS, AXIS, 4, 128)
want = jnp.broadcast_to(x[None], (AXIS, AXIS, 4, 128))
check("ring all-gather == replicated truth", got, want, tol=0)
print(f"validated over {AXIS} devices: every device reconstructed all {AXIS} shards")
source of truth: labs/stage-4/lab-4.1-ring-all-gather.ipynb · cells tagged site:show render here; edit the notebook, rebuild, the page updates
LAB·4.2Ring attention, composed · extracted from the notebook
# LAB 3.1's summarize/combine, verbatim: transport changes, algebra does not
def summarize(s_block, v_block):
    m = jnp.max(s_block, axis=-1)
    p = jnp.exp(s_block - m[..., None])
    return (m, jnp.sum(p, axis=-1), p @ v_block)

def combine(a, b):
    m1, l1, acc1 = a
    m2, l2, acc2 = b
    m = jnp.maximum(m1, m2)
    a1, a2 = jnp.exp(m1 - m), jnp.exp(m2 - m)
    return (m, l1 * a1 + l2 * a2, acc1 * a1[..., None] + acc2 * a2[..., None])

mesh = Mesh(np.array(devs), ("x",))
AXIS = len(devs)

@partial(shard_map, mesh=mesh,
         in_specs=(P("x", None), P("x", None), P("x", None)), out_specs=P("x", None))
def ring_attention(q, k, v):
    n = AXIS  # static mesh size from the closure
    perm = [(i, (i + 1) % n) for i in range(n)]
    state = summarize(q @ k.T, v)                    # my resident KV shard
    def hop(t, carry):
        state, k, v = carry
        k = jax.lax.ppermute(k, "x", perm)           # the shard rides the ring...
        v = jax.lax.ppermute(v, "x", perm)
        return combine(state, summarize(q @ k.T, v)), k, v   # ...while we fold it in
    state, _, _ = jax.lax.fori_loop(0, n - 1, hop, (state, k, v))
    m, l, acc = state
    return acc / l[..., None]
SQ, SKV, D = 16 * AXIS, 32 * AXIS, 64
q = jax.random.normal(jax.random.key(0), (SQ, D))
k = jax.random.normal(jax.random.key(1), (SKV, D))
v = jax.random.normal(jax.random.key(2), (SKV, D))

got = ring_attention(q, k, v)
want = jax.nn.softmax(q @ k.T, axis=-1) @ v
check("ring attention == full attention", got, want, tol=1e-5)
print(f"distributed over {AXIS} devices, no device ever held more than 1/{AXIS} of KV")
source of truth: labs/stage-4/lab-4.2-ring-attention.ipynb · cells tagged site:show render here; edit the notebook, rebuild, the page updates
the self-test, before the gate

Can you

0/7
checked state lives in your browser only · the gate below is the public half
pass or fail, in public

The gate

GATE 04 QUEUED
criterionmeasured
Ring all-gather matches lax.all_gather bitwise, overlap visible in the profile exact on 8 simulated devices; slice profile pending
gates pass only on real hardware, published pass or fail · est./computed rows are predictions holding the slot until a run replaces them · records in bench/

Public artifact for this stage: a collective is just a kernel.