Looped Transformers
Run the same weights more than once per token. See what the extra depth costs, when it beats adding layers, and how parallel loops avoid sequential decoding.
Jump to a section
Why this matters
A looped transformer runs the same weights more than once for each token. The output of a block, or of the whole stack, is fed back in and transformed again. The model behaves like a deeper network while storing only one copy of each block. The idea goes back to the Universal Transformer (2018). Recent work uses it to scale reasoning in latent space instead of in written tokens.
The extra depth is not free. Every loop still has to be computed, during training and for every generated token. On 29 October 2026, FAIR at Meta published How to Loop Your Transformer (Ekbote et al.), a controlled study of when that trade pays. It covers dense decoder-only models of 70M to 1B parameters trained from scratch, on up to 155B tokens with up to 32 loops. Its answer depends on which budget binds:
- Stored weights and data fixed: loop the whole stack. Training compute fixed: loop only the middle third. Gains flatten after a few loops, then reverse or diverge.
- Many loops: exact weight sharing becomes the constraint. Small per-loop LoRA adapters add 1–4% to the weights and help more the more loops there are.
- Decoding latency: ordinary loops run one after another for each token. Parallel Loop Transformers (PLT) let them run at once but lost most of the quality gain. The paper's fix is to keep the current-token embedding, normalize it and the incoming state separately, and gate them. With that fix, two parallel loops at 1B beat two sequential ones.
Same six blocks of weights, different orders of reuse
- pass 1
- pass 2
- Lower sub-row attention, upper sub-row MLP
| Full-Stack: the whole stack, L times | 12 blocks deep |
| Weights stored (the 100M model, non-embedding) | 135.1M at any L |
| Training FLOPs per token vs no looping | 1.45× |
| Sequential sublayer steps to decode one token | 24 |
The paper’s 100M model has six stored blocks. Full-Stack and Per-Layer looping do the same work in a different order; the middle designs run the first and last ⌊B/3⌋ = 2 blocks once and loop the rest, so at the same L they are shallower and cheaper. Attention-only and MLP-only loops repeat one sublayer inside each block. The FLOP ratio is our arithmetic and includes the 98.5M-weight output head, which runs once per token whatever L is; that is why two loops cost 1.45×, not 2×.
Ekbote et al. 2026 · Figure 2, Definitions 4–7, Table 1Build the mental model
Depth without new weights. A standard transformer with blocks stores sets of weights and applies each once. Loop the stack times and the model is blocks deep but still stores . Compute per token grows with ; stored weights do not. Two schedules perform exactly the same evaluations in a different order. Full-Stack looping passes through all blocks, then starts again; Per-Layer looping repeats each block times before moving on. Middle looping keeps the first and last third unique and repeats only the centre. Looping can also be confined to attention or to the MLP inside each block.
Why decoding gets slow. Training sees the whole sequence, so every position runs in parallel within a loop. Generation produces one token at a time, and loop for that token needs loop 's output for the same token. A two-loop model therefore makes two passes over its weights per token. At small batch sizes, decoding is limited by reading weights from memory, so two passes take about twice as long.
The PLT shift. Parallel Loop Transformers (Wu et al., 2025) feed loop the output of loop from the previous token, plus the current token's embedding. When a new token arrives, every one of those inputs already exists. The loop inputs can be stacked as a batch and pushed through the shared weights in one call. Total FLOPs are unchanged, but the sequential chain per token is one call long and the weights are read once.
Shift the input one token back, and every loop can run at once
| Transformer calls in sequence for the newest token | 1 (a batch of 3 inputs) |
| Passes over the shared weights per token (what bounds small-batch decoding) | 1 |
| FLOPs per token | the same: 3 evaluations either way |
| Training | loops still run one after another; positions run in parallel |
Numbered badges give the order in which the newest token’s loops can run. With the shift (Wu et al.’s cross-loop parallelism), loop ℓ for token n reads hn−1(ℓ−1), which already exists when token n arrives, so the 3 loop inputs are stacked along the batch dimension and evaluated together (the paper’s Algorithm 2). Orange lines carry the current-token embedding: ordinary looping feeds it only to loop 1, while PLT adds it to every loop; the first token has no predecessor, so its later loops receive e₁ + 0. Each loop still attends over its own cache of earlier tokens, so a separate cache per loop multiplies KV memory by L unless it is shared.
Ekbote et al. 2026 · Figure 6, Definition 2, Algorithms 1–2The catch. The previous token's state is the model's prediction of what comes next. It is not the token that actually arrived. Without the current-token embedding, every shifted loop has to infer the token from that prediction, and the paper's runs without it diverged or did worse than no looping at all. Re-injecting the embedding is necessary but, as published, not sufficient. The embedding is added to a state whose magnitude is roughly 10 to 600 times larger, so the state drowns it out. Stage 3 measures that ratio.
Work through the math
Looping. For a reused function (one block, a group of blocks, or the whole stack),
If holds weights and costs , the looped model stores about and costs about . Middle looping with repeated blocks out of has effective depth . The paper repeats the middle blocks.
What a loop costs. Training FLOPs per token are about the weights each token passes through, plus the attention scores. The output head, with weights and untied from the input embedding, runs once per token whatever is:
This expression reproduces all ten training-FLOP coordinates in the paper's Figure 1 to within 0.1%. The coefficient 7 is what matches their accounting; the paper does not state it. Because the head is so large, two loops cost 1.45× per token at 100M and 1.80× at 1B, not 2×. Training tokens are set per stored weight (tokens per parameter, TPP). A looped model therefore trains on the same data as its no-loop twin and spends more compute on it.
Looping buys depth with compute, not with stored weights
| Training tokens at 80 per stored weight | 77.4B |
| Total training FLOPs · no looping → looped | 5.95 × 10²⁰ → 1.07 × 10²¹ |
| Loop-specific LoRA, rank 8, all seven projections | +9.2M (+0.9%) |
| Own weights for the same depth instead | +704.6M (+72.8%) |
Shapes from Table 1 (vocabulary 128,256, context 8,192, head dimension 128). Training FLOPs are 6 × weights for every evaluation plus 7 · nctx · dattn per attention evaluation; this reproduces the ten training-FLOP coordinates of the paper’s Figure 1 within 0.1% (two loops at 1B and TPP 80 come to 1.07 × 10²¹). The untied output head is computed once per token, so at small scale it absorbs much of the cost: two loops cost 1.45× per token at 100M and 1.79× at 1B. The hatched sliver is the loop-specific LoRA; at 300M it is the paper’s 1.1–4.3% for L = 2–8.
Ekbote et al. 2026 · Table 1, Appendix D, §3.3Loop-specific adapters. Exact sharing makes the last loop apply the same transformation as the first, although its input has already been transformed several times. The paper gives each loop a low-rank correction (Definition 1) and trains it from scratch together with the shared weights:
One matrix then costs weights, against for fully separate loops. Loop Matrix lets loop mix all low-rank pairs, , with a learned . Loop Softmax constrains each row of to a softmax. With rank 8 on all seven projections of the 300M model, the adapters add 1.1%, 2.1% and 4.3% to the weights at . Doubling the depth with new layers would add 51.4%.
Parallel loops. With the embedding of token and the output of loop , Cross-Loop Parallelism (Definition 2) is
with . The paper's unshifted control uses instead. That is ordinary looping with the embedding added back at every loop, and it remains sequential.
Why the raw sum fails. If the state's RMS is times the embedding's and their directions are uncorrelated, the combined input points almost entirely along the state:
The paper measured between about 10 and 600, varying with loop and training budget. Its fix normalizes the two inputs separately and mixes them with a gate:
Here and . and start at zero, so the gate starts at an exact 50/50 mix. A fixed works almost as well. Normalization is what matters; the gate choice is secondary.
The previous-token state is 10–600× larger than the embedding it is added to
- Full-Stack looping (no re-injection)
- Shifted PLT, raw e + h
- Shifted PLT, RMSNorm + gate
Select any point to set the ratio r below. Selected: Shifted PLT, raw e + h, loop 4, r = 197.6×.
Figure 116 measures the 100M, eight-loop checkpoints on four 2,048-token DCLM documents; each point is the mean hidden-state RMS divided by the mean embedding RMS. Added raw, the embedding contributes a vector 197.6× shorter than the state, so the loop’s input is almost exactly the previous token’s state: it no longer says which token arrived. The ratio also swings with training budget (about 100–600× at TPP 10, about 10–17× at TPP 40), so no fixed coefficient fixes it. The paper normalizes each input separately before mixing, b = g ⊙ ê + (1 − g) ⊙ ĥ. The cosine assumes e and h are uncorrelated, which is typical for high-dimensional vectors; it is our illustration, not a measurement.
Ekbote et al. 2026 · §3.4, Appendix G.2, Figure 116Cache memory. Each loop attends over its own keys and values for earlier tokens, so separate caches multiply KV memory by . Original PLT lets later loops reuse loop 1's cache and keep only a sliding window of positions of their own. That brings memory from to . In this paper's experiments, separate caches gave the best quality. The normalized shifted PLT used in its 1B results keeps one cache per loop.
Read the implementation
This executable Python example uses a toy shared block. Attention is replaced by a running mean over that loop's earlier inputs, but the block is still causal. The example trains PLT the way the paper's Algorithm 1 does, decodes it token by token as in Algorithm 2, and checks that the two give identical outputs. It then rebuilds Table 1's parameter count, the FLOP formula and the adapter overhead, and measures the cosine above on random vectors.
1import math, random
2
3rng = random.Random(0)
4D = 4
5W = [[rng.uniform(-0.8, 0.8) for _ in range(D)] for _ in range(D)]
6add = lambda a, b: [x + y for x, y in zip(a, b)]
7
8def block(x, history):
9 """A toy shared transformer for one position: mix with this loop's earlier inputs (attention), then transform."""
10 ctx = [sum(col) / len(history) for col in zip(*history)]
11 return [math.tanh(sum(w * v for w, v in zip(row, add(x, ctx)))) for row in W]
12
13def run_loop(inputs): # training: every position at once, causally
14 return [block(inputs[n], inputs[: n + 1]) for n in range(len(inputs))]
15
16def plt_train(E, L): # Algorithm 1: B(l) = E + Shift(H(l-1))
17 H = run_loop(E)
18 for _ in range(1, L):
19 H = run_loop([add(e, h) for e, h in zip(E, [[0.0] * D] + H[:-1])])
20 return H
21
22def plt_decode(E, L): # Algorithm 2: one token at a time
23 caches, prev, out, calls = [[] for _ in range(L)], [[0.0] * D] * L, [], 0
24 for e in E:
25 inputs = [e] + [add(e, prev[l - 1]) for l in range(1, L)] # all ready before any loop runs
26 for l in range(L):
27 caches[l].append(inputs[l]) # each loop keeps its own cache
28 prev = [block(inputs[l], caches[l]) for l in range(L)] # independent: one batched call
29 calls += 1
30 out.append(prev[-1])
31 return out, calls
32
33def looped_decode(E, L): # ordinary looping: loop l waits for loop l-1, same token
34 caches, out, calls = [[] for _ in range(L)], [], 0
35 for e in E:
36 x = e
37 for l in range(L):
38 caches[l].append(x)
39 x = block(x, caches[l])
40 calls += 1
41 out.append(x)
42 return out, calls
43
44E = [[rng.gauss(0, 1) for _ in range(D)] for _ in range(6)]
45decoded, plt_calls = plt_decode(E, 3)
46assert all(abs(a - b) < 1e-12 for u, v in zip(plt_train(E, 3), decoded) for a, b in zip(u, v))
47_, seq_calls = looped_decode(E, 3)
48print(f"6 tokens, 3 loops: {plt_calls} batched calls with PLT, {seq_calls} sequential calls without")
49
50# Table 1 shapes reproduce the reported non-embedding counts (blocks + untied output head).
51def shapes(blocks, d, mlp, q, kv, vocab=128_256, hd=128):
52 block = 2 * d * q * hd + 2 * d * kv * hd + 3 * d * mlp
53 return block, vocab * d, blocks * block + vocab * d
54block, head, total = shapes(16, 2048, 5632, 16, 2)
55assert total == 967_311_360 # "1B": 967.311M in Table 1
56
57# Training FLOPs per token: 6 x weights per evaluation, 7 * ctx * d_attn per attention evaluation.
58def flops(blocks, d, mlp, q, kv, loops, tpp, ctx=8192):
59 block, head, total = shapes(blocks, d, mlp, q, kv)
60 per_token = 6 * head + loops * blocks * (6 * block + 7 * ctx * q * 128)
61 return per_token * tpp * total
62two = flops(16, 2048, 5632, 16, 2, loops=2, tpp=80)
63print(f"1B, two loops, TPP 80: {two:.3e} training FLOPs (paper: 1.07e21)")
64
65# Loop-specific LoRA, rank 8 on all seven projections, against doubling the depth (300M).
66blocks, d, mlp, q, kv = 10, 1280, 3584, 10, 1
67block, head, total = shapes(blocks, d, mlp, q, kv)
68per_loop = blocks * 8 * ((d + q * 128) + 2 * (d + kv * 128) + (q * 128 + d) + 3 * (d + mlp))
69print("LoRA adds", [f"{L * per_loop / total:.1%}" for L in (2, 4, 8)], f"vs {blocks * block / total:.1%} for 2x depth")
70
71# Raw re-injection: if the state is r times larger, b = e + h barely points at the current token.
72rms = lambda v: math.sqrt(sum(x * x for x in v) / len(v))
73cos = lambda a, b: sum(x * y for x, y in zip(a, b)) / math.sqrt(sum(x * x for x in a) * sum(y * y for y in b))
74e = [rng.gauss(0, 1) for _ in range(4096)]
75for r in (10, 100, 600):
76 h = [r * rng.gauss(0, 1) for _ in range(4096)]
77 raw = add(e, h)
78 mixed = [0.5 * x / rms(e) + 0.5 * y / rms(h) for x, y in zip(e, h)] # RMSNorm both, gate 1/2
79 assert abs(cos(raw, e) - 1 / math.sqrt(1 + r * r)) < 0.05 and abs(cos(mixed, e) - 0.707) < 0.05
80 print(f"r = {r:3}: cos(e + h, e) = {cos(raw, e):.4f}; normalized 50/50: {cos(mixed, e):.3f}")The equality check is the point of PLT. Decoding builds every loop's input from states cached at the previous token, so all three block calls for a token are independent. On real hardware they become one batched call over the shared weights. Training is unchanged: the loops still run in order, each over the whole sequence. In a real model, block is the full transformer stack, each loop's cache holds keys and values, and the RMSNorm-plus-gate combination replaces add(e, prev[l - 1]).
Questions and trade-offs
Which part should loop? At a fixed model size and data budget, more loops lower the loss only up to a point. At 100M the best loop count is between 4 and 16, and lower when the training budget is small. Beyond it some designs diverge, and at 32 loops Full-Stack looping jumps from 1.077 to 1.21 bits per byte. Looping attention and the MLP separately degrades first. Ranked per loop, Full-Stack looping is the most consistently strong design at 300M and 500M. Ranked per training FLOP, Middle-Stack looping gives the better return when compute is short, because two thirds of the network still runs once.
At fixed size and data, loop the whole stack; returns flatten, then reverse
- Full-Stack
- Middle-Stack
- Per-Layer
- other designs
Values are read from the vector paths of the paper’s Figure 3 (Appendix E adds 70M–300M and other corpora). Missing points were unstable (× at the top, as the paper marks them) or not run. At 100M the best loop count is 4–16, lower at small training budgets; at TPP 160 Full-Stack jumps to 1.2097 at 32 loops. Looping attention and the MLP independently degrades fastest. On the FLOP axis the no-loop level is a single run at 2.32 × 10¹⁹ FLOPs; FLOPs are our count from Table 1, which matches the paper’s axis to within its rounded tick labels (about 2%).
Ekbote et al. 2026 · §3.2, Figure 3When should loops specialize? Only when there are several of them. At 300M with two loops the adapters change nothing measurable; at eight loops Loop Matrix lowers bits per byte by 0.0063. The paper makes Loop Matrix its default. Adapters do not fix decoding latency, and they are a parameter-efficient gain, not a compute-efficient one: a model with genuinely new layers is better at the same FLOPs, if you can store it.
Small per-loop adapters help more the more often the weights are reused
- Loop LoRA
- Loop Softmax
- Loop Matrix
| 300M, TPP 100 | L = 2 | L = 4 | L = 8 |
|---|---|---|---|
| Loop Matrix gain over exact sharing | 0.0001 | 0.0028 | 0.0063 |
| Weights added by loop LoRA (rank 8, our count) | +1.1% | +2.1% | +4.3% |
| Training FLOPs added (Loop Matrix, paper) | +2.1% | +4.4% | +9.3% |
| For scale: 20 distinct layers instead of 10 shared twice | 0.9438 bits per byte · +51.4% weights, so +51% training tokens at the same TPP | ||
From the paper’s Figure 5. Loop LoRA gives every loop its own rank-8 update; Loop Matrix lets each loop mix all L low-rank pairs with learned coefficients; Loop Softmax constrains those coefficients to be positive and sum to one. All three are trained jointly with the shared weights from initialization. The adapters are a parameter-efficient gain, not a compute-efficient one: the 20-layer model is far better at comparable FLOPs, but it stores 51% more weights.
Ekbote et al. 2026 · §3.3, Figures 4–5Can parallel loops keep the gain? Not as originally published. The paper rebuilds PLT one piece at a time at 200M. The shift alone, KV sharing and the gated sliding window recover only a fraction of the gain: they sit close to the no-loop baseline, and the raw shift gets worse with more loops. The unshifted control, meanwhile, beats Full-Stack looping. Separate RMS normalization recovers most of the gap. At 1B with two loops the normalized shifted model beats sequential Full-Stack looping at every budget tested. At four loops it is only level with Full-Stack looping.
Normalizing the two inputs recovers Full-Stack quality
- Full-Stack looping
- Shifted, RMSNorm + 50/50
- Shifted, RMSNorm, plain sum
- + KV sharing
- Original PLT (+ gated sliding window)
- Unshifted control (sequential)
The paper rebuilds Original PLT one piece at a time (its Figure 7). Raw shifted re-injection at TPP 40 diverged at 4 and 8 loops; at TPP 80 it gets worse with more loops. KV sharing and the gated sliding window cut cache memory but do not restore quality. The unshifted control, the same model with h from the current token, is ordinary looping plus a re-added embedding, and it beats Full-Stack. Normalizing e and h separately before a fixed 50/50 mix brings the parallel, shifted variant to Full-Stack quality; adding them after normalization without the ½ weighting is slightly worse. A separate KV cache for every loop gives the best quality.
Ekbote et al. 2026 · §3.4, Figure 7What does it buy overall? In this over-trained regime, the efficiency claims are relative to a scaling curve fitted to the no-loop runs. At 1B, a no-loop model would need 1.3–3.3× as many training tokens to match two loops. Charging the second loop, two-loop models trained on half the tokens of a no-loop model still match losses that the no-loop curve needs 6–37% more training FLOPs to reach, across 100M–1B. Multiple-choice accuracy at 1B rises by about 0.5–1.7 points, with no consistent change on MMLU.
A no-loop 1B model needs 1.3–3.3× the tokens to match two loops
- Full-Stack looping
- Shifted PLT, normalized (parallel)
- Unshifted, normalized (sequential)
The paper fits L(D) = A·D^(−α) + E to the no-loop 1B runs, inverts it at each looped model’s bits per byte, and divides by the tokens actually used. All looped models here use two loops; their extra compute per token is not charged on this axis.
Ekbote et al. 2026 · Figure 1, Appendix DWhat the study does not show. Every model is dense, at most 1B parameters, trained from scratch and evaluated mostly by likelihood. Most settings use one seed. The paper does not test mixture-of-experts or post-trained models, free-form generation, or parallel loops beyond four. It reports training FLOPs, not measured decoding latency. The batching argument for PLT is structural, and the speed-up you see will depend on whether decoding is limited by memory bandwidth.
Check your understanding
You must ship a model with at most 1B stored weights, decode at batch size 1 on a memory-bound GPU, and train within FLOPs. Should you loop, how many times, and with which design?
Loop twice. Using the paper's configurations, a no-loop 1B model at 160 tokens per weight costs FLOPs, which is over budget. Two loops at 80 tokens per weight cost . By the paper's fitted curve, a no-loop model would need FLOPs to match ordinary two-loop looping and to match the normalized shifted PLT, so both beat anything the no-loop model can reach within budget. At those budgets, Figure 8 gives 0.8776 bits per byte for the normalized shifted model against 0.8835 for sequential Full-Stack looping. Ordinary looping would read the weights twice per generated token, roughly doubling latency at batch size 1. The normalized shifted PLT reads them once, as a batch of two. It keeps a separate KV cache per loop: KiB per token per loop, or 256 MiB for both loops at 8,192 tokens. That is negligible next to the 2.5 GB of BF16 weights. Do not go to four loops: the paper finds less return per FLOP and a wider gap to the sequential control.
Source and scope
Sources:
- Ekbote, Pagnoni, Sachan, Bhosale, Lewis, Fazel-Zarandi, Rajput & Gonugondla, How to Loop Your Transformer: Loop Scaling, Specialization, and Parallel Inference (FAIR at Meta, arXiv:2610.40325, 29 October 2026).
- Wu et al., Parallel Loop Transformer for Efficient Test-Time Computation Scaling: the original PLT, with KV sharing and gated sliding-window attention.
- Bae et al., Relaxed Recursive Transformers: per-loop LoRA for a pretrained model converted into a looped one.
- Dehghani et al., Universal Transformers; Saunshi et al., Reasoning with Latent Thoughts; Geiping et al., Scaling up Test-Time Compute with Latent Reasoning.
Every measured value on this page was read from the vector paths of the paper's figures, so it carries the precision of the plot rather than of a table. The paper publishes one table (Table 1). Parameter counts, the FLOP formula, the cosine illustration, the KV-cache sizes, the code and the scenario are LiminalML derivations. We have not trained these models.
Oliver Perrin
Machine Learning Engineer · Founder, LiminalML
LiminalML brings together concept explanations, mathematical examples, and working visuals. Find more writing by Oliver on the LiminalML Substack.
Keep exploring the idea.
Ask a follow-up in a guided session, work through the self-check, or open the visual in Studio to build on it.
Related concepts
Deep Learning
Transformer Architecture
Follow attention, feed-forward layers, normalization, and residual connections through a Transformer block.
LLMs
LoRA and PEFT
Follow a frozen weight path and a trainable low-rank correction. Understand parameter savings, scaling, and merging.
LLMs
KV Cache
See which token projections caching saves, what attention must still read, and how memory use grows with context.