Repository navigation
poly: opt-in ggml Q8_0 KV cache for strata-poly (--kv q8_0, qwen35moe models) - #23
Merged
Merged
Conversation
Collaborator
|
Thanks, this is clean work. I ran it on the 5090 and the 4070 laptop:
Happy to merge it. One thing first: could you squash the branch into a single commit on top of main? Right now it carries two merge commits (kv_q8_base into main, origin/main into feature/kv_q8), and I keep main's history to one commit per change. After that it goes in as is. |
vpkoskinen
force-pushed
the
feature/kv_q8
branch
from
October 15, 2026 19:03
4d47e27 to
98d1947
Compare
Contributor
Author
|
Squashed down the commits, should merge cleanly. |
Collaborator
|
Thanks, that was quick. It's merged on my side and goes out with the next release, which I'm holding until the MiniCPM-V PR is ready so both ship together. The PR will show as merged once I push. |
Collaborator
|
Merged, and it's out in 0.1.44.1-poly.5. Thanks again, this one was easy to review. |
jpb18
pushed a commit
to jpb18/PolyStrata-HIP
that referenced
this pull request
Oct 18, 2026
…), integrated with KV streaming; accurate expert-cache note (simplexai-labs#23) PR simplexai-labs#21 (code-martin): a 4-bit K/V format (orthonormal FWHT-256 rotation of K, V and Q, then q4_0 blocks). On merge: - Q4 is a full third format of the KV-streaming code (host copy, block residency, prompt-path staging, MTP ring, STATE_HASH): with --kv-resident the PR wrote K/V through the residency map's -1 entries. kv_stream_parity now checks Q4 streamed vs resident, bitwise. - The per-token path ran attention twice (once before the blocks were resident); once now. - The q4 group maximum breaks |x| ties the same way in every lane (one scale per block). - Dropped: the duplicate path fixes (in 0.1.7), personal .gitignore entries, the re-encoded CMakeLists. - Measured (bench/results/2026-09-27-kv-q4): int8 matches fp16; q4_0 costs precision on long documents (perplexity +8-12%), needles 5/5, +3.7% decode at 128K. Opt-in: setup asks above 8K context (int8 recommended), START-HERE.bat --kv q4_0, or --kv q4_0 in the config. simplexai-labs#23: the start-up warning "the GPU hit path is NOT CORRECT" dated from a long-fixed fault. Measured teacher-forced on 2,557 tokens (bench/results/2026-09-27-cache-parity): cache on vs off, 95-98% same top-1 and equal perplexity (-0.005 +- 0.005 nats); the warning is now one accurate line. Co-Authored-By: code-martin <code-martin@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Title
poly: opt-in ggml Q8_0 KV cache for strata-poly (--kv q8_0, qwen35moe models)
Summary
Adds an opt-in Q8_0 KV cache (K and V) to
strata-poly, selected with--kv q8_0, forqwen35moemodels only. At 131072 ctx it roughly halves the attention cache's VRAM (~47% less), freeing room for
more resident experts.
--kv fp16(or--kvomitted) is the default and stays byte-identical to theprevious engine.
Model support — this is not wired up for every architecture yet. The engine runs five families;
--kv q8_0is implemented for one of them and refused cleanly for the rest:general.architecture--kv q8_0qwen35moe(Tiel, Ornith, Occamy, …)gemma4,deepseek4,lfm2moe,glm5-next(glm5next)Refusal message, e.g. for LFM:
--kv q8_0 is only supported for qwen35moe models in v1; this is a 'lfm2moe' model. The check runs inEngine::open()right after readinggeneral.architecture, beforeany weights are loaded. Mirroring the cache into another engine (alloc, store, row accounting,
snapshots) is follow-up work, not part of this PR.
The cache uses the ggml Q8_0 layout (
QK8_0 = 32: one fp16 scale + 32 int8 codes a block), stored splitas
codes: int8[n_kv*head_dim]andscales: fp16[n_kv*(head_dim/32)]a row. This is deliberately notthe other engine's
--kv int8(a different layout) — that option is untouched.What changed
11 files (9 modified, 2 new):
src/poly/engine.hpp—enum class KvFmt { kFp16, kQ8_0 }andOptions::kv_format(default fp16).src/poly/main.cpp— parse--kv fp16|q8_0(unknown value → clear error, exit 2);#ifdef STRATA_USE_HIPrejects q8_0; usage line.src/poly/engine.cpp—Engine::open()restricts--kv q8_0toqwen35moeand rejects every otherarchitecture cleanly (the guard above).
src/poly/qwen35.hpp—Blockgains the q8 handles (k_codes/k_scales/v_codes/v_scales) and abounded q8 dequant workspace.
src/poly/qwen35.cpp— allocates the q8 buffers (q8-only) underkQ8_0;card_row_bytes()q8 size;a
n_kv*head_dim % 32load-time guard;attn_store()stores throughstore_q8(_at);attn()readsthrough
Kvq8{...};save_card_rows/load_card_rowshandle codes+scales (parked conversations).include/strata/poly/kernels.hpp—struct Kvq8;store_q8/store_q8_at; q8 parameters onattend()/attend_batch().src/poly/kernels.cu—store_q8_kernel/store_q8_at_kernel(CUDA-graph-capturable, no host sync);the four attention kernels templated on
bool Q8so the fp16 branch is unchanged; a boundeddequant_q8_kernelsoattend_batch()dequantizes only the current block of keys into a smallworkspace — never the whole prefix.
CMakeLists.txt— registerspoly_kv_q8_test.docs/GLM.md, newdocs/Q8_KV.md— the--kv fmtoption, the model-support scope and the measurednumbers.
Extra Notes
Measured on an RTX 3070 8 GB (sm_86), Ryzen 9 5900X, CUDA 13.4; 131072 ctx, greedy (temp 0),
--no-draft, 64 tokens; Tiel, Ornith and Occamy (allqwen35moe).qwen35moeonly. No other architecture has been given a q8 pathin this PR; they refuse
--kv q8_0cleanly (see the table above).--kv fp16matches the pre-change engine on the fixed prompts (12/12).--kv q8_0vs--kv fp1615/15 exact (character agreement 1.0000); an optionalreference against
llama.cpp -ctk q8_0 -ctv q8_0agreed 2/2.With
--kv-wholethe expert cache grows by ~977–1082 experts.kKvGrow,--kv-whole, a parked/restored conversation, the draftblock and a vision request all pass under q8_0, and are unchanged under fp16.
cleanly at startup on HIP (
exit 2), and the q8 kernels use only intrinsics the existing poly kernelsalready use; a HIP report from a ROCm machine would close this out. SYCL is unaffected (
src/polyisnot built for it — SYCL is the separate
sycl/engine).strataengine and its--kv int8;glm5next; gemma4/deepseek4/lfm2moe(no q8yet, refused cleanly); the default path.
main(LFM2.5,gemm_small,deepseek4, …);main's new dim==64attention fast paths are guarded so a q8 cache never enters them.
cudaGraphInstantiate: out of memoryat 131072 with the default 256 MiB VRAM reserve; it reproduces on fp16 too, and--vram-reserve-mib 512removes it. (The local run configs use--kv q8_0 --vram-reserve-mib 512;those configs are gitignored and are not part of this PR.)