You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: README.md
+60Lines changed: 60 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -105,6 +105,66 @@ the binding correctness gate. Mac M4 evidence on `main`:
105
105
106
106
Raw artifacts: [`results/platform-tests/bench_session_4h_1780332893.json`](results/platform-tests/) (4-h evidence) and the v0.3.0 GA tag's smoke run committed at `6399546`.
107
107
108
+
## Kakeya Inference Engine for Mac — MLX speculative-decode port (K3 beta baseline)
109
+
110
+
After the **CUDA** beta (PR #107: f_θ + S5 K/V-restoration verifier, **fused DFlash
111
+
spec-decode at 1.27× AR, recall 1.0 on Gemma-4-26B-A4B / H200**), the engine was
112
+
ported to the **Apple-Silicon MLX** backend. The decode throughput climbed from a
113
+
near-total collapse to **≈AR parity** through a sequence of precisely-diagnosed
114
+
fixes. This is the baseline record of that journey (all numbers are decode-only
115
+
tok/s vs the native `mlx_lm` AR oracle on the same model, measured on a Mac M4 via
116
+
the [Mac bridge](#evaluation-environment); ×AR is the ratio).
117
+
118
+
| Stage | ×AR | Binding problem | Fix |
119
+
| --- | --- | --- | --- |
120
+
| Naïve restored decode |**~0.09×**|**O(T²) collapse** — the restored verifier did a *full-sequence* forward **per generated token** (`restored_logits`); the Mac harness called it once per token. |**Gap-A incremental decode**: prefill **once**, capture the restored K/V into the model's **native** cache, then decode with `mlx_lm.generate_step` (chunked prefill + `mx.async_eval` pipelined) — O(L)/token, never re-forward the sequence. |
121
+
| Hybrid fused spec-decode |**~0.2×**|**Cross-runtime bridge** — MLX verifier + PyTorch/MPS drafter shipped **MB/block of aux-hidden** across runtimes on the critical path; plus a benchmark **forced-over-generation** artifact (`--ignore-turn-stop`) that tanked acceptance. | Recognised the bridge as the bottleneck; moved toward an **all-MLX drafter** (single runtime, zero per-block bridge crossings). |
122
+
| All-MLX + sound rollback |**~0.5×**|**Unsound rollback** — `RotatingKVCache` is not trimmable once the sliding ring wraps (`is_trimmable → offset < max_size`), so the loop **rolled the whole block back and re-forwarded** the carried accepted tokens every partial-accept block (~2 verifier forwards/block). |**CUDA-`DynamicCache` parity**: prefill an **all-`KVCache`** layout (sliding too — byte-exact, the window mask applies regardless of cache capacity) so `trim_prompt_cache` is a sound O(1) slice; **keep accepted K/V, trim only the rejected tail**, never re-forward. |
123
+
| Block-4 CUDA-trim |**~0.7×**|**Per-block Python graph construction** (`build_s` ≈ 50 ms/block building the 26B lazy graph). | Removing the re-forward (above) closed most of it; block-4 lands at **0.68× AR**. |
124
+
| Block-8 tuned |**~1.0×**|**Block size vs the drafter's accept-len plateau.**| Tune to **block-8** (matches the all-MLX drafter's ~4.5 accept-len ceiling); long-code completions reach **~1.0–1.05× AR (parity, best samples just over)**. block-16 is *worse* — `verify(16)` cost is wasted because acceptance plateaus. |
125
+
126
+
**Honest ceiling & what was *ruled out*.** ≈AR parity is the Mac result on the
0 commit comments