Skip to content

改一处要花多少:寻址成本基准测试 ​

一句话结论:说清楚"改哪里",Markdown 要花 21 倍。

同一批文档、同一批 47 处修改。Markdown 里指认一个位置,只能把原文引一遍—— 13,109 字节;GEML 里只要一个地址——611 字节。新增的内容两边完全相同, 已从这个数字里排除,所以剩下的 21.45× 全部是格式造成的差异。

这不是引用一次性测量。跑一条命令就能复现:

sh
GEML_SRC=../geml node benchmarks/addressing-cost.mjs

为什么测这个 ​

一份文档写完之后,绝大多数时间花在改它,而每一次修改都分两步:找到那一处, 然后说清楚要改的就是那一处。人用眼睛和滚动条完成第一步,成本不进账; 但当改文档的是程序——编辑器插件、CI 脚本、大模型 agent——两步都要花钱: 读进来的内容是钱,写出去的内容也是钱,而且写出去的更贵。

Markdown 没有稳定的部件名。要指认一段话,只能引用它的原文,而且引到足够长、 在全文唯一为止。GEML 里每个块有地址,指认它就是写下那个地址。

这份基准测试量的就是这个差别。

怎么测的 ​

设计在第一次运行之前就已固定,脚本开头的注释是原文。

语料本仓库里同时存在两种格式的 4 份文档:规范与 history 规范,中英各一,16.8 KB–70.6 KB。同样的内容、同样的标题数量。A 组改 Markdown,B 组改 GEML,谁也没拿到更好写的那份
取样机械抽样:每第 N 个可寻址块,每份文档约 12 个,不做人工挑选。唯一排除的是文档级 H1——它的"块"就是整个文件,没有人把一整份文档当作一次修改
任务"替换块 B 的内容"。修改者知道要改什么,不知道它在哪,所以两组都必须先找。搜索短语由一条规则生成——块的第一行 ≥12 字符的内容——两组搜同一个短语
A 组(Markdown)grep -n 定位 → sed -n '命中,+45p' 读一个 46 行窗口 →(块装不下时再读一次)→ 写出一段唯一的 old_string,再写新内容
B 组(GEML)geml find 定位并拿到地址 → geml get <地址> 精确读那一块 → 写出地址,再写新内容

新增内容两组完全一样,所以不计入"说清楚改哪里"那一项——剩下的就是每种格式 为"指认位置"收的费。

有两处刻意让 A 组占便宜,好让结果是下限而不是漂亮案例:

  • 窗口取 46 行,是实测中一个 agent 在真实工作中所用窗口的中位数, 而不是一个保险的大窗口;
  • old_string 按最短唯一前缀计算——即仍然能正确命中的最省写法。

结果 ​

47 处修改,4 份文档:

MarkdownGEML比值
说清楚改哪里(写出去的字节)13,10961121.45×
读进来的字节107,86144,1552.44×
每处修改读进来的中位数2,1245653.76×
往返次数103941.10×
  • GEML 更贵的修改:0 / 47。 没有一处例外。
  • 46 行窗口装不下目标块:9 / 47(19%)。 这时 Markdown 一侧必须再读一次—— 而窗口开多大本来就是猜的,猜小了漏内容,猜大了白读。
  • 逐处修改的读取比值:最小 1.41× · 中位 3.10× · 最大 18.12×。

四份文档各自独立计算,结果一致,不是被某一份带偏的:

文档n读取说清楚改哪里
GEML-spec_CN.md122.13×10.7×
GEML-spec.md112.91×11.0×
GEML-history-spec_CN.md122.41×25.6×
GEML-history-spec.md122.30×32.7×

一个旁证 ​

A 组每处修改读进来的中位数是 2,124 字节。另一次完全独立的测量——把一个 agent 一整天真实编辑文档的会话记录逐条统计出来——得到 2,133 字节。 不同的语料、不同的方法,落在 0.4% 以内。这说明上面这套流程复现的是真实行为, 而不是为了好看而设计的模型。

这个测试没有证明什么 ​

  • 往返次数几乎没省(1.10×)。find + get 是两次调用,grep + sed 也是两次。 GEML 省的是每次调用搬多少东西,不是调用次数。
  • 块越小,差距越大。这批文档是规范,块偏大;在块更小的文档上读取比值会更高, 在少数极大的块上会更低。中位 3.10× 比总量 2.44× 更能代表"典型的一次修改"。
  • 这不衡量写作本身。要把一段话写好,需要读懂上下文——这部分成本两种格式一样。 这里量的只是找到它、指认它。
  • 只测了 4 份文档、47 处修改,全部来自本仓库。语料越多结论越稳,脚本可以直接 换语料重跑。

为什么差距在"说清楚改哪里"这一项上最大 ​

因为这一项两种格式的做法根本不同。

Markdown 里没有别的办法:要让一次替换命中正确的位置,就得把原文引够——短了会 撞上文档里另一处相同的文字,改错地方。文档越长、重复的措辞越多,要引的就越长。

GEML 里这一步是一个地址:#3-块 或 === table@412f8f61。地址不随内容变长, 也不随文档变长。

而且这一项花的是写出去的字节——对按 token 计费的系统来说,写比读贵。

复现 ​

sh
git clone https://github.com/geml-spec/geml && cd geml
cd geml-parser && npm install && npm run build && cd ..
GEML_SRC=../geml node benchmarks/addressing-cost.mjs
GEML_SRC=../geml node benchmarks/addressing-cost.mjs --json > result.json   # 逐条数据

本文数字对应 geml-spec 0c6be2c。基准两侧都真跑,语料就是仓库里那四份文档—— 所以改动 spec 或 CLI 之后重跑,末位会变(107,889 → 107,861 就是一次 spec 修订 带来的 28 字节)。数量级和结论不受影响,要引用精确值请自己跑一遍。

脚本、语料、抽样规则都在仓库里。要换成你自己的文档,改脚本顶部的 PAIRS—— 需要同一份内容的 Markdown 与 GEML 两个版本,geml <file.md> --from md --to geml 可以生成后者。

Code MIT · Specification CC BY 4.0