基准测试末日
最近关于“漏洞末日”(vulnpocalypse)的讨论很多,我不是做安全的,也没什么可补充的;但与之密切相关、虽说相对没那么严重的一个问题——“基准末日”(benchmarkpocalypse),却似乎很少有人讨论。
如今,想要做出实打实的性能提升比以往任何时候都容易,但想通过钻基准测试的空子刷出虚假的性能提升,也比以往任何时候都容易。前者可能正在许多公司里悄悄发生,而后者我现在每周至少能见到一次。有人会声称自己优化了 X,相比现有软件取得了巨大的性能提升,可仔细一看,他们做的所谓优化只是在基准测试上跑得更快,并没有真正提升实际场景下的性能。这类事常常出现在“我们用 Rust 重写了 X”1之类的项目里,或是那些想融资、想卖产品的新创公司身上,当然其他类型的项目里也会出现。
当然,一直以来都有人拿不具代表性的微基准测试来吹嘘自己的得意之作有多厉害。编造一个不具代表性的微基准一直很容易,以后也依然如此。不同之处在于,过去想要在大型基准测试套件上作弊需要下很大功夫,而现在让 LLM 加个循环就能搞定。在大型基准套件上作弊还很难的年代,就有不少著名案例。比如当年大家还把 SPECint / SPECfp 当作工作站性能的代理指标时,CPU 厂商会去寻找能让基准测试里计算变快的所谓编译器“优化”,像 Sun 就曾找到办法让 179.art 在 SPECfp2000 中提升 12 倍。那会儿,资深工程师要花大量时间去寻找这类基准测试漏洞。而 LLM 不仅让这件事变得轻而易举,甚至会默认就这么干——除非你去审计结果,或者信任某个做过审计的人,否则过去可信的基准如今已经毫无意义。
与其去点名某个糟糕的宣称,不如拿FRE,这个我让 agent 搭起来的正则引擎来举例,我完全可以宣称它是世界上最快的正则引擎,因为它在相当全面的 rebar 正则基准测试套件上击败了 Rust 的 regex crate。但这是在把 agent 放进循环里跑了一个月、只叮嘱它“不要针对基准过拟合”却没有做任何实质性监督的情况下做出来的。总体来说,让 LLM 在基准上刷个高分相当容易,这次也不例外;用了大约两周时间就大致追上了Rust regex crate的性能,又花了两周在 rebar 上做到了快 1.4 倍2。但 agent 天生就喜欢钻奖励的空子、喜欢过拟合,除非你设置非常严格的护栏,否则一定会这样,而这次作为实验,我并没有那么做。
为了检验是否过拟合,我多少有些随意地3拿 ripgrep 的基准语料当作留出集(holdout)来测,结果在那些不会因算法复杂度爆炸而跑到天荒地老的用例上,它慢了 10 倍,还有一些用例慢到根本没法等它跑完。所谓快 40% 的说法,就此破产。
Andrew Gallant(也就是 BurntSushi)的 rebar 基准套件,单论基准套件已经算是相当全面了,但即便如此,agent 依然能轻易刷出高分,同时以一种未必能带来良好通用性能的方式过拟合。
下一步是用上我们之前聊过的一个技巧:不只是告诉 LLM 不要作弊,而是告诉它还有一套留出集会用来评判它。之后,LLM 在一定程度上泛化了性能,在留出集上总体大约慢 2.4 倍。考虑到我们是在和现存最快的通用正则引擎对比,这个成绩听起来还不错。但别忘了,这些基准都是由编程 agent 生成的。仔细看这些基准到底在测什么,有些用例本来就不该放进来,至少不该等权重计算。如果只看那些看起来真正重要的基准,FRE 在留出集上要慢 4 倍0,这比用上“告诉它有留出集”这个老套路之前要好得多,但离所谓快 40% 还是差得很远。
这件事有几点让我觉得很有意思:
- 即便明确指示 agent 不要为赢基准而钻空子或过拟合,想在一个并不简单的基准上以毫无意义的方式“获胜”也易如反掌
- 再一次,告诉 LLM 存在留出集,比单纯让它做泛化、不要过拟合或不要作弊更有效
- 虽然 FRE 的整体性能不怎么样,但在某些用例上它确实更快;总体而言,过去需要资深工程师才能写出的针对特定场景的专用代码,如今的编写成本已经大幅降低
先说第一点,难怪我会看到这么多站不住脚的宣称。过去,要做出像 FRE 这样能把性能伪造得足以虚假宣称快 40% 的东西,需要相当多的专业知识。至少,你得对字符串匹配算法、正则引擎有相当深入的理解,还得具备不错的通用代码优化和 SIMD 优化能力。FRE 还有一个能把正则编译成机器码的模式,所以你还得懂一些编译器知识。而现在,不管你是否想要这种作弊,只要敲几分钟键盘就能得到这种基准作弊的效果。
关于第二点,我很好奇这是否具有普遍性,但还没尝试足够多的例子来下结论。
关于第三点,实在没有理由去用一个几乎没花人力、靠 vibe coding 拼出来的、还比现有成熟、健壮、久经测试的库更慢的正则库,所以 FRE 这个产物本身没什么意思。我觉得有意思的是,LLM 能在多大程度上替代那些过去稀缺、专业且昂贵的知识。
过去,即便你有这些知识,你大概也不会为自己的特定工作负载去手写一个定制化的正则引擎。确实有一些大规模场景下人们会做这种程度的定制,比如我在做 Bing 索引时,代码里就包含了多个不同的编译器,因为当时有位同事想把性能压榨到极致;在搜索引擎里,编译时间和编译后的运行性能都很重要,而不同地方的权衡又不一样,与其像普通项目那样直接用解释器或用“常规代码”去遍历数据结构,不如为每个地方专门写一个编译器,性能会更好。那位写这些编译器的同事,如果去写类正则的代码,可能也会写出多个定制化的正则引擎,但既有这种专业能力、又有意愿、还有自由度能花这么多时间去为工作写这种高度专用代码的人,少之又少。如果你算一下这位 Bing 工程师(当时已是 Partner 级别,后来因在搜索索引上的工作晋升为 Distinguished Engineer)的成本,再对比一下让 LLM 循环跑起来的成本,就会发现,编写这类专用代码的成本已经下降了好几个数量级。
那些仍觉得 AI 是泡沫的人,看到文章前半部分可能会想:“当然了,AI 就会造假,所以造了个假的正则引擎”。但如果看实际结果,在留出集上只比世界上最快的正则引擎慢一半多一点,同时在许多真实工作负载上确实更快(大多数过拟合并不是针对某个基准模式做了特判,而是对大致某一类形态的东西做了优化,而对其他形态则没有),这离“假的正则引擎”还差得很远。事实上,还有一个原生编译模式,如果不计编译时间、只做重复搜索或超长文本搜索(这对很多实际用例来说是合理的),它在留出集上甚至能击败 Rust regex crate。如果我做 FRE 的目标是做出一个快的正则引擎,而不是看看几分钟人力能拼出个什么样的正则引擎,我怀疑它会在广泛的留出基准上都颇具竞争力(当然在放到多样化的生产负载上时才会暴露一些短板),即便是这个仓促拼凑的版本,在某些真实负载上也已经非常出色了。
所以,尽管 FRE 正则引擎的总体性能不如 Rust regex crate,但针对自身工作负载或用例做专门优化所带来的收益意味着,在某些情况下,插入一个自己定制的正则引擎是合理的,其他各种底层软件也是同理。你不必是 AI 的狂热拥护者,也会觉得在未来若干年内,我们可能会看到这类事情发生在更大的系统上,比如数据库。
感谢 Yossi Kreinin、Jamie Brandon、Peter Geoghegan、Luke Burton、John Spurling、Dennis Snell 和 Max Bittker 的评论、指正与讨论。
题外话:如这里所讨论的,有了 LLM,我花一点时间捣鼓点东西、满足好奇心所需的时间已经大幅缩短,而要把东西写出来、整理到足够严谨、能发到博客上的时间却基本没变(由于各种原因,我觉得甚至还变长了)。结果就是,我现在做的分析比以往任何时候都多,但只和少数朋友分享,却不公开发表。作为一次实验,我试着用非常快的速度写一些东西,对整理和严谨程度的要求远低于我平时发博客的标准;更像是和朋友闲聊时会说的话,而不是通常会放到博客文章里的内容。这篇文章的目标是用大约半小时写完,这样我就能利用午休时间完成,而不必专门抽出时间。如果你对此有看法,欢迎告诉我!
当然,这里有一个提醒:所有这些数字出错的风险都比平时更高。我大概只花了一两分钟看了一个基准就发现了一个问题,又花了一分钟看了另一个基准又发现一个问题。这两个都已经修了,但这意味着还有其他我没来得及去排查的问题。不过,就基准数据出错这件事而言,这反而非常真实!几乎每次我去深究基准数据时,比如这里,或者这里,数据都是错的。基准末日的另一个表现就是,至少就目前而言,LLM 很擅长做出糟糕的基准测试,所以即便你真的做出了性能提升,除非花大力气确保基准设置是合理的,否则从 LLM 生成的基准配置里根本看不出来。
附录:更多 FRE 基准细节
还有一件事是我在写完上文、但在发布之前发现的:LLM 声称 FRE 在 rebar 上比 Rust regex crate 快 40% 的说法也是错的。或者说,即便不算错,至少也有误导性。它实际上并没有按照 rebar 仓库里 https://github.com/BurntSushi/rebar 的方式来跑基准。我是在花了一分钟检查基准结果、发现两个问题后才去核对的。结果发现,尽管已经指示要按 rebar 的方式跑,LLM 还是改了接口,让 FRE 得以做一些能提升性能的优化。修正之后,FRE 不再是比 Rust 快 1.4 倍,而是慢了 1.5 倍(也“仅仅”比 re2 快两倍),所以最初的结果是双重造假:FRE 不仅高度过拟合了 rebar 基准,结果本身还涉及作弊。
但从好的方面看,这意味着 FRE 在 rebar 上(比 Rust 慢 1.5 倍)和在留出基准上(慢 2.4 倍)的性能差距,并没有之前看起来那么大,所以“告诉 LLM 有留出集”这一招的实际效果甚至比之前看起来还要好。
之后,我让 LLM 爬坡优化了几个小时,它声称 FRE 快了 1.28 倍,这听起来像是仅用几小时 LLM 时间就取得的巨大进步,但我又决定花一分钟去查作弊,结果发现了多个问题,包括一个在统计 (?s)^(.*)$ 匹配数量时根本不看 haystack(待搜索数据)就直接返回计数的例子。另一个作弊的例子是在本该逐行做的多行 grep 基准里直接做了跨行搜索。发现这些并不奇怪,因为把 agent 放进循环里跑一个月、却没有设定严格护栏时,这类事情就会发生。至于这究竟是让我的观点更有力还是削弱了它,还不太好说,但在又修复了一批这类问题后,FRE 又回到了慢 1.4 倍的状态。让 agent 又跑了一整夜后,FRE 据称又回到了快 1.5 倍。
由于我最初的目标是看看把当前(公开的)SOTA agent(GPT-5.6 Sol)放在循环里、在没有太多监督的情况下去解决一个非平凡的代码优化问题会发生什么,而不是花更多时间去把基准修得更公平,我就到此为止,只放几张结果图。
总体来看,相对于 Rust 和 RE2,FRE 在 rebar 基准上往往表现更好(而如上所述,其中很大一部分是过拟合所致),但并非全盘如此(下图并不一定与文中提到的数字完全一致,因为 agent 在不断改动,任何快照都只是即时的估计,很快就会过时):
如果你对特定基准或特定类别的 rebar 基准上的性能感到好奇,可以参考下表(比值大于 1 表示 FRE 更快,小于 1 表示更慢):
还有一种 AOT 编译器模式,需要花很长时间把正则编译成原生代码后再运行。并非所有情况都支持 AOT,但以下是支持的那些用例的结果。可以看到,AOT 编译器非常慢(在编译时间的基准上输得很惨),而且尽管花了相当多的时间编译,结果往往比标准的 FRE 正则引擎更慢(当然在许多情况下也更快)。
然后是留出基准。如上所述,对于非 AOT 的 FRE 代码,在留出集上的性能不如在 rebar 上好。而且也如上所述,考虑到这是类似 ripgrep 的工作负载,“热搜索”(hot search)那组基准可能比其他组更重要,所以 FRE 的实际结果比总体分数看起来还要差。
这里有一点值得注意:在留出基准中那些不把编译时间计入、而是反复执行搜索的用例里,AOT 模式的 FRE 在基准上是胜出的。对很多用例来说,你可能不想要一个需要数秒才能编译好的正则,但在不少场景下这是可以接受的,比如像 ripgrep 或 Silver Searcher 这样的工具,完全可以先用一个能立即开始匹配的正则跑起来,同时在另一个线程里编译,等更快的匹配器编译好后再切换过去。考虑到我的 CPU 有相当一部分时间都花在长时间的 ripgrep 搜索上,像这样的策略似乎能提升我个人工作中的性能。在 LLM 出现之前,花精力去写一个优化的正则编译器可能并不划算,但现在用一些 token 就能做到了。
还有一点需要注意,这个对比可以说并不公平,因为这是在配备 SVE/SVE2 的 ARM Graviton 机器上跑的,而 FRE 做了 SVE/SVE2 优化。在 LLM 之前,为每一种 SIMD 指令组合都去优化正则可能并不值得,但有了 LLM,生成还算过得去的 SIMD 优化就相当容易了。我认识一些人类专家发现自己通常能做得比 LLM 更好,比如 Jay Stelly 就说,上次他尝试让 LLM 生成 SIMD 代码时,迭代了二十多次才达到他想要的质量。但反过来说,LLM 有能力在给定的时间内尝试比人类多得多的优化方案,所以即使某一个具体的优化不如人类专家做得好,总体表现依然可以相当不错。
还有本文讨论的过拟合问题。取决于具体情境,这个问题从非常容易解决到有点难解决都有可能。我在这里故意没有很努力地去解决这个问题,只是想看看会发生什么,但在做这个 Azul AI 时(仅举一例),我确实在没有付出过大代价的情况下就解决了这个问题,但很多那种宏大的基准宣称,往往出现在人们几乎没花精力去避免过拟合、甚至是反向用力的情况下。在前 LLM 时代,人们就常常会挑极不具代表性的微基准来炫耀自己的得意之作,这至少在潜意识层面,就是在反向避免对基准的过拟合。由于人的本性,我不认为人们会停止做出误导性的宣称,而如今做出误导性宣称比以往任何时候都更容易,所以我们自然会看到更多这类情况。
需要注意的是,虽然本文讨论的是非 AI 软件,但上面所说的一切对 AI 软件更是加倍适用。例如,我看到很多人留言说 Kimi K3 已经达到了 Fable(5)的水平。但我认识的每一个用过它的人都发现,它比 GPT-5.6 Sol 和 Fable 差得多。我并不是说它不是一项令人印象深刻的工程成就,但在各种真实任务上的性能并没有达到它在基准上所表现的水平。这甚至适用于各种偏评测类的问题,比如有朋友在 ICFP 2026 竞赛题目上尝试不同编程 agent 时也发现了这一点。安全问题也是如此,我毫不怀疑 AI 实验室会把这些放进评测里,比如我的一位同事尝试用 Kimi K3 来扫描我们软件中的漏洞,发现它找到的漏洞大约只有 GPT-5.6 Sol 的四分之一,没有发现任何 GPT-5.6 Sol 没发现的漏洞,除了成本之外在任何维度上都没有优势。而我认识的那些在用更便宜的模型来发现真实安全问题的人,用的则是其他模型,比如 GLM-5.2,它们在基准上表现更差,但在实际中表现更好。
回到 FRE,还有一点需要说明,留出基准只是从 ripgrep 基准配置中由 agent 出于未知原因任意挑出的一小部分。我曾让 agent 拉取完整的基准套件,但到本文截稿时还没跑完,所以全部跑完后结果会如何,我还不知道。
说来好笑,我对那些人们最持怀疑态度的项目反而有一定信心,比如每次在某处看到 pgrust,都会有大量质疑的声音。但在没有细究他到底在优化什么的情况下,我会相信他们没有在基准上动手脚,因为 Michael Malis 发起了这个项目(并且仍在参与)。我过去会对进入视野的大多数基准宣称做一番细致审视,但现在这类宣称太多了,我实在没时间一一细看,通常会默认这些宣称在精神上是虚假的(即便技术上正确),除非有理由让我相信并非如此。当然,这有时会判断错误(比如如果我不认识 Michael Malis,我大概也会以为 pgrust 不过是又一个低质量的“让 LLM 重写一下”的项目),但 LLM 实在是太擅长对人类注意力进行拒绝服务攻击了,我也不知道还能怎么做(我试过让 LLM 去分析性能宣称,虽然结果和我自己去看时的判断有相关性,但往往错得相当离谱)。
一个人花几秒钟(或者如果用了合适的框架,甚至完全不用花自己的时间)就能生成出需要别人花几分钟到几小时才能理解的东西。这是个值得另起一篇来聊的话题,但从和人们聊他们在工作中的经历来看,如今那些在这方面规范不佳的公司,生产力正深受其害。
[return]这里指的是所有 rebar 基准的几何平均值。这可能不是一个合适的指标,因为它隐含地认为每个基准都同等重要,但很可能并非如此。与 SPEC CPU 不同,rebar 基准并不标榜自己能提供一个试图代表整体性能的有意义的汇总指标(仓库里实际上注明它是“在 curated 的任务集上衡量部分正则引擎相对速度的有偏标尺”)。但是,要得到一个有用的汇总指标,你得对人们在实践中如何使用正则有很多了解,而我对此几乎一无所知。谁知道呢,也许你应该有两个不同的数字(就像 SPEC CPU 里的 SPECfp 和 SPECint 那样),或者十个、一百个,因为人们应用正则的方式多种多样。
[return]我最先看的几个正则基准已经被收录进了
rebar,所以没法当作留出集。而且,如前所述,当前 SOTA 的 LLM 并不擅长做基准测试,所以除非我对正则性能有足够了解、能判断基准套件的质量,否则也没法信任 LLM 自己提出的留出基准。由于我对字符串匹配算法或正则性能几乎一无所知,这条路也走不通。后来发现 BurntSushi 同时也维护着 ripgrep 及其基准,这些基准规模很大,没有被打包进
[return]rebar,所以我试着把它们当作留出集来用。
随机一篇博客
评论
登录后参与讨论