Files
Gan, Jimmy 3994e29cb0 Initial: llama.cpp router mode optimization guide for Apple Silicon
Full documentation of optimizing two LLMs on a single M5 Max GPU:
- KV cache quantization (Q4_0)
- Flash attention and batch tuning
- Router mode with --models-max 1
- Per-model thread optimization via INI presets
- Before/after benchmarks (12→48 t/s on 27B, 23→132 t/s on 35B)
2026-07-10 02:27:36 +08:00

88 lines
2.5 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Benchmark Results
## Hardware
| Component | Spec |
|-----------|------|
| Chip | Apple M5 Max |
| GPU Cores | 40 |
| RAM | 128 GB Unified Memory |
| Memory Bandwidth | 614 GB/s |
| OS | macOS 26.3.2 |
| llama.cpp | b9910 (f5525f7e7) |
## Models
| Model | Architecture | Size | Quant |
|-------|-------------|------|-------|
| Qwen3.6-35B-A3B | MoE (3B active) | ~35 GB | Q8_0 |
| Qwen3.6-27B | Dense | ~27 GB | Q8_0 |
## Config
```
-c 131072, --cache-type-k q4_0, --cache-type-v q4_0
--flash-attn on, --spec-type draft-mtp, --spec-draft-n-max 3
--batch-size 512, --ubatch-size 128
```
## Results
### Two Separate Servers (Before)
**Both loaded, both generating:**
| Model | Gen Speed | Notes |
|-------|-----------|-------|
| 35B MoE | 23 t/s | -75% from solo potential |
| 27B dense | 12 t/s | Already bandwidth-saturated |
**Solo (only one running):**
| Model | Gen Speed | Bandwidth Efficiency |
|-------|-----------|---------------------|
| 35B MoE | 94 t/s | 3.2 GB/step × 94 = 49% of 614 GB/s |
| 27B dense | 12 t/s | 30.4 GB/step × 12 = 59% of 614 GB/s |
### Router Mode (After)
**--models-max 1, auto-swap on request:**
| Model | Gen Speed (cold) | Gen Speed (warm) | Gain vs Before |
|-------|-----------------|------------------|----------------|
| 35B MoE | 132 t/s | 148 t/s | **5.7x** |
| 27B dense | 38 t/s | 48 t/s | **3.0x** |
**Per-model thread tuning:**
| Model | Optimal Threads | Speed |
|-------|----------------|-------|
| 35B MoE (attention-compute bound) | 14 | 148 t/s |
| 27B dense (bandwidth bound) | 10 | 48 t/s |
### Model Switch Latency
| Transition | Time |
|-----------|------|
| 35B → 27B (cold start) | ~4s |
| 27B → 35B (reload) | ~3s |
The 3-4s delay is model weights loading from SSD to GPU memory via Metal. Smaller batch sizes and Q4_0 KV cache help minimize this.
## MTP Speculative Decoding
| Model | Acceptance Rate | Mean Draft Length | Effective Tokens/Step |
|-------|----------------|-------------------|----------------------|
| 35B MoE | 74% | 2.84 | 2.1 |
| 27B dense | 82% | 3.62 | 2.6 |
MTP (Multi-Token Prediction) uses the model's built-in speculative heads. Each forward pass produces 3 draft tokens; acceptance rate determines how many are kept. With ~2.5 tokens per forward pass, effective generation speed is ~2.5x the raw decode speed.
## Methodology
- Tested via llama.cpp `/v1/completions` API
- Fixed prompt: 7-11 tokens
- Generation: 200 tokens, temperature 0
- Timings from response body `timings.predicted_per_second`
- Warm = model already loaded in GPU, cold = first request after model load