Skip to content

Commit 4333bc0

Browse files
docs: MLX v0.4-mac multi-tenant is SERIAL-ONLY (batched parallel is CUDA-only)
Record the decision across README, ADR 0014, and the PR-A3c report: on Apple-Silicon MLX, per-session binding is recall-preserving but sessions are served serially. Batched/parallel B>1 cohort decode is CUDA-only, blocked on MLX by the upstream B>1,L=1 quantized-decode kernel bug that persists on the latest published mlx 0.31.2 / mlx-lm 0.31.3 and is not Python-patchable. The 8.04-8.45x parallel throughput numbers are CUDA. Co-authored-by: FluffyAIcode <FluffyAIcode@users.noreply.github.com>
1 parent c12bf40 commit 4333bc0

3 files changed

Lines changed: 38 additions & 8 deletions

File tree

README.md

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ patterns — see [`docs/quickstart.md`](docs/quickstart.md).
9898
| **Restored verifier** | Gemma-4 26B-A4B (AR) + DFlash dLLM proposer + trained **f_θ**; **S5** keeps 5 full-attention layers exact, restores sliding layers → bounded resident KV, recall preserved. | `inference_engine.v04` (CUDA), `inference_engine.backends.mlx` (Apple Silicon) |
9999
| `SinkWindowVerifier` | Lightweight path: Qwen3 (0.6B / 1.7B / 4-bit MLX), sink+window K/V trim (ADR 0001 / 0002). | `kv_cache_proposer.verifier` (CPU), `inference_engine.backends.mlx.verifier` |
100100
| **Per-session binding** | `PerSessionVerifierRegistry` + coordinator resolver: each session owns isolated KV (shared weights) — true multi-tenant serving (PR-A3c, `--multi-tenant`). | `inference_engine.session.verifier_registry` |
101-
| **Batched scheduler** | Fuses a cohort's decode steps into one batched forward — **8.45× served throughput** at 8 sessions, recall 1.0. | `inference_engine.session.batch_scheduler` |
101+
| **Batched scheduler** | Fuses a cohort's decode steps into one batched forward — **8.45× served throughput** at 8 sessions, recall 1.0. **CUDA-only**: on Apple-Silicon MLX, `v0.4-mac` multi-tenant is **serial-only** (batched `B>1` decode is unsupported — upstream MLX `B>1, L=1` quantized-kernel bug, [ADR 0014](docs/adr/0014-agent-connection-capacity-and-cross-host-topology-tests.md)). | `inference_engine.session.batch_scheduler` |
102102
| `AppendTokens` / `Generation` coordinators | Drive prefill / incremental forward / greedy decode; route per-session (multi-tenant) or single. | `inference_engine.session.{coordinator,generator}` |
103103
| Python / TypeScript SDKs | `kakeya.Client` / `Session` (sync gRPC); `@kakeya/runtime` (Node 20+). | [`sdks/`](sdks/) |
104104
| HTTP shim (deprecated) | OpenAI-compatible `/v1/chat/completions`; `Deprecation` + `Sunset` headers. | `inference_engine.server.app` |
@@ -289,6 +289,13 @@ owns isolated KV (shared weights) — making serving truly multi-tenant, and a
289289
at 8 sessions with **per-session recall 1.0** (see the multi-tenant results
290290
below / [ADR 0014 §3.4–3.7](docs/adr/0014-agent-connection-capacity-and-cross-host-topology-tests.md)
291291
and the [detailed report](docs/reports/pr-a3c-multitenant-serving-test-report.md)).
292+
**Platform scope:** the batched/parallel cohort path is **CUDA-only**. On
293+
Apple-Silicon **MLX, `v0.4-mac` multi-tenant is serial-only** — per-session
294+
binding still gives isolated, recall-preserving sessions, but they are served
295+
**one at a time**; batched `B>1` decode is blocked by an upstream MLX
296+
quantized-kernel bug (`B>1, L=1` → per-session recall collapses to 0.125, while
297+
serialized stays 1.0; confirmed on the latest published `mlx 0.31.2 / mlx-lm
298+
0.31.3` — [ADR 0014 §3.4](docs/adr/0014-agent-connection-capacity-and-cross-host-topology-tests.md)).
292299
Pushing the connection sweep further (preset `agent-capacity-stress`, the
293300
open-file-descriptor limit `RLIMIT_NOFILE` raised to 100k / hard unlimited on
294301
the Mac — each connection uses one descriptor) shows the true ceilings: **the
@@ -584,9 +591,9 @@ scripts/
584591
| Milestone | Status | Description |
585592
| --- | --- | --- |
586593
| Session-bound gRPC runtime | ✅ shipped | Long-running gRPC `RuntimeService`, Python + TS SDKs, bounded memory + prefill (4-h Mac M4 evidence), Mac M4 self-hosted integration gate |
587-
| **v0.4 for Mac (`v0.4-mac`)** | ✅ shipped | MLX restored Gemma-4 26B engine: bounded KV (~90% saved), recall 1.0, ≈AR-parity spec-decode |
594+
| **v0.4 for Mac (`v0.4-mac`)** | ✅ shipped | MLX restored Gemma-4 26B engine: bounded KV (~90% saved), recall 1.0, ≈AR-parity spec-decode. Multi-tenant is **serial-only** (no batched `B>1` decode — upstream MLX kernel bug, [ADR 0014](docs/adr/0014-agent-connection-capacity-and-cross-host-topology-tests.md)) |
588595
| **v0.4 for CUDA (`v0.4-cuda`)** | ✅ shipped | Restored Gemma-4 26B engine on NVIDIA: fused DFlash spec-decode **1.79–2.06× AR**, 44–87× KV saving, recall 1.0 |
589-
| **v0.4 multi-tenant (PR-A3c)** | ✅ shipped | Per-session binding (isolated KV, shared weights) + batched scheduler **8.45× served throughput**, per-session recall 1.0 |
596+
| **v0.4 multi-tenant (PR-A3c)** | ✅ shipped | Per-session binding (isolated KV, shared weights) on both platforms. **CUDA**: batched scheduler **8.45× served throughput**, per-session recall 1.0. **MLX (Mac): serial-only** (sessions served one at a time; batched parallel decode unsupported upstream) |
590597
| Async continuous batching | designing | Dynamic mid-flight arrival + ragged-length cohorts under the async gRPC `Generate` handlers (current batcher is fixed-cohort) |
591598
| Deployment polish | queued | PyPI + npm publishing, GHCR Docker image, `kakeya prewarm` CLI, `kakeya chat` REPL |
592599
| Cross-request KV reuse | designing | Sessions survive across requests on gRPC; turns intra-session drift into 0 ms inter-request drift |

docs/adr/0014-agent-connection-capacity-and-cross-host-topology-tests.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,18 @@ remains: **serialized**, or the `L≥2` padding probe (recall-safe but 0.67×).
371371
Evidence: `results/research/k3_mac_bridge_mlx_upstream_batch_probe.json` +
372372
`.mac-bridge/logs/mlx-upgrade-{0,1,2}.log`.
373373

374+
**DECISION — MLX `v0.4-mac` multi-tenant is SERIAL-ONLY.** Given the upstream
375+
bug is present on the latest published mlx/mlx-lm and is not Python-patchable,
376+
the shipped Mac multi-tenant path is **per-session binding served serially**
377+
(one session decoded at a time): isolated, recall-preserving (1.0) sessions on
378+
shared weights, with bounded resident KV. **Batched/parallel cohort decode
379+
(`B>1`) is NOT supported on MLX** and remains a **CUDA-only** capability (§3.5 /
380+
§3.7: 8.04–8.45× near-linear, recall 1.0). Re-evaluate the Mac batched path
381+
only if (a) a future mlx release fixes the `B>1, L=1` quantized-decode kernel,
382+
or (b) a from-source mlx `main` build / upstream patch lands; the `L≥2` padding
383+
workaround stays available as a recall-safe (but sub-parity, 0.67×) escape
384+
hatch in the meantime.
385+
374386
## 4. Case 2 — cross-host proposer/verifier (FEASIBILITY VERDICT)
375387

376388
### 4.1 Verdict: the requested topology is not implementable today, and is architecturally bounded out

docs/reports/pr-a3c-multitenant-serving-test-report.md

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -100,11 +100,20 @@ PR-A3c delivers, recall-preserving (recall 1.0 throughout), the three multi-tena
100100
properties together:
101101

102102
- **Bounded memory**~4.2× more concurrent agents per GB (§3.1).
103-
- **Parallel throughput** — 8.04× engine-level (§3.2) / **8.45× through the
104-
served per-session adapters via the batched scheduler** (§3.4).
103+
- **Parallel throughput (CUDA)** — 8.04× engine-level (§3.2) / **8.45× through
104+
the served per-session adapters via the batched scheduler** (§3.4). This is
105+
**CUDA-only**; see the platform note below.
105106
- **Correct isolation** — true multi-tenant serving end-to-end through gRPC,
106107
per-session recall 1.0 (§3.3).
107108

109+
**Platform scope — MLX (`v0.4-mac`) multi-tenant is serial-only.** Per-session
110+
binding (isolated KV, shared weights, recall 1.0) holds on both platforms, but
111+
the **batched/parallel cohort path is CUDA-only**. On Apple-Silicon MLX,
112+
batched `B>1` decode collapses per-session recall to 0.125 (vs serialized 1.0)
113+
because of an upstream MLX `B>1, L=1` quantized-decode kernel bug that persists
114+
on the latest published `mlx 0.31.2 / mlx-lm 0.31.3` and is not Python-patchable
115+
(ADR 0014 §3.4). Mac therefore serves multi-tenant sessions **serially**.
116+
108117
## 5. Remaining work (productization)
109118

110119
- **Async continuous batching transport**: wire the batched scheduler under the
@@ -113,9 +122,11 @@ properties together:
113122
**dynamic mid-flight arrival + ragged-length** cohorts (this report's
114123
scheduler is a fixed synchronized cohort, the dominant burst case).
115124
- **Batched fused spec-decode** (DFlash is batch-1 today).
116-
- **Mac served path**: the served MLX gemma verifier can't load gemma-4's nested
117-
config (`MLXSinkWindowVerifier`) — a v0.4 item; CUDA is the recall-preserving
118-
served path today.
125+
- **Mac served path**: multi-tenant on MLX is **serial-only by decision** (ADR
126+
0014 §3.4) — batched `B>1` decode is blocked by an upstream MLX kernel bug, so
127+
Mac serves sessions one at a time (recall-preserving). CUDA is the batched
128+
parallel path today. Revisit only if a future mlx release / source build fixes
129+
the `B>1, L=1` quantized-decode kernel.
119130

120131
## 6. Evidence index
121132

0 commit comments

Comments
 (0)