前几天,我看到一条爆火的推文说,那些抱怨LLM导致代码臃肿缓慢的人,等他们把一切用超优化汇编重写后就会被打脸。我们还没到想用汇编写所有东西的地步,但诺兰·劳森关于测试的那句话——你可以选择现在想要多少bug——正在性能方面变得越来越真实,我之前不太优雅地提过这一点。针对我上一篇文章的一条评论——过去专业化性能工作的成本下降了数个数量级,以前需要拥有稀有技能的个人或团队才能做的性能工作,现在任何能打几行字的人都能做——这意味着你可以进行各种曾经过于昂贵、只有最大规模或最赚钱的项目才值得做的优化。马克·布鲁克回应道:完全同意你的结尾观点。动态定制软件,适配特定工作负载而非一类工作负载,似乎是非常可能的结果(这本身也带来了各种有趣的风险和机遇)。有点让我想起FFTW,还有一大堆古怪的老式演示场景技术,它们都是针对非常具体的问题(通常也是非常具体的硬件)做到极快极小。例如,我记得一个演示把代码复用为纹理来获得极佳的缓存局部性。最近有个梗流传,说AI帮不上忙是因为“代码从来都不是难点”。我认为在某些领域确实如此,但在其他领域,编写代码绝对是难点。JIT编译器就是一个很好的例子。对于许多软件来说,JIT编译器能大大加速代码。JIT编译器的稀缺让我相信,实现JIT编译器历来太困难以至于不值得去做。LLM降低了门槛,让编写JIT编译器变得容易得多。这正是pgrust背后的论点。数据库历来是最难构建的软件,也因此受到限制。现在有了AI,我们可以对构建的软件类型更加雄心勃勃。针对一类工作负载进行优化
让我们用FRE来试试这个想法——FRE是我们上一篇文章中构建的正则表达式引擎。回想一下,它是通过让一个智能体循环一个月来改进正则表达式引擎性能,并访问rebar正则基准套件而创建的。结果FRE严重过拟合于rebar,直到我们警告智能体我们有一个保留基准,这才促使智能体泛化优化,使性能在保留集上还算过得去。没有理由使用一个在保留基准上无法击败经过充分测试的正则表达式引擎的“软件工厂”正则表达式引擎,但FRE的一个显著特点是原生AOT编译版本在较长搜索中表现相当不错。我们注意到,按理说可以在ripgrep运行其正常匹配器时,在另一个线程中运行原生代码编译器,然后在编译完成后切换到原生代码,从而普遍获得更好的性能。当然,对于短查询,这会因为丢失一个编译线程而导致性能下降,但我更关心ripgrep运行几十秒或几分钟时的表现,而不是几秒的情况,所以我愿意接受这种权衡。同样,我们可以在几分钟的人工时间内构建一个正则表达式引擎,也可以尝试这个实验。我打了几个句子,一个智能体就去完成工作以实现这一点(对人类来说这将是一大块代码手术),并在实际来自我的codex历史的ripgrep查询上运行了基准测试。对于较长的查询,我们看到一些非常简单的查询有2倍到4倍的性能提升。但大多数查询更复杂,当我们在代表性的保留查询上运行时,对于应启用AOT的查询,我们获得了大约7%的加速。这不是惊天动地的结果,但对于花几分钟打字给codex来说也不算坏(而且它还在继续优化,预计会进一步提速)。建立索引?
这可以说是一件傻事,因为如果我们反复在计算机上搜索文本,加快速度的明显方法不是为正则匹配编写原生代码编译器,而是创建索引。但这里的重点只是,这类过去需要大量时间和专业知识的技术工作,现在可以轻松完成。而如果我们想构建一个文本索引,碰巧我参与过BitFunnel——Bing的搜索索引,专为恒定/快速文本摄入而设计,获得了SIGIR最佳论文奖——所以如果我们要为整个机器构建一个快速本地索引,我能想到几个可以尝试的实验(我见过的项目似乎旨在索引你的代码目录,但真正拖慢我机器性能的是,当codex决定对包含大量生成文件的巨大临时目录运行ripgrep,并且在未命中时扩展到查看整个机器时,所以我想要的是整个磁盘的索引,而不仅仅是某些项目的代码)。如果我是在AI实验室工作,能够访问在Cerebras芯片或其他加速器上运行的SOTA模型(这些加速器大大提高了tok/s,从而增加了搜索的负载/需求),我可能会调查现有的索引器,看看它们是否足够快,或者是否需要自己构建定制版。虽然BitFunnel的开源版本“只”包含一个字节码解释器和一个JIT,但Bing版本包含多个JIT编译器。一个达到这种优化水平的项目过去是一项重大工程,但现在“我可以在周末搞定”对于其中一些项目来说确实是可行的。凭着我每月200美元的廉价账户,我觉得一个稍快的ripgrep加上任何现成的索引就足够了,所以这个快速摄入全机索引项目也许可以留给“在AI实验室工作的读者”作为练习。优化很便宜
优化成本的急剧降低从2025年11月就开始了,甚至可能更早(公开模型出现之前),而且我相信AI实验室里的人早就享受到了。举个例子,在GPT-5.1或5.2时代,我对游戏AI一无所知,却尝试构建了一个Azul AI。结果它以相当大的优势成为了该游戏世界最强的AI。通过阅读描述第二强AI的论文,我认为我的AI在“AI”方面可能稍好一点,但主要赢在优化上,尽管花费的时间大概少两个数量级(根据论文中的过程和与我的过程对比估计),而且大部分时间在我的笔记本上工作,而不是拥有一组机器集群(这意味着用于运行实验、参数调整等的带宽要小得多)。例如,那个其他AI是单线程的,而我的AI是多线程的。由于我有原生代码版本以及一个糟糕的wasm共享内存+JavaScript版本,并且两个不同版本有两种不同的搜索架构,它们“需要”完全不同的多线程算法(极小且快速的网络用minimax,较大网络用MCTS),如果手工完成,这将是一个相当大的工程。而且,因为我让LLM基于它自己的(错误)推理选择了多线程算法几次,然后自己花了30分钟阅读游戏AI的多线程算法,最终我重写了(让codex重写)多次多线程算法。调试和验证此类多线程算法有一系列标准做法,比如实现从调试日志重放的功能,即使算法是非确定性的也能重现bug。仅这一项手工做起来可能就要几天到一周的工作量,但这正是智能体可以轻松循环完成的事情(只需让它尝试重放日志,每次没有得到完美重放时就插入非确定性日志记录)。过去让棘手的优化工作运转起来的许多繁琐之处已经消失了。这也适用于许多其他棘手的优化。从我编写CPU微码、进行CPU验证、优化搜索引擎索引等经历来看,我有大量经验看着优化想:“嗯,这会让性能提高2%,但要花N人天来验证这个棘手的优化是否有效”,然后根据是否值得花时间让优化生效来做决定。现在N已经大幅下降(变化不定,但就人类时间而言,经常是1000倍/10000倍/1000000倍,如果按计量费率比较token成本和编写搜索索引所用JIT编译器的Bing工程师的薪资,美元成本可能更像1000倍),因此值得做的这类优化数量大大增加。同样的情况也适用于你不确定是否有效的优化。我以前有时会看着一个不确定能否提速的优化想:“这需要M小时来实现,才能得到足够好的测量值来猜测性能影响。”现在更多这样的优化值得一试。回到游戏AI的例子,至少在我尝试的AI中,每速度翻倍似乎能获得约100 Elo(比国际象棋多,我怀疑是因为平局非常罕见)。仅添加多线程就足以在大机器上碾压其他可比的AI。如果再叠加10-20个大多数人觉得麻烦而不愿手工做的优化,实力差距是巨大的,手工编写的AI很难跟上。游戏AI的情况比大多数软件更烦人一些,因为你想要的很多优化实际上改变了结果,而且没有廉价简单的方法来判断速度提升加上结果变化在实际中是否更好。而且正如我们之前指出的,目前公开可用的SOTA模型在实验设计上相当差,所以我必须搭建它们用来判断优化是否良好的框架,但一旦框架到位,这就和其他优化问题一样了。我想研究LLM优化的人也必须处理这类问题,但大多数优化问题要直接得多。再举一个例子,在为性能面试做准备时,Jamie Brandon尝试了Anthropic现在公开的性能面试题。尝试之后,他让Claude从他停下的地方继续,结果得到了好得多的结果。当他查看Claude做了而自己没做的事时,他说很多优化是他想到了但还没来得及做的,“其他一些则是疯狂的东西,除非我花几周时间研究,否则绝不会尝试”。他是一个不错的性能工程师,也得到了他想要的性能职位,但在一个定义明确的优化问题上,他对付不了一个像样的模型(我自己也没试过这个问题,但我怀疑在远程可比的时间控制下我也对付不了)。
工作负载特定的优化
动态定制软件,适配特定工作负载而非一类工作负载,似乎是非常可能的结果。
这似乎是不可避免的。在对我文章的另一个回应中,pgrust的Michael Malis说了类似的话:[讨论pgrust优化]……我认为创建这些优化很容易,以至于我们可以查看客户的工作负载并按需添加它们。
没有任何框架或设置,就在我开始写这篇文章之前,我让一个智能体对我的ripgrep查询进行了工作负载特定的优化(不是原生代码编译器切换,只是基于一组基准对通用FRE引擎进行优化),启动大约花了2分钟。优化在一组查询上运行,然后有一个后续的保留查询集进行测试。优化仍在运行,但初步结果看起来很有希望。经过一轮优化后,工作负载优化版本在保留集上比标准ripgrep快2%,并且还在加速。2%对我本地的ripgrep使用来说不算什么,但考虑到这只花了几分钟时间,而且这里的优化在我开始输入这句话时就启动了并且仍在改进,我愿意接受2%的提升(注意这还没有结合原生代码编译器,如果适当结合,整体提升会更大)。而且请记住,这是利用FRE正则引擎,它在保留基准上比Rust正则引擎慢得多,并且在保留集上的改进停滞不前,因为我对正则工作负载一无所知,而SOTA LLM在实验设计上不够好,无法进行无引导的开放式自我改进循环,所以我们没有好的方法来提高保留集性能。但如果我在乎的是自己工作负载的性能,我有大量数据并且一直在生成更多数据。正如Marc Brooker上面指出的,如果出现旧数据中没有的新机制变化,我们必须小心过拟合,但我们仍然处于比以前更好的状态。在更一般的情况下,如果你是像亚马逊的Marc Brooker或从事pgrust的Michael Malis这样的人,那么不仅要做一次性的优化,还要与客户合作试点一个项目,使用他们的数据为他们优化,然后找出如何为客户规模化推广。我不在一个这样做最能发挥我时间的公司,但你能看到这对于规模更大的公司来说是即将到来的趋势,而且鉴于我只花几分钟就能为自己的个人工作流程运行这些实验,在个人项目中摆弄这类事情是相当合理的。感谢Jamie Brandon、Michael Malis和Max Bittker的评论/更正/讨论。P.S. 正如我在最近几篇文章中指出的,有了编码智能体,运行实验并看到足够满足好奇心的结果所需的时间大大减少,而让结果真正严谨所需的时间要么没变要么增加了,所以像我过去那样写文章意味着相对于我拥有的实验带宽,只能运行很少的实验。结果,我只是运行这些实验并与少数朋友分享结果。作为一个实验,我试图以非常快速和不严谨的方式写下来,而不是让这些实验多年只有几个朋友知道。和上一篇文章一样,我设定了半小时内写完并做完所有清理的目标,但没有计时,不过我确信我没能达到。即便如此,写这些东西所花的时间仍然太长,以至于我落后于分享近期结果,但我还不打算改用LLM写的帖子(暂时不?),而且我认为我无法现实地将整理数据和写这样一篇文章的时间缩短到半小时以内。仅就本文的长度而言,打字应该需要20-30分钟,包括停下来思考的时间,然后当我查看数据时,有时会发现某些东西看起来不对劲,需要更仔细地检查是否存在需要修复的问题(这里发生了好几次,我预计因为我没有花更多时间,还有其他我不知道的数据问题)。