【免费下载链接】turboquant_plus
本文以 docs/mlx-port.md 为骨架,介绍 TurboQuant(turboquant_plus 项目的核心算法)在 Apple MLX 框架上的实验性移植:
TurboKVCache如何以零框架侵入的方式替换 mlx-lm / mlx-vlm 的默认KVCache,delegated KVCache 架构如何把 decode 速度拉回基线的 97–100%,以及为什么在密集模型上"对称 turbo"会灾难性失败、而"非对称(K=FP16, V=turbo4)"是强制选择。读完本文,你将掌握 MLX 侧 KV 压缩的安装方式、两套 Quick Start 用法、对称/非对称配置的适用边界,以及如何在 M5 Max / M2 Pro 上复现质量与性能验证。
背景:TurboQuant 算法与 K/V 缓存的分工设计
TurboQuant 是一套面向 KV 缓存的 4-bit 量化压缩算法。在当前仓库的 Python 参考实现中,算法是两阶段的(见 turboquant/turboquant.py):
- PolarQuant 阶段(b-1 位):先做随机旋转,使各坐标近似独立同分布(Beta/Gaussian),再用按该分布标定的最优标量码本做 MSE 最优量化(见 turboquant/polar_quant.py 与 turboquant/codebook.py);
- QJL 阶段(1 位):对第一阶段的残差做 1-bit 量化 Johnson-Lindenstrauss 变换(随机正交投影 + 符号位),以无偏方式保留内积(见 turboquant/qjl.py)。
总计每坐标 b 位。更重要的是 KV 缓存层面的分工——turboquant/kv_cache.py 明确说明:
- K 缓存:使用完整 TurboQuant(Algorithm 2),因为注意力分数依赖内积
Q @ K^T,需要内积保持; - V 缓存:使用仅 MSE 的
TurboQuantMSE(Algorithm 1,即纯 PolarQuant),因为attn_weights @ V的重建质量由 MSE 主导。
这个"K 管内积、V 管 MSE"的分工是理解下文"对称 vs 非对称"实验结果的算法根源。
TurboKVCache:mlx-lm / mlx-vlm 的无侵入替换
MLX 移植的核心交付物是TurboKVCache,定义在 fork 的mlx.nn.layers.turbo_kv_cache模块中。它被设计为 mlx-lmKVCache的drop-in 替换:既兼容 mlx-lm(文本),也兼容 mlx-vlm(多模态),不需要任何框架层修改。仓库中的 scripts/mlx_quality_suite.py 直接以from mlx.nn.layers.turbo_kv_cache import TurboKVCache方式使用,印证了这一点。
Delegated KVCache 架构(提交 7ad7500)
这是把 decode 速度从"61–83%"拉回"97–100%"的关键优化,工作流程如下:
- Prefill 阶段:缓存保持原始 FP16 存储,不做压缩;
- 首个 decode 步:将缓存压缩为打包的 TurboQuant 存储,并用解码出的 FP16 数据 seed 一个内部
KVCache; - 后续 decode:走原生 KVCache 的预分配缓冲 + 零分配 slice-assign,保证每个 token 的 decode 路径无额外分配;
- 后台:打包存储通过 CPU stream 上的周期性批量重压缩持续更新。
之前的性能瓶颈根因是:每个 decode 步 × n_layers 次mx.concatenate都会分配新数组,导致内存分配开销把压缩节省的带宽完全吃掉。delegated 架构把 FP16 存储委托给带预分配缓冲的内部 KVCache 后,decode 路径恢复原生速度,压缩数据则在后台"慢慢补写"。
快速开始:安装与两套用法
安装
pip install git+https://github.com/TheTom/mlx.git@feature/turboquant-plus pip install mlx-lm多模态场景把mlx-lm换成mlx-vlm:
pip install git+https://github.com/TheTom/mlx.git@feature/turboquant-plus pip install mlx-vlm前提是 Apple Silicon 环境(Python/Swift 原生推理)。
mlx-lm(文本)Quick Start
import mlx_lm from mlx.nn.layers.turbo_kv_cache import make_turbo_cache, compact_turbo_cache model, tokenizer = mlx_lm.load("mlx-community/Qwen2.5-7B-Instruct-8bit") cache = make_turbo_cache(model, bits=4) mlx_lm.generate(model, tokenizer, prompt="Hello!", max_tokens=1, prompt_cache=cache) compact_turbo_cache(cache) mlx_lm.generate(model, tokenizer, prompt="Continue.", max_tokens=200, prompt_cache=cache, verbose=True)注意这里的"两段式"写法:先max_tokens=1走 prefill(此时缓存仍是 FP16),调用compact_turbo_cache(cache)触发压缩,再继续生成——对应 delegated 架构中"首个 decode 步压缩"的时机。
mlx-vlm(多模态)Quick Start
from mlx_vlm import load from mlx_vlm.models.cache import make_prompt_cache from mlx_lm.models.cache import KVCache from mlx.nn.layers.turbo_kv_cache import TurboKVCacheLite, compact_turbo_cache model, processor = load("mlx-community/gemma-4-26b-a4b-it-bf16") # Wrap KV layers with TurboKVCacheLite cache = make_prompt_cache(model.language_model) kv_indices = [i for i, c in enumerate(cache) if isinstance(c, KVCache)] for idx in kv_indices: cache[idx] = TurboKVCacheLite(cache[idx], bits=4, key_bits=4) # Generate as normal — prefill stores FP16 from mlx_vlm import generate generate(model, processor, prompt="...", max_tokens=1, prompt_cache=cache) # Compact: compress K+V to 4-bit TurboQuant compact_turbo_cache(cache) # Continue generating — native SDPA at full speed generate(model, processor, prompt="Continue.", max_tokens=200, prompt_cache=cache)多模态路径用的是TurboKVCacheLite——它接收一个已有的KVCache实例做包装(TurboKVCacheLite(cache[idx], bits=4, key_bits=4)),同样遵循"prefill 存 FP16 → compact → 继续生成"的流程。
直接构造缓存
在质量测试脚本中还能看到更细粒度的用法:TurboKVCache(bits=4, key_bits=4)逐层构造,key_bits=0表示 K 保持 FP16(非对称模式),并支持min_compress_tokens参数控制压缩触发的最小 token 数(默认 256,见 scripts/mlx_quality_suite.py)。
对称 vs 非对称:密集模型上的关键决策
这是整个移植中最重要的一条工程结论,原文档给出了明确的警告:
对称 turbo 在密集模型上是灾难性的。所有 K 层都被压缩后,softmax 误差会在 28 层中累积放大。对密集架构而言,非对称(K=FP16, V=turbo4)是强制要求。混合模型(如 Qwen3.5,带 delta net 层)碰巧安全,因为只有一部分层使用 KV cache。
质量验证(Qwen2.5-7B 8bit 密集模型、全部 28 层 KV)给出了量级差异:
| Test | Symmetric turbo4 | Asymmetric (K=FP16) |
|---|---|---|
| KLD | 6.86 (broken) | 0.003 |
| Top-1 match | 10.5% (broken) | 98.1% |
| NIAH | 0/15 FAIL | 15/15 PASS |
对称模式下 KLD 高达 6.86、Top-1 匹配率仅 10.5%、NIAH 全部失败——输出基本不可用;切换到非对称后 KLD 降到 0.003、Top-1 达 98.1%、NIAH 15/15 全过。这与仓库参考实现的分工逻辑完全对应:K 缓存走内积保持路径,一旦所有 K 都被 4-bit 量化,注意力 softmax 的分布误差在深网络中逐层放大;而把 K 留在 FP16、只压缩 V(MSE 主导的路径),误差就收敛在可忽略水平。
此外,mlx_quality_suite.py还实现了boundary layer 保护:默认把首、尾各 2 个 KV 层留在 FP16,避免边界层极端 V 范数引发 NaN(scripts/mlx_quality_suite.py)。
M5 Max 基准结果
以下数据均为移植作者在 M5 Max(128GB)上的实测,文档原文完整保留如下。
Qwen2.5-3B 4bit —— delegated KVCache(5 次运行平均,500 个 decode token,提交 7ad7500)
| Config | Decode tok/s | vs Baseline | PPL | PPL Delta |
|---|---|---|---|---|
| Baseline (f16 KV) | 172.6 | 100% | 1.8764 | — |
| Sym turbo4 | 171.2 | 99.2% | 1.9083 | +1.70% |
| Asym (K=FP16, V=turbo4) | 171.0 | 99.0% | 1.8859 | +0.51% |
质量:输出文本与基线无法区分;KL 散度 < 0.001,余弦相似度 > 0.989。
Qwen3.5-35B-A3B(8bit,MoE)
| Config | Prefill | Decode | vs Baseline |
|---|---|---|---|
| Baseline | 11.4 | 95.7 | 100% |
| turbo4 fused + boundary | 132.7 | 94.2 | 96% |
Qwen3.5-27B Dense(8bit,16/64 KV 层)
| Config | PPL | PPL Delta | Decode | vs Baseline |
|---|---|---|---|---|
| Baseline | 1.4800 | — | 17.9 | 100% |
| turbo4 asymmetric | 1.5082 | +1.91% | 15.5 | 87% |
| turbo4 symmetric | 1.5219 | +2.83% | 15.4 | 86% |
密集模型(短上下文,deferred compression)
| Model | Baseline Decode | turbo4 asym Decode | PPL Delta |
|---|---|---|---|
| Qwen2.5-7B 8bit | 64.2 | 64.1 | 0.00% |
| phi-4 8bit | 32.9 | 32.7 | 0.00% |
M5 Max 上下文缩放(Qwen2.5-7B 8bit,delegated KVCache,提交 7ad7500)
| Context | Baseline | Sym turbo4 | vs Baseline | Asym (K=FP16) | vs Baseline |
|---|---|---|---|---|---|
| 512 | 63.6 | 63.6 | 100% | 64.0 | 101% |
| 1K | 63.1 | 62.8 | 100% | 62.6 | 99% |
| 2K | 62.7 | 61.8 | 98% | 62.2 | 99% |
| 4K | 61.0 | 60.2 | 99% | 61.0 | 100% |
| 8K | 58.2 | 56.9 | 98% | 57.7 | 99% |
| 16K | 54.6 | 53.0 | 97% | 53.8 | 99% |
之前的数字(61–83%)是在 delegated KVCache 优化(提交 7ad7500)之前测得的。根因是
mx.concatenate每个 decode 步 × n_layers 都会分配新数组;修复方式是把 FP16 存储委托给带预分配缓冲的内部 KVCache。
MLX Python vs llama.cpp(Qwen2.5-7B,M5 Max)
| Framework | Prefill (400 tok) | Decode | Memory |
|---|---|---|---|
| llama.cpp (Q8_0) | 387 | 20.9 | 7.5 GB |
| MLX (8bit) | 243 | 21.2 | 8.5 GB |
MLX 的 decode 速度与 llama.cpp 持平;prefill 慢 37%(lazy graph 对比预编译路径)。
M2 Pro 结果与硬件差异
M2 Pro — Qwen2.5-1.5B 8bit(密集模型,28/28 KV 层,非对称):
| Test | Result |
|---|---|
| KLD | 0.004 |
| Top-1 match | 96.8% |
| NIAH | 30/30 PASS |
| Context | Baseline Decode | Turbo Asymmetric | vs Baseline |
|---|---|---|---|
| 128 | 34.8 | 35.2 | 101% |
| 4096 | 46.9 | 21.6 | 46% |
M2 Pro 在长上下文下表现出更明显的 decode 回归——更低的内存带宽放大了 turbo 的额外开销。也就是说:压缩省下的显存/带宽收益,在带宽充足的高端芯片(M5 Max)上能接近完全兑现,但在带宽吃紧的芯片上会被解量化与后台重压缩的固定开销反超。这也提醒读者:同样配置在不同 Apple Silicon 上的速度结论不可直接外推。
MM-NIAH 多模态基准(gemma-4-26b-a4b-it · BF16 · 4-bit TQ+ Compact · M5 Max 128GB,520 样本)
| Bucket | BL Acc | TQ+ Acc | Agree | BL Decode | TQ+ Decode | Speedup | BL KV | TQ+ KV | KV saved |
|---|---|---|---|---|---|---|---|---|---|
| ~1K | 85% | 84% | 99% | 55.1 | 54.7 | 0.99x | 0.21G | 0.19G | 10% |
| ~3K | 81% | 79% | 99% | 55.2 | 54.1 | 0.98x | 0.27G | 0.21G | 22% |
| ~7K | 80% | 81% | 99% | 54.1 | 51.7 | 0.96x | 0.37G | 0.24G | 35% |
| ~15K | 76% | 76% | 100% | 52.0 | 47.8 | 0.92x | 0.53G | 0.28G | 47% |
| ~30K | 77% | 75% | 98% | 46.9 | 40.2 | 0.86x | 0.87G | 0.36G | 59% |
| ~60K | 75% | 76% | 99% | 42.6 | 33.7 | 0.79x | 1.30G | 0.47G | 64% |
| Total | 79% | 78% | 99% | 51.1 | 47.2 | 0.92x | 0.58G | 0.29G | 50% |
结论:全部上下文长度上与基线的答案一致性高达 99%,不存在系统性质量退化;KV 节省 10–64%(TQ+ 生效处);decode 速度比从 ~1K 时的 0.99x 降到 ~60K 时的 0.79x——原因是更长的 prefill 放大了"仅解量化一次"的固定开销。注意:约 1K 长度时 KV 节省只有 10%,因为短上下文中压缩/后台重压缩的边际收益尚未拉开。
用仓库脚本复现与验证
仓库中提供了可直接运行的 MLX 质量测试套件 scripts/mlx_quality_suite.py,它基于 mlx-lm 后端运行三类测试,且不修改任何 llama.cpp 测试脚本:
# 运行全部测试(kld + niah + context) python3 scripts/mlx_quality_suite.py --model mlx-community/Qwen3.5-2B-8bit # 只跑 NIAH / KLD / 上下文缩放 python3 scripts/mlx_quality_suite.py --model mlx-community/Qwen3.5-2B-8bit --test niah python3 scripts/mlx_quality_suite.py --model mlx-community/Qwen3.5-2B-8bit --test kld python3 scripts/mlx_quality_suite.py --model mlx-community/Qwen3.5-2B-8bit --test context # 指定位宽与非对称模式 python3 scripts/mlx_quality_suite.py --model mlx-community/Qwen3.5-2B-8bit --bits 4 --asymmetric参数一览:--model(HF 模型 id 或本地路径,必填)、--test(niah/kld/context,缺省跑全部)、--bits(turbo 压缩位数,默认 4)、--asymmetric(K 保持 FP16、只压缩 V)、--verbose、--output-dir(默认 scripts/results/)。测试细节值得注意:
- NIAH:在 1024/2048/4096 token 上下文、5 种深度(0/25/50/75/90%)下检索固定密码句
BLUE TIGER 42,基线 15/15、turbo 也 15/15 才算通过; - KLD:用固定多主题文本逐 token 计算 baseline 与 turbo 的 KL 散度及 Top-1 匹配率,压缩阈值
min_compress_tokens会调低到 32 以确保前向传播过程中真实触发压缩(scripts/mlx_quality_suite.py); - context:在 128–4096 token 上下文下测 decode 速度与峰值内存。
仓库 scripts/results/ 目录内已保存真实运行报告,例如mlx_quality_Qwen2.5-7B-Instruct-8bit_20260405_091833.md(turbo4_asymmetric 配置):NIAH 基线 15/15、turbo 15/15 全过,且各上下文下 prompt 速度基本持平。这些报告可直接作为复现结果的对照基准。
限制与注意事项
- 对称压缩仅适用于"碰巧安全"的架构:混合模型(Qwen3.5 的 delta net 层)只有部分层用 KV cache,因此对称 turbo 不会灾难性失败;密集模型必须使用
--asymmetric(K=FP16, V=turbo4)。 - 长上下文 decode 有固定开销:TQ+ 的 decode 速度随上下文从 0.99x 降至 0.79x(dequant-once 开销被更长的 prefill 放大),KV 节省与速度之间需要按实际上下文权衡。
- 硬件带宽决定收益上限:M2 Pro 在 4096 token 上下文下 decode 降到基线的 46%,M5 Max 则在 16K 下仍保持 97–99%。
- prefill 较慢:MLX 惰性图对比 llama.cpp 预编译路径,prefill 慢约 37%。
- 基准记录建议:未来跑基准时应记录 Apple Silicon 电源模式(Low / Auto / High),因为它会实质影响吞吐量。
- 实验性状态:该移植目前是实验性的(fork 于 TheTom/mlx 的
feature/turboquant-plus分支),核心压缩理论依据仍来自 turboquant_plus 仓库的 turboquant/ 参考实现与其对应论文(PolarQuant、QJL、TurboQuant 两阶段算法)。
综上,TurboQuant 的 MLX 移植验证了一个关键结论:在非对称(V-only)配置 + delegated KVCache 架构下,KV 缓存压缩可以在 M5 Max 上以 ≈0 的 PPL 代价(+0.51%)、≈99% 的 decode 速度保持,换来多模态场景最高 64%(平均 50%)的 KV 显存节省;而对称配置在密集模型上是不可用的。对于想在自己的 Apple Silicon 上尝试长上下文推理的开发者,推荐路径是:安装 fork 包 → 按 mlx-lm/mlx-vlm Quick Start 接入 → 始终使用非对称配置 → 用 scripts/mlx_quality_suite.py 在目标模型与目标硬件上复测质量与速度。
【免费下载链接】turboquant_plus
相关推荐
TurboQuant+ KV Cache 压缩基准实测:M5 Max 上 4.9× 压缩与 35× 生成速度回退的根因剖析
TurboQuant+ KV Cache 压缩基准实测:M5 Max 上 4.9× 压缩与 35× 生成速度回退的根因剖析 本文基于 benchmarks/be
TurboQuant MLX 质量套件实战:turbo4 非对称 KV 压缩在 Qwen2.5-7B-Instruct-8bit 上的 NIAH 检索全通过验证
TurboQuant MLX 质量套件实战:turbo4 非对称 KV 压缩在 Qwen2.5 7B Instruct 8bit 上的 NIAH 检索全通过验证
LMCache KV Cache 压缩与解压缩实战:通过 Controller 对 KV Cache 执行 CacheGen 压缩
LMCache KV Cache 压缩与解压缩实战:通过 Controller 对 KV Cache 执行 CacheGen 压缩 导读 本篇技术指南完整讲解
人工智能大模型缓存抽象模型推理服务
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考