Checked: 2026-10-10 · Prerequisite: generation and KV cache · Start with the flow and example; read sampling, caches, and the experiment table when implementing.

Generating several tokens can all sound like “parallel prediction.” First ask: who proposes, who decides, and what happens to the work behind a wrong guess? MTP concerns training objectives and prediction modules; DFlash uses a small drafter to accelerate a target model. The names describe different levels of a system.

Separate 3 decisions

What changes? Question What does not follow?
Training objective Should representations predict further-ahead tokens? MTP loss guarantees faster inference
Draft generation How does a small model or auxiliary module propose candidates? Every proposal can be emitted
Verification and scheduling Which proposals survive, and how are caches managed? Verifying a block always costs the same as 1 token

Early MTP work uses multiple heads on a shared trunk; DeepSeek-V3 uses sequential MTP modules. Do not draw every MTP design as the same architecture. These modules can supply speculative drafts, but verification is a separate part of producing reliable output.

One DFlash generation cycle

The original DFlash paper freezes the autoregressive target and trains a lightweight block-diffusion drafter. Target context features enter each draft layer's K/V; a confirmed anchor token and masked positions produce parallel proposals in one forward pass. The target itself is not replaced by a diffusion LLM.

One drafting cycle, illustrated with 1 anchor and 3 proposed positions.
  1. 01 / PREPARE CONTEXTFeatures + a known anchorThe target supplies confirmed-prefix features and selects the next token as the anchor.
  2. 02 / DRAFT IN PARALLELPropose 3 tokens at onceThe draft reads the anchor and 3 masks, then proposes 3 tokens in one forward pass. The anchor is already known.
  3. 03 / VERIFY WITH THE TARGETKeep a continuous prefixCausal scoring checks the proposals. Stop at the first rejection and emit a correction; full acceptance yields a bonus token.
  4. 04 / ALIGN CACHESRetain only valid stateTrim the rejected position and suffix. The target-supplied token becomes the next anchor; its KV may not exist yet.

Training must respect the same information boundary: full answers may be available, but a predicted block must not read target features containing its future answers. The paper uses random anchors and specialized attention masking, with greater loss weight on early positions. The project explanation describes feature injection and shared embedding/LM-head components.

Two concatenations, two different axes

Selected target layers are joined along the feature axis, then projected to draft width. Inside draft attention, context and block K/V are joined along the sequence axis. Only the block supplies Q; context positions do not get new outputs. Compare the official attention implementation.

For an original single-head example, take 2 context tokens, 3 target layers of width 4, and a block containing 1 anchor plus 3 masks. Omit the batch axis.

Tensor Shape
Three layers concatenated [2, 12], not [6, 4]
Projected to draft width 4 [2, 4]
Block queries [4, 4]
Context plus block K / V [6, 4] each
Attention scores Q @ K.T [4, 6]
Attention output [4, 4]: still only 4 block rows

These are teaching dimensions; multi-head attention splits out heads. Draft projections create their own K/V, not copies of the target cache. Layer counts and block sizes are configurable; a size-16 block includes the known anchor, leaving 15 proposals.

Reuse cached context and append newly confirmed features rather than recomputing the entire prefix. Verification determines which positions survive; the cache table below follows that step.

Why emphasize early positions? The following example needs no model run.

Why can't later correct guesses simply stay?

Let A B X D denote 4 draft tokens without assuming a tokenizer. Start with greedy decoding: the target chooses its highest-probability token.

Position Draft Target choice under the draft prefix Action
1 A A Accept
2 B B Accept
3 X C First mismatch; emit C
4 D D Cannot accept; computed under a prefix containing X

The cycle can emit A B C, not declare A B C D correct. Replacing X with C may change the next conditional distribution. What survives is a continuous accepted prefix, not every matching position scattered across the block.

The following implements only greedy verification control flow. target_choices[position] must correspond to that draft prefix; the final element is the bonus token when all proposals match. Real inference also needs logits, masks, and caches. This is not a complete DFlash implementation.

def verify_greedy_block(draft, target_choices, eos_token=None):
    if len(target_choices) != len(draft) + 1:
        raise ValueError("expected one target choice per draft token plus a bonus")
    output = []
    accepted = 0
    for position, candidate in enumerate(draft):
        expected = target_choices[position]
        if candidate != expected:
            output.append(expected)
            return output, accepted
        output.append(candidate)
        accepted += 1
        if eos_token is not None and candidate == eos_token:
            return output, accepted
    output.append(target_choices[-1])
    return output, accepted


emitted, accepted = verify_greedy_block([10, 20, 99, 40], [10, 20, 30, 40, 50])
assert emitted == [10, 20, 30] and accepted == 2

This also explains cache rollback. Verification may compute K/V for X and its suffix; those states cannot remain in the committed prefix. Retain only valid prefix state. If the replacement C has not gone through a forward pass, its K/V still needs to be computed later. Emitted-token count is not automatically the current cache length. Draft and target caches must each stay aligned.

Why can C be emitted before its KV exists?

This switches to DFlash's setup, unlike the table above: A is the target-selected anchor, not a draft token. Let prefix P have length 4, using zero-based positions. The target has predicted anchor A but has not processed A as input yet. This round proposes B, X; verification accepts B and replaces X with C. Ignore EOS and output limits for this example.

Stage Confirmed tokens / target KV
Start Tokens P A
KV: only P, length 4
A, B, X processed; draft not yet accepted Tokens P A
KV: temporary P A B X, length 7
Accept B, emit C, trim invalid state Tokens P A B C
KV: keep P A B, length 6
Process C at position 6 next round Tokens P A B C
KV: now includes C, length 7

C is selected from the logits at B. Choosing C's ID does not compute C's hidden state. The newly emitted tokens are B and C, not the already known A. The draft's temporary block state needs separate handling: this pinned implementation trims it to the round's starting position, then incorporates newly confirmed target features next round.

Sampling with temperature is not argmax matching

Greedy consistency is straightforward. Sampling must preserve the target's distribution, not identical text under the same seed. Speculative sampling uses acceptance/rejection correction rather than accepting plausible-looking tokens.

For target distribution $p$, actual proposal distribution $q$, and proposal $y\sim q$, accept with

$$\min\left(1,\frac{p(y)}{q(y)}\right).$$

On rejection, sample from normalized $[p-q]_+$. See the MTP sampling example for an exact 2-token calculation.

“Actual proposal” matters. After top-k, temperature, or a selector changes the proposal, the verifier needs the corresponding $q$, not the old softmax. Parallel guesses are not automatically samples drawn sequentially from target conditionals. The proof concerns distributions; it cannot repair invalid caches, masks, or probability alignment.

Account for a complete cycle

Let the baseline take $T_{\mathrm{AR}}$ per output token. Drafting, verification, and other cycle costs are $T_d,T_v,T_o$. Let $E[N]$ count actual new emitted tokens, including accepted proposals and the target-supplied token, but not the already-known anchor. Roughly,

$$\text{speedup}\approx \frac{T_{\mathrm{AR}}E[N]}{T_d+T_v+T_o}.$$

This is an amortized fixed-load estimate, not a queueing model. The following numbers are teaching assumptions, not DFlash benchmarks: 10 ms/token baseline, 3 ms draft, 12 ms verify, and 1 ms other cost.

Tokens emitted per cycle Average time per token Relative speed
1 16 ms 0.625×; slower
2 8 ms 1.25×
4 4 ms 2.5×

Change the costs in this small calculation:

import math


def cycle_speedup(baseline_ms, draft_ms, verify_ms, overhead_ms, emitted_tokens):
    values = (baseline_ms, draft_ms, verify_ms, overhead_ms, emitted_tokens)
    if not all(math.isfinite(value) for value in values):
        raise ValueError("inputs must be finite")
    if baseline_ms <= 0 or emitted_tokens <= 0:
        raise ValueError("baseline and emitted tokens must be positive")
    if min(draft_ms, verify_ms, overhead_ms) < 0:
        raise ValueError("costs must be nonnegative")
    cycle_ms = draft_ms + verify_ms + overhead_ms
    if cycle_ms <= 0:
        raise ValueError("cycle cost must be positive")
    return baseline_ms * emitted_tokens / cycle_ms


assert cycle_speedup(10, 3, 12, 1, 4) == 2.5
assert cycle_speedup(10, 3, 12, 1, 1) == 0.625

A longer block may face more suffix rejection and higher verification/cache cost. Parallelism is not free. Under concurrency, drafter compute and memory can also affect other requests. At small batch sizes decode mostly waits on weight reads, so computing a few extra positions adds little time; that slack disappears as the batch grows, see the decode roofline example. A batch=1 latency improvement does not establish a whole-server throughput gain.

Developments in 2026: making parallel guesses coherent

Original DFlash proposes positions in parallel; a later position has not observed the token just selected at its predecessor. That leaves room to improve local coherence.

The official DFlash 2 introduction, dated 2026-08-18, keeps the parallel backbone, adds lightweight adjacent-candidate path selection, and uses short convolutions for local block mixing. Path traversal still has sequential work; parallel drafting does not mean the system has no dependencies.

An original miniature example: candidate columns contain {New, Los} and {York, Angeles}. Independent top-1 choices might produce New Angeles; adjacent-pair scoring can favor New York. This explains coherence, not access to the correct answer. The target still verifies the result.

Another route is D-Loop, submitted 2026-10-05: reuse the drafter to predict a suffix conditioned on a selected prefix. It also changes training; running original DFlash weights twice does not reproduce the reported gains. In §3.2, shared weights learn prefix prediction from an anchor-only input and suffix prediction from a revealed ground-truth prefix. Inference uses a proposed prefix instead. This mechanism has been checked, but experiments have not been reproduced or used to rank hardware.

Approach Treatment of future positions Main cost to examine
MTP Additional training targets/modules, with varying architectures Training/module cost; acceleration still needs verification
Original DFlash One-pass parallel proposals conditioned on target features Proposal quality, verification, and extra state
DFlash 2 Parallel proposals plus local mixing and path selection Local modules and selection
D-Loop Shared-backbone suffix refinement after initial drafting Second draft forward pass

Versions also affect usability. The current official repository lists DFlash 2 and backend-specific support. The old project page still describes vLLM integration as in progress, but as of 2026-10 both vLLM v0.31.0 ( method: dflash in the speculative config) and SGLang v0.5.21 ( --speculative-algorithm DFLASH) ship DFlash in their main code. Check the commit, target/draft checkpoints, and backend before running, rather than copying an old installation command.

What evidence should precede deployment?

Check Minimal comparison Failure to avoid
Output correctness Greedy sequence agreement; distribution tests for sampling Using same-seed equality as the sampling test
Early stopping First rejection, full acceptance, EOS, length limits Extra/missing tokens or invalid retained cache
Stable acceleration Fixed inputs, lengths, temperature, concurrency, and hardware Reporting the longest accepted block instead of mean cycle output
User experience TTFT, streaming intervals, tail latency, throughput Average tokens/s hiding bursty output
Integration cost Both models/caches, feature extraction, engine versions Counting draft weights alone

Establish correctness in a controlled setting before choosing where to enable it. The code here checks local logic and arithmetic, not full drafter training or GPU serving performance. For parallel target scoring, revisit causal masks and decoding; for training-target alignment, read MTP.