turbovec 是第一个把 Google 论文里的 TurboQuant 算法落地的向量检索库。Rust 手写 SIMD零训练零调参比 FAISS 快 20%内存只要 1/8。读完你会知道怎么 10 行代码跑起来、TurboQuant 为什么能做到不训练、手写 SIMD 的架构思路、以及什么时候用它什么时候别用。 这个项目解决什么问题你有没有在本地跑过 RAG加载一个 embedding 模型把几千篇文档向量化存进 FAISS查个 top-10 大概 50ms——还行。然后文档涨到 1 万、10 万、100 万。内存从 300MB 涨到 3GB 再到 30GB查询延迟从 50ms 爬到 200ms。你开始调 IVF 参数、倒排索引、PQ 训练……这些都不是本地 RAG该有的体验。turbovec 给出的答案是同一个 1000 万文档的语料库1536 维 float32FAISS 需要 31GB RAMturbovec 只需要 4GB。而且搜索速度比 FAISS IndexPQFastScan 快 12%–20%。核心武器是 Google Research 的论文TurboQuantarXiv:2504.198742025 年 4 月提交。一种>原始向量 1536 × f326KB/条随机旋转使坐标分布集中LLoyd-Max 最优标量量化每坐标 → 4-bitTurboQuant编码1536 × 4bit 768B/条内存节省 87.5%1000万条60GB → 7.5GB 快速上手先确认环境需要Rust 1.75编译二进制或直接用 Python bindings。安装# Python 用户直接 pippipinstallturbovec# Rust 用户在 Cargo.toml 中添加# turbovec 0.410 行代码跑通importnumpyasnpfromturbovecimportTurboQuantIndex# 创建索引1536维OpenAI ada/text-embedding-3-small 的维度indexTurboQuantIndex(dim1536,bit_width4)# 模拟 10 万条向量实际用你的 embedding 模型生成vectorsnp.random.randn(100_000,1536).astype(np.float32)index.add(vectors)# 查询 top-10querynp.random.randn(1536).astype(np.float32)scores,indicesindex.search(query,k10)print(fTop-10 indices:{indices}, scores:{scores})# 持久化到磁盘index.write(my_index.tq)loadedTurboQuantIndex.load(my_index.tq)如果你需要稳定的外部 ID而不是add顺序用IdMapIndexfromturbovecimportIdMapIndex idxIdMapIndex(dim1536,bit_width4)external_idsnp.array([1001,1002,1003],dtypenp.uint64)idx.add_with_ids(vectors[:3],external_ids)# 搜索返回的是你的外部 IDscores,idsidx.search(query,k2)# ids 会是 [1003, 1001] 而不是 [2, 0]# O(1) 按 ID 删除idx.remove(1002)过滤搜索把 BM25、SQL、ACL 的结果当白名单这是 turbovec 最实用的功能之一——你可以把上游系统的召回结果作为 allowlist 传给searchturbovec 只在这些候选里做向量检索# 假设你从 PostgreSQL 查出 500 个候选 IDcandidate_idsnp.array([101,205,307,...],dtypenp.uint64)scores,idsidx.search(query,k10,allowedcandidate_ids)# 结果只来自 candidate_ids不会召回其他文档⚠️常见踩坑bit_width只支持 2、4、8传别的值会 panic向量维度目前上限约 32768受内部 SIMD 块布局约束search需要T而非mut T天然支持并发读取——多个线程可以同时查同一个索引⚙️ 技术原理核心问题量化器怎么做到不训练的FAISS 的 Product QuantizationPQ需要先拿一批训练数据用 K-means 学出子空间的聚类中心codebook。这个训练过程有四个问题需要代表性数据训练集分布必须和实际查询集分布一致训练耗时K-means 在百万级向量上跑很慢不支持在线添加加新向量后 codebook 可能过时需要重建索引调参地狱子空间数 m、每个子空间的 bits 数、IVF 倒排单元数……TurboQuant 用了一个巧妙的数学技巧绕过了训练随机旋转 坐标独立性。三步看懂 TurboQuant 原理第一步随机旋转对每个输入向量x ⃗ ∈ R d \vec{x} \in \mathbb{R}^dx∈Rd用一个固定的随机正交矩阵R RR做旋转变换y ⃗ R ⋅ x ⃗ \vec{y} R \cdot \vec{x}yR⋅x旋转后y ⃗ \vec{y}y的每个坐标都服从一个集中化的 Beta 分布。这是高维几何的一个经典现象高维球面上均匀分布的向量其单个坐标的分布会向 0 集中。第二步坐标间近似独立在高维空间d ≥ 256中旋转后不同坐标之间的相关性极低。这意味着你可以把每个坐标当作独立的一维信号来量化不需要对跨坐标的向量结构建模。这就是 TurboQuant 不需要训练 codebook 的根本原因——既然坐标之间几乎独立最优的标量量化器Lloyd-Max就够了。第三步两步法消除内积偏差对最近邻搜索我们需要的是近似内积而 MSE 最优的量化器会引入系统性偏差——量化后的内积和原始内积之间存在偏移。TurboQuant 的解法分两步先用 MSE 最优的标量量化器把每坐标压到 4-bit或 2-bit对残差原始值减量化值再做一次1-bit Quantized JL 变换——用一个随机±1向量和残差做内积得到一个无偏估计两步结果的和就是内积的无偏估计。输入向量 x ∈ ℝ^d随机旋转: y Rx每坐标独立 Lloyd-Max 量化y_i → h_i ∈ {0,...,15}4-bit重建: ŷ_i LUT[h_i]内积近似: ⟨x,q⟩ ≈ Σ(ŷ_i · q_i)残差: r y - ŷ1-bit QJL: 随机 ±1 向量投影残差修正: 无偏内积估计排序取 top-k和 FAISS PQ 的核心差异维度turbovec (TurboQuant)FAISS (PQ)训练需求零训练需要代表性数据训练 codebook在线添加直接 add即时可用需要重建或增量训练检索速度手工 SIMD 内核AVX-512/NEONSIMD via MKL/OpenBLAS内存效率4-bit 模式下 0.5B/维PQ 典型 4-8B/维过滤搜索原生支持不损失召回需要后过滤或 IVF 内嵌内存开销无需存 codebookcodebook 本身占用内存TurboQuant 的盲区几十亿向量的场景需要 IVF 做粗筛——turbovec 不做这个。不过说实话到这个量级你应该直接上 Milvus 或 Qdrant本地 RAG 根本碰不到这种规模。️ 架构分析turbovec 的代码组织很 Rust——模块边界小且清晰turbovec/ ├── src/ │ ├── lib.rs # 入口TurboQuantIndex, IdMapIndex │ ├── rotation.rs # 随机旋转矩阵固定 seed42 │ ├── codebook.rs # Lloyd-Max 最优量化器质心表 │ ├── encode.rs # 编码管道旋转→量化→打包 │ ├── search.rs # SIMD 搜索内核NEON/AVX2/AVX-512 │ ├── pack.rs # 打包/解包4-bit→密集字节布局 │ ├── id_map.rs # ID 映射索引 │ └── io.rs # 序列化/反序列化 ├── turbovec-python/ # Python bindings (PyO3 maturin) └── benchmarks/ # 性能对比数据几个让我印象深的设计1. SIMD 双架构一条#[cfg]搞定search.rs里根据编译目标自动选路径aarch64 走 NEONx86_64 走 AVX2/AVX-512BW。x86 端抄了 FAISS 的perm0 交错布局——32 个向量的量化 code 按寄存器宽度交错排MADD 每个 cycle 都吃满。4-bit 模式下一个 AVX-512 寄存器同时处理 128 个维度的查表。ARM 端用顺序布局M 系列芯片缓存策略不同实测比交错布局更快。2. OnceLock 的并发魔法这段把 Rust 的OnceLock用得很漂亮旋转矩阵、质心表、SIMD 块布局的初始化第一个调search的线程结账后续所有线程直接读缓存零锁开销add操作直接换掉OnceLockinvalidate 缓存你不用手动调prepare()也不用担心竞态——Rust 的类型系统替你保证了search要selfadd要mut self。3. 过滤搜索的零开销路径大多数向量数据库做过滤都是多取一些 后过滤——多取 k*N 个结果然后丢掉不在 allowlist 里的。turbovec 直接在 SIMD 内核里跳过整块不包含合法 ID 的向量 block32 个一组用BLOCKS_SKIPPED_BY_MASK原子计数器跟踪跳过比例。不够好的地方没有内存映射mmap索引只能全量加载到内存不能像 FAISS 那样映射磁盘文件。1000 万条 4-bit 向量约 7.5GB能跑但不能懒加载维度上限受 SIMD 块布局约束维度超过 ~32768 就无法编码。虽然 99% 的 embedding 模型远小于这个值但如果要索引拼接向量就受限社区年轻LangChain/LlamaIndex/Haystack 的集成已经写了见docs/integrations/但生态深度还远不如 FAISS✅ 优缺点 适用场景三个优点零门槛向量检索不需要懂量化理论不需要挑训练数据不需要调 IVF 参数。pip install turbovec之后 3 行代码就能跑内存效率接近理论极限Shannon 下界只差 2.7 倍常数实际 4-bit 编码的召回率在 GloVe 100 万数据集上和 brute-force 几乎无差别过滤搜索不牺牲召回allowlist 路径直接在 SIMD 内核完成不是后过滤——你拿 k 个就真的拿到 k 个两个缺点不支持倒排索引亿级以上数据量还是要去用 Milvus纯本地方案无分布式支持没有 server 模式、没有 sharding、没有副本谁应该立刻试试在本地跑 RAG、用 Ollama/llama.cpp 做 embedding 的人在资源受限设备上做向量检索树莓派、边缘设备——ARM NEON 内核表现极好需要隐私保护的场景数据不离开本机不需要托管服务谁应该再等等向量量级超过 1 亿——turbovec 的纯暴力搜索路线不适合依赖完整的向量数据库生态监控、备份、分布式查询需要混合检索dense sparse虽然可以把 sparse 结果作为 allowlist 传入但这套方案不如原生 BM25向量 hybrid 式方案直接总结turbovec 不是要替代 FAISS 或 Milvus。它的定位非常清晰——给 “本地 RAG” 场景一个不需要学习成本的、内存高效的、速度不输任何人的向量索引方案。如果你在笔记本上跑一个本地知识库turbovec 应该成为你的默认选择。项目地址github.com/RyanCodrai/turbovec | 论文arXiv:2504.19874
我拿 TurboQuant 把 1000 万文档塞进 4GB 内存,FAISS 用了 31GB
发布时间:2026/6/10 8:46:15
turbovec 是第一个把 Google 论文里的 TurboQuant 算法落地的向量检索库。Rust 手写 SIMD零训练零调参比 FAISS 快 20%内存只要 1/8。读完你会知道怎么 10 行代码跑起来、TurboQuant 为什么能做到不训练、手写 SIMD 的架构思路、以及什么时候用它什么时候别用。 这个项目解决什么问题你有没有在本地跑过 RAG加载一个 embedding 模型把几千篇文档向量化存进 FAISS查个 top-10 大概 50ms——还行。然后文档涨到 1 万、10 万、100 万。内存从 300MB 涨到 3GB 再到 30GB查询延迟从 50ms 爬到 200ms。你开始调 IVF 参数、倒排索引、PQ 训练……这些都不是本地 RAG该有的体验。turbovec 给出的答案是同一个 1000 万文档的语料库1536 维 float32FAISS 需要 31GB RAMturbovec 只需要 4GB。而且搜索速度比 FAISS IndexPQFastScan 快 12%–20%。核心武器是 Google Research 的论文TurboQuantarXiv:2504.198742025 年 4 月提交。一种>原始向量 1536 × f326KB/条随机旋转使坐标分布集中LLoyd-Max 最优标量量化每坐标 → 4-bitTurboQuant编码1536 × 4bit 768B/条内存节省 87.5%1000万条60GB → 7.5GB 快速上手先确认环境需要Rust 1.75编译二进制或直接用 Python bindings。安装# Python 用户直接 pippipinstallturbovec# Rust 用户在 Cargo.toml 中添加# turbovec 0.410 行代码跑通importnumpyasnpfromturbovecimportTurboQuantIndex# 创建索引1536维OpenAI ada/text-embedding-3-small 的维度indexTurboQuantIndex(dim1536,bit_width4)# 模拟 10 万条向量实际用你的 embedding 模型生成vectorsnp.random.randn(100_000,1536).astype(np.float32)index.add(vectors)# 查询 top-10querynp.random.randn(1536).astype(np.float32)scores,indicesindex.search(query,k10)print(fTop-10 indices:{indices}, scores:{scores})# 持久化到磁盘index.write(my_index.tq)loadedTurboQuantIndex.load(my_index.tq)如果你需要稳定的外部 ID而不是add顺序用IdMapIndexfromturbovecimportIdMapIndex idxIdMapIndex(dim1536,bit_width4)external_idsnp.array([1001,1002,1003],dtypenp.uint64)idx.add_with_ids(vectors[:3],external_ids)# 搜索返回的是你的外部 IDscores,idsidx.search(query,k2)# ids 会是 [1003, 1001] 而不是 [2, 0]# O(1) 按 ID 删除idx.remove(1002)过滤搜索把 BM25、SQL、ACL 的结果当白名单这是 turbovec 最实用的功能之一——你可以把上游系统的召回结果作为 allowlist 传给searchturbovec 只在这些候选里做向量检索# 假设你从 PostgreSQL 查出 500 个候选 IDcandidate_idsnp.array([101,205,307,...],dtypenp.uint64)scores,idsidx.search(query,k10,allowedcandidate_ids)# 结果只来自 candidate_ids不会召回其他文档⚠️常见踩坑bit_width只支持 2、4、8传别的值会 panic向量维度目前上限约 32768受内部 SIMD 块布局约束search需要T而非mut T天然支持并发读取——多个线程可以同时查同一个索引⚙️ 技术原理核心问题量化器怎么做到不训练的FAISS 的 Product QuantizationPQ需要先拿一批训练数据用 K-means 学出子空间的聚类中心codebook。这个训练过程有四个问题需要代表性数据训练集分布必须和实际查询集分布一致训练耗时K-means 在百万级向量上跑很慢不支持在线添加加新向量后 codebook 可能过时需要重建索引调参地狱子空间数 m、每个子空间的 bits 数、IVF 倒排单元数……TurboQuant 用了一个巧妙的数学技巧绕过了训练随机旋转 坐标独立性。三步看懂 TurboQuant 原理第一步随机旋转对每个输入向量x ⃗ ∈ R d \vec{x} \in \mathbb{R}^dx∈Rd用一个固定的随机正交矩阵R RR做旋转变换y ⃗ R ⋅ x ⃗ \vec{y} R \cdot \vec{x}yR⋅x旋转后y ⃗ \vec{y}y的每个坐标都服从一个集中化的 Beta 分布。这是高维几何的一个经典现象高维球面上均匀分布的向量其单个坐标的分布会向 0 集中。第二步坐标间近似独立在高维空间d ≥ 256中旋转后不同坐标之间的相关性极低。这意味着你可以把每个坐标当作独立的一维信号来量化不需要对跨坐标的向量结构建模。这就是 TurboQuant 不需要训练 codebook 的根本原因——既然坐标之间几乎独立最优的标量量化器Lloyd-Max就够了。第三步两步法消除内积偏差对最近邻搜索我们需要的是近似内积而 MSE 最优的量化器会引入系统性偏差——量化后的内积和原始内积之间存在偏移。TurboQuant 的解法分两步先用 MSE 最优的标量量化器把每坐标压到 4-bit或 2-bit对残差原始值减量化值再做一次1-bit Quantized JL 变换——用一个随机±1向量和残差做内积得到一个无偏估计两步结果的和就是内积的无偏估计。输入向量 x ∈ ℝ^d随机旋转: y Rx每坐标独立 Lloyd-Max 量化y_i → h_i ∈ {0,...,15}4-bit重建: ŷ_i LUT[h_i]内积近似: ⟨x,q⟩ ≈ Σ(ŷ_i · q_i)残差: r y - ŷ1-bit QJL: 随机 ±1 向量投影残差修正: 无偏内积估计排序取 top-k和 FAISS PQ 的核心差异维度turbovec (TurboQuant)FAISS (PQ)训练需求零训练需要代表性数据训练 codebook在线添加直接 add即时可用需要重建或增量训练检索速度手工 SIMD 内核AVX-512/NEONSIMD via MKL/OpenBLAS内存效率4-bit 模式下 0.5B/维PQ 典型 4-8B/维过滤搜索原生支持不损失召回需要后过滤或 IVF 内嵌内存开销无需存 codebookcodebook 本身占用内存TurboQuant 的盲区几十亿向量的场景需要 IVF 做粗筛——turbovec 不做这个。不过说实话到这个量级你应该直接上 Milvus 或 Qdrant本地 RAG 根本碰不到这种规模。️ 架构分析turbovec 的代码组织很 Rust——模块边界小且清晰turbovec/ ├── src/ │ ├── lib.rs # 入口TurboQuantIndex, IdMapIndex │ ├── rotation.rs # 随机旋转矩阵固定 seed42 │ ├── codebook.rs # Lloyd-Max 最优量化器质心表 │ ├── encode.rs # 编码管道旋转→量化→打包 │ ├── search.rs # SIMD 搜索内核NEON/AVX2/AVX-512 │ ├── pack.rs # 打包/解包4-bit→密集字节布局 │ ├── id_map.rs # ID 映射索引 │ └── io.rs # 序列化/反序列化 ├── turbovec-python/ # Python bindings (PyO3 maturin) └── benchmarks/ # 性能对比数据几个让我印象深的设计1. SIMD 双架构一条#[cfg]搞定search.rs里根据编译目标自动选路径aarch64 走 NEONx86_64 走 AVX2/AVX-512BW。x86 端抄了 FAISS 的perm0 交错布局——32 个向量的量化 code 按寄存器宽度交错排MADD 每个 cycle 都吃满。4-bit 模式下一个 AVX-512 寄存器同时处理 128 个维度的查表。ARM 端用顺序布局M 系列芯片缓存策略不同实测比交错布局更快。2. OnceLock 的并发魔法这段把 Rust 的OnceLock用得很漂亮旋转矩阵、质心表、SIMD 块布局的初始化第一个调search的线程结账后续所有线程直接读缓存零锁开销add操作直接换掉OnceLockinvalidate 缓存你不用手动调prepare()也不用担心竞态——Rust 的类型系统替你保证了search要selfadd要mut self。3. 过滤搜索的零开销路径大多数向量数据库做过滤都是多取一些 后过滤——多取 k*N 个结果然后丢掉不在 allowlist 里的。turbovec 直接在 SIMD 内核里跳过整块不包含合法 ID 的向量 block32 个一组用BLOCKS_SKIPPED_BY_MASK原子计数器跟踪跳过比例。不够好的地方没有内存映射mmap索引只能全量加载到内存不能像 FAISS 那样映射磁盘文件。1000 万条 4-bit 向量约 7.5GB能跑但不能懒加载维度上限受 SIMD 块布局约束维度超过 ~32768 就无法编码。虽然 99% 的 embedding 模型远小于这个值但如果要索引拼接向量就受限社区年轻LangChain/LlamaIndex/Haystack 的集成已经写了见docs/integrations/但生态深度还远不如 FAISS✅ 优缺点 适用场景三个优点零门槛向量检索不需要懂量化理论不需要挑训练数据不需要调 IVF 参数。pip install turbovec之后 3 行代码就能跑内存效率接近理论极限Shannon 下界只差 2.7 倍常数实际 4-bit 编码的召回率在 GloVe 100 万数据集上和 brute-force 几乎无差别过滤搜索不牺牲召回allowlist 路径直接在 SIMD 内核完成不是后过滤——你拿 k 个就真的拿到 k 个两个缺点不支持倒排索引亿级以上数据量还是要去用 Milvus纯本地方案无分布式支持没有 server 模式、没有 sharding、没有副本谁应该立刻试试在本地跑 RAG、用 Ollama/llama.cpp 做 embedding 的人在资源受限设备上做向量检索树莓派、边缘设备——ARM NEON 内核表现极好需要隐私保护的场景数据不离开本机不需要托管服务谁应该再等等向量量级超过 1 亿——turbovec 的纯暴力搜索路线不适合依赖完整的向量数据库生态监控、备份、分布式查询需要混合检索dense sparse虽然可以把 sparse 结果作为 allowlist 传入但这套方案不如原生 BM25向量 hybrid 式方案直接总结turbovec 不是要替代 FAISS 或 Milvus。它的定位非常清晰——给 “本地 RAG” 场景一个不需要学习成本的、内存高效的、速度不输任何人的向量索引方案。如果你在笔记本上跑一个本地知识库turbovec 应该成为你的默认选择。项目地址github.com/RyanCodrai/turbovec | 论文arXiv:2504.19874