Reed's News
← 返回精选

软件不再有理由变慢

AI 68 Jach 2026/8/22 4298 字 原文 ↗

前几天我看到一条疯传的推文,说那些吐槽大语言模型(LLM)写出臃肿、低效代码的人,等哪天大家都用极致优化的汇编重写一切时,肯定要自打嘴巴。我们还没到凡事都用汇编写的地步,但诺兰·劳森关于测试的观点——你可以选择现在要承担多少bug,我也曾用更直白的方式提过——如今在性能优化领域正变得愈发真实。

我在上一篇文章里提到,「过去高度专业化的性能优化工作,成本已降低了好几个数量级」,以往需要掌握稀缺技能的专人或团队才能完成的性能优化,现在只要会敲几句话的普通人就能搞定。这意味着,过去只有超大规模或超高收益项目才值得投入的各类优化,如今门槛已大幅降低。

对此,马克·布鲁克回应道

完全同意你最后的观点。为特定工作负载而非某类通用场景定制的动态软件,似乎会成为必然趋势(这本身也带来了各种有趣的风险与机遇)。这让我想起了

FFTW,还有许多早年演示场景(demoscene)里的奇技淫巧——它们专为解决某一特定问题(往往还针对特定硬件)而生,追求极致的小巧与快速。比如我记得有个演示程序,会把自身代码当作纹理重复利用,以此提升缓存局部性。

迈克尔·马利斯也指出:

最近有种论调很流行,说AI没什么用,因为「写代码从来都不是最难的部分」。这话在某些领域确实成立,但在另一些领域,写代码恰恰就是最难的环节。即时编译器(JIT)就是绝佳例子。对很多软件而言,JIT能显著提升运行速度,但JIT编译器之所以少见,恰恰说明过去实现它的难度太高,投入产出比太低。而LLM大大降低了门槛,让编写JIT编译器变得容易多了。这正是pgrust项目的核心思路。过去数据库是最难开发的软件之一,难度限制了它的发展;现在有了AI,我们能开发的软件类型也变得更具想象力。

针对一类工作负载优化

我们用FRE——上一篇文章里我们开发的正则引擎——来试试这个思路。还记得吗?我们让智能体循环优化了一个月,期间它可以访问rebar正则基准测试套件,最终得到了FRE。但它过度拟合了rebar测试集,直到我们提醒它还有一个预留测试集,它才将优化方案泛化,让预留测试集上的性能勉强过得去。如果一个「流水线式」开发的正则引擎,连预留测试集都跑不过成熟的竞品,那确实没什么理由用它,但FRE有个值得注意的点:它的原生AOT编译版本在处理长文本搜索时表现相当出色。由此我们想到,可以在ripgrep运行常规匹配器的同时,让另一个线程运行原生代码编译器,等编译完成后再切换到原生代码执行,这样整体性能应该会更好。当然,这种做法对短查询来说反而会降低性能,因为要分出一个线程去编译,但比起耗时几秒的查询,我更在意那些要跑几十秒甚至几分钟的查询,所以这个 trade-off 我完全能接受。

就像我们只用几分钟人力就能开发一个正则引擎一样,验证这个想法也只需要几分钟。我输入了几句话,智能体就完成了所需的代码修改(这要是让人类来做,得是不小的工程),还拿我codex历史记录里的真实ripgrep查询做了基准测试。对于几个非常简单的长查询,性能提升了2到4倍;但大多数查询更复杂,在符合AOT启用条件的代表性预留查询中²,我们只获得了约7%的速度提升。这个结果算不上惊天动地,但考虑到我只花了几分钟给codex写指令,而且它还在继续优化、性能应该还能进一步提升,这已经是个不错的成果了。

要不要建个索引?

说实话,这么做有点舍近求远:如果要反复在电脑上搜索文本,最直接的提速方法不是给正则匹配写原生代码编译器,而是建索引。但我想表达的是,这类过去需要大量时间和专业知识的技术工作,现在已经变得轻而易举。如果我们真要建一个文本索引,刚好我曾参与过BitFunnel——微软Bing的搜索索引,专为快速稳定的文本摄入优化,还获得了SIGIR最佳论文奖——所以我能想到几个实验方向,用来给整台机器构建一个快速本地索引(我见过的同类项目大多只索引代码目录,但真正拖慢我机器的是:codex会用ripgrep搜索包含大量生成文件的巨型临时目录,没找到结果就直接扩展到整台机器,所以我需要的是全磁盘索引,而不只是某些项目的代码索引)。

如果我在AI实验室工作,能用上最先进的模型,比如在Cerebras芯片或其他加速器上运行、能大幅提升token处理速度的模型,进而对搜索的负载和需求更高,那我可能会调研现有索引工具是否够用,或者要不要自己定制一个。虽然BitFunnel的开源版本「只」包含一个字节码解释器和一个JIT,但Bing内部版本有多个JIT编译器。过去,这种级别的优化项目是一项重大工程,但如今对某些这类项目来说,「周末就能搞定」[https://danluu.com/sounds-easy/]已经不再是玩笑。而我只用着每月200美元的普通账号,对我来说,一个稍快些的ripgrep加上现成的索引就足够了,所以这个「快速全磁盘索引」项目,就留给「读者(特指在AI实验室工作的读者)」作为练习吧。

优化的成本已大幅降低

自2025年11月以来,优化成本就开始急剧下降,甚至在那之前,公开模型就已经带来了一定的改变(当然AI实验室内部的工具可能更早实现了这一点)。比如在GPT-5.1或5.2时代,我完全不懂游戏AI,却尝试开发了一个Azul游戏AI,结果它成了当时世界上最强的Azul AI,优势还相当明显。从第二名AI的论文来看,我的AI在「AI算法」层面可能只略胜一筹,但真正拉开差距的是优化——我投入的时间大概只有对方的百分之一(通过阅读论文对比开发流程估算),而且我主要用笔记本电脑开发,对方则用了集群(这意味着我能用来做实验、调参的带宽要少得多)。比如,对方的AI是单线程的,而我的是多线程的。我还做了原生代码版本,以及一个糟糕的Wasm共享内存+JavaScript版本,针对两个不同版本用了两种不同的搜索架构(小型快速网络用极小极大算法,大型网络用蒙特卡洛树搜索),这两种架构需要完全不同的多线程实现,如果手动开发,工作量相当大。而且,我一开始让LLM自己选多线程算法(它的推理是错的),后来花了30分钟自学游戏AI的多线程算法,才让codex重新写了好几次多线程实现。

调试和验证这类多线程算法有一套标准流程,比如实现基于调试日志的重放功能,即使算法本身是非确定性的,也能复现bug。要是手动做这件事,可能要花几天到一周,但智能体可以轻松地循环完成(只要让它尝试重放日志,每次重放失败就添加针对非确定性的日志)。过去实现这类复杂优化时的大量繁琐工作,现在都消失了。

这一点也适用于其他很多复杂优化。我有过编写CPU微码、做CPU验证、优化搜索引擎索引等经历,遇到过很多这样的情况:「这个优化能提升2%的性能,但要花N个人日来验证它能正常工作」,然后根据投入产出比决定要不要做。现在,这个N已经大幅降低(具体幅度不定,但从人力时间来看,经常是1000倍、10000倍甚至百万倍;如果算资金成本,对比按token计费的成本和Bing开发JIT编译器的工程师薪资,大概是1000倍左右),值得做的优化数量也就大幅增加了。对于那些不确定是否有效的优化也是如此:过去我看到一个可能有用的优化,会想「要花M小时才能实现到可以准确评估性能影响的程度」,现在这类优化也值得尝试了。

回到游戏AI的例子,至少对我开发的那个AI来说,速度每提升一倍,Elo评分大概能涨100分(比国际象棋的涨幅高,我猜是因为Azul很少出现平局)。光是添加多线程,就能在高性能机器上轻松击败其他条件相当的AI。再加上10到20个大多数人嫌麻烦不愿手动实现的优化,实力差距会变得巨大,手动开发的AI根本无法与之抗衡³。

游戏AI的情况比大多数软件更棘手,因为很多优化会改变结果,而且没有简单廉价的方法判断「速度提升+结果改变」到底是好是坏。而且,正如我们之前提到的,目前公开的最先进模型在实验设计方面能力很差,所以我得先搭建好框架,让它们能判断一个优化是否有效,但框架搭好后,就和其他优化问题没什么区别了。我想做LLM优化的人也会遇到这类问题,但大多数优化问题要简单得多。

再举个例子:杰米·布兰登为了准备性能相关的面试,尝试了Anthropic现已公开的性能测试题。他做完后让Claude接着做,结果Claude的成绩好得多。他对比后发现,Claude做的很多优化是他想到了但还没来得及做的,而「另一些是疯狂的操作,我除非连续做几周才会尝试」⁴。他是个相当不错的性能工程师,也拿到了心仪的性能岗位offer,但在明确的优化问题上,他根本比不上一个像样的模型(我自己没试过这个题,但如果时间限制差不多,我估计也赢不了)。

针对特定工作负载优化

回到马克·布鲁克评论里的这句话:

为特定工作负载而非某类通用场景定制的动态软件,似乎会成为必然趋势。

这看起来是不可避免的。在对我文章的另一条回复中,pgrust的迈克尔·马利斯也表达了类似观点:

[关于pgrust优化的讨论]……我觉得这些优化实现起来很容易,我们可以根据客户的工作负载按需添加。

在开始写这篇文章前,我还没有任何框架或准备,就让智能体针对我的ripgrep查询做了特定工作负载的优化(不是切换到原生代码编译器,而是基于一组基准测试优化通用FRE引擎),整个启动过程只花了我两分钟。优化会在一组查询上运行,之后再用预留查询集验证。目前还在运行中,但初步结果看起来不错。经过一轮优化后,针对我的工作负载定制的版本,在预留测试集上比标准ripgrep快了2%,而且还在继续提速。2%对我本地的ripgrep使用来说不算什么,但考虑到我只花了几分钟时间,而且优化是我开始写这段内容时才启动的,这个结果我已经很满意了(注意这还没和原生代码编译器结合,如果结合得当,提升会更大)。别忘了,这是基于FRE正则引擎做的优化⁵——它原本在预留测试集上的性能远不如Rust正则引擎,而且在预留测试集上的提升一直很慢,因为我不懂正则工作负载,最先进的LLM在实验设计方面也不够好,无法进行无引导的开放式自我优化循环,所以我们之前找不到提升预留测试集性能的好方法。但如果我只关心自己工作负载的性能,我有大量数据,而且还在不断生成新数据。正如马克·布鲁克所说,如果数据中没有涵盖某些场景变化,我们确实要小心过度拟合,但即便如此,我们的处境也比以前好得多。

更普遍的情况是,如果你是亚马逊的马克·布鲁克,或是pgrust的迈克尔·马利斯,合理的做法不是只做一次性优化,而是和客户合作,试点用他们的数据为其定制优化,再想办法将方案规模化推广给更多客户。我所在的公司,这类工作并不是我时间的最佳用途⁶,但显而易见,这种模式正在向规模更大的公司走来。而对我来说,为个人工作流做这类实验只需要几分钟,所以在个人项目里尝试一下完全合理。

感谢杰米·布兰登、迈克尔·马利斯和马克斯·比特克提供的评论、修正和讨论。

附言:正如我在上一篇文章中提到的,有了代码智能体后,我运行一个实验并得到足够满足好奇心的结果的时间大幅缩短,但让结果真正严谨的时间却没变化,甚至更长了。如果还像以前那样写文章,我能分享的实验数量会远远跟不上我能做的实验数量。因此,我最近只是不断做实验,然后把结果分享给几个朋友。这次我想试试新方式:快速、非严谨地写出来,而不是让这些实验几年后只有几个朋友知道。和上一篇文章一样,我给自己定的目标是半小时内写完并完成整理,虽然没计时,但肯定超时了一点。

即便如此,写文章的时间还是太长,导致我分享最新结果的进度跟不上了。不过我还不想(至少现在不想)让LLM帮我写文章,而且我也不觉得能把整理数据和写文章的时间压缩到半小时以内。就这篇文章的长度而言,打字加上思考的时间大概就要20到30分钟,而且有时候看数据会发现不对劲,得仔细排查有没有问题(这篇文章里就出现了好几次这种情况,而且因为我没花太多时间,肯定还有其他没发现的数据问题)。

不管怎样,如果你对这种快速(肯定也更不严谨)的文章有什么看法,欢迎告诉我(X Bsky Mastodon)!

附录:软件没有理由再变慢了

我一直公开反对一种普遍观点:「X软件的开发者很菜,写出这么慢的代码应该感到羞愧」。因为编程技能有很多种,大多数程序员没有性能优化专长,而且从业务需求、就业市场等角度来看,他们也没必要去学这个专长——所以大多数项目的性能远不如性能专家能做到的水平,这是理所当然的。我能理解性能专家看到理论性能与实际性能之间日益扩大的差距[https://danluu.com/input-lag/]时,会觉得荒谬。我也认同这种差距很离谱,但反过来想,「理论上优秀的UI」和「我手动能做出来的UI」之间的差距,同样荒谬,但我也不会花时间去学怎么做好UI,理由和大多数人没必要学性能优化一样。

回到之前的例子:杰米·布兰登拿到了Anthropic的offer,除非你是OpenAI,否则你可能雇不起他,但你可以用一个能在限定优化问题上击败他的代码智能体。智能体没有他的判断力,在开放式问题上表现会更差(还记得吗?我们之前开发优化正则引擎时,只告诉它不要过度拟合,结果它在预留测试集上的表现比最好的正则引擎差了一个数量级;但后来我们告诉它预留测试集表现很差,它就把正则引擎性能提升到了能和二线竞品持平的水平,这已经比大多数代码的性能优化水平好太多了),但这已经足以让各种问题获得不错的性能。本文主要讨论的是后端性能问题,但如果你想优化LCP、INP等前端指标,智能体的表现也不差。

我仍然认为,如果某人开发的软件性能差,没必要因此觉得自己不行,但我确实认为,一个不懂性能优化、但能熟练使用LLM的普通用户(不需要专门懂性能优化),现在应该能开发出性能不错的软件。如果你只是让LLM去优化,它经常会做各种错误的操作,需要你去纠正,但这本来就是有效使用LLM的常态——所以,获得不错的性能不再是一项专属技能了。

附录:codex是怎么用ripgrep的?

以下是我机器上ripgrep查询的一些分布情况,我不保证这具有代表性。搜索模式的长度分布中,长模式的数量比我预期的多得多:中位数(p50)是55个Unicode码点(为了简单起见,我就称之为字符),这已经比我手动grep的模式长了,而90分位数(p90)达到了119个字符!

我们再看看正则表达式中的分支数,同样比我手动写的复杂得多。

另一个角度是看两者的相关性:正则表达式越长,分支数越多吗?是的。

这些超长正则表达式到底是什么?看一下就会发现,最长的那些大多是函数名或测试名的长分支,比如下面这个和FRE开发相关的正则:

fn (hot_byte_compiler_is_generic_only_and_anonymous_count_uses_auto_count|
one_pattern_count_spans_uses_the_retained_complete_span_session|
formal_compact_state_byte_visitors_coexist_with_native_count|
fixed_boundary_record_visit_matches_line_relative_reference_and_is_atomic|
unbounded_languages_refuse_finite_extraction_before_allocation|
formal_single_raw_span_sweep_preflight|
assert_exact_fixture_uses_formal_large_continuation_sweep|
url_only_compile_identity_binds_language_and_owner_mode|
url_only_compile_exact_limits_and_runtime_refusals_close|
url_only_compile_post_plan_allocation_faults_close|
url_only_owner_discriminator_is_stable_and_precharged|
url_only_compile_owner_is_strategy_and_operation_scoped|
formal_rebar_url_owner_is_compile_only_and_matches_oracle|
formal_rebar_url_exact_fixture_uses_certified_execution|
formal_fixed_schema_materialization_matches_both_record_oracles_and_controls|
formal_single_count_selects_compact_state_byte_complete_bound_visitors|
authenticated_bound_line_total_lf_free_domain_opportunity_exceeds_five_percent|
prepared_absolute_onepass_fuses_slots_and_preserves_pre_source_fallback|
authenticated_word_boundary_russian_compact_lowering_public_canary|
ordered_nfa_x86_epsilon_edges_bypass_the_assertion_call|
ordered_nfa_aarch64_epsilon_edges_bypass_the_assertion_call|
ordered_edge_dispatch_v2_is_target_neutral_deterministic_and_relocation_free|
ordered_edge_dispatch_v2_copies_canonical_tables_and_cap_falls_back_to_v1|
ordered_nfa_v3_composes_terminal_range_and_dispatch_without_data_relocations|
ordered_nfa_x86_terminal_range_emits_authenticated_reverse_scan|
ordered_nfa_aarch64_terminal_range_emits_authenticated_reverse_scan|
ordered_nfa_x86_boundary_assertion_cache_is_lazy_and_boundary_scoped|
ordered_nfa_aarch64_caches_repeated_assertions_once_per_boundary|
boundary_assertion_cache_requires_dense_exact_kind_reuse|
boundary_assertion_cache_selection_is_compiler_only_and_deterministic)

但也有些是有趣的数字构造,比如:

:(13[0-9]|14[0-9]|15[0-9]|16[0-9]|17[0-9]|18[0-9]|19[0-9]|20[0-9]|21[0-9]|22[0-9]|23[0-9]|24[0-9]|25[0-9]|26[0-9]|27[0-9]|28[0-9]|29[0-9]|30[0-9]|31[0-9]|32[0-9]|33[0-9]|34[0-9]|35[0-9]|36[0-9]|37[0-9]|38[0-9]|39[0-9]|40[0-9]|41[0-9]|42[0-9]|43[0-9]|44[0-9]|45[0-9]|46[0-9]|47[0-9]|48[0-9]|49[0-9]|50[0-9]|51[0-9]|52[0-9]|53[0-9]|54[0-9]|55[0-9]|56[0-9]|57[0-9]|58[0-9]|59[0-9]|60[0-9]|61[0-9]|62[0-9]|63[0-9]|64[0-9]|65[0-9]|66[0-9]|67[0-9]|68[0-9]|69[0-9]|70[0-9]|71[0-9]|72[0-9]|73[0-9]|74[0-9]|75[0-9]|76[0-9]|77[0-9]|78[0-9]|79[0-9]|80[0-9]|81[0-9]|82[0-9]|83[0-9]|84[0-9]|85[0-9]|86[0-9]|87[0-9]|88[0-9]|89[0-9]|90[0-9]|91[0-9]|92[0-9]|93[0-9]|94[0-9]|95[0-9]|96[0-9]|97[0-9]|98[0-9]|99[0-9])[0-9]:

它等价于:(?:1[3-9]|[2-9][0-9])[0-9]{2}:(用ripgrep在原始输入上搜索,两者性能差不多)。生成这个正则的完整流程是:

cargo clippy … | rg 'crates/fre-aot-regex/src/module.rs:' | rg NUMBER_REGEX | head -250

这对人类来说可能有点奇怪,但智能体似乎经常这么做。

再看看ripgrep查询的耗时:有相当多的慢查询,比如99分位数(p99)接近1分钟!99.9分位数(p999)接近10分钟!这段时间(一台笔记本电脑上约一个月;AWS主机上的查询和分布可能不同,但我没查)里最长的查询耗时接近2小时!

从命令行选项来看,不出所料,codex经常需要行号,而且不知为何,偶尔会使用PCRE2正则。

我就不放图表了,但还有几点值得注意:搜索模式的局部性相当低(约94%的模式只出现过一次),考虑到很多查询都很长,这也说得通;但被搜索文件的局部性相当高,一个文件被搜索后,短时间内很可能再次被搜索,这说明(对于足够小的文件)它们很可能在内存中被搜索。

另外,99%的查询是正则查询(1%是非正则字符串搜索),99.9%的搜索查询只包含ASCII字符,但从被搜索的文件来看,约45%是纯ASCII文件,55%包含Unicode,这个Unicode比例比我预期的要高。

在上一篇文章的草稿中,彼得·吉根指出:

正则实现也可以通过减少支持的特性来提升速度。有些实现不支持反向引用等特性。

这一点在这里也成立。我在这里做的特定工作负载优化相当表面,因为我只是给codex写了简短的指令,让它自由发挥(这通常不是使用codex的最有效方式),但如果有更详细的计划,针对我的查询常见场景做更聚焦的优化,应该能获得更大的提升。

  • 不过,正如我们在[那篇文章](https://danluu.com/(/benchpocalypse/)[以及之前](https://danluu.com/ai-coding/)讨论的,最先进模型的基准测试和实验设计能力还不够强,没有人类(或具备相关技能的角色)为智能体搭建基准测试环境的话,无法在通用场景下做到这一点。[返回]
  • 从我们之前的基准测试可以看到,即使算上编译时间,很多情况下原生代码编译版本的性能也不如Rust正则库。究其原因,这些通常是更复杂的查询,Rust正则库有算法层面的优化,而FRE的原生代码编译器退化成了简单实现(开发FRE的智能体在原生代码编译器上花的时间,远少于在「常规」正则引擎上花的时间)。[返回]
  • 我毫不怀疑,如果是真正懂AI的人手动开发AI——比如那些开发过顶尖围棋和国际象棋引擎的人——他们能在「AI算法」层面做得比我这个不懂AI的人更好,从而击败我的AI,但如果双方专业水平差不多,在投入时间相同的情况下,LLM写的版本肯定会占上风。 [返回]
  • 说起来,让智能体接着他的工作做,可能有点不公平,因为他的工作是一个起点,可能让智能体做得比从零开始好得多,所以我试过让智能体从零开始做这个任务,结果它的得分和智能体接着他的工作做的得分非常接近(另一个智能体快速检查后,没有发现作弊的迹象)。 [返回]
  • 如果我让智能体直接修改ripgrep的分支,性能可能会更好,但我好奇这样能不能解决FRE在我的查询上的过度拟合问题。 [返回]
  • 不久前,我把注册流程页面的大小从50MB降到了5MB,营收A/B测试显示这让营收增加了约0.5%。总的来说,我非常赞成先做简单易实现的优化,比如这个,比起开发定制编译器或其他高度专业化的技术工作,可能还有很多投资回报率更高的优化可以做。[返回]