软件没有理由再慢了
摘要
该文章指出,LLMs和AI工具正在降低软件性能优化的门槛,使得之前因成本过高而无法实施的自定义适配(如JIT编译器和正则表达式引擎)成为可能。
暂无内容
查看缓存全文
缓存时间: 2026/08/22 04:28
# 软件没理由再慢下去了
来源:http://danluu.com/perf-opt/
前几天我看到一则爆火的推文,声称那些批评LLM导致代码臃肿缓慢的人,等到LLM用超级优化的汇编语言重写一切时就会被打脸。我们还没到要用汇编编写所有东西的程度(http://danluu.com/pl-tokens/),但诺兰·劳森关于测试的某些观点演变——你现在可以选择想要多少个bug(https://nolanlawson.com/2026/08/16/you-can-just-choose-how-many-bugs-you-want-now/)——正如我在此处(http://danluu.com/ai-coding/)笨拙表述的那样,正在性能优化领域变得更加真实。
针对我上篇文章评论中关于“曾经高度专业化的性能优化工作成本已降低数个数量级”(http://danluu.com/benchpocalypse/)以及“过去需要具备稀缺技能的个人或团队才能完成的性能优化,现在任何能输入几句话的人都能完成¹”(http://danluu.com/perf-opt/#fn:B)的观点——这意味着过去对中小规模项目而言过于昂贵而不值得进行的各种优化如今触手可及——马克·布洛克回应道:
> 完全同意你的最终观点。针对特定工作负载(而非一类工作负载)的动态定制软件,很可能会成为常态。(这本身就伴随着各种有趣的风险与机遇。)让我想起了FFTW(https://www.fftw.org/faq/section4.html#whyfast),以及大量古怪的旧派演示场景技术——这些技术都致力于在特定问题(通常是特定硬件)上实现极速与精简。例如,我记得有个演示程序会重用自身代码作为纹理,以获得极佳的缓存局部性。
而迈克尔·马利斯则指出:
> 最近有个流行的说法称AI并无助益,因为“代码从来都不是难点”。我认为这在某些领域属实,但在其他领域,编写代码确实是难点。JIT编译器就是个典型例子。对许多软件而言,JIT编译器能极大提升代码速度。历史上JIT编译器的稀缺性表明,实现它的难度曾高到令其不值得投入。而LLM降低了入门门槛,使编写JIT编译器变得容易许多。这正是pgrust背后的理论基础。数据库历史上是最难构建的软件类型,也因此受限。如今借助AI,我们能够构建更具雄心的软件。
### 为特定类型工作负载优化
让我们用FRE(https://github.com/danluu/fre)——上篇文章构建的正则表达式引擎——来实践。回想它是由代理程序在获取rebar正则表达式基准测试套件(https://github.com/BurntSushi/rebar)权限后,循环优化一个月生成的。结果导致FRE严重过拟合rebar,直到我们警告代理程序存在保留基准测试集,它才调整优化策略使其在保留集上表现尚可。使用无法在保留基准测试中击败成熟正则引擎的“软件工厂”引擎并无特别理由,但FRE有个显著特点:其原生AOT编译版本在长查询搜索中表现优异。我们曾指出,理论上可以在ripgrep运行常规匹配器的同时,在另一线程运行原生代码编译器,待编译完成后切换至原生代码,从而普遍提升性能。当然,这通常会导致短查询性能下降,因为我们为编译占用了一个线程。但相比ripgrep仅运行数秒的情况,我更关心它运行数秒乃至数分钟的场景,因此愿意接受这种权衡。
就像我们能在几分钟人工时间内构建正则引擎一样,我们也能用同样短的时间尝试这个实验。我输入了几句话,代理程序便完成了实现该功能所需的代码重构工作(这对人类而言是相当繁重的任务),并在来自我codex历史记录的实际ripgrep查询上运行了基准测试。对于较长查询,我们看到针对几个简单查询实现了2倍-4倍的性能提升。但多数查询更复杂,当我们在代表性保留查询上运行时——对于应启用AOT²(http://danluu.com/perf-opt/#fn:E)的查询——我们获得了约7%的加速。虽非颠覆性结果,但为几分钟的codex输入时间而言已属不错(且它仍在进行更多优化,预计将进一步提升速度)。
### 构建索引?
这或许显得愚蠢,因为若我们需反复在计算机中搜索文本,加速的显而易见做法并非为正则匹配编写原生代码编译器,而是创建索引。但此处重点在于:过去需耗费大量时间和专业知识的此类技术工作,如今可轻松完成。若我们想构建文本索引,恰好我曾在BitFunnel(微软Bing搜索索引,专为常速/快速文本摄取设计,http://danluu.com/bitfunnel-sigir.pdf)项目工作,该项目曾获SIGIR最佳论文奖。因此,如果我们打算为整台机器构建快速本地索引,我可以设想一些尝试方案(我见过的项目似乎旨在索引代码目录,但真正拖累我机器性能的是codex在包含大量生成文件的巨大临时目录中运行ripgrep,失败后又扩展至整机扫描。因此我需要的是全盘索引,而非仅针对某些项目的代码)。
若我在AI实验室工作,能接触运行在Cerebras芯片或其他加速器上的最先进模型——这些设备大幅提升吞吐量(tok/s),从而增加搜索负载/需求——我或许真会调研现有索引器是否足够快速,或是否想自行构建定制方案。虽然BitFunnel的开源版本“仅”包含字节码解释器和单个JIT,但Bing版本包含多个JIT编译器。曾几何时,达到此类优化水平的项目堪称大工程,但“我能在周末完成它”(http://danluu.com/sounds-easy/)如今对某些项目已成现实。以我每月200美元的账户额度,我认为稍快的ripgrep配合任意现成索引已足够,因此这个快速摄取的全机索引项目或可留作“读者练习(适用于AI实验室工作者)”。
### 优化成本低廉
优化成本的急剧下降可追溯至2025年11月甚至更早的公开模型时期(当然,AI实验室内部人员接触的模型会更早)。以GPT-5.1或5.2时代为例,在对游戏AI一无所知的情况下,我尝试构建了Azul游戏AI。最终它成为该游戏史上最强AI,且优势显著。通过阅读描述第二强AI的论文,我认为我的AI在“智能”层面可能稍胜一筹,但主要优势在于优化。例如,那个AI是单线程运行,而我的AI是多线程运行。由于我同时拥有原生代码版本和一个可怕的wasm共享内存+javascript版本(http://danluu.com/game/tile/),以及针对两个版本的不同搜索架构(分别适用于极小快速网络的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³(http://danluu.com/perf-opt/#fn:A)已不现实。
游戏AI案例比多数软件更复杂,因为许多优化会改变结果,且不存在廉价便捷的方式判断速度提升与结果变化的综合效果在实践中是好是坏。正如前文(http://danluu.com/ai-coding/)所述,当前公开可用的最先进模型在实验设计上表现欠佳,因此我需手动搭建验证优化效果的框架。但一旦框架就绪,后续便与其他优化问题无异。我推测从事LLM优化的人员也需应对这类问题,但多数优化问题要直接得多。
再举一例:杰米·布兰登为准备性能面试,尝试了Anthropic现已公开的性能测试题(https://github.com/anthropics/original_performance_takehome/)。完成后,他让Claude接手未完成部分,结果获得更优表现。当他分析Claude所做而他未做的优化时,表示“许多优化是我想到但未及实施的,其他则是若非耗时数周研究我绝不会尝试的疯狂方案”⁴(http://danluu.com/perf-opt/#fn:C)。他是个合格的性能工程师,也拿到了心仪的性能岗位offer,但在明确界定的优化问题上,他毫无胜算面对一个像样的模型(我未亲试该问题,但推测在相似时间约束下我同样毫无胜算)。
### 工作负载特异性优化
回到马克·布洛克评论的这部分:
> 针对特定工作负载(而非一类工作负载)的动态定制软件,很可能会成为常态。
这似乎不可避免。在我文章的另一回复中,pgrust的迈克尔·马利斯表达了类似观点:
> [pgrust优化讨论]...我认为创建这些优化足够简单,我们可以分析客户的工作负载并按需添加。
无需任何框架或设置,在我开始撰写本文前,刚让代理程序为我的ripgrep查询进行了工作负载特异性优化(仅针对通用FRE引擎基于一组基准测试的优化,未涉及原生代码编译器切换),启动耗时约2分钟。优化在一组查询上运行,后续另有一组保留查询用于验证。该过程仍在进行中,但初步结果喜人。经过一轮优化,工作负载优化版本在保留集上比标准ripgrep快2%,且仍在持续提升。2%的提升对我本地ripgrep使用而言不算显著,但考虑到仅耗时数分钟,且优化从我输入此观点时启动至今仍在改进,我乐于接受这2%的收益(注意此未与原生代码编译器结合,若合理结合将带来更大整体提升)。需知这利用了FRE(https://github.com/danluu/fre)正则引擎⁵(http://danluu.com/perf-opt/#fn:F)——该引擎在保留集基准测试中比Rust正则引擎慢得多,且因我对正则工作负载一无所知、最先进LLM实验设计能力不足无法进行无引导的开放式自改进循环,我们在保留集上的改进一直停滞。但若我关心的是自身工作负载的性能,我拥有充足数据且持续生成新数据。如马克·布兰克所述,若出现旧数据未涵盖的范式转变等情况,我们必须警惕过拟合,但相比过去处境已大为改善。
在更广泛的应用场景中,若你是亚马逊的马克·布洛克或从事pgrust的迈克尔·马利斯,合理做法不仅是单次应用此方法,而是与客户合作试点项目,利用其数据进行优化,再设法为通用客户实现规模化。我所在的公司并非最适于此工作的场所⁶(http://danluu.com/perf-opt/#fn:T),但显而易见的是,更大规模的企业即将迎来这类技术。鉴于仅为个人工作流运行此类实验仅需数分钟时间,在个人项目中尝试这类优化完全可行。
*感谢杰米·布兰登、迈克尔·马利斯和马克斯·比特克提供的评论/修正/讨论。*
附言:正如我在最近两篇文章(http://danluu.com/benchpocalypse/,http://danluu.com/pl-tokens/)中所述,借助编码代理,运行实验并获得满足好奇心的初步结果的时间大幅缩短,而使结果真正严谨的时间未减反增。因此若按旧方式撰写,相对我的实验产能,能发布的文章将寥寥无几。故我转而直接运行这些实验,仅与几位朋友分享结果。作为尝试,我正尝试以非常快速且非严谨的方式撰写本文,而非多年积累后再发表。
相似文章
软件变慢的原因持续存在
作者认为,尽管LLMs使某些优化变得更廉价,但由于异步工作流中对延迟的容忍度增加以及其他实际约束,软件仍然可能变慢。
快速与硬核代码
本文探讨了大语言模型如何降低编程语言选择的摩擦,促使开发者采用如 Rust 和 Zig 等面向性能的语言来开发快速、小型的软件,并使得处理以前被认为困难的技术成为可能。
JIT编译代码在5μs内完成
文章探讨了AI辅助如何简化了创建具有亚微秒级编译时间的快速JIT编译器的过程,并通过一个基于Rust的正则表达式引擎示例进行了演示。
@Thom_Wolf: AI主导的软件世界中的结构变迁。一些初步反思(末尾有TL;DR):减少软...
AI缩减软件供应链,复兴单体架构,削弱遗留代码持久性,青睐强类型语言,并重构开源经济,对为LLM定制的新编程语言产生影响。
基准测试末日
本文揭示了大型语言模型如何轻松操纵性能基准测试,导致虚假的软件优化声明,例如一个过度拟合基准的正则表达式引擎。