Ron Minsky 谈 Jane Street 的多核 OCaml 迁移历程

原标题Ron Minsky on the Saga of Multicore OCaml at Jane Street

一次生产环境迁移暴露了垃圾回收器的性能回退,催生新的节奏控制设计,也展示了将研究变为可靠基础设施的困难。

RMRon Minsky讲者 · Jane Street
频道
Jane Street
发布
2025年7月24日
视频
1:27:41
阅读
14 分钟
幻灯片
4
原文
英语
在 YouTube 打开
往下滚动阅读时,视频会缩到右下角继续播放。字幕跟随阅读语言。
阅读语言共 2 种语言可读
由 AI 从英语原文翻译。打开“对照原文”可以逐段核对。
摘要

30 秒看懂

Ron Minsky 介绍 Jane Street 在 OCaml 多核运行时上游发布约两年半后采用它的历程。他解释新旧回收器、生产环境中的性能回退和重新设计的节奏控制策略,并在问答中讨论基准测试、兼容性、并行编程和公司的语言选择。

  1. 1从上游到生产的差距多核运行时进入上游后,Jane Street 花了约两年半时间,才让自己的工作负载与运行时相适应。
  2. 2并行回收器结构Runtime 5 使用每核独立的次堆、共享主堆和同步点;其主回收器将标记与清扫合并。
  3. 3真实工作负载中的回退早期测试发现一些程序慢了 10%–20%,或多使用了 10%–20% 的内存;后来还发现了更严重的情况。
  4. 4浮动垃圾合并的标记—清扫周期可能延迟回收。一个 mark-delay 补丁增加同步,使行为更接近旧运行时。
  5. 5重新思考节奏控制团队在稳态分析中统一考虑堆内和堆外分配,避免对堆大小形成反馈依赖。
  6. 6技术转移的经验Minsky 建议在投入完善方案之前,使用有代表性的大型工作负载、可比较的运行时、针对性基准和快速实验性修复。

要点建议

  1. 即使多核回收器设计时考虑了兼容性,仍需要大量针对生产环境的评估和工程工作。
  2. 与旧回收器相比,合并标记与清扫的设计延迟了浮动垃圾的回收。
  3. 堆外分配暴露了节奏控制问题,其中包括现有工作负载已经依赖的错误。
  4. 统一的稳态分析带来了基于分配量而非当前堆大小的开环节奏控制方案。
  5. 可比较的运行时、具有代表性的工作负载和快速原型有助于调查性能回退。
  6. 有了并行运行时之后,无数据竞争、调度、性能分析和教学仍是各自独立的挑战。
全文

按章节阅读整理后的全文

只看原始文稿
01

从研究到生产

Minsky 介绍多核回收器的目标,并解释为什么它进入上游后,Jane Street 没有立即采用。

Ron Minsky 介绍 Jane Street 将多核 OCaml 垃圾回收器投入生产的经历。这场演讲既讲技术问题,也讲技术转移:如何将研究中的想法变成成熟组织能够采用的基础设施。

多年来,OCaml 缺少共享内存并行能力,Jane Street 的生态是在这一前提下发展起来的。2020 年的一篇 ICFP 论文描述了一个多核回收器,目标是实现高性能、易维护和易采用,并用同一个运行时支持顺序及多核工作负载。

设计旨在保持顺序吞吐量、较短暂停时间和外部函数接口,避免重写所有现有 C 扩展。论文的基准测试报告称,顺序性能下降约 3%,内存使用和暂停时间相近,并行运行也有良好的加速效果。

Minsky 提醒,垃圾回收器的性能是一条曲线,而不是一个数字:调参会改变回收工作量和内存使用之间的平衡。从多核项目启动到进入上游约用了九年;即使回收器在 2022 年进入上游,Jane Street 内部采用它又花了约两年半。

有些工作早在预料之中:多核开发期间对旧回收器所做的改进,包括预取,都需要移植到新版本。团队还让同一个语言版本同时支持两个运行时,以便直接比较。一旦使用真实工作负载测试,他们发现性能无法接受,随后花了约一年半诊断和修复回退。

02

新旧回收器

他解释分代回收、屏障、增量标记,以及多核运行时的堆、同步和颜色方案。

旧版 Runtime 4 回收器是顺序式、分代式的。分代假说认为大多数对象很快失效。新对象通过移动指针以极低成本分配在次堆上;次堆填满时,回收器找出存活对象并将其提升到主堆,其余失效对象则可一起丢弃。

写屏障记录从主堆指回次堆的指针。没有它,次回收可能需要扫描整个主堆,才能发现年轻对象是否仍然可达。在主要采用函数式风格的程序中,这些反向指针通过修改产生,因此可以在写入时附加记录工作。

主回收器从栈和寄存器中的值等根出发,标记可达对象,然后清扫内存以回收未标记对象。Minsky 的动画展示了标记和未标记颜色,以及清扫如何为下一周期重置存活对象并释放不可达对象。

追踪可达对象
幻灯片 1 / 4追踪可达对象标记动画区分了根、可达对象和仍未标记的对象。

旧回收器是增量式的:标记和清扫分成若干切片进行,而不是一次长时间暂停。这用一部分吞吐量换取响应性。起始快照规则意味着,一个周期不必回收快照之后变得不可达的新对象,从而有助于保证标记阶段最终结束。

Runtime 5 保留了旧设计的大部分内容,但增加了并行和同步点。编译器插入安全点,让各个域能够停止并协调。每个核心有自己的次堆,以保持分配成本低并减少竞争;主堆则由所有核心共享。

一个次堆填满时,各个域同步进行次回收。提前完成的域可以在等待其他域时执行主回收工作。共享主回收器将标记与清扫合并,使两者能够推进,而不必在独立阶段之间再增加一次同步。

旧回收器的颜色在已标记、未标记和空闲之间转换。Runtime 5 增加垃圾状态:标记将未标记变为已标记,清扫将垃圾变为空闲,因此两种操作处理互不重叠的状态。周期结束时,各域同步并轮换颜色位的解释,而不是遍历堆重写它们。

03

性能回退与节奏控制

真实工作负载出现 CPU 和内存表现回退。Minsky 解释托管堆内和堆外内存的回收节奏,以及被现有系统依赖的错误造成的问题。

真实测试发现一些程序慢了 10%–20%,一些程序多使用了 10%–20% 的内存,后来还发现更严重的回退。Minsky 列出了若干原因:回收节奏、透明大页、旧线程切换缓慢、与 effect 相关的栈检查,以及尚未像旧运行时那样充分优化的代码。

他重点讨论节奏控制:相对于应用工作,回收器应做多少工作。OCaml 不使用固定的堆大小上限,而是提供空间开销目标,表示回收器可以使用多少额外空间。这适合一组内存需求各不相同且不断变化的程序。

对于普通堆内分配,原始分析假定处于稳态:长期运行的程序最终回收的内存大致等于分配的内存。这得出每个提升到主堆的机器字对应固定量的标记和清扫工作。次堆分配则单独控制节奏。

堆外分配带来另一个问题。OCaml 堆对象可能持有大型外部分配的句柄,例如 bigstring 或由外部库管理的内存。堆内回收器必须做足够的工作,才能发现失效句柄并允许回收外部资源。

旧的堆外方案使用一个公式累积额外堆工作:该值与外部分配大小成正比,与当前堆大小成反比。累计资源值达到阈值时,就触发额外回收。Minsky 说,这个启发式方法看似合理,实际表现却很差。

他用控制理论解释两者的区别。堆内节奏控制是开环的:工作速率基于分配量,而不是控制器自身改变的量。堆外公式则是闭环的,因为堆大小受回收影响;反馈会让系统难以调节并可能产生振荡。

基准测试显示,普通分配工作负载比堆外分配密集的工作负载更接近请求的空间开销目标,后者的曲线几乎是平的。Minsky 说,对外部分配过度回收是 Jane Street 程序长期面临的严重问题,需要在应用层做麻烦的调整。

Runtime 4 的节奏控制结果
幻灯片 2 / 4Runtime 4 的节奏控制结果在堆外分配密集的基准中,请求的空间开销与实际测量值明显偏离。

Runtime 5 保留基本的堆内方法,但修复了若干堆外实现错误。例如,即使次堆上的堆外句柄在提升前就失效,Runtime 4 也可能把它计入,从而让节奏控制过于激进。其他错误则向不同方向施加影响,而应用已经适应了它们共同产生的行为。

修复之后,一个堆外基准有所改善,但普通堆工作负载可能使用多得多的内存。团队发现,统一的标记—清扫设计在回收浮动垃圾时不够积极:这些对象已经失效,但根据当前快照和阶段顺序还不能回收。

04

浮动垃圾与新的节奏模型

mark-delay 补丁改善了回收,而统一的稳态模型在不依赖堆大小的情况下处理堆外节奏控制。

Minsky 比较两种设计中新分配对象的回收时机。在他的简化分析中,Runtime 4 清扫期间分配的对象平均约经过一个周期就可回收;Runtime 5 的合并周期中,这种情况可能需要约两个周期。各类情况的平均值从约 1.25 个周期增加到 1.75 个周期。

比较回收周期
幻灯片 3 / 4比较回收周期图示对比了标记和清扫分离或合并时,对象何时可以回收。

mark-delay 补丁增加一次同步,使回收行为更接近旧回收器。实验表明,原始设计所担心的同步在实际中的成本低于预期。它改善了普通堆的表现,但更激进的回收又让堆外工作负载变得更差。

团队首先尝试修改堆外公式的分母,让它同时包含堆内和堆外内存。这改善了普通堆很小时的一些极端情况,但闭环方案仍存在振荡和调参问题。他们没有继续修补启发式规则,而是回到基本原理。

他们将两种分配放进同一组方程,重新进行稳态分析。堆大小在计算中被消去,结果变成每分配一个机器字就执行固定量的回收工作,无须读取当前堆大小。堆内分配需要额外一单位清扫工作,因为它自己的内存必须被清扫;堆外分配本身则不需要。

mark-delay 补丁后的节奏控制
幻灯片 4 / 4mark-delay 补丁后的节奏控制图表比较不同运行时和工作负载中请求的空间开销与实际测量值。

Minsky 说,由此得到的节奏曲线更接近预期,也更容易理解。他还提到正在进行的启动阶段实验:程序可能主要分配长期存活的数据,此时激进回收不太可能释放多少内存。这些想法在演讲时仍处于探索阶段。

05

经验与下一阶段的并行挑战

演讲回顾基准测试和兼容性,随后介绍无数据竞争、调度、工具,以及当时计划公开发布的 OxCaml。

Minsky 从迁移中总结经验。基准测试很难:原始评估倾向于使用方便、相对较小的程序,没有涵盖足够多的行为,因而未能暴露大型生产应用的问题。他认为,如果更早在同一语言版本下提供两个运行时,就能进行更可比的测试。

调试也很困难,因为不同的性能回退会互相掩盖。团队有时还没确定某个看似最大的问题究竟有多重要,就先构建了完善的解决方案。Minsky 说,快速制作即使不够完美的原型,以及小型针对性基准,会有助于更快检验假设。

有时渐进修补并不足够:节奏控制工作因回到基础方程而受益。向后兼容也包括意外形成的行为。应用围绕某个错误调优后,错误可能成为系统所依赖的部分,因此即使新行为更有原则,修复也可能扰乱生产环境。

Minsky 接着讨论并行挑战的第二阶段。运行时可以并行执行共享内存代码,但程序员仍需要可用的编程模型。2025 年一篇关于无数据竞争的论文描述了旨在排除数据竞争的类型级保证,包括标注可以移到另一个核心运行的函数。

将注解传播到各个库是一项任务,但修复旧代码中的数据竞争更难。类型约束暴露了开发者可能早已忘记的可变状态和技巧。Minsky 说,即使代码库以函数式编程著称,团队也发现了真实问题。

其他困难包括选择合适的任务粒度和并行量。任务太小会被开销吞噬;太大则会让核心闲置。团队正在探索包括心跳调度在内的调度方法,动态调整并行工作量。

并行程序也难以进行性能分析:即使使用共享内存,代码仍受到缓存行流量和竞争的影响。团队希望构建解释性能不佳原因的工具、结合并行与并发的抽象,以及帮助开发者使用新能力的教学材料。

Minsky 宣布计划公开提供 OxCaml,即 Jane Street 带扩展的 OCaml 工作所使用的名称。演讲时它是公司的生产编译器,但他提醒外部用户,实验性扩展可能发生不兼容变化。他邀请研究人员和爱好者试验,而不是把它当作稳定的外部生产平台。

06

观众提问

Minsky 讨论语言取舍、发布过程、回收器并发性、生产验证、早期结果、性能分析和语言生态选择。

在问答中,一位观众问为什么选择垃圾回收语言,以及它与 Rust 相比如何。Minsky 指出,Jane Street 选择 OCaml 时 Rust 尚不存在。他认为,OxCaml 将垃圾回收与局部分配、所有权相关模式等可选控制结合起来,适合公司的多样工作负载。

他对比了处处承担显式内存管理约束的成本,与只在需要时采用更精确控制的方式。对于极其延迟敏感的循环,公司力求避免在关键路径上进行堆分配;与此同时,垃圾回收对其他应用和共享结构仍然方便。

谈到发布过程,Minsky 说 Runtime 5 当时已在内部正式可用,但并非所有应用都采用了它;一些性能回退仍然存在。测试之后,团队被要求观察性能,并可在需要时回滚。更早一次发布进展很糟,已经撤回,因此他并不认为该过程万无一失。

一位观众询问共享主堆的竞争。Minsky 区分了回收器竞争和应用层缓存行共享,并说预测后者仍然困难。在他看来,静态资源分析能力有限,因此程序员仍需要理解和测量并行工作负载。

谈到形式化验证,Minsky 说 Jane Street 在生产中使用完整定理证明的情况相对较少。硬件设计可以让部分静态分析和求解器技术更实用;C 绑定和编译器优化也受益于针对性工具。其他地方,公司主要依赖测试,并为某些重要风险系统提供独立参考实现。

被问及如何预测启动阶段的分配生命周期时,他介绍了正在进行的工作:使用次堆对象被提升的比例作为启发式指标。较高的提升率可能表明许多分配会存活更久,从而可以降低回收节奏。目标是让用户不必在启动期间手动调节回收。

一位观众对比了低延迟金融场景中的并行和并发回收器。Minsky 说,兼容性和更广泛的工作负载共同塑造了这一设计;一个域执行应用工作时,其他域可以进行部分主回收。他说,对于最关键的交易循环,公司的做法是避免分配,而不是依靠回收器提供确定性的延迟。

他补充说,交易公司要解决的问题远不止数据包往返。不同任务需要通用系统,而极快的数据包处理可能使用专用硬件。围绕对交易的单一狭窄想象来优化回收器,会忽视公司的大量计算工作。

被问及为什么出现回退后公司仍坚持推进时,Minsky 指出公司已有非常庞大的 OCaml 代码库。引入另一门语言本身也有成本,即使像为机器学习引入 Python 那样有充分理由。他认为,改进成熟生态往往比替换建立在其上的软件更便宜。

另一位观众质疑研究人员是否应更早比较两个运行时。Minsky 称赞原始工作,并指出有些人参与了两个阶段。他认为长期只维护一个运行时是合理目标,但暂时同时提供两个运行时会让评估和迁移更容易;当时大多数修复已经进入上游。

被问及 Jane Street 为什么举办技术演讲时,Minsky 澄清自己并非公司 CTO,并说公司没有这一职位。他表示,分享技术工作让人们了解公司解决的问题,也有助于招聘。所述迁移工作在公司内部完成,其中一些参与者也曾贡献原始研究。

内部开发可以借助规模庞大且积极参与的用户群反馈,快速迭代雄心勃勃的语言改动。Minsky 将其与上游社区理所当然的谨慎作对比:社区必须维护公开 API,避免让用户依赖错误设计。

对于公司是否在服务技术而非业务的问题,Minsky 承认使用不太流行的语言有成本,包括较难利用外部生态工具。他说,早期交易应用相对独立,而 OCaml 在实际业务中证明了自己有用、快速且安全,并非出于纯粹主义而采用。

被问及多核最直接的好处时,Minsky 说采用仍处于早期。一些已经并行的系统围绕共享内存调度重新设计后获得显著加速;他也预计更简单的任务可以获得并行能力,无须构建分布式系统或在进程间序列化数据。

他最担心的是语言的复杂性预算:能力增强也会增加理解负担。团队正努力采用按需付出的设计,并思考如何教授新模型,但他说很难预先知道这种平衡是否合适。

关于如何组织迁移的问题促使 Minsky 介绍了约 20 人的语言团队所做的集中工作:垃圾回收器和运行时专家开展实验并使用性能分析工具。他特别提到 Magic Trace 和 Intel Processor Trace,可以在采样分析之外查看短时间内的精确执行过程。

最后,有人问新公司是否应选择小众语言以获得控制权。Minsky 提醒说,构建语言生态需要大量时间和精力,Jane Street 早期并没有获得这种控制权。他说,小众语言吸引优秀人才的能力是一项重要收益,但他不愿将公司的道路直接推荐给一个新成立的小公司。

同频道的其他文章

1:48:29EN → 简体中文
JSJane Street· 2026年10月16日AI 如何改变编程:John Krazit 与 Ron Minsky 的对话Ron Minsky, John Krazit · Jane StreetJane Street 的 AI 工具负责人和 Signals and Threads 主持人探讨智能体基础设施、安全、代码审查,以及软件开发体验的变化。编程智能体Developer productivity评估+2
9 张幻灯片1:05:45EN → 简体中文
JSJane Street· 2026年9月11日Cyrus Omar:实时编程与人与 AI 共享的计算公共空间Cyrus Omar通过 Hazel 演示,探讨即时反馈、结构化协作和精细权限如何帮助人类与 AI 智能体共同工作。Programming LanguagesHuman-AI Collaboration开发流程与测试+2
1:25:00EN → 简体中文
JSJane Street· 2026年9月2日Eric Mannes:如何将杂乱的外部数据用于交易研究Ron, Eric Mannes · Jane Street从商品期货和 Excel 风险系统,到时点数据集和 AI,Eric Mannes 解释 Jane Street 数据工程背后的领域知识。Data quality & curation开发流程与测试Investing+2
7 张幻灯片1:16:29EN → 简体中文
JSJane Street· 2026年8月26日Grant Sanderson 与 Alok Puranik:从群论看位置编码Grant Sanderson, Alok Puranik · 3Blue1Brown从注意力机制的假设出发,他们推导矩阵指数,并将旋转、衰减和 Jordan 块与 RoPE、ALiBi 联系起来。Transformers & attentionMathematics机器学习基础+1
4:41EN → 简体中文
JSJane Street· 2026年7月6日走进 Jane Street 实习项目:工作、导师与学习Cody · Jane Street实习生和员工讲述协作式面试、从零学习金融,以及参与公司实际使用的项目的经历。CareersCompany CultureMachine Learning+1
1:34:29EN → 简体中文
JSJane Street· 2026年6月24日Jacob Baskin:构建 Superstore 与调度 HiveYaron Minsky, Jacob Baskin · Jane Street这位 Jane Street 工程师回顾分布式分析数据库背后的设计选择,以及为不断扩大的研究集群分配资源的挑战。基础设施与部署Software engineeringDecision Making+1