docs: PolyKV cross-arch evals (#710) + bug-2203 hybrid gotcha + pool-prefill token-array recipe — sync from git 9969839543
Browse files
USAGE.md
CHANGED
|
@@ -67,8 +67,8 @@ trusting aggressive KV tiers on anything else.
|
|
| 67 |
|
| 68 |
opencoti-llamafile is **upstream llamafile 0.10.3 plus an additive
|
| 69 |
patch series** (`patches/` in the HF repo,
|
| 70 |
-
`vendors/patches/llamafile/` in the git repo — ~
|
| 71 |
-
`0006`–`
|
| 72 |
|
| 73 |
1. **Off means off.** Every opencoti feature is opt-in behind a flag,
|
| 74 |
env var, or per-request JSON field. With no opencoti flags set, the
|
|
@@ -293,6 +293,65 @@ slot never decodes, so its prefix never slides — every re-attach is
|
|
| 293 |
free. Note the capacity row is about per-session speed, not just
|
| 294 |
aggregate: 12 pooled sessions each decode faster than 8 private ones.
|
| 295 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 296 |
Sizing note: the pooled prefix pins P cells in **both** iSWA caches
|
| 297 |
(global + SWA) for the pool's lifetime. Budget `-c` for pool prefix
|
| 298 |
+ N session windows + generation headroom, or long-running sessions
|
|
@@ -558,7 +617,7 @@ currently appear only in the server log.
|
|
| 558 |
| **quant-KV** | — | K-only honors | ✅ (tiles dequant-on-lift) | ✅ | ✅ | ✅ (the win case) | ✅ (turbo+MTP is the top decode combo) | ✅ |
|
| 559 |
| **auto-tier** | | — | ✅ (it *manages* spill) | ✅ | ✅ (probes in DCA state) | ✅ | ✅ | ✅ (sizing is eff-plan-aware) |
|
| 560 |
| **rolling-KV** | | | — | ✅ | ✅ | ✅ | ✅ | ✅ (validated: window spill × RYS on hybrid) |
|
| 561 |
-
| **PolyKV pool (SharedKVPool)** | | | | — | ✅ | ✅ | ✅ | ✅ (validated: 2-agent share gate × `--repeat-layers` on A4B) |
|
| 562 |
| **DCA** | | | | | — | ✅ | ✅ (dual-ctx) | ✅ (eff→src mapped) |
|
| 563 |
| **sparse-attn** | | | | | | — | ✅ | ✅ |
|
| 564 |
| **MTP** | | | | | | | — | ✅ (draft runs base stack; target keeps RYS) |
|
|
|
|
| 67 |
|
| 68 |
opencoti-llamafile is **upstream llamafile 0.10.3 plus an additive
|
| 69 |
patch series** (`patches/` in the HF repo,
|
| 70 |
+
`vendors/patches/llamafile/` in the git repo — ~81 patches, numbered
|
| 71 |
+
`0006`–`0135`). Three properties are contractual:
|
| 72 |
|
| 73 |
1. **Off means off.** Every opencoti feature is opt-in behind a flag,
|
| 74 |
env var, or per-request JSON field. With no opencoti flags set, the
|
|
|
|
| 293 |
free. Note the capacity row is about per-session speed, not just
|
| 294 |
aggregate: 12 pooled sessions each decode faster than 8 private ones.
|
| 295 |
|
| 296 |
+
**Cross-architecture results** (RTX 6000 96GB, P≈5073, GEN=256,
|
| 297 |
+
A/B/A naive/shared/naive): the pool is validated on all three
|
| 298 |
+
attention architectures, and the memory axis is
|
| 299 |
+
architecture-independent (~6.8–6.9× at N=8 — it counts cells, not
|
| 300 |
+
attention math).
|
| 301 |
+
|
| 302 |
+
| axis | Qwen2.5-14B-1M Q8_0 (pure full attention) | Qwen3.6-27B-Omnimerge-v4 Q4_K_M (hybrid GDN + NextN MTP n=3) |
|
| 303 |
+
|---|---|---|
|
| 304 |
+
| KV cells (N=8) | **6.78×** (~406 vs ~9 agents on a fixed 8192-cell buffer) | **6.89×** (~304 vs ~9 agents) |
|
| 305 |
+
| batched decode N=8 | 358 → 403 tok/s (**+12.5%**) | 106 → 123 tok/s (**+15.6%**) |
|
| 306 |
+
| batched decode N=24 | 412 → 742 tok/s (**+80%**; 17.2 → 30.9 tok/s per session) | 90 → 125 tok/s (**+39.7%**; 3.7 → 5.2 per session) |
|
| 307 |
+
| shared-only sweep N=32/48/64 | 813 / 871 / 865 tok/s (plateau ~870 near N=48) | 126 / 124 / 124 tok/s (saturates by N≈24–32) |
|
| 308 |
+
|
| 309 |
+
The shared-vs-naive decode gain **grows with N** on both. On
|
| 310 |
+
hybrid/recurrent models (delta-net, mamba) the *absolute* aggregate
|
| 311 |
+
saturates much earlier than on pure attention — the recurrent layers
|
| 312 |
+
batch worse — so there the pool buys **concurrency capacity and
|
| 313 |
+
memory**, not aggregate throughput past N≈24.
|
| 314 |
+
|
| 315 |
+
**How to prefill the pool — use the common-prefix token array, not
|
| 316 |
+
the document text.** Tokenizers merge across the document/suffix
|
| 317 |
+
boundary (on the Qwen tokenizer the last prefix token fuses with the
|
| 318 |
+
suffix start), so `tok(DOC)` can be one token longer than the common
|
| 319 |
+
prefix the agents actually share — and a pool that is even one token
|
| 320 |
+
longer than `shared_prefix_n_tokens` cannot be shared exactly. The
|
| 321 |
+
correct client sequence:
|
| 322 |
+
|
| 323 |
+
```jsonc
|
| 324 |
+
// 1. tokenize the FULL agent prompts and compute
|
| 325 |
+
// P = min over agents of commonPrefixLen(tok(DOC), tok(DOC+suffix_i))
|
| 326 |
+
// 2. prefill the pool slot with the token array itself (llama.cpp
|
| 327 |
+
// /completion accepts token arrays) — pool state == P on ANY tokenizer:
|
| 328 |
+
{ "prompt": [/* tok(DOC+suffix_1)[:P] */], "id_slot": 0,
|
| 329 |
+
"cache_prompt": true, "n_predict": 1 }
|
| 330 |
+
// 3. agents attach with prompts STRICTLY longer than P:
|
| 331 |
+
{ "prompt": "<DOC + private suffix>", "id_slot": 1,
|
| 332 |
+
"shared_pool_slot": 0, "shared_prefix_n_tokens": P, … }
|
| 333 |
+
```
|
| 334 |
+
|
| 335 |
+
On attention-only models a text prefill happens to work (the ranged
|
| 336 |
+
cell copy tolerates the extra token); on hybrid/recurrent targets it
|
| 337 |
+
silently disables every share — see the gotcha below. The token-array
|
| 338 |
+
prefill is correct everywhere.
|
| 339 |
+
|
| 340 |
+
**Hybrid/recurrent gotcha (GDN / mamba / `qwen35moe`-class models).**
|
| 341 |
+
A recurrent cache has one rolling state per sequence, not per-position
|
| 342 |
+
cells, so a pool share is only possible as an **exact full-state**
|
| 343 |
+
share. The server enforces this: the share engages only when
|
| 344 |
+
`shared_prefix_n_tokens == pool state length` *and* the request prompt
|
| 345 |
+
is strictly longer than the shared prefix; anything else logs
|
| 346 |
+
`poly-kv-pool: hybrid/recurrent target needs exact full-state share …
|
| 347 |
+
skipping share, full reprocess (bug-2203)` and falls back to a full
|
| 348 |
+
(correct, slower) reprocess. If you see zero speedup on a hybrid
|
| 349 |
+
model — or mass HTTP 500s at high N because N unshared full prompt
|
| 350 |
+
copies overflow the unified KV — grep the server log for that WARN:
|
| 351 |
+
it almost always means the pool was prefilled with text instead of
|
| 352 |
+
the token array. (Older builds crashed outright here —
|
| 353 |
+
`failed to remove sequence N with p0=…` — fixed by patch `0135`.)
|
| 354 |
+
|
| 355 |
Sizing note: the pooled prefix pins P cells in **both** iSWA caches
|
| 356 |
(global + SWA) for the pool's lifetime. Budget `-c` for pool prefix
|
| 357 |
+ N session windows + generation headroom, or long-running sessions
|
|
|
|
| 617 |
| **quant-KV** | — | K-only honors | ✅ (tiles dequant-on-lift) | ✅ | ✅ | ✅ (the win case) | ✅ (turbo+MTP is the top decode combo) | ✅ |
|
| 618 |
| **auto-tier** | | — | ✅ (it *manages* spill) | ✅ | ✅ (probes in DCA state) | ✅ | ✅ | ✅ (sizing is eff-plan-aware) |
|
| 619 |
| **rolling-KV** | | | — | ✅ | ✅ | ✅ | ✅ | ✅ (validated: window spill × RYS on hybrid) |
|
| 620 |
+
| **PolyKV pool (SharedKVPool)** | | | | — | ✅ | ✅ | ✅ | ✅ (validated: 2-agent share gate × `--repeat-layers` on A4B; hybrid-GDN omnimerge × NextN MTP full gate, patch `0135`) |
|
| 621 |
| **DCA** | | | | | — | ✅ | ✅ (dual-ctx) | ✅ (eff→src mapped) |
|
| 622 |
| **sparse-attn** | | | | | | — | ✅ | ✅ |
|
| 623 |
| **MTP** | | | | | | | — | ✅ (draft runs base stack; target keeps RYS) |
|