30 秒看懂
Ron Minsky 解释 OCaml 如何从研究工具发展为 Jane Street 的主要语言,以及公司为何开始为编译器作出贡献。他讨论意想不到的语言特性、性能分析、优化、语言设计和与更广泛社区合作的重要性。
- 1偶然的开始Minsky 本以为是临时性的研究代码,逐渐成为 Jane Street OCaml 代码库的基础。
- 2交易系统中的一等模块这一特性帮助团队用更易扩展的设计替代脆弱的组件连接方式。
- 3分析堆内存Spacetime 将分配与调用路径的紧凑表示关联起来,但精确分析的成本相当高。
- 4Flambda 与可预测的分配内联扩大了优化范围,包括消除短生命周期的内存分配。
- 5Dune 源于发布工具内部构建规则的可移植解释器发展成由外部贡献者共同参与的共享构建系统。
要点建议
- 语言选择会影响安全性、性能可预测性以及周边工具的易用性。
- 一等模块和 GADT 实现了公司起初未曾预料的系统编程技术。
- 元编程除了表达能力强的转换机制,还需要共享语法和工具支持。
- 改进编译器既可能需要持续的工程投入,也可能需要新的语言研究。
- 依赖小众技术的公司有充分动力支持其更广泛的生态。
按章节阅读整理后的全文
OCaml 如何进入 Jane Street
Minsky 回忆了一项起初只为临时研究做出的选择如何成为生产语言。不断增长的使用规模最终促成了专门的工具与编译器团队。
Ron Minsky 讲述 Jane Street 从普通 OCaml 用户逐渐成为其编译器和生态贡献者的过程。公司采用了一门并非自己创建的少见语言,因此与社区的关系不同于掌控自有语言的公司。
Minsky 说,他加入时没有编译器实现方面的专业知识,背景是分布式系统。他在研究生阶段学过 OCaml,发现 Python 太慢后开始用它做量化研究,当时以为代码只是临时性的,以后可以重写。
结果他留了下来,大约 8 万行研究代码成为公司 OCaml 代码库的开端。他说早期代码中有很多写得很糟糕。最初的权宜选择最终需要组织做出更慎重的决定。
公司需要替换原有基础设施,之前大部分是电子表格背后经过仔细审查的 VBA。最初的 OCaml 实验效果良好,公司因此选择用它构建生产交易系统,既看重系统本身,也看重代码的可读性。
大约在 2005 至 2006 年,有 10 人使用 OCaml 编程;到 2009 年约有 30 人。2010 年交易员也开始学习。Minsky 说,交易员具备很强的技术能力,但兼职编程的人与全职开发者对语言和工具的需求不同。
随着组织扩大,公司在 2015 年成立了专门的工具与编译器团队。开发者工具和编译器工作从偶尔贡献变成正式投入。Minsky 提供的数字反映的是演讲时公司的情况。
语言和工具为什么重要
性能、安全和易用性影响系统与组织。Minsky 解释随着用户群扩大,可发现性为何变得重要。
语言会影响性能及其可预测性。Minsky 将直观的代码与 SQL 作比较:优化器和数据统计信息可能决定成本差异很大的执行计划。程序员并不总能仅凭源代码推断性能。
当程序同时连接市场和公司的资金时,安全至关重要。测试必不可少,但表达能力强的类型系统也能捕捉某些类别的错误,让修改更安全、更快速。其价值取决于开发者如何使用。
易用性和可发现性也很重要,尤其是在代码库不断扩大、用户既有全职开发者也有兼职编程交易员时。随着更多人需要理解陌生系统,交互式帮助和代码导航变得更有价值。
意想不到的语言特性
一等模块和 GADT 解决了公司未曾预料的问题。Merlin 和 PPX 改进了代码导航和元编程。
Minsky 将例子分为两类:公司起初不知道自己需要的特性,以及后来主动推动的改进。一等模块是一个意外出现的特性,让原本属于独立静态模块语言的类型和值能够打包成普通值。
OCaml 模块暴露类型和操作,函子则转换模块。一等模块允许把符合要求的模块放进表中,在运行时选择并动态组合,让强大的模块抽象能在更灵活的环境中使用。
这一特性帮助公司重写了一个通过计算图估算证券价值的交易系统。手工连接组件的工作变得如此复杂,以至于开发者不敢扩展系统。一等模块让组件职责更容易表达和连接,尽管不使用它也可能找到解决方案。
广义代数数据类型,即 GADT,是普通代数数据类型的扩展。在模式匹配中,特定分支可以揭示额外的类型信息。Minsky 起初以为它主要帮助设计领域专用语言的人,而不是系统程序员。
他在性能敏感的系统中发现了意想不到的用途。数据包处理程序可能需要直接控制内存并尽量减少分配,而开发者仍希望保有丰富的类型层结构和安全性。GADT 可以为按速度要求选择的表示提供带类型的接口。
GADT 还可以表达存在量化类型:即使调用者不知道具体类型,一个值仍具有某个特定类型。Minsky 说,这对高性能消息系统很有用,也是团队此前没有预料到的用途。
Merlin 是公司没有主动要求的另一个工具。它提供类似 IDE 的功能,例如查看值的类型、查找定义和打开文档。Minsky 承认,自己曾经因为现有环境似乎已经够用而轻视这些工具。
他将这称为一种习惯:开发者的思维模型受现有工具塑造。随着组织和代码库扩大,导航也确实变得更加重要,特别是对新人或只在部分时间编程的人而言。
下一个意想不到的改进是元编程系统 PPX。元编程用代码生成代码;它可以消除重复且容易出错的工作,但生成的程序可能更难推理。
较早的 Camlp4 系统使用可扩展解析器。Jane Street 使用语法扩展,根据类型声明自动生成 S 表达式的序列化和反序列化代码。这消除了大量手写代码,但不受限制的语法扩展与工具支持产生了冲突。
如果不同项目任意改变具体语法,编辑器、格式化工具或语法高亮工具就无法依赖共享语法。Minsky 说,Merlin 不得不通过自己的修改版解析器识别流行扩展,体现了由此产生的技术债务。
PPX 则将 OCaml 抽象语法树转换为抽象语法树。语言提供共享扩展点和注解,供转换程序解释,同时保留共同的具体语法。Minsky 将这种方法与 Lisp 宏作比较,但没有声称 OCaml 的元编程已经完美。
他将认识到统一工具体系重要性的功劳归于 OCaml 社区的其他成员。他也赞赏这门语言相对保守的演进方式,同时指出自己对对象系统等较老特性仍有保留。
编译器与工具投入
Spacetime、调试器支持和 Flambda 分别解决性能分析、调试和性能问题。GADT 实现的改进展示了持续工程投入的价值。
Jane Street 从与编译器开发者讨论问题,转向招聘具有相关专长的人并参与编译器工作。其中一个项目 Spacetime 旨在提供精确的内存性能分析:哪些分配仍留在堆上,以及产生它们的调用路径。
当共享的列表构造函数负责大量分配时,仅有源代码位置可能没有帮助。堆栈跟踪提供更多上下文,但为每个对象存储完整跟踪成本很高。Spacetime 使用跟踪树和紧凑标识符来降低成本。
即使经过优化,精确的堆分析仍然昂贵;Minsky 说,公司使用配备额外内存的机器分析运行结果。不需要精确信息时,采样成本更低,团队正在探索一系列性能分析选择。
公司也致力于改进调试。当时 GDB 能显示 OCaml 堆栈跟踪,但在暂停的进程中检查值或调用程序函数的能力有限。该项目涉及编译器插桩和调试器扩展,部分修改希望进入 OCaml 4.08。
Flambda 是为优化、尤其是内联而设计的中间表示。编译器通过多层表示转换程序;展开函数调用可以让更大范围的代码对分析可见,从而实现不止于消除调用本身的优化。
例如,一个函数可能分配元组,另一个函数立即将其拆解。如果编译器能看到足够范围,就可以消除这次分配。Minsky 说,目标不仅是提高平均速度,还要让分配何时消失具有可预测性,这对延迟敏感的系统很重要。
Flambda 是一项多年投入。当时,团队经常看到程序在不做额外工作的情况下快 10% 至 20%;Minsky 希望更复杂的第二版能让编写可靠的零分配代码更加容易。他把这一未来改进描述为目标,而非已经取得的成果。
一位观众问 Flambda 和 Spacetime 是否冲突。Minsky 说两者大体可以互操作,但由于插桩和优化成本等技术原因,同时使用会让编译非常慢。他认为这是工程问题,而不是根本性的不兼容。
回到 GADT,Minsky 指出成熟的语言特性与其实现不一定以相同的速度成熟。编译器的一些部分写于当前最佳实践建立之前,较少使用的路径也存在粗糙之处。
一项让代码更简洁的类型推断功能适用于普通变体,却不适用于 GADT。他说,原因是模式匹配有两套实现:支持 GADT 的实现存在错误和性能问题。工程师修复它并将其变成唯一实现,使各项功能得以统一。
这说明并非每一项改进都需要新的理论想法。公司可以投入工程时间修复一些不够光鲜的问题,研究导向的社区可能没有资源把它们打磨完善;这与开发真正的新特性同样重要。
新特性与生态优先事项
Minsky 将模块化隐式参数和带类型代数效应作为正在探索的想法讨论,随后介绍 Dune 等共享工具及公司的长期投入。
模块化隐式参数是一个长期项目。Minsky 将其与 Haskell 类型类作比较:类型信息可以隐式选择合适的实现,让更通用的代码减少显式连接工作。他说设计和工程工作可能需要数年。
带类型的代数效应是另一个提议方向。效应可以表示状态修改或网络通信等操作;类型层面的跟踪可以区分有副作用的代码和纯代码,同时效应也能支持灵活的并发机制。
Minsky 指出,团队中一些人帮助制定了最初提案,但这项特性不一定是公司内部的优先事项。他看到了理论优势,却仍不确定是否值得增加语言复杂性。演讲时结果尚未确定。
更广泛的优先事项是提高整个生态的易用性。Minsky 回忆,实习生发现 Jane Street 内部的 OCaml 工具比大学里使用的更好;与此同时,Merlin 和 OCamlformat 等外部开发的工具也让公司受益。
构建工具提供了一个合作案例。公司有名为 Jenga 的内部系统,希望避免在发布开源软件包时重写构建规则。Jeremy Dimino 开始为 JBuild 文件编写可移植解释器,最初并不打算创建完整的构建系统。
新工具快得出人意料:Minsky 说,一些端到端构建比使用 OCamlbuild 快约 5 倍。它吸引了用户,从 JBuilder 发展为 Dune,成为协作构建系统。演讲时,公司计划用它替换内部引擎。
Minsky 重视与外部贡献者共享适合共享的基础设施,也认识到某些工具能从紧密的内部集成中获益。健康的生态对公司很重要:公司依赖 OCaml,需要有才华的人愿意使用这门语言。
其他优先事项包括简化编译器和语言实现,以及改进性能工程。Minsky 将其视为长期责任,而不仅仅是满足公司眼前需求的方式。
他说,演讲时 Jane Street 大约有 1200 万行 OCaml 代码和数百名用户。这种依赖让公司有强烈动力帮助语言繁荣。他强调 Jane Street 并不拥有 OCaml;更广泛的社区和维护者负责决策。
观众提问
问题涉及多态变体、原生编译、JavaScript 和学习编译器内部机制的资料。
在提问环节,Minsky 说多态变体是很早就有的特性,Jane Street 早期便使用了它,但随着其他简化代码的方式出现,公司使用得越来越少。他质疑这一特性的收益是否足以抵消其复杂性。
被问到原生编译时,他说 OCaml 在 Jane Street 参与之前就已经有一个优秀的原生编译器。即使不进行激进优化,其直接的编译方式也能生成高效代码。他还称赞独立开发的 Js_of_ocaml 后端,让 OCaml 库能够用于 JavaScript。
被问到学习编译器内部机制的资料时,Minsky 推荐 Benjamin Pierce 的《Types and Programming Languages》,用来学习一般编译器和类型系统。他说自己不知道有同样好的 OCaml 编译器指南,并认为这是一处不足。
同频道的其他文章
1:48:29EN → 简体中文
1:36:37EN → 简体中文
1:05:45EN → 简体中文
1:25:00EN → 简体中文
1:16:29EN → 简体中文
27:39EN → 简体中文