Chris Casinghino:让 OCaml 安全地支持性能工程

原标题Chris Casinghino on Making OCaml Safe for Performance Engineering

Jane Street 的 OCaml 扩展通过布局和模式提供对内存与并行性的更多控制,同时保留安全性和熟悉的编程方式。

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

30 秒看懂

Chris Casinghino 解释了 OCaml 统一表示和多核内存模型的权衡,随后介绍 Jane Street 在未装箱布局、栈分配和无数据竞争保障方面的工作。2025 年演讲时,未装箱类型和栈分配已用于生产,而无数据竞争支持正开始推广。

  1. 1统一的值表示经典 OCaml 用一个字表示值,该值可以是立即数或指向堆块的指针。
  2. 2统一表示的代价一个字节可能占用一个机器字,装箱的 int64 需要堆块,影响局部性和垃圾回收。
  3. 3未装箱布局种类描述类型的表示方式,使基本类型和记录更紧凑,同时让编译器了解其布局。
  4. 4局部与全局模式模式追踪值是否能逃逸作用域,从而支持安全的栈分配和资源 API。
  5. 5通过模式防止数据竞争争用模式追踪共享情况,可移植性模式控制值是否能在线程间传递。
  6. 6生产使用与推广Casinghino 表示,栈分配和未装箱类型已用于生产,无数据竞争功能正开始测试推广。

要点建议

  1. 统一的单字值表示简化了垃圾回收和多态,却限制了对内存布局的控制。
  2. 未装箱类型和布局种类让编译器能够追踪更窄、更扁平的表示。
  3. 局部模式可以强制遵守栈纪律,防止值逃逸其作用域。
  4. 争用与可移植性模式有助于静态防止不安全的跨线程访问。
  5. 设计目标包括保留类型和内存安全、类型推断、兼容性,并让复杂度随使用需求增加。
  6. 据 2025 年的介绍,部分功能已用于生产,无数据竞争保障则处于推广初期。
全文

按章节阅读整理后的全文

只看原始文稿
01

OCaml 值、多态与并行性

Casinghino 解释统一的值表示、类型推断和多核 OCaml,并对比 OCaml 对数据竞争影响的限制与静态无竞争保障。

我在 Jane Street 的 OCaml 语言团队工作,该团队属于工具与编译器组。我们构建库和开发工具,也开发语言扩展。今天我会讨论 OCaml 在高性能软件方面可以改进的地方,以及我们为解决这些问题而构建的功能。

我会先解释 OCaml 的一些现有行为,因为理解它们的收益和成本对新设计很重要。第一个细节是内存中值的统一表示。

在经典表示中,每个 OCaml 值占一个机器字。它要么是将数据打包在字中的立即数,要么是指向堆块的指针。在 64 位机器上,立即数有 63 位数据和一位标签;整数、字符和布尔值使用这种形式。

统一的值表示
幻灯片 1 / 10统一的值表示经典值是立即数或指向堆块的指针,简化了垃圾回收和多态实现。

不能放进这种表示的值通过指向堆块的指针表示,堆块包含头部和字段。统一表示帮助垃圾回收器区分指针与立即数,并扫描相关的堆数据。

OCaml 还具有表达力丰富的静态类型、类型推断和广泛使用的多态。例如,map 函数将一个函数应用到列表上,产生另一个类型的列表。编译器可以推断它的泛型类型,无需程序员编写注解。

统一的值使多态函数的单独编译很直接:一个编译后的函数可以处理使用相同单字调用约定表示的类型。相比之下,C++ 泛型通常为各类型创建特化副本,以代码体积和指令缓存压力换取优化机会。

其他语言实现可以通过即时编译在运行时生成特化代码。接下来 Casinghino 谈到 OCaml 5:它引入了共享内存的多核并行,要求运行时和垃圾回收器能够处理并发访问。

他认为 OCaml 的内存模型将数据竞争的影响限制在一定的空间和时间范围内。相比之下,C++ 将数据竞争视为未定义行为,后果可能出人意料且难以定位。数据竞争涉及对同一内存的并发访问,且至少有一次写入。

这些限制让调试更容易,但 OCaml 5 本身没有提供静态的无竞争保障。安全 Rust 通过类型系统提供这种保障。Casinghino 希望在不失去 OCaml 熟悉特性的情况下,给它带来类似能力。

02

性能目标与未装箱布局

统一表示带来空间和局部性成本。团队在保持兼容性和安全性的同时,引入布局种类和未装箱类型。

统一表示也限制了对内存布局的控制。8 位数据可能占用整个机器字。装箱的 int64 无法放进立即数的 63 位;在所描述的例子中,它需要一个包含元数据的三字堆块。

统一表示的成本
幻灯片 2 / 10统一表示的成本幻灯片说明字节和装箱 int64 的空间成本,以及局部性方面的权衡。

指针数组可能将数据分散在堆中,损害预取和缓存局部性,而回收器必须管理这些分配的块。OCaml 的回收器很快,但减少不必要的分配仍有助于性能敏感的程序。

即使内存模型限制了影响范围,数据竞争仍是错误。运行时同步也有成本。静态保障可以防止竞争,并可能避免部分检查,同时保持语言安全。

设计目标是安全、方便且可预测地控制性能关键行为。在保留类型与内存安全及 OCaml 高层风格的同时,让程序员表达所需行为,而不是完全依赖可能因微小代码修改而变化的优化启发式规则。

大多数开发者不需要逐纳秒调优,因此只有使用相关功能时才应承担额外复杂度。团队还希望兼容上游 OCaml 和公共库,并希望将这些功能贡献回上游。

三个核心功能是窄而扁平的数据布局、栈分配和无数据竞争的并行编程。团队使用类型系统中称为种类(kind)和模式(mode)的概念表达所需属性。

int32#、int8# 和 float# 等未装箱基本类型可以使用与底层位数相符的表示,而不是经典的装箱值或带标签机器字。它们可以放入记录,使字段表示更加紧凑。

未装箱类型
幻灯片 3 / 10未装箱类型示例展示未装箱基本类型、记录和未装箱记录数组。

未装箱记录向程序员呈现记录抽象,而其组成部分可以分别传递,例如通过寄存器传递,而不是通过堆指针。未装箱记录数组可以让字段在内存中相邻排列,而不是存储一个指针数组。

这带来一个问题:垃圾回收器和多态代码此前依赖统一的值。拟议的类型系统追踪称为布局的表示信息,使编译器和回收器知道如何处理不同形状的数据。

种类可以看作对类型的分类,正如类型对值进行分类一样。布局包括经典的 value 表示、位宽、浮点布局,以及用于不需要运行时表示的值的 void。

布局种类
幻灯片 4 / 10布局种类图表对 value、位、浮点数、void 和未知布局等表示进行分类。

any 布局表示具体布局未知的类型。没有更多信息时,使用它的代码不能直接编译,但它可用于共享签名,具体实现再特化到确定的布局。聚合布局用于描述字段组合。

对于多态函数,编译器可以按布局而不是按每个单独类型生成副本。回收器也需要新的元数据:混合记录的头部可以标识哪些字段必须扫描,以及哪些未装箱字段不是指针。

03

使用模式实现安全栈分配

栈分配可以减轻垃圾回收压力,但要求值不能逃逸。局部和全局模式表达这一约束,也有助于资源管理。

第二个功能是栈分配。OCaml 的堆分配效率很高,尤其是次要堆,但回收和缓存局部性仍有成本。更大的次要堆需要回收的频率较低,却可能无法很好地装入缓存。

栈分配同样会移动指针,但函数返回时只需将栈指针移回,就能回收空间,无需逐个扫描值。复用栈空间可以触及更少的缓存行。难点是确保引用绝不会比所分配的值活得更久。

安全栈分配
幻灯片 5 / 10安全栈分配栈纪律防止堆中出现指向栈值的指针,也防止值在生命周期结束后仍被返回。

C 中经典的释放后使用错误,是存下一个指向栈上值的指针,然后在函数返回后使用它。安全的栈分配需要遵守栈纪律:不要在堆中创建指向局部值的引用,也不要在其生命周期结束后返回它。

为什么不直接使用 Rust 风格的生命周期?Casinghino 说,完整追踪任意生命周期可能需要注解和复杂的高阶推理,这与方便推断的目标冲突。OCaml 可以改为把生命周期复杂的值放在垃圾回收堆中。

模式提供与类型正交的属性。布局描述表示方式,而模式追踪值可以如何使用,包括嵌套值的深层属性。第一个区分是 global 和 local。

global 是传统默认模式,不限制生命周期。local 表示值遵守栈纪律,不能通过存入堆或在作用域结束后返回而逃逸。类型系统追踪使用情况,以判断这一保障是否成立。

这里存在子模式关系:global 值可以安全地用于期望 local 值的地方,因为这样做只会对其使用施加更多限制。这让已有的长生命周期值也能参与要求局部行为的函数。

map 的局部模式
幻灯片 6 / 10map 的局部模式函数参数的局部模式使调用方的闭包能够留在其作用域内。

以 map 为例:它调用函数参数,却不存储或返回该函数。将函数参数标为 local 就能表达这一点。multiply_by 这样的调用方随后可以在栈上而不是堆上为乘数创建闭包,避免一次垃圾回收器分配。

防止文件句柄逃逸
幻灯片 7 / 10防止文件句柄逃逸with_file 的签名约束了回调对通道的使用。

局部性除了支持栈分配,还有助于保证正确性。with_file 函数打开文件,将通道传给所提供的函数,之后关闭文件。如果回调保存通道供以后使用,就可能暴露已关闭的句柄;local 约束可以阻止通道逃逸。

04

无数据竞争保障与推广

争用和可移植性模式支持安全的线程 API 与共享内存。Casinghino 最后介绍生产使用和初步推广情况。

模式还可以通过追踪值的操作来支持无数据竞争保障,包括别名和跨线程共享。第一个新增维度是争用:uncontended 值局限于一个线程,contended 值则已经共享。

争用模式
幻灯片 8 / 10争用模式这一维度追踪值是否已经跨线程共享。

类型系统可以禁止读取或写入 contended 值的可变部分。这阻止了一种明显的数据竞争来源,但实用系统还必须提供受控方式,安全地修改共享状态。

可移植性关注另一个问题:值是否可以发送给其他线程。捕获了自身能修改的可变状态的函数不可移植,因为发送该闭包也会传递修改另一个线程状态的能力。

可移植性模式
幻灯片 9 / 10可移植性模式幻灯片区分可安全在线程间共享的值与不可移植的闭包。

系统有多个模式维度。Casinghino 区分已经发生的事情,例如值是否已共享或已有别名,以及以后可能发生的事情,例如值是否可以逃逸或共享。他还提到唯一性和线性等维度。

线程创建 API 可以要求传入可移植函数,从而防止不安全的闭包情况。join 操作等待另一个线程结束并返回其值;由于那个线程已不再运行,返回数据不必仅因为跨越线程边界就继续保持 contended 状态。

为安全使用可变共享内存,他勾勒了一个基于锁的 API。类型参数将共享单元与特定的锁关联起来,操作时需要匹配的键。键不能发送给其他线程,因此持有键有助于在操作受保护数据时实现独占访问。

锁保护的共享内存 API
幻灯片 10 / 10锁保护的共享内存 API示例将共享单元与一个键关联起来,并要求使用该键才能操作单元。

2025 年演讲时,栈分配和未装箱类型已经用于生产,但仍有更多工作要做。根据 Casinghino 的介绍,无数据竞争功能的实现已经完成,但正处于推广初期,由部分团队测试 API。

他表示,这些功能帮助重视性能的开发者用更少的变通方法编写更符合 OCaml 风格的代码。团队也在为新模式给库添加注解,并关注对不需要这些功能的用户而言,额外复杂度是否仍然可控。

同频道的其他文章

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
1:36:37EN → 简体中文
JSJane Street· 2026年9月23日Aaron Bauer:在 AI 时代教授软件工程Ron Minsky, Aaron Bauer · Jane Street当模型能够生成代码时,学习目标、主动练习和编辑器设计如何影响工程教育。AI in EducationSoftware engineeringEducation+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
27:39EN → 简体中文
JSJane Street· 2026年8月21日3Blue1Brown 与 Jane Street 谈交易、机器学习和文化Grant Sanderson, Nitya, In Young, Craig, Alok · 3Blue1BrownGrant Sanderson 与 Jane Street 的同事讨论做市、共享算力、研究,以及他们加入公司的不同经历。Machine LearningFintechCompany Culture+2