返回博客列表

微软 tgrep:比 ripgrep 快 52 倍的三元索引搜索引擎,已集成进 Copilot CLI

2026-09-10T20:35:00+08:00
tgrep微软ripgrep三表达式索引代码搜索RustCopilot CLI

微软 tgrep:比 ripgrep 快 52 倍的三元索引搜索引擎,已集成进 Copilot CLI

ripgrep 扫每个文件,tgrep 扫索引。38 万文件的 Firefox 仓库,ripgrep 33 秒,tgrep 0.6 秒。

grepripgrep 是程序员每天用的工具,但它们的搜索方式是"暴力扫描"——每次查询都读遍所有文件,复杂度是 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  ---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

三个规律

  1. 仓库越大,优势越大。38.8 万文件的 gecko-dev 快 52 倍,1.6 万文件的 go 只快 7.5 倍——索引的价值随仓库线性增长
  2. macOS/Windows 上优势最大。Linux 的文件系统缓存已经很快,ripgrep 在 Linux 上本来就不算慢
  3. 唯一输的一场: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人工智能时代,转载请注明出处。

分享给朋友