Files
DS4Server/docs/METAL_PARITY.md
2026-08-30 15:26:27 +02:00

8.5 KiB

Apple Metal parity for issue #78

Frozen comparison

  • DS4 oracle: 8db89fe083ae4d17c9a2428ccd29803d3ae8f577
  • DS4Server pre-issue baseline: 79468c65b68c28e1539acf0a2a14bd42e000e365
  • DeepSeek V4 Flash 0731 IQ2XXS/w2Q2K model SHA-256: ca22ae2f838e14077c22bc1c1417b71b45b5e5a3687bd96c2ac6e17fdb6261c0
  • DSpark support-model SHA-256: 7e319924541db3f7a163ed7e11d7532a70d48228ab59d36cb81e1d4511885360
  • Machine: MacBook Pro Mac17,7, Apple M5 Max, 128 GiB unified memory, macOS 26.6.2, power 100%
  • Context and prefill allocation: 4096 tokens; quality, steering, speculative decoding, and DSpark disabled for the throughput gate
  • SSD mode: 4096 cached experts, 4096 popularity-preloaded experts, ordinary (not cold) streaming
  • Prompt: Count from one to two hundred, spelling out every number. rendered to 16 tokens; 128 greedy non-EOS tokens are measured

The longer prompt is deliberate: all 128 generated tokens are reachable before EOS and are identical between DS4 and DS4Server in each mode. A short-response fixture reached EOS early and made a forced post-EOS speed run compare unreachable, mode-dependent states.

DS4 performance-change matrix

The audited range is efdadd41e20134af4f3381e1ed90e96fe4faef6f..8db89fe083ae4d17c9a2428ccd29803d3ae8f577. Documentation-only, parser/server-only, CUDA, ROCm, tensor-parallel, model download, and merge commits do not affect the Rust-owned single-session Metal graph. The applicable performance/runtime commits are accounted for below.

DS4 commit(s) DS4 call site or behavior DS4Server result
3ce6777, 8e0f997 Native streamed-expert cache pinning and safe fallback Active in the byte-matched native Metal cache; Rust preserves the cache budget and fallback.
df04887 ds4_gpu_attention_indexed_mixed_batch_heads_tensor Active through encode_layer_with_cache_rows; the long-context fixture crosses the indexed boundary.
74e8f11, 4591cb1, a51e6ec, f284165, af80694, 7fb2830, 0e89a0e, 769a8ba, 8d683d6 DSpark block commit, deterministic scheduling, acceptance replay/direct commit, greedy identity, and exact/opportunistic sampling Active in Dspark::propose and the verifier commit paths; greedy and exact stochastic fixtures cover the frozen support model.
96c3ba4, d69a017, 81f8534, ac5cb2c, 48392e3, 222b2cb, 532ec8b Metal 4 Q4 projection, compact routed-MoE, aligned Q8, indexed-attention, and routed prefill kernels Active through the existing encode_batch_layer Q4/indexed/routed entry points; no second prefill graph was added.
24903f3 ds4_gpu_head_rms_norm_rope_tail_tensor Existing Rust Q-head norm/RoPE fusion is retained and used.
4893e0c, d14ce35 Mapped-prefill expert seeding and consistent expert/non-expert maps Rust seeds with ds4_gpu_stream_expert_cache_seed_experts_gpu_copy, falls back to CPU/pread, and restores one cached static decode map with the same non-routed spans.
0dd0d36 Release speed regression gate Rust has an ignored release gate with retained raw CSV, DS4 reference threshold, baseline threshold, p50/p95, cache metrics, and token hashes.
4e401a1, 0e125e6 Automatic pre-M5/M5 admission for exact decode fusions Rust binds both device predicates and mirrors per-generation rollback variables without a UI preference.
71d8c2a Q/KV pair plus ratio-4/128 compressor projection/store try_qkv_pair_compressor_fusion calls the native combined entry point only for DS4's resident full-decode shapes.
b64d3e0 Emit-path compressor finalizer update_compression_pair defers both updates only when the resident finalizer is eligible, then requires exact success.
3826f52, ad4d05c Exact compressor softmax/product and ratio-4 pool Native kernels are active; Rust's compressor ABI now passes the upstream decode_one_token and defer_finalize flags exactly.
92d83e6 HC norm/mix producer clustering decode_hc_mix uses split-norm producer fusion, with the established norm/mix/split sequence as fallback.
1be41c8 Packed exact split-K attention reduction Active in the vendored native attention kernel reached by Rust's decode attention calls.
0e8fb54, e6a6ab5 Router/select, shared gate/up, QKV norm/KV RoPE/store, parallel full FFN, inverse-attention RoPE, and pre-M5 ports All applicable entry points are called with DS4 shape, tensor, quality, steering, profile, SSD, and device guards. Parallel FFN aborts on partial failure; inverse RoPE falls back when the attention kernel does not consume it.
0658dac Long-context prefill/decode correctness The 4096-crossing ignored fixture covers compressed/indexed state and the next decode token.
023614e, 0ad494e, 42033ee Remove stream-512 experiment; make exact paths automatic; verify DFlash pipeline No stream-512 or opt-in experiment remains. Feature rollbacks retain identical token hashes.
24fa85e Keep successfully loaded disk KV checkpoints Existing Rust checkpoint load/round-trip behavior is verified; only invalid checkpoints follow deletion policy.
7694112 Reuse a shorter exact GLM live prefix live_prefix_rewind_target rewinds to prompt_len - 1, clears pending speculation, and reevaluates the last prompt token.
5186e2c Keep Metal decode changes portable Device checks and unavailable returns preserve non-M5 and unfused execution.

The Metal MXFP4 commits (725b084, 1f862bd, 7bec128, bcd1e57, f75a065, and their merge/format work) are intentionally excluded: MXFP4 is issue #79. ds4_gpu_set_decode_pipeline_fast_lookup is reset to disabled, matching DS4 because that cache is enabled only for the pre-M5 MXFP4 path.

Results

Three implementations were run sequentially, three times per mode, with a five-second cooldown. Medians are from metal-parity-issue-78.csv.

Implementation Mode Prefill tok/s TTFT ms First decode ms Stable tok/s p50 ms p95 ms
DS4Server baseline resident 69.63 256.07 26.37 39.53 24.88 26.61
DS4 resident 56.49 305.94 22.71 45.60 21.91 22.32
DS4Server issue #78 resident 69.76 252.01 22.66 45.60 21.94 22.33
DS4Server baseline SSD 6.81 2476.94 125.30 8.63 114.93 124.28
DS4 SSD 11.88 1393.54 46.75 26.11 35.62 46.86
DS4Server issue #78 SSD 11.49 1440.79 48.84 25.07 37.70 48.85

TTFT is the per-run prompt prefill duration plus first decode latency; its median is reported separately from the first decode kernel latency.

Resident stable decode improves 15.4% over the baseline and matches DS4. SSD stable decode improves 190.3% over the baseline and is 4.0% below DS4, inside the required 5% boundary. SSD cache accounting is identical to DS4 for the measured token sequence: 4096 entries, 32,958 hits, 4,194 misses, and 29,684,662,272 pread bytes. The resident and SSD token SHA-256 values are, respectively, ed5461476a82824c3e0440e943a0b2f6b8a8f19dc26a550a1bc255003851ab8d and 2bc33a04bb88f9f3c56f8bae0124b7bf25fd2f32aee81a1bdaf6e979448da052; DS4 emitted the same 128-token sequences in the corresponding modes.

Representative /usr/bin/time -l measurements report:

Implementation Mode Maximum RSS bytes Peak footprint bytes Process swaps
DS4 resident 78,790,656 4,746,511,440 0
DS4Server issue #78 resident 129,024,000 104,071,816 0
DS4 SSD 29,066,280,960 29,595,620,664 0
DS4Server issue #78 SSD 29,098,393,600 104,350,344 0

The post-run system memory-pressure reading was 92% free. Every measured process reported zero swaps; the post-run host counters were 0 swap-ins and 20 historical swap-outs.

Reproduction

DS4 uses the exact 16 Rust-rendered prompt token IDs with its speed-benchmark format. DS4Server uses the installed-model fixture:

DS4_REFERENCE_TPS=45.60 \
DS4SERVER_BASELINE_TPS=39.526849 \
cargo test --release --all-features flash_0731_m5_decode_performance_gate \
  -- --ignored --nocapture --test-threads=1

DS4SERVER_BENCH_SSD=1 \
DS4_REFERENCE_TPS=26.11 \
DS4SERVER_BASELINE_TPS=8.634659 \
cargo test --release --all-features flash_0731_m5_decode_performance_gate \
  -- --ignored --nocapture --test-threads=1

The gate fails unless throughput improves over the frozen DS4Server baseline and remains at least 95% of DS4. DS4SERVER_BENCH_TOKENS=1 prints the prompt and generated sequences for direct DS4 comparison.