|
1 | 1 | # self-draft - drop-in latency optimization for LLM inference servers on Arm64 cloud |
2 | 2 |
|
3 | | -**One flag. Your model's own Multi-Token-Prediction heads become the draft. ~1.5x |
4 | | -lower-latency decoding for agentic/reasoning workloads on AWS Graviton - zero extra |
5 | | -model download, measurable cost-per-token reduction, stacked on Arm KleidiAI kernels.** |
| 3 | +**One flag. Your model's own Multi-Token-Prediction heads become the draft. Up to ~2x |
| 4 | +lower-latency decoding for agentic/reasoning workloads on Arm64 cloud - zero extra |
| 5 | +model download, ~50% lower cost-per-token, stacked on Arm KleidiAI kernels.** |
| 6 | + |
| 7 | +> **Verified on a free GitHub-hosted Arm64 runner (4 vCPU aarch64): 2.0x speedup, |
| 8 | +> 1.87x lower latency, 99.1% output similarity, n-max=3** - reproducible in CI, see below. |
6 | 9 |
|
7 | 10 | > **Arm Create: AI Optimization Challenge - Cloud AI track.** |
8 | 11 | > Target: AWS Graviton (Arm64) inference serving. Dev/validation: Apple Silicon (Arm64). |
@@ -33,35 +36,42 @@ python3 selfdraft/sd.py run models/gemma-4-12b-it-Q4_K_M.gguf --ngl 0 # CPU = |
33 | 36 |
|
34 | 37 | ## Results (measured) |
35 | 38 |
|
36 | | -`gemma-4-12b-it` Q4_K_M, MTP draft `mtp-gemma-4-12b-it.gguf` (0.47 GB), greedy, |
37 | | -reasoning/code workload, CPU-only (`--ngl 0`, the Graviton-equivalent path). |
38 | | -**Dev numbers below are Apple M4 Pro (Arm64); reproduce on Graviton with |
39 | | -[`scripts/graviton.sh`](scripts/graviton.sh).** |
| 39 | +`gemma-4-12b-it`, MTP draft `mtp-gemma-4-12b-it.gguf` (0.47 GB), greedy, reasoning/code |
| 40 | +workload, CPU-only (`--ngl 0`, the native Arm64-cloud path). |
| 41 | + |
| 42 | +### Arm64 cloud (headline) - free GitHub `ubuntu-24.04-arm` runner, 4 vCPU aarch64, Q4_0 + KleidiAI |
| 43 | + |
| 44 | +Fully reproducible in CI ([`.github/workflows/arm-bench.yml`](.github/workflows/arm-bench.yml)): |
40 | 45 |
|
41 | 46 | **Draft-length autotune** (`sd.py autotune`): |
42 | 47 |
|
43 | 48 | | n-max | tok/s | speedup | accept | |
44 | 49 | |------:|------:|--------:|-------:| |
45 | | -| vanilla | 18.6 | 1.00x | - | |
46 | | -| 2 | 20.2 | 1.09x | 86% | |
47 | | -| **3** | **28.6** | **1.54x** | 81% | |
48 | | -| 4 | 28.3 | 1.52x | 77% | |
49 | | -| 6 | 20.5 | 1.10x | 65% | |
50 | | - |
51 | | -**Server metrics + cost** (`sd.py bench --price`, n-max=3; $/hr is illustrative |
52 | | -c7g.xlarge - replace with your Graviton numbers): |
53 | | - |
54 | | -| | tok/s | TTFT | latency | $/1M tok | |
55 | | -|---|------:|-----:|--------:|---------:| |
56 | | -| vanilla | 21.3 | 3267 ms | 10.8 s | $1.89 | |
57 | | -| **self-draft** | **27.8** | 3049 ms | **8.8 s** | **$1.45 (-23%)** | |
58 | | - |
59 | | -**Agent loop** (`sd.py agent`, ReAct + calculator tool, correct answer both): |
60 | | -~1.1-1.2x end-to-end (short per-step generations are TTFT-bound; the decode win |
| 50 | +| vanilla | 5.99 | 1.00x | - | |
| 51 | +| 1 | 7.74 | 1.29x | 90% | |
| 52 | +| 2 | 9.21 | 1.54x | 82% | |
| 53 | +| **3** | **12.11** | **2.02x** | 78% | |
| 54 | +| 4 | 7.53 | 1.26x | 70% | |
| 55 | + |
| 56 | +**Bench** (n-max=3): vanilla **5.99 -> 12.01 tok/s = 2.0x**; end-to-end latency |
| 57 | +**36.2 s -> 19.3 s (1.87x lower)**; draft acceptance 76%; output similarity **99.1%** |
| 58 | +(1 floating-point-tie flip, see notes). Since cost is inversely proportional to |
| 59 | +throughput, 2.0x tok/s = **~50% lower $/1M tokens** on any priced Arm instance. |
| 60 | + |
| 61 | +### Apple M4 Pro (Arm64 laptop, dev) - Q4_K_M |
| 62 | + |
| 63 | +For reference, a fast M-series core (less memory-bound) shows a smaller but real win: |
| 64 | +n-max=3 -> **1.54x** decode (vanilla 18.6 -> 28.6 tok/s, 81% accept), latency 1.23x lower, |
| 65 | +~23% lower $/token. **Self-draft helps *more* on the weaker, more memory-bound Arm cloud |
| 66 | +cores** - exactly where it matters for cost. |
| 67 | + |
| 68 | +**Agent loop** (`sd.py agent`, ReAct + calculator tool, correct answer both): end-to-end |
| 69 | +win on agentic workloads (short per-step generations are TTFT-bound; the decode win |
61 | 70 | dominates on longer outputs). |
62 | 71 |
|
63 | | -Key takeaways: **~1.5x decode / ~23% lower $/token, zero extra download**, n-max tuning |
64 | | -is decisive (too long *loses* - the autotuner picks the sweet spot per instance). |
| 72 | +Key takeaways: **up to 2.0x decode / ~50% lower $/token on Arm64 cloud, zero extra |
| 73 | +download.** n-max tuning is decisive (too long *loses*) - the autotuner picks the sweet |
| 74 | +spot per instance. |
65 | 75 |
|
66 | 76 | ## Install / build |
67 | 77 |
|
|
0 commit comments