微软 tgrep:比 ripgrep 快 52 倍的三元索引搜索引擎,已集成进 Copilot CLI
微软 tgrep:比 ripgrep 快 52 倍的三元索引搜索引擎,已集成进 Copilot CLI
ripgrep 扫每个文件,tgrep 扫索引。38 万文件的 Firefox 仓库,ripgrep 33 秒,tgrep 0.6 秒。
grep 和 ripgrep 是程序员每天用的工具,但它们的搜索方式是"暴力扫描"——每次查询都读遍所有文件,复杂度是 O(总字节数)。在 10 万+ 文件的大型 monorepo 里,一次搜索等 30 秒是常态。
微软开源的 tgrep 换了个思路:预先建三元索引(trigram index),查询时只碰可能匹配的文件。结果是在 Mozilla Firefox 的 gecko-dev 仓库(38.8 万文件)上,比 ripgrep 快 51.9 倍——33.4 秒降到 643 毫秒。
这个 2026 年 4 月创建、5 个月攒到 2710 Star 的 Rust 项目,已被集成进 GitHub Copilot CLI,为 AI 编码 Agent 的代码搜索提供加速。
本文基于 tgrep GitHub 仓库 全面拆解其架构设计、性能数据和使用方法。
为什么需要 tgrep
传统搜索工具(grep / ripgrep / ack)的瓶颈在于"每次查询都全量扫描"。在一个 38.8 万文件的大型仓库里:
- ripgrep(macOS arm64):33,402ms(33.4 秒)
- ripgrep(Windows):17,841ms(17.8 秒)
这对人是"等一下",对 AI Agent 是致命的——Agent 一次任务可能执行几十次搜索,每次 30 秒意味着一个任务光搜索就花 15 分钟。这正是 Copilot CLI 集成 tgrep 的原因。
tgrep 的核心洞察:大部分搜索不会匹配大部分文件。三元索引预先记录"哪些文件包含哪些三字符组合",查询时先用索引过滤出候选文件集,只扫这些文件——候选集通常是全库的 1% 以下。
三条命令上手
tgrep index . # 构建三元索引
tgrep serve . # 启动服务端(监听文件变更)
tgrep "fn main" . # 即时搜索——自动连接运行中的服务端
"启动一次服务端,之后永久即时搜索"——这是 tgrep 与 ripgrep 最大的体验差异。
架构:客户端/服务端 + 混合索引
tgrep <pattern> ---TCP---> tgrep serve (多客户端)
(客户端) |
HybridIndex
/ \
IndexReader LiveIndex
(mmap 磁盘) (内存覆盖层)
六层设计:
IndexReader:mmap 映射的磁盘索引,零拷贝读取。在排序的三元查找表上做二分搜索——不需要把索引加载到内存,操作系统按需将页映射进来。
LiveIndex:内存中的覆盖层,处理服务端启动后被修改的文件。所有新写入先到 LiveIndex,再定期刷盘。
HybridIndex:合并两层——查询时先看 LiveIndex(覆盖优先级高),再看 IndexReader。
Background Indexer:后台并行建索引,每批 1024 文件(按字节进一步切分)。冷启动时先返回空索引直到首轮构建完成;恢复部分索引时以 500 文件为批次处理。
Periodic Flush:每 5 万文件或 5 分钟,内存索引刷盘并切换 IndexReader——保持内存有界。这是 tgrep 在超大型仓库上内存可控的关键机制。
File Watcher:原生 notify 订阅实时更新 LiveIndex;当系统 watch 预算耗尽或注册失败时自动降级为轮询。
基准测试:18 个场景赢 17 个
官方在多个大型开源仓库上对比 tgrep 和 ripgrep(索引预建,查询平均延迟):
| 仓库 | 文件数 | 平台 | ripgrep | tgrep | 加速比 |
|---|---|---|---|---|---|
| gecko-dev | 388K | macOS arm64 | 33,402ms | 643ms | 51.9x |
| gecko-dev | 388K | Windows | 17,841ms | 463ms | 38.6x |
| gecko-dev | 388K | Linux | 1,195ms | 162ms | 7.36x |
| chromium | 504K | macOS arm64 | 41,806ms | 2,643ms | 15.8x |
| chromium | 504K | Windows | 24,576ms | 1,396ms | 17.6x |
| chromium | 504K | Linux | 2,404ms | 631ms | 3.81x |
| linux | 96K | macOS arm64 | 5,390ms | 256ms | 21.0x |
| linux | 96K | Windows | 3,280ms | 94ms | 34.8x |
| linux | 96K | Linux | 427ms | 46ms | 9.38x |
| rust | 62K | Windows | 1,489ms | 194ms | 7.69x |
| kubernetes | 31K | Windows | 1,342ms | 190ms | 7.08x |
| go | 16K | Windows | 592ms | 79ms | 7.53x |
三个规律:
- 仓库越大,优势越大。38.8 万文件的 gecko-dev 快 52 倍,1.6 万文件的 go 只快 7.5 倍——索引的价值随仓库线性增长
- macOS/Windows 上优势最大。Linux 的文件系统缓存已经很快,ripgrep 在 Linux 上本来就不算慢
- 唯一输的一场:Kubernetes on Linux,ripgrep 427ms vs tgrep 46ms,实际 tgrep 仍然更快,只是优势小。18 个场景赢 17 个
工程细节:六个值得抄的设计
1. 内存可预测的外部归并排序
默认使用 --index-strategy=external——三元组在固定大小的内存竞技场(arena)中累积,满了就溢出为排序的磁盘段,最后 k 路归并进索引。峰值内存与仓库大小无关,基本恒定。
Linux 内核(94,634 文件)实测:external 策略峰值 160.1 MiB,memory 策略峰值 2.2-3.76 GiB——17 倍内存削减,速度不降。
更妙的是:如果竞技场从不满,就直接走内存路径,小仓库不付任何额外代价。
2. 大文件 mmap 而非读入堆
64 MiB 默认上限以下的文件用内存映射而非堆分配。同一个 Linux 内核仓库,从堆读取的 197-200 MiB / 41-42 秒降到了 152 MiB / 27 秒——因为 20 MB 的生成头文件不再在每个 worker 里占完整堆空间。
3. 案例:Windows 大小写不敏感导致的 13.4 GiB 构建产物
一个真实的 Windows 场景:.gitignore 规则写了 QLogs,但 Windows 文件系统不区分大小写,git 设置 core.ignorecase 后会用不区分大小写的方式匹配。ripgrep 总是区分大小写,导致一个名为 qlogs 的目录(实际是 13.4 GiB 的构建产物)被遍历、读取、索引——占了整个语料库的 71%,给每次查询增加 16 秒。
tgrep 读取 core.ignorecase 并按仓库自身的方式匹配,走完和 git ls-files 完全一致的文件列表,代价仅 0.4 秒。这种对平台细节的深度适配,是通用搜索工具不会做的。
4. 热服务:建索引时也能查
后台建索引时查询不会被阻塞——冷启动返回空索引结果,恢复部分索引时返回已索引部分的结果。tgrep status 报告索引进度。
5. 文件名索引
--files 标志合并内容索引路径和一个只含路径的边车索引——让"列出文件名"类查询也能走索引而非遍历。
6. JSON-RPC over TCP
服务端使用 JSON-RPC 2.0 over 换行分隔 TCP——每条连接独立线程,多客户端同时连接。没有 HTTP 开销,协议简单到可以用任何语言实现客户端。
Agent 集成
tgrep 专门写了 AGENTS.md 指导 AI 编码 Agent 如何使用——Copilot CLI 已集成 tgrep 为 AI Agent 的代码搜索加速。
这和 Spotify Portal 的教训一脉相承:Agent 的 I/O 操作占大头,用索引替代全量扫描是降延迟的关键。区别在于 Spotify 路由的是模型调用(贵模型→便宜模型),tgrep 路由的是搜索方式(全量扫描→索引查找)。
使用方式
构建索引
tgrep index . # 当前目录
tgrep index /path/to/repo # 指定仓库
tgrep index . --index-path /tmp/idx # 自定义索引位置
tgrep index . --exclude vendor --exclude third_party # 排除目录
启动服务端
tgrep serve . # 启动(无索引自动建)
tgrep serve . --watch-mode poll # 纯轮询
tgrep serve . --poll-interval 60 # 轮询间隔
tgrep serve . --no-watch # 禁用自动刷新
tgrep serve . --exclude node_modules # 排除目录
资源调优
| 参数 | 默认值 | 效果 |
|---|---|---|
--max-memory <MB> |
RAM 的 50% (512MB-16GB) | 内存索引超此值则刷盘 |
--max-cpu <PERCENT> |
50 | 限制并行读和三元提取的 CPU 占比 |
--auto-save-mutations <N> |
5000 | 触发后台保存的累积变更数 |
--watcher-queue-cap <N> |
16384 | OS 事件缓冲队列大小 |
安装
# 从源码构建(需要 Rust 工具链)
git clone https://github.com/microsoft/tgrep
cd tgrep
make build
# 或直接 cargo install
cargo install --path tgrep-cli
和 ripgrep 的定位差异
| 维度 | ripgrep | tgrep |
|---|---|---|
| 搜索方式 | 全量扫描 | 三元索引查找 |
| 架构 | 单进程 | 客户端/服务端 |
| 首次查询 | 即时 | 需先建索引 |
| 后续查询 | O(总字节) | O(候选文件) |
| 小仓库 | 快 | 有建索引开销 |
| 大仓库 | 慢(30s+) | 极快(<1s) |
| 文件监听 | 无 | 实时更新索引 |
| Agent 集成 | 无 | Copilot CLI 已集成 |
选型建议:日常小仓库搜索用 ripgrep(无需建索引,即时);大型 monorepo 或 AI Agent 的频繁搜索场景用 tgrep(一次性建索引,后续永久即时)。
放回大图
tgrep 解决的是 AI Agent 时代的"搜索基础设施"问题——Agent 一次任务可能搜索几十次,每次 30 秒不可接受。tgrep 用经典的"空间换时间"思路(预建索引)把搜索从 O(n) 降到 O(log n) 量级,和 Spotify Portal(模型路由降 token 成本)、OKF Agent Memory(BM25 替代向量数据库)是同一个思想:在 AI 工程的每个环节,用正确的数据结构替代暴力计算。
微软把 tgrep 集成进 Copilot CLI 而非仅作为独立工具,也验证了一个趋势:AI 编码 Agent 的竞争力越来越取决于底层基础设施的性能——谁搜索快、谁索引准、谁路由对,谁就能在同样的 token 预算下完成更多任务。
作者: itech001
来源: 公众号:AI人工智能时代(the-ai-era)
网站: https://www.theaiera.top/
关注每日最新AI新闻和技术博客,主页有更多的文章的AI 技术参考:https://www.theaiera.top
本文首发于 AI人工智能时代,转载请注明出处。

浙公网安备 33010602011771号