Speculative decoding checks several drafted tokens in one verification pass. On a mixture-of-experts (MoE) model, consecutive tokens often select the same experts. A deduplicating engine reads each selected expert once per pass, so it requests fewer bytes than a per-slot engine, which reads it once per token. We write a step's time as the bytes each kernel requests over its effective bandwidth, plus fixed time. Under geometric acceptance, we derive from this step time when speculation is faster than plain decoding. The best draft length is a monotone staircase in the acceptance rate. We bound how a bandwidth error moves each result, and we check the main steps in Lean. Deduplication is faster exactly where its share uˉ(n) of the per-slot bytes is below its ratio r(n) of the per-slot bandwidth. On llama.cpp Metal, MLX-LM and llama.cpp CUDA, which we modified to deduplicate, the per-slot kernel has the higher effective bandwidth (r = 0.32 to 0.75). Thus deduplication is faster only from 5 to 8 tokens per pass, or never. We fit the bandwidths on one layer's expert operation, run alone, in under an hour per model. Bytes from routing traces over these bandwidths predict the time that deduplication saves. The measured saving lies inside its error bound at every point on CUDA and MLX-LM and at 36 of 48 points on Metal. The crossover is the first pass size at which deduplication is faster. The prediction places it within one pass size in 10 of 12 configurations of engine, model and depth. We computed these predictions after the measurements. On DeepSeek-V2-Lite, operation times measured before its speculative runs give the best draft length on CUDA within one at all 22 acceptance values.
Figures & tables
Figure 1: Consecutive-token reuse ρl(n) , layer mean, on the WikiText-103 fit documents, against the number n of consecutive tokens. Dotted: independent uniform routing, 1−qn−1 ( Huang et al.,, 2025 ) . In every figure, color marks the model: blue OLMoE, green Qwen3, orange gpt-oss and brown DeepSeek-V2-Lite.
Figure 2: Effective bandwidth of each kernel on the expert operation alone. Per-slot kernel: on requested bytes nmb (solid line, filled markers). Deduplicating kernel: on union bytes Ub (dashed line, open markers). Median and interquartile range over all MoE layers and windows of recorded routing; color: model; dotted: nominal DRAM bandwidth. CUDA was measured on OLMoE, gpt-oss and DeepSeek-V2-Lite, and MLX-LM on OLMoE.
re-read
n=2
n=8
engine
model
cost
βslot
βdedup
r
uˉ
βslot
βdedup
r
uˉ
llama.cpp CUDA
OLMoE
0.18-0.22
618
464
0.75
0.82
933
546
0.58
0.50
llama.cpp CUDA
gpt-oss
0.34
654
346
0.53
0.72
1018
434
0.43
0.36
llama.cpp CUDA
DeepSeek-V2-Lite
−
523
355
0.68
0.83
814
480
0.59
0.62
llama.cpp Metal
OLMoE
0.88-0.90
250
147
0.59
0.82
428
219
0.51
0.50
llama.cpp Metal
Qwen3
0.92-0.98
131
65
0.50
0.77
280
98
0.35
0.43
Table 1: Effective bandwidth of each kernel in GB/s, over all MoE layers and windows of recorded routing, including the fixed work of the operation. The values of r and uˉ come from bytes and times summed over layers and windows. Re-read cost: cre/cu of ( 4 ), as a range over two layers ( − : no fit on chosen ids). The deduplicating kernel is faster where uˉ<r ( 7 ).
prediction
bound
verify time
∣τ^p(n)−τp(n)∣≤1−εεMp(n)≤1−εετp(n) (17)
saving
∣S^(n)−S(n)∣≤1−εε(Mslote(n)+Mdedupe(n)) (18)
faster kernel
sides of ( 7 ) agree if ∣lnr(n)−lnuˉ(n)∣>ln1−ε1+ε (19)
best draft length
Ta(k^∗)≥1+δ1−δTa(k∗) : at most 1+δ2δ of the throughput lost; k^∗=k∗ if 1−δ1+δTa(i)<Ta(k∗) for every i=k∗ (20)
Table 2: Error bounds for a relative bandwidth error ∣e∣≤ε (first three rows) or a relative step-time error δ (last two).
Figure 3: Bytes. Left: DRAM bytes read by the CUDA expert kernels over the union bytes Ub , against nm/U (Nsight Compute, chosen ids, one layer of OLMoE and gpt-oss). Filled: per-slot kernel; open: deduplicating kernel; color: model. The deduplicating kernel reads Ub (horizontal line). The per-slot kernel reads between Ub and its requested nmb (dotted). Right: the share of a pass’s nm slots that name distinct experts, U/(nm) , at n=2,4,6,8 . It is shown on the windows the whole-model runs verified, against WikiText-103 windows of the same model. Both axes lie between 1/n (every token repeats the first token’s experts) and 1 (no expert repeats). A point above the dotted line means the verified text repeated experts less often than WikiText-103. Color: model; shape: the engine of the runs (circle: llama.cpp CUDA, triangle: llama.cpp Metal). The labeled gpt-oss points at d=4096 below the line are its repeating continuations.
saving error (ms)
crossover
k∗ within one
engine
model
d
wiki
text
op. time
meas.
wiki
text
op. time
wiki
text
op. time
Metal
OLMoE
512
0.43
0.42
0.77
7
7
7
8
9
9
9
Metal
OLMoE
4096
0.98
0.91
2.28
7
7
7
8
8
8
8
Metal
Qwen3
512
1.51
0.92
2.24
−
−
−
−
7
8
7
Metal
Qwen3
4096
3.70
1.83
1.25
−
−
−
−
8
8
8
Metal
gpt-oss
512
2.23
3.77
3.89
8
−
−
−
10
9
10
Table 3: The prediction with WikiText-103 routing (wiki) and with the routing of the verified text (text), and the measured-time version (op. time). All three are compared with the same measurements, one row per configuration. Saving error: largest ∣S^(n)−S(n)∣ over n=2,…,9 ( n≤8 on CUDA), in ms. Crossover as defined in Section 2 (meas.: measured; − : none). k∗ : acceptance values, of 11, at which the predicted best draft length is within one of the measured one. Metal and CUDA are llama.cpp.
Figure 4: Saving S(n) . Black and grey: measured on the six validation prompts, with its 95% bootstrap interval. Red: the prediction, dotted to an open circle where n=9 is extrapolated. Shaded red: the band of the saving bound ( 2 ) for the engine’s ε . Grey dashed: the measured-time version. The number in each panel counts the measured points inside the band. Positive: the deduplicating kernel is faster. CUDA is shown to n=8 , since it runs the deduplicating kernel at n=9 with deduplication off and on.
Figure 5: Best draft length and the bounds ( 2 ) and ( 2 ). Each configuration has one point under the measured-time version (filled) and one under the calibrated version (open), with δ as in Section 4.3 . Color: model; shape: engine (circle: llama.cpp CUDA, triangle: llama.cpp Metal, square: MLX-LM). Left: predicted against measured k∗ , measured-time version; area grows with the number of acceptance values; dotted: exact prediction; shaded: within one. Middle: largest share of throughput lost by drafting k^∗ , against its bound ( 2 ). Right: largest relative error of the predicted best speedup, against its bound ( 2 ). In the middle and right panels the solid line is where the error equals its bound. The theory requires every point to lie in the shaded region on or below it, and every point does.
engine
version
d
crossover
sign
saving error
k∗ within 1
loss
τ error / δ
llama.cpp CUDA
measured-time
512
− / −
7 of 7
0.23
11
2.9%
9.9% / 0.072
measured-time
4096
− / −
7 of 7
0.28
11
4.1%
10.1% / 0.077
llama.cpp Metal
measured-time
512
− / −
8 of 8
1.6
9
20.1%
34.0% / 0.27
measured-time
4096
− / −
8 of 8
4.4
6
22.5%
28.4% / 0.25
calibrated
512
− / −
8 of 8
1.5
11
6.4%
4.3% / 0.036
calibrated
4096
− / −
8 of 8
4.4
9
8.3%
10.5% / 0.098
Table 4: DeepSeek-V2-Lite-Chat: the measured-time version and the calibrated version, written before its speculative runs. Crossover: predicted / measured ( − : none), over n≤8 on CUDA. Sign: values of n with the measured sign of S(n) , of those whose interval excludes 0. Saving error in ms. k∗ : acceptance values, of 11, with k^∗ within one of the measured best. Loss: largest share of the best throughput lost at k^∗ . τ error: largest relative error of the predicted verify time, over both kernels and n=2,…,9 . δ as in Section 4.3 . The best speedup lies inside its bound ( 2 ) in every row.
Figure 6: Saving S(n) on DeepSeek-V2-Lite, as in Figure 4 . Black and grey: measured, with its 95% interval. Red: the prediction from fits run after the whole-model runs, with the band of the saving bound ( 2 ) for the engine’s ε . Grey dashed: the measured-time version, written before its speculative runs. The number in each panel counts the measured points inside the band.
Appendix figures & tables6 assets
Supplementary material from the paper’s appendix.
Appendix
Symbol
Meaning
a
acceptance rate (geometric acceptance)
a0
V(1)/V(0)−1 , the acceptance above which one draft is faster than none
aj
j -th step point
b
bytes of one expert of the timed layer, b=wl
Bk
βdedup(k+1)
ci
V(i)−V(i−1) , the time the i -th draft adds
Appendix
Table 11
engine
tested sizes
other sizes
where the source decides
llama.cpp Metal b11617
per-slot
per-slot for n≤31 ; tiled (32 tokens per tile) for 32≤n≤512 , deduplicating only at n=32 for two models
dispatch test “batch ≥32 rows” in ggml-metal-common.cpp ; matrix-vector kernel in mul_mv.metal , one grid slice per token-slot pair
MLX-LM 0.32.0
per-slot
per-slot for n<4N/m (32 or 64); tiled (16 per tile), neither, above
dispatch branch on rows per expert in mlx/backend/metal/quantized.cpp ; MoE call in mlx_lm/models/switch_layers.py
llama.cpp CUDA b11617
upstream; run in Sec. 4
both at n=1 ; per-slot for 2≤n≤8 ; deduplicating for 9≤n≤128 (OLMoE, Qwen3), 16/15 times union (gpt-oss)
batch limit 8 for the matrix-vector kernel in ggml-cuda/mmvq.cuh ; dispatch in ggml_cuda_mul_mat_id ( ggml-cuda.cu ); tiles in ggml-cuda/mmq.cu
Appendix
Table 5: Kernel that runs the expert products, read from source. n is the number of tokens verified in one pass, and the tests verify n≤9 tokens per step. “Neither”: the requested bytes equal neither the union nor the per-slot count on every trace. Bytes are requested bytes. The source does not determine how many requests on-chip caches serve. The CUDA row at 2≤n≤8 is the source reading of the matrix-vector kernel, which requests each slot’s expert once. Profiled L2 requests of this kernel fall below nmb when repeated experts sit in the same slot of every token. They fall as far as 0.14 of nmb , at n=8 and U=8 . Two causes would both produce this: L1 hits under per-slot requests, and a pattern that requests each expert once per slot position. The hardware counters do not decide between them, so the row rests on the source. For gpt-oss, the factor 16/15 counts the loads of the tiled kernel in the source reading. The factor fl=500/459 of Table 6 instead counts the padded rows of each weight tensor, the byte unit of Appendix E .
Table 6: The deduplicating engines. The kernel each setting runs is read from source at n=1,…,9 verified tokens. At n=1 , an engine runs the same kernel with deduplication off and on. fl : bytes the deduplicating kernel reads per distinct expert, over the expert’s bytes. On Qwen3 Q4_K_M, the tiled kernel reads each K-quant expert more than once. MMVQ and MMQ: llama.cpp CUDA’s quantized matrix-vector and matrix-matrix kernels.
ε
verify-time error
engine
model
d
amplification
implied
engine
prediction
measured-time
calibrated
Metal
OLMoE
512
6.0 to 79
0.014
0.031
19%
20%
6%
Metal
OLMoE
4096
6.2 to 86
0.028
0.031
24%
24%
6%
Metal
Qwen3
512
3.9 to 7.8
0.020
0.031
21%
22%
6%
Metal
Qwen3
4096
4.2 to 19
0.047
0.031
25%
31%
4%
Metal
gpt-oss
512
3.3 to 343
0.033
0.031
20%
15%
9%
Appendix
Table 7: Per configuration, three quantities. Amplification: the factor (M^slote+M^dedupe)/∣S(n)∣ by which the saving bound ( 2 ) turns a relative bandwidth error into a relative saving error. It is given as its range over n=2,…,9 ( n≤8 on CUDA; ∣S(n)∣ floored at 0.05 ms), and it is largest where S(n) crosses 0. Implied ε : the bandwidth error that the prediction’s largest saving error requires under ( 2 ), against the engine’s ε . Verify-time error: the largest relative error of the verify time with deduplication on over n=2,…,8 , for the prediction, the measured-time version and the calibrated version. Metal and CUDA are llama.cpp.
rule
CUDA 512
CUDA 4096
Metal 512
Metal 4096
crossover within one
− / − , holds
− /9, holds
− / − , holds
− / − , holds
k^∗ within one at ≥9 of 11 values
11, holds
11, holds
9, holds
6, fails
loss at most 6% at every value
2.9%, holds
4.1%, holds
20.1%, fails
22.5%, fails
sign of S(n) where the interval excludes 0
7 of 7, holds
7 of 7, holds
8 of 8, holds
8 of 8, holds
verify time within 15%, both kernels
9.9%, holds
10.1%, holds
34.0%, fails
28.4%, fails
best speedup inside ( 2 )
δ=0.072 , holds
0.077, holds
0.27, holds
0.25, holds
Appendix
Table 8: Scoring rules for DeepSeek-V2-Lite and the measured values, measured-time version. The speedup rule uses δ , the largest relative error of the predicted step time. The crossover rule is scored over n=2,…,9 , and a measured crossover at n=9 counts as within one of none. On CUDA, the deduplicating kernel runs at n=9 with deduplication off and on. So Table 4 , like Table 3 , reads the CUDA crossover over n≤8 .
Figure 7: Bandwidth of a held-out model, predicted from the other models of the same engine. It is plotted against the bandwidth measured on the expert operation of the held-out model, at n=2,4,6,8 . Left: each kernel’s bandwidth (filled: per-slot; open: deduplicating), log scale. Right: the ratio r(n) . Color: the held-out model; shape: engine (circle: llama.cpp CUDA, triangle: llama.cpp Metal). Dotted: equal.
Department of Mathematical Sciences, Tsinghua University, Beijing, China · School of Computer Science and Technology, Beijing Institute of Technology, Beijing, China · JDT AI Infra +2