glm: fix streaming prefill failures for real-size prompts#520
glm: fix streaming prefill failures for real-size prompts#520andreaborio wants to merge 2 commits into
Conversation
The indexed generation prefill hardcoded full_layer_prefill=false in its per-layer mapping and forced the runtime flag off, so every chunk took the selected-expert addr path regardless of size. A chunk above ~64 tokens selects more unique experts per layer than the cache holds; the loader's overflow branch then addresses experts straight out of the mapped model, but decode-only spans do not cover the routed expert tensors, and prefill died with "failed to map overflow expert views". Net effect: every real generation prompt above ~64 tokens failed, which went unnoticed because benchmarks used short prompts and the perplexity flow stayed under the overflow threshold. Compute the same full-layer threshold the plain prefill flow uses (glm_graph_stream_prefill_full_layer_enabled) and pass it to both the layer mapping and the runtime flag: big chunks now stream full layers with the mm_id kernels (measured prefill 1.5 -> 9.4 t/s on a 795-token prompt, and it completes instead of failing), short prompts keep the addr path unchanged. Decode is untouched (decode-consistency stays bit-exact); scoring a text under the overflow threshold shifts +0.7% ppl from the documented addr-vs-mm_id accumulation-order difference. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
When the expert cache was at budget, prepare_selected_batch spilled every
uncached expert to whole-tensor overflow views instead of evicting. The
overflow wrap needs the routed expert tensors in the mapped view set, which
the decode-expert-cache prefill mapping does not provide, so any small-chunk
prefill against a full cache failed ("failed to map overflow expert views")
— most visibly checkpoint-extend prefills, which broke the disk KV cache
flow right after it loaded a matching checkpoint.
Reuse-evict at budget exactly like the decode loader does: commands are
synced by the caller before prepare, so reuse cannot race the GPU, and
prepare_load_buffers protects the layer's unique selected set. Overflow
views remain as the last resort for allocation failure.
With this plus the indexed-prefill full-layer threshold fix, the disk KV
cache works end to end on GLM 5.2: a 321-token request costs 181 s cold and
32 s on a same-prefix follow-up (checkpoint loaded in ~20 ms, 14-token
suffix prefill), including across a server restart (33 s, disk checkpoint).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Independent confirmation and validation from a second machine. We hit the same failure yesterday on a Mac Studio M4 Max 128 GB (macOS 26.3.1, Metal) running the published Validation of this PR's two commits applied as-is on top of glm5.2 (
We did not specifically exercise the commit-2 checkpoint-extend repro, but the cache-pressure and decode-prefill-correctness suites pass with the evict-at-budget change in place. Thanks for the fix — we are carrying these two commits on our integration branch until this merges. |
On the glm5.2 branch, GLM 5.2 SSD-streaming generation fails for any prompt above roughly the full-layer threshold (~64 tokens):
Repro:
./ds4 --metal --ssd-streaming -m GLM-5.2-<routed-Q2K>.gguf --ctx 1024 --nothink --temp 0 -n 8 -p "<any ~300-word prompt>". Short benchmark prompts and--perplexity-filetexts under the overflow point never hit it, which is probably why it survived so far.Two root causes, one commit each:
The indexed prefill hardcodes
full_layer_prefill=falsein its per-layer mapping and forces the runtime flag off, so every chunk takes the selected-expert addr path. A chunk above ~64 tokens selects more unique experts per layer than the cache holds; the loader's overflow branch then addresses experts straight out of the mapped model, but the decode-expert-cache spans do not cover the routed expert tensors, and prefill dies. The fix computes the same threshold the plain prefill flow uses (glm_graph_stream_prefill_full_layer_enabled) and passes it to both the mapping and the runtime flag. Big chunks now stream full layers with the mm_id kernels; short prompts keep the addr path unchanged.prepare_selected_batchspills to overflow views instead of evicting when the cache is at budget (force_reusewas hardcoded 0). Any small-chunk prefill against a full cache failed the same way — most visibly checkpoint-extend prefills, which broke the disk KV cache flow right after it loaded a matching checkpoint. The fix reuse-evicts at budget exactly like the decode loader does (commands are synced by the caller before prepare, so reuse cannot race the GPU, andprepare_load_buffersprotects the layer's unique selected set). Overflow views remain as the last resort for allocation failure.Testing
Machine: Apple M5 Pro 64 GB, macOS 26 (Darwin 25.5.0), Metal backend, branch = glm5.2 + these two commits,
make clean && make,git diff --checkclean.Correctness track:
./ds4_test --server— OK./ds4_test --metal-kernels— OKDS4_TEST_SSD_STREAMING=1 DS4_TEST_MODEL=DeepSeek-V4-Flash-IQ2XXS-w2Q2K-AProjQ8-SExpQ8-OutQ8-chat-v2-imatrix.gguf ./ds4_test --metal-ssd-streaming-cache-pressure— OK (exercises the modified loader on the DeepSeek family)./ds4_test --logprob-vectors— OK (official-vector gate; the shared-loader change does not perturb DeepSeek logits)GLM 5.2 runtime (753B, routed experts Q2_K, attention Q8_0; note: my local artifact uses a ds4-native tensor layout enabled by a separate local patch — the two fixes here are layout-independent and sit in code that never inspects tensor dtypes):
--decode-consistency 8stays bit-exact vs a fresh prefill (decode untouched);Speed track: decode is unaffected (same path); prefill only changes for chunk sizes that previously failed outright, where full-layer streaming measures 6x faster than the (broken) addr path would need to be. One observable shift: scoring a text that stays under the overflow point moves +0.7% ppl because those chunks now take the mm_id kernels instead of addr — the same accumulation-order difference already documented in
ds4_gpu_stream_prefill_batch_selected_addr_autogating.Found while enabling the disk KV cache for agentic use; happy to split or adjust if you prefer a different fix shape (e.g. mapping the expert spans in addr mode instead of switching to full-layer above the threshold).