余音再起

系列「太阳底下无新事」开篇——计算机科学的那几个老想法

太阳底下无新事。这句老话通常当牢骚讲——万物打转,终无所成。我想把它偷过来,反着读。若果真没有全新之事,那你手头要做的,多半早有人以某种形式做过了。在诗人,这是绝望;在工程师,这是整门生意的根基:第二遍,理应比第一遍便宜。

这是一个系列的第一篇。系列要写的,是那么几个想法:计算机科学总在给它们换上新衣,然后一遍遍重新「发现」——根本到你只要认出一个,就会在十几个名目底下四处撞见它。开篇选的这一个,说来也巧,讲的正是「认出见过的东西」本身。姑且称之为去重(deduplication):同一份工,绝不付两次全价。

听着像个实现细节,像省磁盘的小技巧。其实它是我们手里最深的一招,还会沿着抽象层级一路上行,收在一个意想不到的地方。

跨时间:同一份工,晚些再来

最低一层,程序员人人皆知。算过的东西还会用到,于是留下答案,省去劳作。记忆化(memoization)是它的纯粹形态:函数把每个结果记进一张表,下回遇到一模一样的调用,照表奉还。这一招在 1968 年的一篇论文里得名「memo function」;耐人寻味的是,那篇论文把它归到了机器学习名下——记住,本就是学习的一种。动态规划是同一个直觉,只是公关做得更好:重叠的子问题各解一次,写下来,指数就塌成了多项式。

镜头拉远,这一招铺满了整个技术栈。缓存,是跨时间的去重。Git 把一份文件的内容只存一次,把哈希发给每个引用它的人。构建系统跳过输入未变的目标,是在拒付第二次。LLM 同时服务一千场对话、共享同一段冗长的系统提示时,好的系统把那段前缀算一次,用一千次。所有这些底下是同一笔买卖——以空间换时间:花一点内存收着答案,就永远不必再花时钟周期去重建它。面具千副,脸只一张:此事我做过,不必再做。

跨实例:同一份工,同时进行

再上一层就难认了,因为这里的重复不摊在时间里,而是齐刷刷并排站在你面前:许多几乎相同的计算,同时发生。

我自己的一件工作就落在这一层。假设你要审计一台 web 服务器:把它声称做过的一切重跑一遍,核对输出。老实的做法是逐个请求重新执行,可那和原本的服务一样贵。但在一台真实的服务器上,多数请求走的路几乎相同——同样的代码,同样的分支,只在几个与用户相关的值上有出入。那就不必一个接一个地跑,而是叠加着跑:一次执行驮着全部请求同行,一条指令只要在所有请求上操作数一致,就只执行一次;只在请求真正分岔之处,才裂成不同的值。我们称之为 SIMD-on-demand。这样审计,比诚实重跑便宜数倍,而省下的,恰恰是那些原本就暗中相同的工。

一般原则:不要整个重新执行——只执行差量。看似各自为政的一群计算,多半是同一个计算,穿了一身人群的衣裳。

跨规模:同一套推理,每个尺寸都作数

最高一层我以为最美,因为到这里,去重不再关乎计算,而关乎推理本身

现代 AI 跑在庞大的张量程序上,我们很想知道:改写或并行化之后的版本,算的还是原来那个函数吗?直接查是无望的:这些对象有数十亿个元素。但这些算子有一种深层的规整——同一个小核,敷在每个元素、每一行、每一个分片上。既然同一条规则统辖一切尺寸,那就不必查遍一切尺寸。查那个仍保有全部本质结构的最小尺寸,其余不查自明。

张量图优化器先让这件事成了正统:生成候选改写,证明它与原图等价,永久收进库里——所谓超级优化器,就是一张代数恒等式的备忘表。我们最近把它推进到了全尺寸训练:要验证一个分布式训练方案与它那份简单的、未并行的规约相符,我们不去验证那个巨物。我们证明:在收缩到最小形状的张量上——外加单个有代表性的输出位置上——核对等价,足以担保真实尺寸上的等价。玩具张量上的一次测试,为庞然大物签发了证书。

然后是让我起意写这个系列的那记点睛之笔。为「小可推大」背书的那条定理,是用归纳法证得的——证基例,再证每一例可推出下一例,便以两例之价,证得无穷例。归纳法,就是推理的去重:这个想法最古老的化身,比计算机还老,如今静静驱动着我们用来信任计算机的最新工具。太阳底下,并无新事。

学习,就是你引以为傲的那种去重

退得够远,整架梯子指向同一处。认出此物即彼物——认出新问题是旧问题化了妆——就是压缩。而压缩,事实证明并不是理解的隐喻;它可能就是理解的定义。把世界建模得好的预测器,正是一段找到了世界的规律、并拒绝将其存储两次的程序;现代语言模型只被训练去猜下一个词,结果竟比我们为图像与音频特制的工具更擅长通用压缩。它靠给世界去重,学会了这个世界。

这又领我回到那句老话,回到它何以其实是件礼物。文明的进步,恰在于不断扩充「无需思考便能完成」之事的数目——每一个习惯,每一段缓存下来的例程,每一个一经命名便无需重推的想法。这个系列做的正是这件事:给那几个去而复返的想法起名字,好让我们不必一次次付全价,把它们重新发现一遍。

最后把口号说准:要点从来不是避免把事做两遍——尽管去做一千遍。要点是别每一遍都付全价:让工作累积起来,而不是每次重复都推倒重来,从零起步。

参考文献