perf(qwen4): typical acceptance behind --typical-threshold (default off) - #478
Open
davidtai wants to merge 3 commits into
Open
perf(qwen4): typical acceptance behind --typical-threshold (default off)#478davidtai wants to merge 3 commits into
davidtai wants to merge 3 commits into
Conversation
Add the Qwen3.8 Flash-Next serving stack: 20 runtime optimizations, the server code that arms them for the served model, and the tests. Three engines ran one battery with identical request bodies per cell, 1,024 output tokens, temperature 1, top-p 0.95, top-k 20 and cold prefill. Cell Metric upstream 2.10.2 branch mlx-serve 26.8.11 16K decode tok/s 57.65 80.92 65.73 16K prefill tok/s 1,191 1,332 1,507 16K TTFT s 13.972 12.495 10.914 255K decode tok/s 45.86 65.53 35.15 255K prefill tok/s 952 1,189 437 255K TTFT s 275.476 220.629 597.681 Decode optimizations, in merge order: MTPLX_QWEN4_FIXED_M4_VERIFY compiled fixed-M4 verifier MTPLX_QWEN4_M4_STAGE3 fused MoE combine tail MTPLX_QSA_M4_FUSED_KV_GATHER fused QSA key/value gather MTPLX_QWEN4_M4_ROUTED_DOWN_REDUCE routed-down reduction MTPLX_QWEN4_M4_ROUTED_DOWN_RESIDUAL_TAIL reduction plus residual tail MTPLX_QWEN4_M4_ROUTED_GLU paired gate and up producer MTPLX_BATCH_TARGET_ARRAYS batched target distributions MTPLX_FRSPEC_DRAFT, MTPLX_FRSPEC_VOCAB FR-Spec ranked draft head MTPLX_QWEN4_COMPILED_MTP_PREPARE compiled draft preparation MTPLX_FABLE_HC_M4 hyper-connection read kernel MTPLX_FABLE_OPDIET verify-graph operation diet MTPLX_FABLE_BLOCK_VERIFY block verification MTPLX_FABLE_ROUTE_KERNEL single-launch route kernel MTPLX_FABLE_DRAFT_K20_PRESCATTER draft top-20 pre-scatter MTPLX_FABLE_GRAPH_BUILD_OVERLAP verify-graph build overlap MTPLX_FABLE_VERIFY_GLUE rotary and indexer glue MTPLX_FABLE_QSA_SPARSE_DECODE split-K sparse attention Prefill optimizations, in merge order: MTPLX_FABLE_PLE_PREFILL_LOOKAHEAD n-gram row lookahead MTPLX_FABLE_PLE_FIRST_GATHER_EARLY early first-chunk gather MTPLX_PREFILL_CHUNK_SIZE chunk size 4,096 MTPLX_QSA_PREFILL_COMPILE_ROWS compiled indexer rows 4,096 MTPLX_FABLE_PREFILL_QSA_QUERY_TILE query tile of 2,048 rows MTPLX_GDN_BLOCKED_PREFILL GDN blocked prefill kernel MTPLX_FABLE_PREFILL_MASK_FUSE causal fused attention MTPLX_SESSION_BANK_MAX_BYTES session bank budget 8 GiB Serving and install: --disable-optimization, MTPLX_FABLE_DISABLE per-key and per-lane opt-out fable_install_receipts install verdicts on /health --ngram-prewarm n-gram table pre-read pcg64_tape exact uniform random tape The server arms all 26 keys above by default for the served Flash-Next model. Set a key to 0, set MTPLX_FABLE_DISABLE, or pass --disable-optimization <lane>|all to turn one or all of them off. The three MTPLX_QWEN4_M4_ROUTED_* keys and the route kernel are one chain on the route_kernel lane; turn the 4 off together. Quality: HumanEval pass@1 0.951 (156/164) and HumanEval+ 0.933 (153/164) with the full set, against 0.933 (153/164) and 0.921 (151/164) with every key off and the same pair on upstream 2.10.2, greedy over all 164 problems. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NH95J2BGTyDX76P6EyYEGS
…ync PLE, pooled-key rowsel) Adds two decode lanes on top of the Flash-Next stack, both armed by default for a served Flash-Next pack with per-lane opt-out, an install verdict line, and a /health entry (aux_lane_defaults). - ple_cached_aux: a native CPU-stream provider stages the fixed 64 M4 n-gram rows and the auxiliary embedding plane is produced with mx.async_eval outside the compiled verifier. The stock owner-side row cache is preserved; at most two pending tickets; a failed submission marks the installation unhealthy. - qsa_pooled_rowsel: the twelve QSA indexers' pooled-key preparation binds the pool kernel metadata once per indexer at construction and shares one inv_freq object; the pooled block count derives from the write width. Measured at the 16,384-prompt / 1,024-output cell, three seeds, one build: both lanes on 81.75 tok/s decode vs 80.47 with both off (+1.59%), reasoning and text digests identical on every seed, prefill / peak / TTFT flat. ABBA retest of each lane alone: cached PLE +1.54% (separates from the 1.5% window noise), pooled rowsel +1.03% (inside noise). Data appendix and chart in docs/perf/pr391-aux-lanes.md. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01C5oDbNTShwy1QS9dT8VDQD
Adds an opt-in acceptance law for the native MTP verify on Qwen3.8 Flash-Next. Unset, the verify keeps the exact rejection-sampling rule and the output stream is unchanged. With --typical-threshold (env MTPLX_FABLE_TYPICAL_THRESHOLD), a draft token is accepted when it is typical under the target distribution, p(x) > min(eps, delta * exp(-H(p))); the longest typical prefix is kept and the first atypical position is resampled from the target. Higher thresholds are stricter. This is not distribution-exact; quality is gated by task evals. Measured at the 16,384-prompt / 1,024-output cell, three seeds, one build: exact 81.78 tok/s; threshold 0.2 97.99 (+19.8%); threshold 0.09 107.95 (+32.0%). Greedy HumanEval, HumanEval+, and MBPP are byte-identical across the dial; sampled HumanEval moves within one to two tasks of 164 in either direction depending on harness. Data appendix and chart in docs/perf/pr391-typical-acceptance.md. The code-eval gate gains an opt-in --save-completions sidecar so HumanEval+ can be scored offline. Typical acceptance follows Cai et al., "Medusa: Simple LLM Inference Acceleration Framework with Multiple Decoding Heads", 2024, arXiv:2401.10774. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Typical acceptance: an opt-in tokens-per-cycle lane for Qwen3.8 Flash-Next
This pull request adds one decode lane, typical acceptance (Medusa-2), behind a
single operator knob that is unset by default. When unset, the exact
speculative-sampling law runs byte-for-byte unchanged. When set to a positive
threshold, the lane accepts more draft tokens per cycle and decode gets faster.
Headline, at the canonical 16,384 / 1,024 cell, three seeds: at threshold 0.09
decode is 107.95 tok/s against 81.78 with the lane off, +32.0%, clearing the
100 tok/s target; tokens per cycle rise from 2.54 to 3.73. Quality holds within
measurement noise (Section 5). A stricter threshold 0.20 gives +19.8%.
Terms:
min(eps, delta*exp(-H)), where H is theentropy of the target row.
1. Chart
Three arms at the 16,384 / 1,024 cell: exact (lane off), threshold 0.09, threshold
0.20. The dashed line is the 100 tok/s target. Threshold 0.09 clears it; threshold
0.20 is a stricter setting between exact and 0.09. The full data is in
docs/perf/pr391-typical-acceptance.md. (Replace4c963f8829a5f3403bd9ac26650fa1719cd36a7ewith the pushed SHA.)2. How it works, in plain terms
The exact rule verifies each draft token with a rejection coin. It accepts a draft
token
xwith probabilitymin(1, p(x)/q(x)), wherepis the target row andqis the draft row. On a rejection it resamples that position from the residual(p - q)+and ends the block. This reproduces the target distribution exactly.Typical acceptance changes the accept test. It accepts a draft token when the
token is "typical" under the target's own distribution:
It accepts the longest run of typical positions in the block. At the first
position that is not typical, it resamples that one position from the target row
pitself (not from the residual) and ends the block. The per-position test usesno coin; it is deterministic.
The floor adapts to the target's entropy. A confident target (low H) has a high
floor, so only a strong token is typical. An uncertain target (high H) has a low
floor, so more tokens are typical. This accepts more tokens per cycle than the
exact coin, which is where the speed comes from, and why the emitted stream no
longer matches the target distribution. At temperature 0 the argmax is always
typical, so the lane is a no-op there and engages only at temperature > 0.
Reference: Cai, Li, Geng, Peng, Lee, Chen, Dao, "Medusa: Simple LLM Inference
Acceleration Framework with Multiple Decoding Heads," 2024, arXiv:2401.10774,
Section 2.3.1 "Typical Acceptance" (criterion
p_original(x) > min(epsilon, delta * exp(-H(p_original)))). Medusa adapts thetypicality idea from Hewitt, Manning, Liang, "Truncation Sampling as Language
Model Desmoothing," 2022, arXiv:2210.15191.
3. What to merge
One lane, one knob, default off. The threshold is the operator control; it maps to
delta.
GET /healthreports the resolved state undertypical_acceptance, andeach active request prints a
[typical-accept] NOT distribution-exact; ...line.p(x) > min(eps, delta*exp(-H)), resample the first atypical position from the target row--typical-threshold/MTPLX_FABLE_TYPICAL_THRESHOLD(maps to delta)mtplx/sampling.py,mtplx/generation.py,mtplx/server/openai.py;tests/test_typical_acceptance.py,tests/test_typical_acceptance_defaults_cpu.py,tests/test_typical_threshold_cli_health_cpu.py,docs/perf/pr391-typical-acceptance.mdNotes on this table:
default, so the exact law runs. The lane is not part of the served Flash-Next
default stack; a served pack does not turn it on. A CPU test asserts the served
default config leaves it off.
--typical-thresholdmaps to delta; higher = stricter. A higher thresholdraises the entropy floor, so fewer positions are typical and the arm sits closer
to the exact rule. Unset or 0 is the exact law. As the threshold grows very
large, every position is resampled from the target row and each cycle commits
one exact token.
epsis an advanced cap (MTPLX_FABLE_TYPICAL_EPS, default 1.0). Witheps = 1.0 the cap never binds for any delta <= 1, so it is inert at the operating
points here. A CPU test pins that the delta=0.09 numerics are identical under the
old eps=0.3 and the new eps=1.0. Most operators never touch eps.
scripts/code_eval_gate.pygains an opt-in--save-completionssidecar (task_id + raw completion + extracted solution).Without the flag the scored report is byte-for-byte the same. This is how the
HumanEval+ column below was produced offline on the exact same completions, the
reproducibility the PR-391 quality screen needs on-box.
4. How to enable
The lane is off unless you set the threshold. Any of:
--typical-threshold 0.09MTPLX_FABLE_TYPICAL_THRESHOLD=0.09Set it to
0(or leave it unset) to keep the exact law. Raise it (for example0.20) for a stricter, closer-to-exact setting. It engages only attemperature > 0.
5. Benchmark method and results
Youssofal/Qwen3.8-Flash-Next-MTPLX-Optimized-Speed, revision29ba90f82124961d0d902a9ea9bbb1034972af2fxhigh/ 3Column sources: HE and HE+ are evalplus 0.3.1 greedy (the tool the PR-391 quality
screen used); MBPP and sampled HE are the repo gate. Each is internally consistent
across arms.
Prefill (~1329 tok/s), TTFT (~12.5 s) and peak memory (< 100 GiB) are flat across
arms: the lane touches only the decode accept decision.
6. Quality
Greedy HumanEval, HumanEval+ and MBPP are byte-identical across all three
arms. At temperature 0 the lane is a no-op (the argmax is always typical), so
greedy completions match exact. This is verified on the base tests and on the
extended HumanEval+ tests.
Sampled HumanEval moves by at most one or two tasks of 164, and the repo gate and
evalplus disagree on the direction (evalplus sampled HE+: exact 0.8963, 0.09
0.9207, 0.20 0.9268). The sampled effect is therefore within n=164 sampling and
harness noise, neither a gain nor a crater. On this battery, typical acceptance
preserves code-evaluation quality within measurement noise at both thresholds.
That is the trade: more tokens per cycle for a token stream that is not
distribution-exact, with a quality cost that stays inside noise here. The default
is the exact law; the threshold is how an operator opts in.
🤖 Generated with Claude Code