ManniX-ITA commited on
Commit
4a740a4
·
verified ·
1 Parent(s): 2b8ba64

docs: PolyKV cross-arch evals (#710) + bug-2203 hybrid gotcha + pool-prefill token-array recipe — sync from git 9969839543

Browse files
Files changed (1) hide show
  1. USAGE.md +62 -3
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 — ~79 patches, numbered
71
- `0006`–`0133`). 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,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) |