改一个数字,为什么要把整张表从头算一遍?VisiCalc 四十多年前就要面对这个问题。Jane Street 把这问题的一种现代解法,搬上了 GitHub。
这家以高频交易和神秘感著称的公司,公开了内部用的 OCaml 库 Incremental。思路很直接:给一堆计算搭一张依赖图,输入一变,只找出真正受影响的节点重算,其余原样不动。省的不是单次计算的速度,是不必要的计算量。
仓库里到底是什么
repo 名叫 janestreet/incremental,主语言 OCaml,MIT 许可证。目前接近 1000 星、69 次 fork、31 人 watch,发过 18 个版本标签。这些数字只说明有人关注,仓库里没有公开的跑分、延迟或吞吐量数据,不能当成生产验证。
核心机制就一句话:维护一张计算之间的依赖关系图。某个输入变化时,只更新图上真正受影响的节点,其余节点保持原值。文档入口在 incremental/src/incremental_intf.ml,另有一篇博客和一段视频做非正式介绍。
这套思路不是 Jane Street 凭空发明的。它的学术源头是 Umut Acar 等人关于"自调整计算"(self-adjusting computation)的研究,Jane Street 做的是工程化落地。
README 里点名的典型场景,摆在一起看更清楚:
| 场景 | 传统做法的问题 | Incremental 的处理方式 |
|---|---|---|
| 电子表格式大规模计算 | 改一格要全表重算 | 只重算依赖这一格的单元 |
| GUI 视图更新 | 新数据来了要整体刷新 | 定位受影响视图,局部更新 |
| 衍生数据同步 | 源数据一变,过滤/映射结果要重新算一遍 | 沿依赖图局部传播变化 |
省的是算力,不是让算力变快
"牵一发而动全身",说的就是复杂系统里改一处、处处要联动的麻烦。Incremental 想解决的正是这个。它不让机器算得更快,它让机器少算。
效率来自"只碰真正受影响的那部分"。在依赖图设计合理的前提下,计算量更多取决于改动量,而不是简单和系统总规模挂钩——规模越大,这个差距通常越明显。
省的从来不是算力,是不必要的算力。
依赖图本身不是免费的。构建它、维护它有开销,还要应对图结构随程序运行动态变化的复杂度。对变化不频繁、中间结果很少复用的一次性计算,维护一张依赖图未必比直接全量算划算。
OCaml 的选择不代表性能必然领先。语言特性、库设计、具体工作负载是三件事,不能划等号。OCaml 只是让"描述一张依赖图、靠类型系统检查节点连接对不对"这件事写起来更顺手。
Jane Street 这些年一直在把内部的 OCaml 基础设施开源出来,Incremental 是其中一个。它在公司内部具体用在哪些系统上,官方没有细说,不必替它脑补一个交易系统的故事。
谁该看,谁先别凑热闹
| 情况 | 建议 |
|---|---|
| 做响应式 UI、增量数据管道,或需要衍生数据实时对齐源数据的系统 | 值得读源码,评估能不能替换掉手写的脏检查逻辑 |
| 一次性批处理、变化很少、中间结果不复用的场景 | 全量重算大概率更省心,先别急着引入这套复杂度 |
评估 OCaml、函数式编程路线的开发者,也值得翻一下它的接口设计,这比等社区跑分更实在。
接下来该盯的不是 star 数字往上涨多少,是几件更硬的事:仓库有没有持续吸纳外部 PR、有没有人在生产环境公开踩坑或对照数据、Jane Street 是不是打算长期维护,还是把它当一次性的代码整理丢出来。这几点没有答案之前,"开源"两个字只能说明可见,不能说明可靠。
四十年前,电子表格解决的是"改一格不用算全表"。今天 Incremental 想把这套本事搬到表格之外——谁的系统真的长成一张值得画的依赖图,才是这件事该往下问的问题。
