AnyLearn
All lessons
AIadvanced

Making Diffusion LLMs Actually Fast

Why bidirectional attention breaks the KV cache, how block-wise approximate caching brings it back, and the conditional-independence problem that decides how many tokens you can safely unmask at once.

Updated · AI-authored, review-gated · how lessons are made

Not signed in: your progress and quiz score won't be saved.
Progress1 / 9

The embarrassing fact about early diffusion LLMs

Diffusion language models are sold on speed: a fixed budget of parallel refinement rounds instead of one sequential pass per token. For a long time the open implementations were slower than autoregressive models of the same size.

This was not a coding failure. It follows from the paradigm, and understanding why is the whole of this lesson.

Autoregressive serving is fast because of an accounting trick most people never examine. Generating token nn appears to require attending over nn previous tokens, which would make total work quadratic. But under a causal mask, the keys and values of previous positions never change, because nothing later can influence them. So you compute each position's key and value once, store them, and each new token costs one incremental attention over cached state.

That is the KV cache, and it is the single optimisation that makes autoregressive inference economically viable. Roughly a decade of serving infrastructure, from paged memory management to prefix sharing, is built on top of it.

Diffusion language models cannot use it, at least not as written.

Full lesson text

All 9 steps on one page, for reading, reference, and search.

Show

1. The embarrassing fact about early diffusion LLMs

Diffusion language models are sold on speed: a fixed budget of parallel refinement rounds instead of one sequential pass per token. For a long time the open implementations were slower than autoregressive models of the same size.

This was not a coding failure. It follows from the paradigm, and understanding why is the whole of this lesson.

Autoregressive serving is fast because of an accounting trick most people never examine. Generating token nn appears to require attending over nn previous tokens, which would make total work quadratic. But under a causal mask, the keys and values of previous positions never change, because nothing later can influence them. So you compute each position's key and value once, store them, and each new token costs one incremental attention over cached state.

That is the KV cache, and it is the single optimisation that makes autoregressive inference economically viable. Roughly a decade of serving infrastructure, from paged memory management to prefix sharing, is built on top of it.

Diffusion language models cannot use it, at least not as written.

2. Why the cache does not transfer

Two properties of masked diffusion each independently break the caching argument.

Attention is bidirectional. Every position attends to every other, so position 5's key and value depend on the content at position 500. There is no prefix whose representations are settled.

The sequence changes every round. Each refinement step commits tokens at previously masked positions. Those positions had [MASK] embeddings before and real token embeddings now, so their keys and values are genuinely different, and by bidirectionality every other position's representation shifts too.

The cost accounting is then brutal. Let LL be the output length and RR the number of refinement rounds. Autoregressive decoding runs LL passes, each incremental over cached state. Diffusion runs RR passes, each a full recomputation over the entire sequence.

With RR around 30 to 60 and no caching, the per-pass cost difference can easily exceed the round-count advantage. A model doing 20 times fewer passes at 40 times the cost per pass is not faster. This is precisely the regime early implementations landed in, and it explains the gap between the paradigm's promise and its measured throughput.

3. Block-wise approximate caching

Fast-dLLM, from NVIDIA researchers in 2025, restored caching without retraining anything, by conceding that the cache does not have to be exact.

The observation is empirical: although keys and values technically change every round, they change very little for positions that are already decoded. A position holding a committed token has a settled representation, and later rounds perturb it only slightly.

So divide the generation window into blocks. Decode one block at a time. While working on the current block, cache the keys and values of all previously completed blocks and reuse them across the many denoising rounds the current block requires. When the block finishes, refresh the cache once with the true values.

The cache is therefore approximate within a block and exact at block boundaries, which bounds the drift. The reported result is cache reuse with negligible quality degradation, delivering roughly 3.2x to 3.6x speedup on its own.

This is a classic systems trade: accept a small, bounded numerical error to recover an optimisation the architecture was not supposed to permit.

4. The conditional independence problem, precisely

The second bottleneck is quality, not speed, and it decides how aggressively you can parallelise.

At each round the model outputs, for every masked position ii, a distribution pθ(xixvisible)p_\theta(x_i \mid x_{\text{visible}}). These are marginals, each conditioned only on what is currently visible. They are not a joint distribution over the masked positions.

Committing two tokens in the same round therefore samples from the product of marginals:

p(xi,xjxvis)    p(xixvis)p(xjxvis)p(x_i, x_j \mid x_{\text{vis}}) \;\neq\; p(x_i \mid x_{\text{vis}})\, p(x_j \mid x_{\text{vis}})

The two tokens never see each other. Where they are genuinely dependent, this produces the characteristic failure: duplicated words, disagreeing determiners, a variable declared with one name and used with another.

Now the useful part. If the model is highly confident about each token, with p(xi)>1ϵp(x_i) > 1 - \epsilon for each, then almost all mass sits on one combination and the product of marginals approximates the joint to within order ϵ\epsilon. High confidence implies the dependency does not matter, because the conditional is sharp enough that knowing the other token would not change the answer.

That gives a principled decoding rule rather than a heuristic.

5. Confidence-aware parallel decoding

The rule that follows is to stop committing a fixed number of tokens per round and start committing however many clear a confidence threshold.

def decode_block(model, x, mask_id, threshold=0.9, max_rounds=64):
    for _ in range(max_rounds):
        masked = (x == mask_id)
        if not masked.any():
            break

        probs = model(x).softmax(-1)         # cached KV for finished blocks
        conf, pred = probs.max(-1)

        commit = masked & (conf > threshold)

        # never stall: if nothing clears the bar, take the single best
        if not commit.any():
            best = (conf * masked).argmax()
            commit = torch.zeros_like(masked).index_fill_(0, best, True)

        x = torch.where(commit, pred, x)
    return x

The behaviour adapts to the text automatically. Predictable spans, boilerplate, closing brackets, the second half of a common phrase, clear the threshold in bulk and get committed together. Genuinely ambiguous positions stay masked and resolve in later rounds with more context.

The fallback line matters: without it, a passage where nothing is confident would loop forever. Committing the single best token degrades gracefully to autoregressive behaviour exactly where autoregressive behaviour is warranted.

The reported gain from this alone is roughly 2.5x to 5.8x.

6. The optimised inference loop

The two optimisations are independent and multiply. Caching cuts the cost of each round; confidence thresholding cuts the number of rounds needed. Combined, Fast-dLLM reports up to 11x on LLaDA and up to 27.6x end to end depending on configuration.

flowchart TD
  A["Split the generation window into blocks"] --> B["Load cached keys and values from finished blocks"]
  B --> C["Predict all masked positions in the current block"]
  C --> D["Commit every token above the confidence threshold"]
  D --> E{"Block fully decoded?"}
  E -- no --> C
  E -- yes --> F["Refresh cache with true values"]
  F --> G{"More blocks?"}
  G -- yes --> B
  G -- no --> H["Return the sequence"]

7. The spectrum from parallel to sequential

Block decoding, which the caching scheme requires anyway, turns out to be the same knob as the confidence threshold viewed from a different angle. Together they place any diffusion decoder on a continuous spectrum.

At one extreme, one block covering the whole output and a threshold of zero gives fully parallel generation: maximum speed, maximum risk of dependency violations. At the other, block size one gives strictly sequential generation, which is autoregression paying bidirectional prices.

Everything useful sits between. Semi-autoregressive block decoding generates a block in parallel and conditions the next block on it, which recovers long-range coherence between blocks while keeping parallelism within them.

One capability survives across the whole spectrum and has no autoregressive analogue. Because a committed token can be re-masked, the decoder can reconsider. Schemes that re-mask low-confidence committed tokens let the model repair its own earlier decisions, which is structurally impossible once an autoregressive model has emitted a token.

The practical tuning rule: lower the threshold and enlarge blocks for structured, predictable text such as code; raise it for prose where agreement matters.

8. What is still hard

Two structural problems remain after caching and thresholding, and both concentrate at long context.

Prefill is expensive. A long prompt must be processed bidirectionally, and unlike autoregressive prefill it may be revisited as decoding proceeds. Work on predictive prefilling for long-context diffusion inference targets exactly this.

Memory grows with the whole window. Autoregressive serving stores the KV cache for tokens generated so far. Diffusion serving must hold state for the entire generation window from the start, including positions still masked, because they are all attended over. Peak memory is set by the maximum output length rather than the current one.

There is also an ecosystem gap that no single technique closes. Paged attention, continuous batching, prefix sharing across requests, and speculative decoding are mature, deeply optimised, and assume causal attention. Each needs a bespoke redesign here, and work such as speculative decoding for masked diffusion is early.

So the honest position: the algorithmic gap has largely been closed by caching plus confidence decoding, while the infrastructure gap has not.

9. The serving scorecard

Where the two paradigms stand on inference, mechanism by mechanism:

AutoregressiveMasked diffusion
Passes per outputOne per tokenFixed round budget
KV cacheExact, by constructionApproximate, block-wise
Parallelism limitOne token per passSet by confidence threshold
Peak memoryGrows with tokens producedSet by full window upfront
Long promptsCheap causal prefillExpensive bidirectional prefill
Self-correctionImpossible after emissionNative via re-masking
EcosystemDeeply matureBeing rebuilt

Two gotchas to carry away. First, quoted speedups are configuration-dependent. Figures like 27.6x are end-to-end results under specific block sizes, thresholds, sequence lengths, and hardware, and they shrink substantially at long context. Always ask what was held fixed.

Second, the threshold is a quality dial disguised as a speed dial. Tuning it for throughput without measuring dependency-violation artefacts produces text that benchmarks acceptably and reads badly, because the failures are local incoherences that aggregate metrics smooth over.

Check your understanding

The lesson ends with a 5-question quiz. Take it in the player above to see your score.

  1. Why can standard KV caching not be applied directly to a masked diffusion language model?
    • Diffusion models do not compute keys and values at all
    • Attention is bidirectional and committed tokens change each round, so cached representations are no longer valid
    • The cache would exceed GPU memory at any sequence length
    • Keys and values are recomputed in floating point that cannot be stored
  2. What makes Fast-dLLM's block-wise KV cache an acceptable approximation?
    • It caches only the prompt, which never changes
    • It recomputes the full cache after every denoising round
    • Representations of already-decoded positions change only slightly, and the cache is refreshed exactly at block boundaries
    • It stores keys and values in reduced precision to bound the error
  3. Why does committing many tokens in one round risk incoherent text?
    • The transformer cannot attend to positions that are still masked
    • Confidence scores are uncalibrated by construction
    • The noise schedule assigns them inconsistent weights
    • The model emits independent marginals per position, so simultaneously committed tokens never condition on each other
  4. What is the theoretical justification for confidence-threshold decoding?
    • When each marginal is highly confident, the product of marginals approximates the true joint distribution
    • Confident tokens are always shorter and cheaper to generate
    • Thresholding guarantees the exact likelihood can be computed
    • High confidence means the token appeared in the prompt
  5. How does peak memory for diffusion serving differ from autoregressive serving?
    • It is lower because no cache is kept between rounds
    • It is identical, since both store one entry per token
    • It is set by the full generation window upfront, since all positions including masked ones are attended over
    • It depends only on the prompt length

Related lessons

AI
advanced

Finding the Next One: Fusion Beyond Attention

The pattern that made attention slow recurs across the stack, and once you know what to look for it is easy to find. This lesson applies the diagnosis to normalisation layers, optimizer steps, loss functions and inference decoding, covers why fused attention silently stops applying when a model deviates slightly from standard, and gives the profiling routine that decides where to look first.

10 steps·~15 min
AI
advanced

Writing Fused Kernels Without Writing CUDA

The reason most teams never fuse anything is that CUDA asks you to manage threads, shared memory and synchronisation by hand. Triton moves the unit of programming from a thread to a block and hands the rest to a compiler. This lesson covers what that buys, what it still asks of you, how to decide a kernel is worth writing, and how to be sure it is correct.

10 steps·~15 min
AI
advanced

Attention Is Memory-Bound, and Nobody Noticed for Five Years

For years attention was optimised by reducing FLOPs, and approximate methods that cut FLOPs kept failing to run faster. The reason is that attention was never compute-bound: it spends its time moving a matrix between GPU memory tiers. This lesson establishes that hierarchy, counts the traffic a standard implementation generates, and shows why an exact algorithm beat every approximation.

10 steps·~15 min
AI
advanced

Measuring the Damage, and Shipping It

Quantization damage does not show up where people look for it. Perplexity barely moves while hard tasks degrade, and long reasoning suffers most because error compounds. This lesson covers building an evaluation that detects real loss, where the published cliffs are, KV cache quantization as a separate lever, end-to-end memory sizing, and the rollout that catches what evals miss.

10 steps·~15 min