Conversation
ac32df9 to
dfeb967
Compare
This comment was marked as outdated.
This comment was marked as outdated.
dfeb967 to
64e15fa
Compare
7dc5039 to
12d07eb
Compare
64e15fa to
a57afe4
Compare
6ebbc51 to
bf512c0
Compare
bf512c0 to
2812881
Compare
953aff6 to
9e01125
Compare
2812881 to
f5a54b1
Compare
f5a54b1 to
c3081c3
Compare
|
Thanks for working on this. Making the recurrent Gated DeltaNet state safely rewindable is the right problem to solve, and it unlocks useful target/draft combinations for Qwen3.5/Qwen3.8. I compared the current PR head ( @zcbenz I'm dropping a series of suggestions based not only on the needed functionality that is missing here, but also from some hard won lessons here. If you'd rather that I upstream my own implementation here, I can open a new PR. But I also thought this would be a good channel here to explain what is needed; I found several correctness issues that I think need addressing before this is used by speculative generation (MTP, PLD, Dflash etc). I also wanted to share an alternative design that has worked well for us and avoids changing the ordinary GDN execution path. In these comments is a bare minimum to make it work. Not included: does not include ragged/continuous batching, dynamic batch membership, persistent prefix checkpoints, arbitrary prefix-cache truncation, or our Qwen4/self-MTP serving machinery. It is intentionally limited to transaction-scoped exact rollback for single-sequence speculative decoding with Qwen3.5/Qwen3-Next targets and drafts. If you're interested in seeing the pieces missing, will include links to PRs covering the topic. 1. A fresh
|
|
@pierre427 Since you are comparing designs, here is what I have on #990: Do you think the "split the forward, snapshot the boundary, replay nothing" shape could avoid problems your closure-replay approach has to handle? Or maybe does it just move the same complexity somewhere else? |
|
Here's where I landed, reach out to me directly if you'd like to chat pierre @ userid.org |
|
So I finally got around to publishing some updated work about caches. https://github.com/pierre427/mlx2/blob/main/docs/mlx2_deep_dive_feature_architecture_review.pdf - section 3+ Some related code: https://github.com/pierre427/mlx2/blob/main/src/mlx2/runtime/apc_v2.py Why? Because legacy cache can't really scale and support advanced features & functionality. Splitting it into layers + segments allows for cow-branching, rollbacks, APC suspend to disk etc. No performance regressions, I had some early on in the dev process but it's at or slightly above parity with legacy. |
Refs #990
The
ArraysCacheonly stores the last hidden states so it is not trimmable and does not work with speculative decoding like MTP.This PR experiments with a new
RecurrentCacheimplementation that, stores the hidden states in a temporal manner so we can roll back in speculative decoding. The downside is that the hidden states per token is much larger than traditional KV cache and the sequence length would be very limited, so it would not help much in the case of prefix prompt cache, but for speculative decoding we usually only draft 4~6 tokens and additional RAM usage would be quite small.To try this branch:
mlx_lm.generate --model mlx-community/Qwen3.8-27B-4bit --max-tokens 200 --prompt "Write a story about George Washington" --chat-template-config "{\"enable_thinking\": false}" --draft-model mlx-community/Qwen3.5-0.8B-4bit --num-draft-tokens 4