You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Phases 0 and 3 of #1185 have landed and the profile has been re-measured. The drafter side is no longer where the time is.
Round split on M5 Max, qwen3.8-27b-4bit with the qwen3_5_mtp drafter, --draft-block-size 3 --temp 0, after #1203:
component
ms/round
share
verify forward (T=3)
~40.0
~85%
draft_block
2.70
~5.7%
accept hook
2.74
~5.8%
finalize + residual
~1.6
~3.4%
A round costs about 47 ms and emits 2.33 tokens. Driving the drafter to zero cost leaves 41.6 ms and the same 2.33 tokens: 56.0 tok/s, or 1.77x classic decode. We are at roughly 1.5x. Everything left is within 0.27x, and none of it is on the drafter.
Past that ceiling the round has to emit more tokens, not cost less.
Deeper linear chains do not do it, and that is measured
The obvious move is a wider block. It fails, and not marginally. Sweeping --draft-block-size on this pairing (single sweep, thermally degraded host, so read the trend and not the absolute tok/s):
block
acceptance
emitted/verify
tok/s
3
0.65
2.3
21.30
4
0.49
2.47
16.24
6
0.35
2.72
12.12
8
0.25
2.74
9.12
12
0.17
2.82
5.80
Acceptance collapses by a factor of four while emitted-per-verify moves 2.3 to 2.8. A linear chain spends its whole budget on one guess about the future, so each extra position multiplies the chance that the prefix already diverged. The verify cost rises with the block and the yield does not.
That is the argument for branching rather than lengthening: spend the extra verify positions on alternatives at uncertain steps instead of on more of a single guess.
The headroom exists on the verify side
From the M5 Max report on #1182 (verify_forward_cost_scaling): this hardware verifies T=4 for 1.29x the price of T=1 and T=8 for 2.48x. A multi-token verify forward is close to free per extra position here, which is precisely the property tree drafting needs and the property a linear chain cannot convert into tokens.
Rough arithmetic with today's numbers: if a tree lifted emitted-per-round from 2.33 to about 3.2 while the verify forward grew to T=4 cost, the round lands near 48 ms for 3.2 tokens, about 66 tok/s, or 2.1x classic. That is a projection from the cost model, not a measurement, and the acceptance term is the part that has to be measured rather than assumed.
What this needs
Substantially more than Phases 0 through 4. Three pieces, none of which exist today:
A tree attention mask on the verify path. The verify forward currently builds a causal mask over a linear block. A tree needs each node to attend to its ancestors only. The mask is a function of the tree topology, which changes per round, so it cannot be cached the way the causal mask can.
A tree-aware accept walk.speculative_walk accepts the longest matching prefix of a chain. For a tree it has to descend the branch the target actually agreed with, which changes both the walk and what emitted_per_verify means.
Tree-aware rollback.verify_finalize trims the KV cache back to the accepted prefix. With a tree, the rejected siblings' positions were also written and have to be trimmed correctly. This is the piece most likely to produce silent corruption if it is wrong, and it interacts with the recurrent state in the GatedDeltaNet layers, which cannot be trimmed the way a KV cache can (see fix(speculative): Qwen 3.5 MTP output is not byte-identical to classic decode, and the exactness probe does not catch it #1201 for what that failure mode looks like in practice).
Where the branches go is a fourth decision: EAGLE-2 branches by drafter confidence, SpecInfer by a fixed topology. Confidence-based branching needs the drafter's logits, which the current step already computes and discards past the argmax.
Exactness
Unchanged in principle: the target verifies every node, so tree drafting can only cost acceptance, never output. But the byte-identity contract is a per-position claim against the single-token chain, and #1186 shows it depends on which MLX kernel the verify forward dispatches at each M. A tree changes M per round, so the #1189 probe has to be re-derived for the tree case rather than assumed to carry over.
Do not build this on the assumption that a passing probe implies end-to-end parity. It does not, which is what #1201 established the hard way.
Acceptance criteria
Tree topology, mask construction, walk and rollback implemented behind a flag, defaulting off.
Rollback correctness pinned by test, including the recurrent-state path, before any throughput number is quoted.
Acceptance and emitted-per-round measured against the linear chain at the same block budget, on the Qwen 3.8 27B pairing and regression-checked on Gemma 4.
Temperature-0 output compared against classic decode end to end, by diffing generated text, not by trusting the probe.
Measured on M5-class and on M1 Ultra, since the verify cost scaling that motivates this differs by generation.
Out of scope
The verify-side early-exit walk (#1179), which is a different way to spend the same verify forward and should be measured against this rather than combined with it.
Why now
Phases 0 and 3 of #1185 have landed and the profile has been re-measured. The drafter side is no longer where the time is.
Round split on M5 Max,
qwen3.8-27b-4bitwith theqwen3_5_mtpdrafter,--draft-block-size 3 --temp 0, after #1203:draft_blockA round costs about 47 ms and emits 2.33 tokens. Driving the drafter to zero cost leaves 41.6 ms and the same 2.33 tokens: 56.0 tok/s, or 1.77x classic decode. We are at roughly 1.5x. Everything left is within 0.27x, and none of it is on the drafter.
Past that ceiling the round has to emit more tokens, not cost less.
Deeper linear chains do not do it, and that is measured
The obvious move is a wider block. It fails, and not marginally. Sweeping
--draft-block-sizeon this pairing (single sweep, thermally degraded host, so read the trend and not the absolute tok/s):Acceptance collapses by a factor of four while emitted-per-verify moves 2.3 to 2.8. A linear chain spends its whole budget on one guess about the future, so each extra position multiplies the chance that the prefix already diverged. The verify cost rises with the block and the yield does not.
That is the argument for branching rather than lengthening: spend the extra verify positions on alternatives at uncertain steps instead of on more of a single guess.
The headroom exists on the verify side
From the M5 Max report on #1182 (
verify_forward_cost_scaling): this hardware verifies T=4 for 1.29x the price of T=1 and T=8 for 2.48x. A multi-token verify forward is close to free per extra position here, which is precisely the property tree drafting needs and the property a linear chain cannot convert into tokens.Rough arithmetic with today's numbers: if a tree lifted emitted-per-round from 2.33 to about 3.2 while the verify forward grew to T=4 cost, the round lands near 48 ms for 3.2 tokens, about 66 tok/s, or 2.1x classic. That is a projection from the cost model, not a measurement, and the acceptance term is the part that has to be measured rather than assumed.
What this needs
Substantially more than Phases 0 through 4. Three pieces, none of which exist today:
speculative_walkaccepts the longest matching prefix of a chain. For a tree it has to descend the branch the target actually agreed with, which changes both the walk and whatemitted_per_verifymeans.verify_finalizetrims the KV cache back to the accepted prefix. With a tree, the rejected siblings' positions were also written and have to be trimmed correctly. This is the piece most likely to produce silent corruption if it is wrong, and it interacts with the recurrent state in the GatedDeltaNet layers, which cannot be trimmed the way a KV cache can (see fix(speculative): Qwen 3.5 MTP output is not byte-identical to classic decode, and the exactness probe does not catch it #1201 for what that failure mode looks like in practice).Where the branches go is a fourth decision: EAGLE-2 branches by drafter confidence, SpecInfer by a fixed topology. Confidence-based branching needs the drafter's logits, which the current step already computes and discards past the argmax.
Exactness
Unchanged in principle: the target verifies every node, so tree drafting can only cost acceptance, never output. But the byte-identity contract is a per-position claim against the single-token chain, and #1186 shows it depends on which MLX kernel the verify forward dispatches at each
M. A tree changesMper round, so the #1189 probe has to be re-derived for the tree case rather than assumed to carry over.Do not build this on the assumption that a passing probe implies end-to-end parity. It does not, which is what #1201 established the hard way.
Acceptance criteria
Out of scope
The verify-side early-exit walk (#1179), which is a different way to spend the same verify forward and should be measured against this rather than combined with it.
Refs #1185, #1186, #1189, #1201