【免费下载链接】git-ai
Git designed for AI agents.
git-ai 是一个专为 AI Agent 设计的 Git 服务端(Graft):数百个 Agent 可以同时提交、并发落地(land)代码到同一分支。要回答"它扛不扛得住",项目内置了基准测试工具graft-bench——本文揭秘它如何用 20 个并发 Agent 压测落地吞吐与延迟,并盘点 18 个模糊测试目标如何守住整个解析层。🔍
为什么 AI Agent 场景必须做落地压测
传统 Git 的使用模式是:人克隆仓库、离线工作几小时、最后 push 一次。而 coding agent 的模式完全相反——成千上万个短生命周期任务,每个只改几个文件,都希望在几秒内落地,且全部挤在同一条分支上。
在这个模式下,"落地"走的是 compare-and-swap(CAS)重试:分支 tip 每动一次,落地的 Agent 就可能失败重试一次。并发越高,重试越多,服务端做的无用功呈指数放大。这正是graft-bench要量化压测的核心问题:当 20、50、100 个 Agent 同时落地时,重试、吞吐和尾部延迟会怎么变化?工具源码位于 cmd/graft-bench/,设计文档见 doc/graft/BENCH.md。
graft-bench 工作原理:一次压测如何发起
graft-bench通过项目自己的公开 SDK 发起请求,因此压测走的是 Agent 真实使用的 RPC 路径。一次运行的工作模型很直观:
- 启动
--agents个并发 Agent(每个是一个 goroutine),每个 Agent 在私有分支上提交--changes个变更,再逐一落地到目标分支; --branches N时,Agent 按轮询方式分散到 N 条目标分支,测量独立的分支内竞争;--conflict-rate控制一部分变更同时追加写同一个共享文件——这些变更落地时必然产生冲突,是可调的"压力旋钮";--seed固定变更计划,保证不同运行之间数字可比。
graft-bench --address unix:/var/lib/graft/run/gitaly.socket \ --repo bench.git --branch bench-land-1 \ --agents 20 --changes 5 --conflict-rate 0.2 \ --mode land --token "$GRAFT_TOKEN"四种压测模式(定义见 cmd/graft-bench/bench.go)覆盖了不同的落地策略:
| 模式 | 测量内容 |
|---|---|
land | 基础落地(rebase + CAS),冲突即失败 |
land-resolve | 冲突交给服务端 resolver 自动解决 |
land-batch | 服务端把同一分支的并发落地批量合并为一次引用更新 |
queue | 模拟串行 merge queue,每个变更落地后"等测试"一段时间 |
20 个并发 Agent:关键指标怎么读
压测结果默认输出表格,也可--format json落盘供后续对比。最值得盯的指标:
| 指标 | 含义 | 怎么看 |
|---|---|---|
attempts/landed change | 每次成功落地平均消耗的服务端 CAS 尝试次数 | 接近 1 说明无竞争;十几、几十倍说明重试风暴 |
lands/min | 每分钟成功落地数(吞吐) | 直接对比不同配置 |
p50 / p95 / p99 | 成功落地变更的端到端延迟百分位(含提交阶段) | Agent 的真实等待时长 |
batch size mean | 一次引用更新平均承载几个落地 | >1 说明批量合并生效 |
failed / conflicts | 失败数与其中冲突数 | 验证"只有计划内的冲突才失败" |
延迟百分位是 Agent 体验的直接刻度,下图展示了典型的 95 分位 / 99 分位 / 均值随时间波动的形态——p99 正是压测中最容易恶化的那条线:
批量落地基准:重试次数从 11 次压到 1 次 ⚡
项目用bench-batch.sh脚本(examples/graft/demo/bench-batch.sh)启动两台只相差[graft.land.batch]开关的 Gitaly 服务器,在 20 / 50 / 100 个 Agent 下各跑一轮。20 个 Agent、5 个变更、20% 冲突率的官方记录结果:
| 指标 | 批量关闭 (off-20) | 批量开启 (on-20) |
|---|---|---|
| 落地 / 失败 | 62 / 38 | 78 / 22(全部是计划内冲突) |
| attempts/landed change | 11.39 | 1.00 |
| 最大尝试次数 | 20(多个变更重试到放弃) | 1 |
| batch size 均值 | 1.00 | 9.86(最大 18) |
| lands/min | 212.4 | 411.9 |
| p99 延迟(landed) | 10022.5 ms | 2789.0 ms |
三个结论值得记住:
- 竞争被彻底消解:批量开启后每个落地变更只花 1 次 CAS 尝试,失败的全部是刻意制造的冲突——
acceptance-phase4.sh验收脚本会自动检查这类标准并打印 PASS/FAIL(examples/graft/demo/acceptance-phase4.sh)。 - 瓶颈转移到了串行通道:Prometheus 阶段指标(
graft_land_batch_phase_seconds)显示,land_member(把批次成员 rebase 进 tip)占批次时间的 56%~72%,每个成员约 80–115 ms 且串行执行;p99 里大部分其实是排队时间。 - 更大的批次不是万能的:关闭自适应批次大小的对照实验中,批次变大只是省了
update阶段,串行的成员落地成本依旧存在。
多轮结果可以直接并排对比:
graft-bench compare --format markdown off-20.json on-20.jsoncompare子命令实现见 cmd/graft-bench/compare.go。
模糊测试:18 个 fuzz 目标守护解析层 🧪
压测回答"跑得快不快",模糊测试回答"喂脏数据会不会崩"。git-ai 用 Go 原生 fuzzing(testing.F)覆盖了所有读取进程外数据的解析器与解码器:git 输出、hook 输入、changeset 日志、resolver 协议、分支策略、blame 输出等,共 18 个目标,总览见 doc/graft/FUZZING.md。
| 包 | 代表目标 | 守护性质 |
|---|---|---|
| internal/git/localrepo/ | FuzzParseMergeTreeError | 冲突解析不 panic、字段级可复现 |
| internal/graft/changelog/ | FuzzParseHookLine/FuzzEntryJSON | hook 行可精确重组、JSON 往返一致 |
| internal/gitlab/ | FuzzEvaluatePolicy | 放行的变更集只含规则允许的引用 |
| internal/graft/review/classify/ | FuzzGoMatchesGoScanner | 分类器与官方词法器结论一致 |
| tools/graft-agents/protocol/ | FuzzRequestDecode | 协议解码往返字节级一致 |
每个目标都配有真实格式的种子语料,且种子会在每次go test中运行——所以任何种子上的回归都会直接打挂常规测试套件,无需专门开启-fuzz。单独跑一个模糊器只需 20 秒:
go test -run=^$ -fuzz=FuzzParseHookLine -fuzztime=20s ./internal/graft/changelog官方最近一轮"每目标 20 秒"的摸底中,18 个目标共执行约2200 万次变异输入(FuzzRequestDecode单目标即近 800 万次),全部通过。
两个被真实找到的漏洞案例
案例一:merge-tree 输出的路径计数 panic。parseMergeTreeError(internal/git/localrepo/merge.go)用strconv.Atoi读取冲突路径数,负数计数会触发反向切片 panic,9223372036854775807这样的巨大整数则因整数回绕绕过边界检查。修复后,这些输入被固化为回归种子,存于internal/git/localrepo/testdata/fuzz/,此后每次测试都会重放。
案例二:分类器的 Go 词法误判。FuzzGoMatchesGoScanner发现了两组"拼写不同但分类器认为相同"的 Go 源码:>>> >与>>>>被三语言共享的超集运算符表错误地都读成>>>>;.666_0p0与. 666_0p0中,前者在 Go 里是浮点字面量开头,后者却是点号加数字。这意味着一次纯注释修改可能绕过逻辑变更的审批要求。修复落在 internal/graft/review/classify/lex_c.go:每种语言使用精确的运算符集合(goOps/javaOps/rustOps),修复后该目标连跑 90 秒、440 万次输入再未复现。
用可观测性定位落地瓶颈 🔭
基准测试告诉你"哪里慢",可观测性告诉你"为什么慢"。Graft 服务端每次落地都是一条完整的 gRPC 链路,可用 Jaeger 追踪逐层下钻——先看全局瀑布图定位耗时最长的 span:
再点开具体 span,查看它携带的命令参数、real_time_ms、exit_code等 tag,判断是 git 子进程慢还是策略/hook 慢:
配合bench-batch.sh每轮压测后自动抓取的 Prometheus/metrics,就能把 p99 劣化精确归因到quarantine、tip、land_member、update四个批次阶段——这正是上文"串行通道"结论的数据来源(指标定义见 doc/graft/LAND-BATCHING.md)。
如何快速复现这套基准测试
无需手工搭建:examples/graft/demo/demo.sh 会以 50 个 Agent 跑完land、land-resolve、queue三种模式并打印对比表;批量基准则直接运行examples/graft/demo/bench-batch.sh(支持AGENT_COUNTS、RATE、SEED等环境变量调整工作负载)。前置环境按 doc/graft/QUICKSTART.md 十分钟单机搭好即可。
使用小贴士:
- 每轮压测务必换新分支(
--branch),工具会拒绝复用含agents/目录的分支; - 数字强依赖机器、存储与 hook 配置,引用结果时请附上完整 JSON;
- 想看冲突的"延迟代价"而非"失败代价",对比同一
--conflict-rate下的land与land-resolve两行。
关键文件导航 🗂
| 内容 | 路径 |
|---|---|
| 压测工具入口与模式定义 | cmd/graft-bench/main.go / cmd/graft-bench/bench.go |
| 结果对比子命令 | cmd/graft-bench/compare.go |
| 基准测试完整文档 | doc/graft/BENCH.md |
| 模糊测试目标与发现 | doc/graft/FUZZING.md |
| 批量落地指标 | doc/graft/LAND-BATCHING.md |
| 单机快速上手 | doc/graft/QUICKSTART.md |
| merge 冲突解析模糊目标 | internal/git/localrepo/merge_fuzz_test.go |
| 分类器模糊目标 | internal/graft/review/classify/fuzz_test.go |
| 策略模糊目标 | internal/gitlab/policy_fuzz_test.go |
压测与模糊测试构成了 git-ai 的双重防线:前者用 20 到 100 个并发 Agent 量化落地路径的每一毫秒,后者用数千万次变异输入确保任何脏数据都无法击穿解析层。对想接入自家 Agent 流水线的团队来说,直接复用graft-bench的指标口径,就能把"落地性能"变成一个可对比、可验收的工程指标。
【免费下载链接】git-ai
Git designed for AI agents.
相关推荐
终极指南:用Mousecape打造你的macOS个性化光标世界
终极指南:用Mousecape打造你的macOS个性化光标世界 厌倦了千篇一律的macOS默认鼠标指针?想让你的Mac桌面焕然一新?今天要介绍的 Mouseca
桌面应用系统编程Pipy性能测试:基准测试与压力测试
Pipy性能测试:基准测试与压力测试 概述 Pipy作为一款高性能的可编程网络代理,其性能表现直接影响着云、边缘和物联网场景下的服务质量和用户体验。本文将深入探
后端API网关网络通信Megatron-LM性能测试:基准测试与压力测试
Megatron LM性能测试:基准测试与压力测试 概述 Megatron LM是NVIDIA开发的大规模Transformer模型训练框架,支持GPT、LLa
人工智能大模型预训练分布式训练深度学习强化学习
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考