14倍加速嵌入:Manticore Search 重建 ONNX 推理路径的工程实录

Manticore Search 团队发布工程博客,详细记录了如何将数据库嵌入生成速度提升约 14 倍——从 5-11 docs/sec 提升到 70-230 docs/sec。关键优化包括:用 ONNX Runtime 替换 Candle、发现 ORT 的 C API 线程安全特性实现无锁并发、关闭 intra_op_spinning、放弃批处理改为逐文档处理。这是一篇极具参考价值的 AI 推理优化案例。
2026年7月,Manticore Search 团队发布了一篇详细的工程博客,记录了他们如何将数据库的嵌入(embeddings)生成速度提升约 14 倍——从 5-11 docs/sec 提升到 70-230 docs/sec。这篇文章是 AI 推理优化的教科书级案例。
问题背景
Manticore Search 的 auto-embeddings 特性会在每次 INSERT 时由数据库本身运行嵌入模型,因此嵌入速度直接等于 INSERT 速度。旧路径基于 Candle,存在严重瓶颈:
- 吞吐量停滞在 5-11 docs/sec,无论怎么调线程数和批大小
- 并发调用在单个模型会话上串行化,无法利用多核
- 因 padding 导致批处理性能停滞
- CPU 远未满载,大量算力被浪费
技术选型:ONNX Runtime
团队选择 ONNX Runtime (ORT) 替代 Candle,因为 ORT 支持图融合、常量折叠和内核自动调优,且 HuggingFace 上主流嵌入模型已发布预融合的 model.onnx 文件。
关键突破:无锁并发
团队尝试了两种常见并发模式均被否定:
| 模式 | 问题 |
|---|---|
| 单一共享 Session + Mutex | 并发时吞吐量崩溃 |
| 每 CPU 一个 Session 的会话池 | 冷启动慢、内存浪费 |
关键发现:在 Linux/macOS 上,ORT 的 C Run() API 本身是线程安全的,可以无需任何锁在多并发调用者间共享一个 Session。Rust 的 borrow-checker 规则与底层库实际允许的行为不匹配,团队使用 unsafe 代码绕过检查。
两个最大优化点
- 关闭
intra_op_spinning(最大单项优化):ORT 默认让线程在调用间忙等(spinning),导致每个核心 100% 占用 CPU。关闭后线程在调用间让出 CPU,同时提升吞吐量并降低 CPU 使用率。 - 放弃 Worker 内批处理:padding tax 使得混合长度文本的批次大量计算浪费在填充 token 上。逐文档处理反而更高效,因为仅按文档实际 token 数计算。
最终配置
.with_optimization_level(GraphOptimizationLevel::Level3)
.with_intra_threads(0) // 让 ORT 自动选择(全部核心)
.with_intra_op_spinning(false) // 不在调用间忙等
.with_flush_to_zero() // 消除非正规数
.with_approximate_gelu() // 约10%更快的激活函数,无质量损失
性能数据
| 场景 | 旧路径 (Candle) | 新路径 (ONNX) | 提升 |
|---|---|---|---|
| 吞吐量范围 | 5-11 docs/sec | 70-230 docs/sec | ~14x |
| 单行 INSERT 延迟 | 200+ ms | ~14 ms | ~14x |
| 批量峰值 (1线程+batch64) | - | 233 docs/sec | - |
对开发者的启示
这篇文章提供了几个可迁移的优化经验:
- 质疑默认配置:ORT 的默认 spinning 行为是通用场景的优化,但在高并发数据库场景中适得其反
- 批处理不一定更快:当 padding 开销超过并行收益时,逐文档处理反而更优
- 阅读底层 API 文档:ORT C API 的线程安全特性是其 Rust 封装未暴露的,需要深入底层才能发现
- 测量驱动优化:全网格扫描(threads x batch)帮助团队发现非直觉的最优配置
关联推荐
- AI模型提速10000倍:Chalmers大学TITO模型 — 另一个 AI 推理优化的案例
- 知识蒸馏:让大模型"教会"小模型 — 模型优化的另一种路径
- 机器学习原子间势 — ML 推理在科学计算中的优化应用