turbovec 深度解析:基于 Google TurboQuant 的 Rust 向量索引,10M 文档从 31 GB 砍到 4 GB
posts posts 2026-08-23T10:00:00+08:00RyanCodrai/turbovec 是基于 Google Research 论文 TurboQuant(arXiv:2504.19874,ICLR 2026)的 Rust 向量索引 + Python 绑定:无码本训练、在线 ingest、NEON/AVX-512 SIMD 内核、按 ID 过滤搜索。项目自报在 4-bit 下平均比 FAISS IndexPQFastScan 快约 3.4 倍,将 10M float32 向量集从 31 GB 压缩到 2-bit 约 4 GB。技术笔记向量检索, RAG, Rust, Python, SIMD, 量化, 开源项目深拆turbovec 深度解析:基于 Google TurboQuant 的 Rust 向量索引,10M 文档从 31 GB 砍到 4 GB
一、turbovec 是什么
turbovec(GitHub: RyanCodrai/turbovec)是 Ryan Codrai 开源的面向 Python 用户的 Rust 向量索引库,底层算法是 Google Research 等的 TurboQuant。TurboQuant 于 2025-04-28 发布在 arXiv(arXiv:2504.19874,作者 Amir Zandieh / Majid Daliri / Majid Hadian / Vahab Mirrokni),2026 年 1 月被 ICLR 2026 接收。turbovec 在 2026 年上线 PyPI 与 crates.io,2026-08-18 发布 1.0.0。
项目的核心卖点,README 写得很直白:
A 10 million document corpus takes 31 GB of RAM as float32. turbovec fits it in 4 GB - and searches it faster than FAISS.
三个主要优势:
- 极小内存:10M × 1536 维 float32 向量约 31 GB,用量化压到约 4 GB(2-bit)。
- 比 FAISS 快:手写 NEON(ARM)与 AVX-512(x86)内核,项目自报在 4-bit 下平均比
FAISS IndexPQFastScan快约 3.4 倍。 - 零训练 / 零码本:TurboQuant 是 data-oblivious 量化器,没有 codebook 训练步骤,新向量加进来立刻可搜,不需要 rebuild。
它不是要取代 FAISS 在云端做亿级召回,而是瞄准了「中规模 + 私有 / 离线 + Python 友好」这个窗口——10M 量级的数据在 FAISS 里要走 IndexIVFPQ + train,对中小团队偏重。
二、TurboQuant 是什么:为什么能让内存砍到 1/8
2.1 传统乘积量化的痛点
FAISS 的 IndexPQ 和 IndexIVFPQ 用乘积量化(Product Quantization):
- 把 1536 维向量切成子向量
- 每个子向量在码本(codebook)里找最近的中心点
- 用中心的索引代替原向量
代价有三个:
- 必须 train:要在语料上跑 k-means 才能得到像样的码本
- 冷启动差:新文档进来,码本要重跑
- 过滤搜索别扭:带 allowlist 的搜索往往要先取大 top-k 再过滤,recall 不稳
2.2 TurboQuant 的不同思路
TurboQuant 是一族在线(online)向量量化算法,目标是在不依赖数据分布的前提下逼近信息论给出的失真率下界(实测差距约常数因子 2.7)。它由两个变体组成:
- TurboQuant_mse:优化均方误差(MSE)。
- TurboQuant_prod:优化无偏内积估计,在 MSE 量化器之后,对残差再加一个 1-bit 的 Quantized JL(QJL)变换。
它的基本步骤是:
- 归一化:把向量长度(范数)单独存成一个 float,向量变成单位方向。
- 随机旋转:用一个固定种子的随机正交矩阵打乱坐标,旋转后每个坐标服从已知的 Beta 分布(高维下渐近取向正态)。
- 逐坐标标量量化:既然坐标分布是已知的,就能按分布算好最优边界(Lloyd-Max),对每个坐标只做查表式的标量量化——不需要看数据。
- (prod 变体)QJL 残差校准:对量化残差做 1-bit 变换,消除标量量化带来的内积偏置。
关键结论:在多数维度下,turboquant 的 4-bit 召回可以达到甚至超过 PQ 8-bit,但不需要 train。维度偏低(如 GloVe 200 维)时 Beta 渐近假设变松,表现会打折扣——这是它的适用边界,后面会展开。
2.3 turbovec 的工程化
turbovec 在论文的基础上做了四件工程化的事:
- Rust 实现 + PyO3/maturin 绑定:关键计算全在 Rust,Python 侧只是胶水。
- SIMD 内核:ARM 上用手写的 NEON SDOT/SMMLA 点积内核,x86 上用 AVX-512 VNNI 与
vpermb,并保留 AVX2 与标量兜底。 - 过滤感知搜索:把 allowlist / bitmask 直接编进 kernel,按 32 向量一块的粒度短路跳过,不在 Python 层后过滤。
- 增量落盘:
sync(path)只写自上次同步以来变化的部分,支持大规模索引下的毫秒级删除与追加。
需要区分的是,TurboQuant 是算法论文,它解决的问题是"给固定分布做最优量化";turbovec 是把它做成了可用的索引。两者不是一回事——看论文能理解它为什么快,看 turbovec 才知道怎么用。
三、安装与基础使用
3.1 安装
pip install turbovec # Python
# 或者
cargo add turbovec # Rustwheel 已发布到 PyPI 与 crates.io,覆盖 Linux / macOS / Windows,Python 要求 >= 3.9。
3.2 最简 RAG 例子
turbovec 只支持 2-bit 和 4-bit 两种位宽。向量必须是 2 维的 float32 数组,其他 dtype 会被拒绝而不会悄悄转换。
import numpy as np
from turbovec import TurboQuantIndex
# 1536 维(OpenAI text-embedding-3-small 维度)
index = TurboQuantIndex(dim=1536, bit_width=4)
# 直接 add,不需要 train
vecs = np.random.randn(1_000_000, 1536).astype(np.float32)
index.add(vecs)
# 新向量来了再 add
more = np.random.randn(50_000, 1536).astype(np.float32)
index.add(more)
# 搜索
query = np.random.randn(1536).astype(np.float32)
scores, indices = index.search(query, k=10)
# 整库快照(全量)
index.write("my_index.tv")
loaded = TurboQuantIndex.load("my_index.tv")
# 之后增量落盘:只写自上次以来的变化
index.sync("my_index.tv")3.3 带稳定 ID 的版本
import numpy as np
from turbovec import IdMapIndex
index = IdMapIndex(dim=1536, bit_width=4)
ids = np.array([1001, 1002, 1003], dtype=np.uint64) # 你的业务 ID
index.add_with_ids(vectors, ids)
# 查询返回你的业务 ID
scores, ids = index.search(query, k=10)
# 按 ID 删除:O(1)
index.remove(1002)
# IdMapIndex 的落盘扩展名是 .tvim
index.write("index.tvim")3.4 搜索时过滤
过滤搜索是 turbovec 常用的用法。典型做法是混合检索:先用外部系统(SQL / BM25 / 权限过滤 / 时间窗)把候选 ID 收窄,再把候选集喂给 search,让 SIMD 内核只在这些候选里打分:
import numpy as np
from turbovec import IdMapIndex
idx = IdMapIndex(dim=1536, bit_width=4)
idx.add_with_ids(vectors, ids)
# Stage 1: 外部系统收窄为候选 ID
allowed = np.array(db.execute(
"SELECT id FROM docs WHERE tenant=?", (t,)
).fetchall(), dtype=np.uint64)
# Stage 2: 在候选集内稠密重排
scores, ids = idx.search(query, k=10, allowlist=allowed)过滤在 SIMD 内核内、以 32 向量为块进行:完全没有允许槽位的块在查表打分前就被短路,块内不被允许的槽位在堆插入时丢弃。它的返回长度是 min(k, n_allowed),当允许的向量少于 k 时就返回实际数量,不会补 null 兜底。
3.5 框架集成
turbovec 提供了四个主流 RAG 框架的即插即用替代,换 import 即可保留原有 pipeline:
| 框架 | 安装 | 替换的默认存储 |
|---|---|---|
| LangChain | pip install turbovec[langchain] | InMemoryVectorStore |
| LlamaIndex | pip install turbovec[llama-index] | SimpleVectorStore |
| Haystack | pip install turbovec[haystack] | InMemoryDocumentStore |
| Agno | pip install turbovec[agno] | LanceDb |
四、性能:测的是什么,不能推出什么
性能数据全部来自项目自报(README 的 benchmark,v1.0),不是第三方独立复现。看清它的测试口径再决定怎么用:
- 数据:100K 向量、1K 个 query、k=64、5 次取中位数。
- 对比基准:FAISS
IndexPQFastScan。turbovec README 强调这是比论文更强的一个基线(FAISS 用的是 float32 LUT 与 k-means++ 训练码本)。
搜索速度(相对 FAISS IndexPQFastScan):
| 架构 | 4-bit | 2-bit |
|---|---|---|
| ARM(Google Axion,GCP c4a-standard-8) | 平均约 3.5×(3.4–3.7×) | 平均约 26%(22–29%) |
| x86(Intel Sapphire Rapids) | 平均约 3.4×(3.2–3.5×) | 平均约 20%(5–32%) |
在线增删(每操作,含 Python 调用开销):
- 单向量
add():6.3–19.7 µs,比 FAISS 快 7.6–13.9×。 - 100 向量批量
add():摊到 4.6–16.3 µs/向量。 IdMapIndex.remove(id):0.44–1.37 µs/次;FAISS 的remove_ids(IndexIDMapoverIndexPQFastScan)每次要重排存储编码,100K 规模下单次删除 0.19–1.02 秒。
召回率(校准后的 TQ+):
- OpenAI d=1536 / d=3072:在 k≤4 时召回即达 ≥0.997,k=8 时两边都到 1.0。
- GloVe d=200(低维,Beta 渐近假设更松):4-bit 在 R@1 领先 FAISS 约 1.9 个点,2-bit 在深 k 处 FAISS 仍保持微弱优势。
两个必须说清的边界:
- 速度优势主要来自 4-bit + 点积内核。2-bit 的短累加循环没法完全吃到 AVX-512 的展开收益,优势会明显收窄。想冲速度,首选 4-bit。
- 删除差距不等于" FAISS 慢"。这个差距来自
IndexIDMap的重排成本,是 FAISS「用重排换直立 ID」的设计代价,不是单纯实现不济。对查询多、删除少的场景,这点不足以构成替换理由。
内存口径:项目头条数字「31 GB float32 → 4 GB」对应的是 2-bit 存储(1536 维 float32 单向量 6 KB,2-bit 约 384 B,约 16× 压缩;4-bit 约 768 B,约 8×)。如果你用 4-bit,内存大约是 2-bit 的两倍。不要去追一个固定的「4 GB」数字,按实际位宽和维度算。
五、典型使用场景
5.1 推荐
- 本地 / 私有部署的中小型 RAG:不想走 FAISS 的 train 仪式,
pip install就能跑。 - Apple Silicon 或 ARM 单板上的本地检索:M 系列、AWS Graviton、Raspberry Pi 5、RK3588 这类,CPU 端就能跑得动。
- 多租户 / 按权限过滤的检索:业务上「只在这个用户可见的文档里搜」是常态,过滤感知内核正好。删除频繁、索引经常增删的场景(delete-heavy)收益最明显。
- 离线 / 完全内网:纯本地,无托管服务,可配合任意开源 embedding 模型组成完全隔离(air-gapped)的 RAG 栈。
5.2 不推荐
- 亿级以上召回:FAISS GPU / Milvus / Qdrant 集群仍是单机库打不动的主战场。
- 要 GPU 加速:turbovec 是纯 CPU SIMD,没有 GPU 路径。
- 要 sparse + dense 混合检索:Qdrant / Weaviate / Vespa 这类更适合。
- 需要长驻服务、多进程并发写:turbovec 是内存索引 + 文件快照,不是 Qdrant 那种长进程服务,也没有分布式能力。
- 低维 embedding:几百维以下时 TurboQuant 的分布假设失效,优势消失,直接用 FAISS 更稳。
六、和其他开源向量库的对比
| 项目 | 训练 | 内核级过滤 | 内存 | 速度 | 服务化 | 适合 |
|---|---|---|---|---|---|---|
| turbovec | ❌ | ✅ | 极低 | 高 | ❌ | Python 嵌入式 / 本地 |
| FAISS | ✅(多数索引) | ❌ | 中 | 极高 | ❌ | 研究 / 大规模 |
| Qdrant | ❌ | ✅ | 中 | 中 | ✅ | 生产服务 |
| Milvus | ✅ | ✅ | 高 | 极高 | ✅ | 亿级 |
| Weaviate | ❌ | ✅ | 高 | 中 | ✅ | 多模态 |
| LanceDB | ❌ | ✅ | 低 | 中 | ❌ | 嵌入式 OLAP |
| Chroma | ❌ | ⚠️ | 中 | 低 | ⚠️ | 原型 |
turbovec 的定位是「单机 + Python + 不 train + 过滤 + SIMD 快」。这几条一起满足的开源选项,2026 年年中之前基本是空白。
七、动手实验:30 分钟跑起来
# 1. 安装
pip install turbovec numpy
# 2. 最小 demo
python << 'PY'
import numpy as np, time
from turbovec import TurboQuantIndex
# 造 100K 1536 维数据 —— 注意量级,别一上来就上千万
np.random.seed(42)
N, D = 100_000, 1536
vecs = np.random.randn(N, D).astype(np.float32)
index = TurboQuantIndex(dim=D, bit_width=4)
t = time.time()
index.add(vecs)
print(f"add {N} took {time.time()-t:.2f}s")
# 搜索
query = np.random.randn(D).astype(np.float32)
t = time.time()
for _ in range(100):
s, i = index.search(query, k=10)
print(f"avg search {(time.time()-t)/100*1000:.2f} ms / query")
PY
# 3. 持久化 & 重载
python << 'PY'
from turbovec import TurboQuantIndex
# 假设上面 index 还存在
index.write("demo.tv")
loaded = TurboQuantIndex.load("demo.tv")
print("reload ok")
PY第一次跑建议先用 100K 量级验证,再逐步放大;千万级数据生成的随机数组本身就要占几十 GB 内存,demo 阶段没必要。
八、选用顺序与建议
如果你的场景是「本机 / 内网、中规模(百万到千万级)、Python 为主、有过滤或频繁删除需求」,turbovec 值得直接试:
- 先验证召回:用自己的 embedding 在 100K 子集上各跑一遍 2-bit 和 4-bit,确认召回达标。
- 默认 4-bit:速度与质量平衡最好;内存吃紧再降 2-bit。
- 用 IdMapIndex 而不是裸 TurboQuantIndex:只要你有真实业务 ID,就应选它,删除才 O(1)。
- 过滤走候选集 +
allowlist:别在 Python 层做后过滤,把候选 ID 直接喂给内核。 - 落盘用
sync而非反复全量write:索引大了以后增量写的成本是毫秒级。
注意一件事,这也是它引用的算法埋的雷:TurboQuant 论文在 ICLR 2026 评审期间引发了公开争议。RaBitQ 论文一作高健扬公开指出:TurboQuant 在方法上和 RaBitQ 高度相关却回避引用、在实验对比中称对方"次优",且用不同条件对比(如以降低条件跑 RaBitQ)。第三方研究者也在 OpenReview 提出其论文与推广博客的基准口径不一致(PyTorch vs JAX、FP32 对比)。turbovec 是实现方,算法争议不影响它的软件可用性,但在引用 TurboQuant 论文学术结论时要意识到这些质疑并未完全澄清。别在技术选型的论证里只依赖论文单方面宣称的收益。
九、参考链接
- GitHub: https://github.com/RyanCodrai/turbovec
- 论文: https://arxiv.org/abs/2504.19874(TurboQuant,ICLR 2026)
- PyPI: https://pypi.org/project/turbovec/
- crates.io: https://crates.io/crates/turbovec
- 许可: MIT
2026-08-23 · GitHub Trending 收录 · 文本矩阵「技术笔记」专栏
参与讨论
使用 GitHub 登录。欢迎补充事实、异议与实践。
讨论暂时无法加载。