mlx-bun 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +57 -32
- package/docs/README.md +55 -42
- package/docs/archive/investigations/benchmark-ledger-through-v0.4.0.md +3617 -0
- package/docs/archive/planning/release-notes-v0.5.0.md +37 -0
- package/docs/design/batching.md +220 -7
- package/docs/design/decode-speed-program.md +121 -66
- package/docs/design/generic-model-support.md +116 -0
- package/docs/design/kv-cache.md +380 -211
- package/docs/design/speculative-decoding.md +165 -26
- package/docs/design/turboquant.md +20 -7
- package/docs/design/unified-engine-frontier-plan.md +120 -13
- package/docs/planning/PRODUCT_ROADMAP.md +5 -6
- package/docs/planning/public-readiness-handoff.md +244 -0
- package/docs/planning/public-readiness.md +149 -0
- package/docs/reference/benchmarks.md +3463 -3489
- package/docs/reference/cli.md +62 -1
- package/docs/reference/distribution.md +29 -4
- package/docs/reference/environment.md +104 -8
- package/docs/reference/library-api.md +18 -4
- package/docs/reference/memory.md +1 -1
- package/docs/reference/models.md +17 -5
- package/docs/reference/server-api.md +181 -11
- package/docs/reference/server-config.md +232 -105
- package/package.json +2 -2
- package/src/audio/audiotoolbox.ts +112 -0
- package/src/audio/conformer.ts +8 -0
- package/src/audio/mic-capture.ts +98 -0
- package/src/audio/silero-vad.ts +379 -0
- package/src/audio/whisper-decode.ts +774 -0
- package/src/audio/whisper-fast.ts +732 -0
- package/src/audio/whisper-format.ts +73 -0
- package/src/audio/whisper-mel.ts +185 -0
- package/src/audio/whisper-timing.ts +274 -0
- package/src/audio/whisper-tokenizer.ts +239 -0
- package/src/audio/whisper-transcribe.ts +410 -0
- package/src/backends/mlx/autoregressive.ts +22 -1
- package/src/backends/mlx/batch-group.ts +111 -27
- package/src/backends/mlx/cache-layout.ts +3 -3
- package/src/backends/mlx/continuation-persistence.ts +5 -4
- package/src/backends/mlx/encoder-cache.ts +25 -0
- package/src/backends/mlx/fill-append.ts +21 -8
- package/src/backends/mlx/fill-group.ts +497 -0
- package/src/backends/mlx/gateway-binding.ts +56 -12
- package/src/backends/mlx/kv-maintenance.ts +30 -2
- package/src/backends/mlx/mixed-iteration.ts +62 -0
- package/src/backends/mlx/model-prompt.ts +46 -32
- package/src/backends/mlx/model-serving.ts +4 -3
- package/src/backends/mlx/paged-cache-codec.ts +27 -0
- package/src/backends/mlx/prefill-cohort.ts +15 -4
- package/src/backends/mlx/prefill-policy.ts +26 -0
- package/src/backends/mlx/prefill-rows.ts +57 -13
- package/src/backends/mlx/prompt-input.ts +29 -0
- package/src/backends/mlx/qwen-prompt-input.ts +40 -0
- package/src/backends/mlx/request-state-policy.ts +18 -5
- package/src/backends/mlx/rollback.ts +6 -3
- package/src/backends/mlx/serial-executor.ts +16 -7
- package/src/backends/mlx/speculative-group.ts +105 -26
- package/src/backends/mlx/speculative.ts +24 -7
- package/src/backends/mlx/target-layout-capability.ts +1 -1
- package/src/backends/mlx/transitioning-kv-rows.ts +8 -0
- package/src/cli.ts +322 -0
- package/src/contracts/execution.ts +10 -0
- package/src/contracts/object-cache.ts +8 -0
- package/src/contracts/pi-provider.ts +1 -1
- package/src/contracts/pi.ts +3 -1
- package/src/contracts/prefix-cache.ts +13 -3
- package/src/contracts/scheduling.ts +9 -0
- package/src/engine/execution-plan.ts +23 -12
- package/src/engine/execution-tasks.ts +44 -0
- package/src/engine/namespaced-cache.ts +13 -0
- package/src/engine/scheduler.ts +22 -2
- package/src/fill/fill-session.ts +14 -3
- package/src/generate.ts +28 -22
- package/src/grammar.ts +13 -0
- package/src/inference/prefill.ts +6 -0
- package/src/kv-scheme.ts +22 -0
- package/src/kv-store.ts +115 -79
- package/src/lab/README.md +5 -1
- package/src/lab/paged-kv/paged-attention.ts +117 -0
- package/src/lab/paged-kv/paged-kv-rows.ts +33 -5
- package/src/lab/paged-kv/paged-kv.ts +108 -42
- package/src/md-modules.d.ts +5 -0
- package/src/mlx/array.ts +37 -0
- package/src/mlx/ffi.ts +19 -0
- package/src/mlx/normalized-argmax.ts +116 -0
- package/src/mlx/ops.ts +97 -10
- package/src/model/batched-turboquant-kv.ts +6 -5
- package/src/model/delayed-quantized-kv.ts +3 -3
- package/src/model/delayed-rotating-quantized-kv.ts +1 -1
- package/src/model/delayed-turboquant-kv.ts +6 -4
- package/src/model/factory.ts +26 -0
- package/src/model/fingerprint.ts +12 -0
- package/src/model/full-prefill-row.ts +1 -1
- package/src/model/gemma4-base.ts +25 -6
- package/src/model/gemma4.ts +82 -2
- package/src/model/kv-attention-view.ts +4 -1
- package/src/model/profile.ts +29 -4
- package/src/model/quantized-append-attention.ts +31 -0
- package/src/model/qwen3-mrope.ts +7 -9
- package/src/model/qwen3_5.ts +99 -37
- package/src/model/support.ts +8 -0
- package/src/model/token-groups.ts +66 -0
- package/src/model/trellis-linear.ts +2 -2
- package/src/model/turboquant-codec.ts +6 -0
- package/src/model/whisper.ts +435 -0
- package/src/native/mic_capture.swift +92 -0
- package/src/native-pack.ts +6 -5
- package/src/pi-web.ts +9 -1
- package/src/prompt-cache.ts +265 -43
- package/src/runtime-config.ts +1 -5
- package/src/safetensors.ts +9 -2
- package/src/sampler.ts +5 -4
- package/src/serve/audio-routes.ts +323 -0
- package/src/serve/chat-request.ts +2 -0
- package/src/serve/chat-stage.ts +17 -12
- package/src/serve/completion-executor.ts +9 -1
- package/src/serve/discovery-routes.ts +17 -1
- package/src/serve/generated-token-history.ts +57 -0
- package/src/serve/generation-gateway.ts +52 -11
- package/src/serve/model-binding.ts +7 -1
- package/src/serve/preparation.ts +3 -3
- package/src/serve/prompt-response-trace.ts +14 -0
- package/src/serve/request-plan.ts +2 -0
- package/src/serve/request-prep.ts +38 -21
- package/src/serve/transcription-server.ts +64 -0
- package/src/serve/transcription-service.ts +502 -0
- package/src/server.ts +132 -164
- package/src/spec/assistant-rows.ts +1 -0
- package/src/spec/deepspec-rows.ts +1 -1
- package/src/spec/dflash-source.ts +1 -1
- package/src/spec/glm52-mtp-source.ts +1 -0
- package/src/spec/ngram-source.ts +27 -5
- package/src/spec/projected-draft-rows.ts +2 -2
- package/src/spec/qwen-mtp-rows.ts +11 -0
- package/src/spec/qwen-mtp-source.ts +3 -0
- package/src/spec/serve-loop.ts +5 -3
- package/src/spec/source.ts +14 -1
- package/src/spec/two-model-rows.ts +1 -0
- package/src/spec/two-model.ts +2 -2
- package/src/ssd-cache.ts +76 -17
- package/src/ssd-durability.ts +20 -22
- package/src/storage/host-buffer.ts +24 -0
- package/src/storage/kv-writer.ts +83 -0
- package/src/storage/kv-writer.worker.js +264 -0
- package/src/storage/retention-policy.ts +37 -0
- package/src/tiered-prompt-cache.ts +45 -0
- package/src/vision/embedder.ts +8 -0
- package/src/vision/prompt.ts +101 -65
- package/src/vision/qwen3vl-prompt.ts +101 -64
- package/src/vision/qwen3vl-tower.ts +11 -0
- package/src/vision/siglip.ts +8 -0
- package/src/web/app.html +12 -0
- package/src/web/app.js +160 -0
- package/src/web/src/chat.ts +4 -1
- package/src/web/src/composer.ts +3 -0
- package/src/web/src/voice.ts +141 -0
- package/src/web/sw.js +1 -1
- /package/docs/{planning → archive/planning}/release-notes-v0.4.0.md +0 -0
package/docs/design/kv-cache.md
CHANGED
|
@@ -3,7 +3,7 @@ status: landed
|
|
|
3
3
|
axis: ON
|
|
4
4
|
canonical-for: kv-cache
|
|
5
5
|
plan-anchor: "Phase 18 — Concurrent / batched serving (slots) + parallel load benchmark `[~]` (2026-06-13)"
|
|
6
|
-
last-verified: 2026-09-
|
|
6
|
+
last-verified: 2026-09-12
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
# KV cache — residency and layout
|
|
@@ -24,27 +24,31 @@ and open items.
|
|
|
24
24
|
Code: `src/model/gemma4-base.ts` (Cache interface + the K/V cache family),
|
|
25
25
|
`src/model/batched-*.ts`, `src/lab/paged-kv/paged-kv.ts`, `src/prompt-cache.ts`,
|
|
26
26
|
`src/kv-store.ts`, `src/ssd-cache.ts`, `src/ssd-durability.ts`,
|
|
27
|
+
`src/tiered-prompt-cache.ts`, `src/storage/kv-writer.ts`,
|
|
27
28
|
`src/kv-scheme.ts`, `src/generate.ts` (`maybeQuantizeKv` / `maybePageKv`),
|
|
28
29
|
`src/server.ts` (wiring, `/stats`, `POST /admin/cache/flush`),
|
|
29
30
|
`src/serve/generation-gateway.ts`, `src/serve/batch-scheduler.ts`.
|
|
30
31
|
|
|
31
32
|
## 1. Layering
|
|
32
33
|
|
|
33
|
-
|
|
34
|
-
|
|
34
|
+
Active generation owns mutable cache containers. Published checkpoints belong
|
|
35
|
+
to one logical cache, with RAM and SSD storage. On Apple Silicon the GPU and
|
|
36
|
+
CPU access unified memory; publication does not require a GPU-to-CPU copy.
|
|
37
|
+
Each checkpoint is keyed by the exact token prefix its state encodes:
|
|
35
38
|
|
|
36
39
|
| tier | owner | unit | key |
|
|
37
40
|
|---|---|---|---|
|
|
38
41
|
| live caches | one generation (serial lane or a batch row) | `Cache[]`, one per layer | — |
|
|
39
|
-
| RAM prompt cache | `
|
|
42
|
+
| RAM prompt cache | `TieredPromptCache` over `PromptCache` | whole prefix entry (tokens + `Cache[]`) | tokens + adapter ns |
|
|
40
43
|
| SSD cold tier | `SsdCacheStore` (`src/ssd-cache.ts`) over `src/kv-store.ts` files | one `.mlxkv` file per entry | tokens + ns + model/scheme/tokenizer fingerprint |
|
|
41
44
|
|
|
42
45
|
The prompt cache tiers over the SSD store *inside* `take()` (structural
|
|
43
46
|
`ColdTier` interface), so every consumer (serial lane, batch scheduler
|
|
44
47
|
joiners, `/admin/drain`) gets both tiers through the same `take()`/`put()`
|
|
45
|
-
calls.
|
|
46
|
-
|
|
47
|
-
|
|
48
|
+
calls. The cache queues persistence on publication or eviction. A dedicated
|
|
49
|
+
CPU worker writes evaluated immutable buffers while inference continues.
|
|
50
|
+
Persistence does not evict RAM; only cache residency policy does. RAM reuse
|
|
51
|
+
does not enqueue an extra write or wait for the queue. Explicit flushes await the queued work. The opt-in `--generation-checkpoint N` path transfers owned snapshots at coarse decode boundaries and queues atomic
|
|
48
52
|
persistence without awaiting disk in decode (section 5.7). Paged KV
|
|
49
53
|
(section 6) is a *layout* choice for the live tier and, in v1, bypasses the
|
|
50
54
|
other two.
|
|
@@ -217,9 +221,11 @@ changing the retained checkpoint.
|
|
|
217
221
|
|
|
218
222
|
SSD is an eventually consistent persistence layer. RAM reuse never writes
|
|
219
223
|
to SSD or waits for durability. Background writes retain immutable snapshot
|
|
220
|
-
views
|
|
221
|
-
wait for persistence. A pending or failed write does not prevent RAM reuse.
|
|
222
|
-
The
|
|
224
|
+
views while a CPU worker packs, hashes and writes them; explicit flush and
|
|
225
|
+
graceful shutdown wait for persistence. A pending or failed write does not prevent RAM reuse.
|
|
226
|
+
The byte target controls LRU eviction. With SSD enabled, an unwritten victim
|
|
227
|
+
stays resident until storage commits; the target is soft while writes are
|
|
228
|
+
pending or failing.
|
|
223
229
|
|
|
224
230
|
The native sharing gate checks logical checkpoint bytes after repeated decode
|
|
225
231
|
and after SSD restore. It passes on both development Macs; the M1 long-prefix
|
|
@@ -232,18 +238,23 @@ and explicit flush remains pending until the write completes.
|
|
|
232
238
|
longer usable prefix; then `restore(handle)` materializes it and the
|
|
233
239
|
divergent tail is trimmed. A cold entry that would need a trim but is
|
|
234
240
|
untrimmable (wrapped ring, SSM) is disposed loudly and the RAM candidate
|
|
235
|
-
(or a fresh prefill) serves.
|
|
241
|
+
(or a fresh prefill) serves. Tiered restore immediately publishes shared views
|
|
242
|
+
in RAM so another request can reuse them before the borrower finishes.
|
|
243
|
+
`hits`/`misses` count RAM candidacy only.
|
|
236
244
|
|
|
237
245
|
**`put(tokens, caches, ns, retain)` — supersession + eviction.** Exact
|
|
238
246
|
duplicates (same tokens) are replaced regardless of trimmability. Strict
|
|
239
247
|
prefix-ancestors in the same ns are superseded without spill — but only
|
|
240
248
|
when the *new* entry is fully trimmable; an untrimmable new entry (wrapped
|
|
241
249
|
ring) can only serve exact-length matches, so shorter ancestors (the
|
|
242
|
-
prompt-boundary snapshot) must survive.
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
250
|
+
prompt-boundary snapshot) must survive. `TieredPromptCache` selects the
|
|
251
|
+
least recently used RAM entry when over budget. If it lacks durable SSD
|
|
252
|
+
coverage, the cache queues a write and retains it until that write succeeds.
|
|
253
|
+
It does not evict a hotter entry merely because that entry is already on SSD.
|
|
254
|
+
An oversized checkpoint follows the same persist-before-demotion rule.
|
|
255
|
+
Without SSD, the RAM-only cache discards evicted or oversized entries.
|
|
256
|
+
`TieredPromptCache.put` also owns proactive persistence; `onPut` remains an
|
|
257
|
+
independent observer for generated-token history.
|
|
247
258
|
|
|
248
259
|
**Prompt-boundary snapshot.** Serial lane (`server.ts`) and batch
|
|
249
260
|
scheduler (`snapshotAt`) both clone-and-put a trim-free *prompt-only*
|
|
@@ -257,12 +268,11 @@ always an exact prefix of the next rendering.
|
|
|
257
268
|
**Namespaces.** `ns` = the adapter spec (`adapters.join("+")`, `""` = base).
|
|
258
269
|
KV computed under one adapter never seeds another's prefill.
|
|
259
270
|
|
|
260
|
-
**Bypasses.** Media
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
mutates the batched inners.
|
|
271
|
+
**Bypasses.** Media and optional paged-KV requests retain their documented
|
|
272
|
+
cache qualification rules. Shared speculative execution publishes target and
|
|
273
|
+
method state together where supported; method attachments preserve the exact
|
|
274
|
+
boundary needed for reuse. Merged batch rows are extracted before retirement
|
|
275
|
+
changes the batched state. The cache owns the extracted checkpoint.
|
|
266
276
|
|
|
267
277
|
**Idle demotion (`demoteIdle`).** Entries unused past a threshold spill to
|
|
268
278
|
the cold tier and free their GPU arrays; the prefix stays reachable via
|
|
@@ -287,17 +297,14 @@ full prefill (GEMV-vs-GEMM reduction order). Both tiers share it.
|
|
|
287
297
|
|
|
288
298
|
### 5.1 Decisions
|
|
289
299
|
|
|
290
|
-
- **D1 — Whole-prefix-entry
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
- **D2 — One store, two tiers.** The cold tier is bound *into*
|
|
299
|
-
`PromptCache` (section 4); restart persistence comes from a debounced
|
|
300
|
-
write-behind snapshot after each put, not only from spill-on-evict.
|
|
300
|
+
- **D1 — Whole-prefix-entry storage.** A checkpoint stores a complete
|
|
301
|
+
conversation boundary. This reuses the existing cache codecs and avoids
|
|
302
|
+
per-token storage bookkeeping, but duplicates shared attention history
|
|
303
|
+
between files. Block deduplication is a separate layout optimization to
|
|
304
|
+
measure against this baseline; recurrent state still needs exact boundaries.
|
|
305
|
+
- **D2 — One store, two tiers.** `TieredPromptCache` owns RAM residency and
|
|
306
|
+
the SSD backend. Proactive persistence after publication provides restart
|
|
307
|
+
survival without waiting for an eviction.
|
|
301
308
|
- **D3 — Own page-aligned format, not safetensors.** `src/kv-store.ts`
|
|
302
309
|
writes `MLXBUNKV2\n`-magic files with a streaming writer: ordinary KV
|
|
303
310
|
retains format v3; checkpoints with method companion state use v4.
|
|
@@ -377,9 +384,9 @@ ordering is unchanged (the server's `coldTier.restore` returns
|
|
|
377
384
|
`() => {}`). Comments in `prompt-cache.ts` that still say "zero-copy
|
|
378
385
|
mmap" describe the superseded mechanism.
|
|
379
386
|
|
|
380
|
-
The
|
|
381
|
-
|
|
382
|
-
|
|
387
|
+
The asynchronous writer reads evaluated native storage directly. Contiguous
|
|
388
|
+
tensors need no packing copy. Strided tensors are packed on the CPU, one
|
|
389
|
+
tensor at a time, to omit capacity padding without a GPU `contiguous` call.
|
|
383
390
|
|
|
384
391
|
### 5.3 `find()` returns partial matches, gated on trimmability
|
|
385
392
|
|
|
@@ -394,56 +401,52 @@ Store-side supersession mirrors `PromptCache.put`: exact duplicates
|
|
|
394
401
|
replaced; prefix-ancestors superseded only when the new entry is
|
|
395
402
|
trimmable.
|
|
396
403
|
|
|
397
|
-
### 5.4
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
Clones are disposed on every settle path (fulfil, reject, drop). The
|
|
444
|
-
snapshot timer never holds batch admission: while rows are active it
|
|
445
|
-
re-arms instead of grabbing the exclusive (entries are immutable; a late
|
|
446
|
-
snapshot is equally valid).
|
|
404
|
+
### 5.4 Background persistence and RAM residency
|
|
405
|
+
|
|
406
|
+
`TieredPromptCache` owns RAM residency, SSD writes and durable-coverage checks.
|
|
407
|
+
`put` materializes the checkpoint before publication. `saveKvCacheAsync`
|
|
408
|
+
uses the existing codecs to describe the file and obtains host-visible native
|
|
409
|
+
pointers, shapes and strides on the MLX owner thread. Its optional preparation
|
|
410
|
+
runner does not cover disk I/O. No MLX handles cross into the writer.
|
|
411
|
+
|
|
412
|
+
`KvWriter` embeds a dedicated Bun CPU worker in source and compiled builds.
|
|
413
|
+
The worker packs strided layouts, hashes tensor bytes, writes a temporary file,
|
|
414
|
+
fsyncs it and renames it atomically. It imports no MLX or scheduler code and
|
|
415
|
+
needs no generation lock. Contiguous tensors use direct buffer views; packing
|
|
416
|
+
scratch is reused across strided tensors. The cache owns the retained state,
|
|
417
|
+
and the queued write holds shared views through its completion. Those views
|
|
418
|
+
share immutable storage rather than copying the entire checkpoint.
|
|
419
|
+
|
|
420
|
+
The owner thread still prepares native views and updates the SSD metadata
|
|
421
|
+
index after completion. A background worker removes blocking payload I/O from
|
|
422
|
+
that thread; it does not make shared memory bandwidth or snapshot preparation
|
|
423
|
+
free. Measure those costs with concurrent decode before claiming a speedup.
|
|
424
|
+
|
|
425
|
+
Writing a copy does not remove RAM residency. Byte pressure chooses the actual
|
|
426
|
+
LRU entry. If its SSD copy is missing, it queues persistence and waits for the
|
|
427
|
+
completion callback to retry eviction. Failed writes leave the RAM entry
|
|
428
|
+
usable and dirty. Age-based eviction follows the same rule and runs without
|
|
429
|
+
acquiring the scheduler's generation lock. Neither policy refuses requests or
|
|
430
|
+
changes context, batch size or sampling settings.
|
|
431
|
+
|
|
432
|
+
`MLX_BUN_SSD_WRITEBEHIND=0` disables proactive persistence after publication;
|
|
433
|
+
eviction still queues persistence before removing RAM. Explicit flush and
|
|
434
|
+
shutdown await outstanding writes. A hard exit can lose uncommitted writes.
|
|
435
|
+
|
|
436
|
+
### 5.5 Persistence queue and capacity
|
|
437
|
+
|
|
438
|
+
Normal prefix persistence uses one serial `SpillQueue` with no independent
|
|
439
|
+
byte-drop policy. RAM ownership and pending write views share the checkpoint;
|
|
440
|
+
queue completion releases only the writer's reference. RAM cache capacity
|
|
441
|
+
and age policy decide demotion. They do not cancel an unwritten snapshot to
|
|
442
|
+
reduce the queue's accounting. While the oldest victim is writing, RAM can
|
|
443
|
+
exceed its target. A storage failure keeps that victim resident and retryable.
|
|
444
|
+
|
|
445
|
+
The optional interrupted-generation coordinator still uses the generic
|
|
446
|
+
bounded queue for supersedable intervals. `MLX_BUN_SSD_SPILL_QUEUE_GB`
|
|
447
|
+
(default 2 GiB) now controls that queue only. It does not limit retained prompt
|
|
448
|
+
history. SSD capacity is independent: no capacity eviction without an explicit
|
|
449
|
+
positive `--ssd-cache-max`.
|
|
447
450
|
|
|
448
451
|
### 5.6 Durability boundary (`src/ssd-durability.ts`)
|
|
449
452
|
|
|
@@ -454,17 +457,16 @@ the write — the server implied durability it did not have.
|
|
|
454
457
|
|
|
455
458
|
- `schedule(tokens, ns)` records a *dirty* key that includes the exact
|
|
456
459
|
token sequence (two same-ns, same-length conversations cannot cancel
|
|
457
|
-
each other) and arms
|
|
458
|
-
- An attempt checks SSD coverage
|
|
460
|
+
each other) and arms a next-tick attempt.
|
|
461
|
+
- An attempt checks committed SSD coverage before cloning.
|
|
459
462
|
Otherwise it snapshots the RAM entry (`findExact` + `cloneKvCaches`) and
|
|
460
|
-
enqueues it
|
|
461
|
-
|
|
463
|
+
enqueues it without consulting scheduler activity. A failed store leaves
|
|
464
|
+
the key dirty so it is retryable.
|
|
462
465
|
A snapshot that vanished from RAM is "stored" only if the SSD index
|
|
463
466
|
already covers the prefix (`hasDurablePrefix`), else "missing" and still
|
|
464
467
|
dirty. A second flush cannot clear that failure without durable coverage.
|
|
465
468
|
- `flush()` cancels timers, awaits in-flight attempts and `drain()`, then
|
|
466
|
-
forces each dirty record version once, one at a time
|
|
467
|
-
cannot drop a boundary snapshot while a large final snapshot is in flight).
|
|
469
|
+
forces each dirty record version once, one at a time.
|
|
468
470
|
A replacement scheduled during a write receives its own attempt. After
|
|
469
471
|
all writes settle, the coordinator checks missing ancestors against the
|
|
470
472
|
committed SSD index again: a later trimmable descendant may now cover a
|
|
@@ -513,7 +515,8 @@ recover sampler seed, history and sample index. The server binding qualifies
|
|
|
513
515
|
ordinary requests through supported row-cache layouts, including recurrent Qwen
|
|
514
516
|
and full-attention Llama/MiniCPM as well as Gemma4. It transfers owned row
|
|
515
517
|
snapshots to a bounded persistence coordinator without awaiting disk in decode.
|
|
516
|
-
|
|
518
|
+
Snapshot preparation runs on the owner thread; payload serialization and
|
|
519
|
+
writes use the CPU worker. Completion
|
|
517
520
|
invalidates reuse immediately and queues cleanup after any in-flight write;
|
|
518
521
|
new attempts cannot be erased by older cleanup. Cancellation retains eventual
|
|
519
522
|
persistence, and explicit flush reports failed or pending durability. A hard
|
|
@@ -525,8 +528,8 @@ compare interrupted/restored runs at the same batch geometry; arbitrary
|
|
|
525
528
|
cohort changes are not an exact numerical identity guarantee. The HTTP fixture
|
|
526
529
|
checks serial B1, shared B1 and shared B4 with identical seeded requests and
|
|
527
530
|
held arrivals, and inspects the generated checkpoint before restart.
|
|
528
|
-
|
|
529
|
-
|
|
531
|
+
The configured spill queue budget applies to continuation snapshots; normal
|
|
532
|
+
prefix persistence follows the RAM cache residency policy. Flush duration and pending/failed
|
|
530
533
|
statistics cover both queues.
|
|
531
534
|
|
|
532
535
|
Only the newest successful checkpoint for a request is retained. The old file
|
|
@@ -559,7 +562,7 @@ Method and drafter compatibility must be part of the namespace before a
|
|
|
559
562
|
producer publishes these records. Storage does not interpret draft state as
|
|
560
563
|
extra target-model layers.
|
|
561
564
|
|
|
562
|
-
RAM accounting, eviction, idle demotion and
|
|
565
|
+
RAM accounting, eviction, idle demotion and SSD persistence include
|
|
563
566
|
companion tensors. Checkpoints containing them require an exact prefix
|
|
564
567
|
boundary and preserve shorter fallback entries. Write-behind captures companion
|
|
565
568
|
views together with target-cache views under the existing exclusive boundary.
|
|
@@ -598,13 +601,45 @@ through repeated RAM reuse and SSD cache/provider restart. SSD allocation
|
|
|
598
601
|
padding is excluded from the live-state comparison. Generated-output acceptance
|
|
599
602
|
is described below; long-conversation timing remains R17 work.
|
|
600
603
|
|
|
601
|
-
The
|
|
604
|
+
The shared method publishes completed row state before
|
|
602
605
|
retirement through the same `PrefixCache.put` port. It records only inputs
|
|
603
606
|
retained by committed target/draft rounds; an unprocessed correction or bonus
|
|
604
607
|
token is absent from the key. The target row layout and draft provider supply
|
|
605
608
|
their snapshots independently. Failed and cancelled consumers publish no
|
|
606
609
|
generated checkpoint. Persistence continues through the existing queue.
|
|
607
610
|
|
|
611
|
+
Generated BPE IDs are not necessarily the canonical encoding of their decoded
|
|
612
|
+
text. `GeneratedTokenHistory` retains bounded text/ID provenance from published
|
|
613
|
+
prefixes and recovered SSD headers. Request preparation uses the original IDs
|
|
614
|
+
when the rendered conversation contains that exact text prefix, encodes only
|
|
615
|
+
the remaining suffix without another BOS, and checks that the combined IDs
|
|
616
|
+
decode to the complete rendered request. Edited history or an unsafe decoder
|
|
617
|
+
boundary keeps canonical tokenization. KV lookup still requires exact IDs and
|
|
618
|
+
the existing execution namespace; no state is relabeled under different IDs.
|
|
619
|
+
The template's primer length is measured canonically and applied to the actual
|
|
620
|
+
history-preserving token sequence. This can change input token counts, and
|
|
621
|
+
therefore continuation logits, relative to re-encoding all generated text.
|
|
622
|
+
Logit parity remains a contract for the same input IDs and execution shape.
|
|
623
|
+
The stable-boundary probe includes both text and tool replies followed by a
|
|
624
|
+
new user turn. Qwen can omit an empty thinking primer when rendering a prior
|
|
625
|
+
tool turn; a text-only probe retained three extra tokens in the small-model
|
|
626
|
+
HTTP fixture and prevented recurrent-cache reuse. The corrected probe keeps
|
|
627
|
+
the last shared prefix. It never rewrites existing KV under the changed text.
|
|
628
|
+
Republishing an identical token sequence refreshes its existing provenance
|
|
629
|
+
entry without decoding the whole conversation again. Equality covers every ID;
|
|
630
|
+
equal text from different IDs still replaces provenance. This avoids repeated
|
|
631
|
+
text conversion during request completion and preserves the retention limit.
|
|
632
|
+
|
|
633
|
+
Cache pressure maintenance uses 85% of the smaller of the recommended device
|
|
634
|
+
working set and an explicit allocator limit as a residency target. It includes
|
|
635
|
+
known weight bytes when file-backed weights have not appeared in MLX active
|
|
636
|
+
allocation. `PrefixCache.reclaim` asks the cache to demote LRU donors. With SSD,
|
|
637
|
+
only durable donors can leave RAM; pending writes remain queued. A selected
|
|
638
|
+
request retains its own views. Shared buffer ownership is not counted twice as
|
|
639
|
+
RAM plus queued data. Lookup, prefill boundaries and periodic decode maintenance
|
|
640
|
+
call this interface; decode checks every 256 steps. It does not constrain
|
|
641
|
+
request admission, context, sampling or batch size.
|
|
642
|
+
|
|
608
643
|
M1 and M4 native bf16, affine KV4 and fused TurboQuant K8/V3 tests cover four active
|
|
609
644
|
rows, early retirement, exact sampled-ID coverage, unchanged retained bytes
|
|
610
645
|
after sibling and follow-up work, shorter-prefix selection after an edit, and
|
|
@@ -662,120 +697,254 @@ the model's prototype cache before opening the tensor mmap.
|
|
|
662
697
|
ever lands.
|
|
663
698
|
- Block-granular dedup for multi-user shared prefixes stays out of scope
|
|
664
699
|
(D1); revisit only with the paged follow-ups in 6.4.
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
vLLM
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
700
|
+
|
|
701
|
+
### 5.10 Comparison with other serving caches
|
|
702
|
+
|
|
703
|
+
Reviewed 2026-09-12 against published designs, not private provider internals.
|
|
704
|
+
|
|
705
|
+
| System | Algorithm and ownership | Application here |
|
|
706
|
+
| --- | --- | --- |
|
|
707
|
+
| [Mooncake / Kimi](https://github.com/kvcache-ai/Mooncake), [SSD offload](https://kvcache-ai.github.io/Mooncake/design/store/ssd-offload.html) | A cache service owns RAM/SSD placement. Background offload records a disk replica on successful write; the same lookup can restore from SSD. | One cache API with independent storage work. No application-managed demotion. |
|
|
708
|
+
| [SGLang HiCache](https://docs.sglang.io/docs/advanced_features/hicache_design) | A radix tree shares token spans and tracks memory placement. Supports proactive, selective and eviction-triggered writes, plus storage prefetch. Decode output can populate storage. | Keep output-produced checkpoints. Evaluate block sharing and prefetch after the persistence correction. |
|
|
709
|
+
| [LMCache MP](https://docs.lmcache.ai/mp/) | Independent store, eviction and prefetch controllers. Watermark-triggered RAM eviction uses LRU; asynchronous storage adapters handle lower tiers. | Separate write completion from residency policy. Keep the storage interface independent of inference. |
|
|
710
|
+
| [vLLM prefix caching](https://docs.vllm.ai/en/latest/design/prefix_caching/) | Token blocks use hashes including their preceding prefix. Unused blocks are evicted by recency. | Shared prefix blocks could reduce whole-entry duplication. |
|
|
711
|
+
| [Anthropic prompt caching](https://platform.claude.com/docs/en/build-with-claude/prompt-caching) | Public API documents exact-prefix reuse and expiration. | The same avoided-prefill benefit; this documentation does not establish its internal storage policy. |
|
|
712
|
+
|
|
713
|
+
The baseline storage unit is a complete checkpoint. Repeated conversation
|
|
714
|
+
snapshots duplicate attention history on SSD. Block deduplication in §5.11
|
|
715
|
+
preserves the existing state-kind contract: Qwen recurrent state and method
|
|
716
|
+
attachments cannot be trimmed to arbitrary earlier tokens. They need valid
|
|
717
|
+
checkpoint boundaries alongside any shared attention blocks. RAM LRU is the
|
|
718
|
+
initial policy; §5.11 records the measured frequency-weighting, prefetch and
|
|
719
|
+
block-layout alternatives.
|
|
720
|
+
|
|
721
|
+
### 5.11 Cache expansion program
|
|
722
|
+
|
|
723
|
+
Authorized scope: implement and measure all five opportunities from §5.10.
|
|
724
|
+
Baseline is `c953f2f`, with an independent CPU writer and one RAM/SSD cache.
|
|
725
|
+
Preserve existing cache files and explicit serial support. Storage format,
|
|
726
|
+
residency policy, transfer work and attention kernels remain independently
|
|
727
|
+
selectable for paired comparisons; select defaults from measured results.
|
|
728
|
+
|
|
729
|
+
| Step | Implementation | Measurement and completion |
|
|
730
|
+
| --- | --- | --- |
|
|
731
|
+
| C1 | Immutable content-addressed blocks shared by checkpoint manifests. Attention token spans share storage; recurrent and method state preserve exact boundaries. | Same restored bytes/continuations, atomic publication and shared-block lifetime; bytes written and retained across growing/divergent conversations. |
|
|
732
|
+
| C2 | Cache-owned asynchronous reads and preparation-time prefetch with promotion into the existing RAM tier. | Same selected prefix and continuation; restore/TTFT, event-loop delay and concurrent decode against synchronous restore. |
|
|
733
|
+
| C3 | Pluggable retention policy, with LRU baseline and an alternative using reuse frequency, state size and saved processing cost. | Replay identical access traces; compare misses, avoided prefill, eviction/reload traffic and peak residency. |
|
|
734
|
+
| C4 | Segmented transfers and bounded packing scratch, preserving tensor layout contracts. | Copied bytes, temporary memory, write/read time and inference overlap on the same payload. |
|
|
735
|
+
| C5 | Direct paged-attention Metal execution through cache/attention interfaces, including shared row layouts and quantized representations. | Same-B numerical controls, B1/B>1 prefill/decode, gather/padding allocations and complete-request timing. |
|
|
736
|
+
|
|
737
|
+
Implementation is in `storage/kv-writer.worker.js` (CPU transfers), `kv-store`
|
|
738
|
+
(codec reconstruction), `PromptCache.prefetch` (request preparation),
|
|
739
|
+
`storage/retention-policy` (RAM choices), and the paged attention/layout modules.
|
|
740
|
+
Format 5 manifests reference SHA-256 content blocks. Block boundaries reset per
|
|
741
|
+
leading row so growing token axes preserve earlier head blocks. Blocks are
|
|
742
|
+
committed before their manifest; garbage collection is serialized with writes
|
|
743
|
+
and removes only content no remaining manifest references. Explicit SSD caps
|
|
744
|
+
account unique committed blocks rather than charging every checkpoint for a
|
|
745
|
+
shared copy. Existing format 3/4 files remain readable.
|
|
746
|
+
|
|
747
|
+
Restore reads use a separate worker queue and page-aligned host allocations.
|
|
748
|
+
After reads finish, the owner thread transfers allocation ownership to MLX
|
|
749
|
+
through a native destructor; no JavaScript callback runs on GPU completion.
|
|
750
|
+
Cache codecs are shared with synchronous restore. Plain KV still uses its
|
|
751
|
+
established capacity-growth operation; the async transport does not change
|
|
752
|
+
cached values or recurrent/checkpoint boundaries.
|
|
753
|
+
|
|
754
|
+
Use the saved Kanban token histories and seeded synthetic branches to separate
|
|
755
|
+
cache-policy effects from model-generated changes in task length. Existing
|
|
756
|
+
native oracles remain authoritative. Each implementation records paired M4
|
|
757
|
+
measurements and applicable M1 correctness; a slower arm stays explicit rather
|
|
758
|
+
than being promoted on architectural grounds alone. Raw reports stay under
|
|
759
|
+
`reports/`; published measurements belong in benchmarks.md.
|
|
760
|
+
|
|
761
|
+
The bounded C1–C5 implementation and measurements are complete. Shared blocks
|
|
762
|
+
trade write/read latency for space; segmentation removes packing copies for
|
|
763
|
+
contiguous spans. Asynchronous transport overlaps ongoing decode, while a lone
|
|
764
|
+
restart request still pays restore completion. Cost-size retention loses on the
|
|
765
|
+
saved Kanban boundary replay; direct paged attention has shape-dependent wins
|
|
766
|
+
and regressions. The measured selection keeps whole files and LRU, enables
|
|
767
|
+
asynchronous preparation, and leaves direct attention and block storage explicit.
|
|
768
|
+
[Measurements and reproduction](../reference/benchmarks.md#cache-expansion-c1c5-storage-restore-retention-and-paged-attention)
|
|
769
|
+
include the negative results and limits. Full long-task retention acceptance
|
|
770
|
+
and the paging extensions below remain separate work.
|
|
771
|
+
|
|
772
|
+
### 5.12 Session checkpoint index
|
|
773
|
+
|
|
774
|
+
The cache owns a second index: application session ID plus numerical namespace
|
|
775
|
+
maps to its latest immutable checkpoint. Numerical/content identity remains
|
|
776
|
+
independent, so session and anonymous requests can share the same state.
|
|
777
|
+
The request carries a session hint through the existing cache port's lookup,
|
|
778
|
+
preparation and publication calls. Ordinary, speculative and paged methods
|
|
779
|
+
forward the hint; scheduling does not own the index or residency decisions.
|
|
780
|
+
|
|
781
|
+
A matching resident checkpoint bypasses candidate scans. For an evicted known
|
|
782
|
+
checkpoint, the SSD exact index selects its manifest before asynchronous
|
|
783
|
+
preparation adopts state through the existing codecs. Hashes accelerate SSD
|
|
784
|
+
selection; the selected token sequence still establishes identity. A missing
|
|
785
|
+
or incompatible session checkpoint uses ordinary prefix matching, allowing
|
|
786
|
+
history edits, branches and retries without rejecting the request.
|
|
787
|
+
|
|
788
|
+
New publications advance the session's reference. Explicit sessions also retain
|
|
789
|
+
ordinary generated checkpoints below the anonymous publication threshold.
|
|
790
|
+
RAM eviction first considers
|
|
791
|
+
entries with no session references, then applies the configured policy to
|
|
792
|
+
referenced entries when needed. This is a preference, not an unlimited memory
|
|
793
|
+
pin. Request-scoped preparation interests retain their existing short-lived
|
|
794
|
+
ownership. Closing a session removes its references without deleting cached
|
|
795
|
+
state; clearing the cache removes the session index. Session metadata is
|
|
796
|
+
process-local, and restart rebuilds associations through ordinary SSD hits.
|
|
797
|
+
|
|
798
|
+
The current API still accepts full histories. This change removes candidate
|
|
799
|
+
search and supplies retention information; it does not claim delta-only input,
|
|
800
|
+
zero tokenization, or automatic reconstruction from a session label. The Pi
|
|
801
|
+
provider passes the session identity its application already owns.
|
|
802
|
+
The index, native retention and fixed-input M4 HTTP comparisons are complete.
|
|
803
|
+
Lookup and reload work decrease; warmed model throughput is effectively
|
|
804
|
+
unchanged. The full Kanban task now completes with all follow-up session hits
|
|
805
|
+
and a durable final flush; independent app acceptance retains two defects.
|
|
806
|
+
[Measurements](../reference/benchmarks.md#session-checkpoint-index-c6) preserve
|
|
807
|
+
both the benefits and the limits. Existing idle-demotion settings remain in
|
|
808
|
+
effect; session preference changes budget/pressure victim selection.
|
|
809
|
+
|
|
810
|
+
|
|
811
|
+
### 5.13 Exact tensor objects in the shared cache
|
|
812
|
+
|
|
813
|
+
`ObjectCache<Value>` exposes exact-key lookup and ownership transfer separately
|
|
814
|
+
from `PrefixCache` matching. `PromptCache.objects` uses the same resident records,
|
|
815
|
+
byte accounting, retention policy, pressure callback and SSD durability queue.
|
|
816
|
+
An object has an empty conversation history, no target KV layers and a private
|
|
817
|
+
namespace containing its full key. Its versioned tensor attachment owns the
|
|
818
|
+
payload. It never supplies a language-model prefix. No synthetic language token
|
|
819
|
+
IDs or second RAM budget are introduced.
|
|
820
|
+
|
|
821
|
+
RAM reads lend immutable native views. Evicting the resident entry cannot
|
|
822
|
+
invalidate a request's borrowed tensors. A cold read uses the existing async
|
|
823
|
+
serializer with an empty target-layer schema, outside the execution queue;
|
|
824
|
+
publication joins the same RAM policy. Cache clear invalidates pending restore
|
|
825
|
+
publication. A newer resident publication wins over an older in-flight restore.
|
|
826
|
+
The durability coordinator includes attachment-only objects; RAM eviction waits
|
|
827
|
+
for the shared writer to finish as it does for conversation checkpoints.
|
|
828
|
+
|
|
829
|
+
The Qwen encoder adapter hashes exact preprocessed pixel bytes, grid dimensions
|
|
830
|
+
and its schema revision. A SHA-256 of the actual loaded encoder tensors supplies weight identity,
|
|
831
|
+
computed once under native preparation. The containing SSD store also supplies
|
|
832
|
+
configuration, tokenizer and native numerical identity. Different image bytes or grids miss;
|
|
833
|
+
repeated media can reuse the tensor across request histories. Prompt rendering,
|
|
834
|
+
video timestamps, mRoPE positions, sampling and scheduling remain independent.
|
|
835
|
+
The producer retains its own result while storage takes a separate native view.
|
|
836
|
+
The object lookup finishes before the prompt builder enters the native queue.
|
|
837
|
+
Preprocessing and video extraction are not cached by this change.
|
|
838
|
+
|
|
839
|
+
Gemma's encoder-free and SigLIP vision towers and its audio tower use the same
|
|
840
|
+
`EncoderCache` adapter. Each tower hashes its immutable weight names, shapes,
|
|
841
|
+
dtypes, bytes and computation settings once. Image/WAV content hashes and a
|
|
842
|
+
producer revision identify individual objects. Vision retains its existing
|
|
843
|
+
pre-divided features; audio retains raw float32 features, then each prompt casts
|
|
844
|
+
them to its embedding dtype before dividing by the embedding scale. That order
|
|
845
|
+
is unchanged. CPU image preprocessing and audio mel extraction precede the
|
|
846
|
+
native execution lease, as do RAM/SSD lookups. The producer never caches chat
|
|
847
|
+
tokens, bidirectional masks or positions. Custom vision encoders without an
|
|
848
|
+
immutable `cacheIdentity` continue to encode normally.
|
|
849
|
+
|
|
850
|
+
[vLLM's encoder cache](https://docs.vllm.ai/en/v0.18.0/api/vllm/v1/core/encoder_cache_manager/)
|
|
851
|
+
also identifies individual media items by hash and retains encoder outputs
|
|
852
|
+
across requests. Here the existing cache owns both RAM residency and queued
|
|
853
|
+
SSD persistence on unified memory. The bounded Qwen screen and integrated
|
|
854
|
+
acceptance pass on M4; both-machine native/HTTP checks and fresh-process SSD
|
|
855
|
+
reuse preserve their recorded outputs. [Measurements](../reference/benchmarks.md#qwen-encoder-reuse-through-shared-ram-and-ssd-storage-2026-09-13)
|
|
856
|
+
include the one-time weight-fingerprint cost. Gemma prepared inputs and mixed
|
|
857
|
+
RAM/SSD HTTP reuse pass both-machine identity checks. Its M4 ABBA preserves all
|
|
858
|
+
168 paired responses/usage and improves every measured workload; first-use
|
|
859
|
+
cost and changed image overlap are recorded in the
|
|
860
|
+
[Gemma measurements](../reference/benchmarks.md#gemma-encoder-reuse-through-shared-ram-and-ssd-storage-2026-09-13).
|
|
861
|
+
Prepared media conversation state uses the same cache, as described below.
|
|
862
|
+
|
|
863
|
+
|
|
864
|
+
### 5.14 Prepared media prefix identity
|
|
865
|
+
|
|
866
|
+
The Gemma producer can supply a versioned `prefixIdentity` containing every
|
|
867
|
+
media item's exact input/encoder identity, its causal policy, and the rendered
|
|
868
|
+
token prefix through the last media span. The ordinary cache namespace combines
|
|
869
|
+
this identity with the adapter namespace. Consequently, matching histories
|
|
870
|
+
include identical media and preceding tokens; a changed media set or a change
|
|
871
|
+
from image-only bidirectional attention to mixed causal input cannot collide.
|
|
872
|
+
Appending later text leaves the identity stable.
|
|
873
|
+
|
|
874
|
+
By default, the shared Gemma binding uses ordinary
|
|
875
|
+
RAM/SSD lookup, generated snapshots, prefetch and persistence. The prepared-input
|
|
876
|
+
interface receives the cache's starting position. An uncached input performs
|
|
877
|
+
its atomic media prefill; a restored prefix resumes text after all media through
|
|
878
|
+
the model's ordinary forward method. Scheduling and sampling are unchanged.
|
|
879
|
+
Qwen supplies the same opaque identity with prepared image/video hashes,
|
|
880
|
+
rendered timestamps and all mRoPE positions through its final media span. Its
|
|
881
|
+
resumed text forward uses the current request positions and preserves the
|
|
882
|
+
recurrent state. Recurrent layers still require an exact retained prefix;
|
|
883
|
+
explicit serial prefix reuse remains unqualified. `MLX_BUN_MEDIA_PREFIX_CACHE=0`
|
|
884
|
+
selects encoder-only reuse for comparisons.
|
|
885
|
+
|
|
886
|
+
The existing minimum reusable offset still identifies a precision transition.
|
|
887
|
+
If delayed quantization occurs after an atomic media prefill, its cached state
|
|
888
|
+
can serve a later conversation turn but cannot rewind before that conversion.
|
|
889
|
+
A shorter request recomputes normally. Start-zero affine/TurboQuant and bf16
|
|
890
|
+
retain their applicable prefix-trimming behavior. This is cache selection,
|
|
891
|
+
not request admission or a memory refusal.
|
|
892
|
+
|
|
893
|
+
|
|
894
|
+
## 6. Optional paged KV
|
|
895
|
+
|
|
896
|
+
`PagedKVCache` stores full-attention planes in per-request block pools.
|
|
897
|
+
`PagedKvRows` composes those pools through the existing row-state interface.
|
|
898
|
+
Scheduling sees request state and attention ports, not block tables or codecs.
|
|
899
|
+
Flags, defaults and supported combinations are documented in
|
|
900
|
+
[server-config.md](../reference/server-config.md).
|
|
901
|
+
|
|
902
|
+
### 6.1 Storage and ownership
|
|
903
|
+
|
|
904
|
+
Pools hold bf16 planes or affine packed/scales/biases planes. New tokens alone
|
|
905
|
+
are quantized and written into their destination blocks. Logical block tables
|
|
906
|
+
map sequence positions to physical slots. Trimming returns unused tail blocks;
|
|
907
|
+
restored prefixes can grow beyond their original request's capacity.
|
|
908
|
+
|
|
909
|
+
Published attention views, cache borrowers and persistence jobs own independent
|
|
910
|
+
MLX handles. A subsequent functional append cannot overwrite their buffers.
|
|
911
|
+
The backend's `paged-cache-codec` preserves block tables, encoded planes and
|
|
912
|
+
layout metadata through the common RAM/SSD cache. `namespacedCache` separates
|
|
913
|
+
paged numerical settings from ordinary prefixes. Neither persistence nor a
|
|
914
|
+
successful write removes a RAM donor.
|
|
915
|
+
|
|
916
|
+
The current pools are separate per request. Their snapshots share immutable
|
|
917
|
+
MLX storage, but divergent writes can detach a whole pool. Shared physical
|
|
918
|
+
arenas with per-block copy-on-write remain a further optimization.
|
|
919
|
+
|
|
920
|
+
### 6.2 Attention
|
|
921
|
+
|
|
922
|
+
The established attention arm gathers blocks and uses stock SDPA. Its bf16
|
|
923
|
+
storage-layout contract remains bit-exact against plain KVCache.
|
|
924
|
+
|
|
925
|
+
The optional direct Metal arm reads block tables for short causal queries.
|
|
926
|
+
Each SIMD group processes one query/head/sequence partition, retaining a
|
|
927
|
+
softmax maximum, denominator and partial value vector. A second kernel merges
|
|
928
|
+
those partials. Affine variants unpack KV4/KV8 values in the read operation;
|
|
929
|
+
no full-history dequantization or contiguous KV gather is materialized.
|
|
930
|
+
Shared rows retain their own lengths, so direct attention does not pad KV to
|
|
931
|
+
the longest row. Long prefill and explicit masks use the established SDPA
|
|
932
|
+
shape dispatch through the same attention view.
|
|
933
|
+
|
|
934
|
+
This changes reduction order. Direct attention is Lab, with numerical tolerance
|
|
935
|
+
checks against the same encoded values; it is not a bit-exact mlx-lm claim.
|
|
936
|
+
Tests cover unequal K/V dimensions, grouped heads, partial blocks, multiple
|
|
937
|
+
queries, row retirement, immutable views and restored growth. Performance
|
|
938
|
+
measurements retain both short-context losses and long-context wins in
|
|
939
|
+
[benchmarks.md](../reference/benchmarks.md).
|
|
940
|
+
|
|
941
|
+
### 6.3 Remaining extensions
|
|
942
|
+
|
|
943
|
+
TurboQuant page encoding, paged speculative transactions, media/adapter paging
|
|
944
|
+
and shared physical arenas are outside the implemented bf16/affine page layout.
|
|
945
|
+
Existing non-paged TurboQuant and speculative RAM/SSD state remain supported
|
|
946
|
+
through their own codecs and attention interfaces. Extending a page codec or
|
|
947
|
+
kernel does not require a second scheduler.
|
|
779
948
|
|
|
780
949
|
## History
|
|
781
950
|
|