feat(turboquant): add opt-in mid-prefill KV conversion - #2543
Conversation
|
Thanks for taking this on. I reviewed the memory math, conversion path, and tests again. I think the feature has a valid use case: it can let a long-context request finish when dense prefill would otherwise hit the memory guard. The KV memory reduction is real. With the Qwen geometry used in this PR, the full-attention KV payload at 131,071 tokens is about 8.00 GiB dense, 4.28 GiB with Q8, and 2.40 GiB with Q4. That saves 3.72 GiB or 5.60 GiB respectively. Model weights and the 48 recurrent cache states do not shrink, so these percentages do not apply to the total process footprint. I also ran a synthetic MLX check on an M3 Ultra. Dense K/V storage fell from 32.0 MiB to 16.1 MiB with Q8 and 8.1 MiB with Q4. In the same check, quantized attention was about 1.85 times slower than dense attention. The relative attention-output difference was about 1.3% for Q8 and 10.0% for Q4. This is not an end-to-end model benchmark, but it confirms that the memory, speed, and precision tradeoff is real. I found two validation gaps and two runtime issues that should be addressed before another review. 1. Add real quality validationMid-prefill conversion changes more than the cache format. After the trigger, the rest of the prompt attends to quantized history, so later hidden states and K/V values can diverge from dense prefill. The existing equality test uses Please compare dense prefill, ordinary post-prefill TurboQuant, and mid-prefill TurboQuant on the same real model and prompts. Q8 and Q4 both need coverage. A token-level KL or perplexity result plus a long-context retrieval test would be enough to show whether the change is safe for the intended workload. The TurboQuant paper and a later vLLM study report different results depending on bit width and workload, so oMLX needs its own measurements. 2. Add a controlled performance comparisonThe reported conversion pause of about 1.1 seconds is small. The main cost is the quantized attention path used for the rest of the prefill. The 131K run completed, but it used a forced trigger, a 64-token chunk, and took 2,753.20 seconds. There is no matching dense or ordinary TurboQuant baseline, so the slowdown caused by mid-prefill is still unknown. Please add an A/B comparison at 32K and 64K using the same model, prompt, chunk size, and memory limits. Report peak process footprint, total prefill time, prefill throughput after the trigger, and decode throughput. The 131K case should also be repeated with a normal pressure trigger rather than a forced one. 3. Keep exclusivity conflicts retryableIf another engine prevents the process-exclusive claim, Please handle this as a retryable busy or capability error instead of calling 4. Require confirmed eligibility before deferring preflightRoute-level preflight runs before a prompt cache exists. Please defer preflight only when conversion eligibility is confirmed. Add a streaming test with an unsupported cache layout and verify that the request receives the normal HTTP 400 before the response starts. The branch also needs a rebase against current Once these items are addressed, I can review this as an experimental, default-off path for requests that would otherwise fail under memory pressure. I would keep the PR in draft until then. |
a71e563 to
0bef73a
Compare
Summary
Completes the opt-in TurboQuant mid-prefill path from #1843 and resolves the long-context validation blocker.
turboquant_mid_prefill, gated by normal TurboQuant enablement and off by default.stream_experts: falseinert. Streaming code does not use@mx.compile.Safety contract
mx.eval(router_logits)completes before any expert-slot mutation.resolve()/__call__hot paths add no dynamic allocation..safetensorsbefore and after every run. Darwin reads must successfully set and verifyF_NOCACHEthroughfcntl; there is no cached-read fallback.Independent review fixes
An independent high-risk review found four blockers; all are fixed and re-reviewed:
The second review found no P0-P3 issue.
Audited real-model validation
Model:
Justanut/Qwen3.6-27B-uncensored-heretic-v2-Native-MTP-Preserved-oQ6e-mtpQuality matrix
Clean commit
c40dc069; 30/30 cells passed across six pinned MRCR rows and five modes: dense, ordinary Q8, ordinary Q4, mid-prefill Q8, and mid-prefill Q4.1.0on five rows and0.999418942475305on row 311 in every mode.2.064e-6, ordinary Q43.144e-6, mid Q81.840e-6, mid Q43.770e-6.23,716,288,094bytes) matched exactly before and after.Controlled prefill performance
t/sis total prefill throughput;suffix t/smeasures the same post-midpoint suffix boundary in every mode. Peak is supervised childphys_footprint.Mid-prefill is a memory-safety option, not a universal speed win. Q4 cut the supervised 64K peak by 5.42 GiB, with a 14.8% total-prefill and 23.5% same-boundary suffix cost. Q8 saved 1.74 GiB at 32K, but only 0.10 GiB at the supervised 64K process peak.
Organic 131,071-token run
Clean commit
0bef73a3; statusok; forced trigger disabled; verified no-cache source reads; source shards matched exactly.2.338879, perplexity10.369607, decode6.81t/s.The first final-head repeat stopped fail closed when unrelated host pressure briefly reduced headroom to 5.76 GiB, below the unchanged 6 GiB floor. Source hashes still matched. The unchanged-command retry started after
memory_pressure -Qreported 90% free and passed without lowering either safety limit.Automated checks
0bef73a3).The PR remains draft because the feature is experimental and the 64K tradeoff is material.