Optimize Qwen3.8 inference on Apple silicon

This commit is contained in:
Georg Bauer
2026-09-04 23:27:09 +02:00
parent bf82df77cb
commit bd6353804b
14 changed files with 10149 additions and 1145 deletions

146
docs/QWEN38_PERFORMANCE.md Normal file
View File

@@ -0,0 +1,146 @@
# Qwen3.8 Flash Next performance and parity receipt
Issue #99 optimizes the pinned `Youssofal/Qwen3.8-Flash-Next-MTPLX-Bare-Speed`
text model without changing its artifact precision, sampler, or token behavior.
All application and inference work remains Rust; native additions are confined to
the existing Objective-C Metal bridge and Qwen `.metal` kernels. Integrated MTP
is the Qwen production default; an explicitly stored user opt-out remains
authoritative.
## Result
The acceptance B1 median is **60.825779 tok/s MTP**. A later B2 confirmation,
after adding complete repair telemetry and under the then-current machine load,
measured **58.019436 tok/s**. Both sequences produced the same 64-token SHA-256
`46ce63601c44d80b5ef8297eaa49a636f6680040b916fe30cf7a154c05daef72`, with 44
accepted drafts from 60 proposals in 20 verifier passes. The fresh matched MTPLX
median was 59.751170 tok/s, putting B1 1.8% above and B2 2.9% below the oracle;
both are inside the issue's 5% parity band. Raw per-run data is in
`docs/qwen38-issue-99.csv`.
Plain AR reached a 50.671077 tok/s median with stable token hash
`15373689bcb1c667123accfcfbe49736466f28ce7beb89d2cbe5e4120416056d`.
The published 47 tok/s MTPLX figure is contextual rather than a fresh matched
AR baseline: after the valid MTP oracle run, the installed MTPLX package and
checked-out source drifted apart. The current CLI rejects this Flash Next pack
as unsupported and the source path fails at the missing
`DecoderLayer.input_layernorm`, so no invalid number is presented as a fresh AR
comparison.
The published 75.9 tok/s MTP result is from a different MTPLX runtime/workload.
With identical pinned artifacts, 4,096 context, prompt, sampling, seed, and
64-token measurement window, the current usable oracle result is 59.751170
tok/s. The remaining gap is therefore not explained by quantization drift or a
different output distribution.
Maximum fan control could not be established because neither ThermalForge nor
TG Pro is installed; MTPLX reports its fan boost as unavailable. Both compared
engines ran under the same no-boost constraint. No system utility was installed
or substituted for the missing control.
## Fixed workload
- Hardware: MacBook Pro `Mac17,7`, Apple M5 Max, 18 CPU cores, 128 GiB unified
memory.
- OS: macOS 26.6.2, build 25G83, arm64.
- Model revision: `74559cdf34fbfc0b593de72d17e93f37fd4f9ea7`.
- Model manifest contract SHA-256:
`6f1172de47fa30b9602e13fc7ad14e578a813bb5b1fab8f0ef320041aac6c19a`.
- Tensor inventory SHA-256:
`b5731e6febcf865d276a0e7b144da02375f2d7e3129594f271ee9c6c351f4c8f`.
- `config.json` SHA-256:
`826302af9ff3293cfc9975c9a5a96546fa0aa9f0808505adfc30a62fef806965`.
- `mtplx_runtime.json` SHA-256:
`381c7666872f878542494a48a874bb0784c72a955d18cc077324d3c9e74b8cdb`.
- Prompt: `Write a concise Rust implementation of binary search and explain its invariants.`
- Prompt tokens: 26; context: 4,096; generated tokens: 64.
- Sampling: temperature 1.0, top-p 0.95, top-k 20, min-p 0, seed 12,345.
- One process, three runs: first run cold, following runs warm.
The exact DS4Server commands were:
```sh
DS4SERVER_QWEN38_SOURCE=/private/tmp/qwen38-bare-source \
DS4SERVER_QWEN38_MEASURED=64 DS4SERVER_QWEN38_RUNS=3 \
cargo test --release qwen_m5_performance_gate -- --ignored --nocapture
DS4SERVER_QWEN38_SOURCE=/private/tmp/qwen38-bare-source \
DS4SERVER_QWEN38_MTP=1 DS4SERVER_QWEN38_MEASURED=64 \
DS4SERVER_QWEN38_RUNS=3 \
cargo test --release qwen_m5_performance_gate -- --ignored --nocapture
DS4SERVER_QWEN38_SOURCE=/private/tmp/qwen38-bare-source \
cargo test --release qwen_mtp_matches_target_and_restores_its_cache \
-- --ignored --nocapture
```
The MTPLX comparison used its normal `serve` path with the same local Bare Speed
directory, generation mode, prompt, context, sampler, seed, and single request
stream. A later retry is not a valid benchmark because the local MTPLX
source/package compatibility gate fails as described above.
## MTPLX logic review
The production path now follows the MTPLX/MLX graph rather than retaining the
temporary Qwen experiments used while earlier issues established correctness:
- PLE uses the artifact's 4-bit, group-32 rows, streams them with `pread`, keeps
a bounded hot cache, and publishes hit, miss, eviction, byte, and read-time
counters. The warm MTP runs had 2,080 hits, no misses, and no reads.
- GDN uses fused input projection, MTPLX-equivalent conv+SiLU+L2 normalization,
BF16 rounding points, decay/delta recurrence, gated norm, and direct verifier
capture buffers. Rejected rows restore or advance authoritative recurrence
rather than inventing state.
- QSA uses the MTPLX `qsa_flash_skip` row ownership, online-softmax merge, and
native 256-thread/eight-SIMD layout. Long-context state remains linear and
admitted through 262,144 tokens without a gather-copy fallback.
- MoE uses the exact router ownership and merged gate/up projection. The custom
verifier MoE experiment and NAX path were removed because they are not the
oracle's promoted path.
- MTP uses the same checkpoint, history, proposal, verifier, rejection-repair,
full-accept bonus, and NumPy-compatible PCG64 sampling rules. The exact cache
restoration test passes after the final telemetry change.
- Sparse top-k sampling stays on device for the supported official sampler and
uses the existing exact CPU path only when the requested sampler is outside
that kernel's contract. Full production logits are not copied to the CPU.
- Capability fallbacks remain exact implementations, not alternate Qwen
behavior. Experiments that changed tokens (normal Metal math, wider Tensor
QMM, and recurrence-only repair) were removed rather than left dormant.
The 4-bit/group-64 core, declared 8-bit router/QSA matrices, BF16 recurrent and
normalization tensors, 4-bit/group-32 PLE, and published 2/4/8-bit MTP tensors
are consumed as stored. No blanket up-conversion or replacement quantization is
used. Peak RSS in the final MTP sequence was 52.61 GB, down from the original
102.65 GB path; the 105.44 GB artifact size includes a 32.00 GB file-backed PLE
table that is not resident wholesale.
## A/B evidence
The optimization work used exact hashes or tensor comparisons before promotion:
| Area | Before | After | Decision |
| --- | ---: | ---: | --- |
| Resident memory | 102.65 GB RSS | 52.61 GB RSS | Reused staging and streamed PLE promoted |
| Router | 49.7 ms GPU | 18.7 ms GPU, 23.4 ms wall | Row-owned reduced router promoted |
| 16K QSA AR | 2.6 tok/s | 45.7 tok/s | MTPLX flash-skip rewrite promoted |
| Shared verifier gate/up | 54.7 tok/s | 58.3 tok/s | Exact fused projection promoted |
| Direct GDN verifier capture | 58.5/58.7 tok/s | 60.8 tok/s | Direct capture buffers promoted |
The direct-capture B1 acceptance was followed by the B2 confirmation in the CSV;
both retained the same token hash. Normal Metal math, Tensor QMM at verifier
width, and shortened recurrence repair failed exact-oracle checks and were
removed. Custom MoE verify, fused GDN output projection, and async AR were not
promoted because the MTPLX evidence did not show a win and DS4Server profiles did
not justify overriding that result.
## Context and verification
The long-context gate exercised 2,048, 16,384, 65,536, 131,072, and the native
262,144-token maximum. AR remained 43.488 tok/s at the maximum, and live state
grew from 58,195,968 bytes at 2K to 7,449,083,904 bytes at 262K without collapse
or over-admission. The CSV records every context result and state size.
Final issue verification comprises the exact MTP cache/token oracle, affine
QMV/QMM MLX reference comparisons, focused Qwen tests, the long-context state
gate, the release performance gate, formatting, Clippy with warnings denied, the
macOS application bundle, and the full all-features Rust test suite.