Yukihiro "Matz" Matsumoto 在 RubyConf 2022 的主题演讲题为《Performance does (not) Matter》。这个标题有用,正在于它拒绝了开源世界里最容易说出口的论点。一门语言总能说自己想变快。Ruby 更难守住的主张是,性能工作必须保存用户最初走向 Ruby 的东西:一种让人感到规则一致的对象模型,让迭代和内部 DSL 写起来顺手的代码块,以及一种让程序员表达意图时少一点仪式感的语法。[1][2]

这并不把速度降成装饰。Ruby 一直处在真实的运营预算里:Web 请求延迟、后台任务吞吐、长时间运行进程里的内存压力、gem 兼容性、部署体积,以及为了 VM 写得聪明却让维护者看不清代码时产生的人力成本。带着这层拉扯观看这支视频,才更接近它的价值。Matz 并没有把 benchmark 演讲藏在语言设计演讲里。他在维护一个产品约束:Ruby 应当在保护这门语言面向人的 API 时变快,而不是让 Ruby 程序员围着实现细节改写代码。[1]

后来的 Ruby 3.4.0 发布说明展示了这种立场怎样落入工程。Ruby 3.4 让 Prism 成为默认解析器,改进 YJIT,加入 Modular GC,并引入 it 代码块参数引用,此外还有许多改动。[3] 这些并非同一种功能,但共享同一条纹理。有些工作藏在语言表面之下。有些工作在源码层面轻微改变表达方式。成败要看 Ruby 在实现拥有更大改进空间时,是否仍然让人一眼认得出来。

Yukihiro Matsumoto 在 EuRuKo 2011 发表主题演讲。
图片语境:Mathias Meyer 的照片记录了 Matsumoto 在 EuRuKo 2011 演讲的现场。它是一张真实会议照片,并非 logo、图解、图表或生成图像,也把本文落回 Ruby 作为公共开源语言社区的现实土壤中。[6]

标题是一道边界线

第一个值得标注的地方,正是这场主题演讲的标题。"Performance does (not) Matter" 之所以成立,是因为两半都为真。只要 Ruby 被用于生产系统,CPU 时间、内存、启动时间、队列滞后和程序员注意力都要付账,性能就有分量。若代价是一门语言变得更难读、更难兼容,或者更难愉快地写成领域代码,性能作为孤立美德就失去意义。[1][2]

Ruby 官方 FAQ 仍给出了最简短也最准确的表面描述:Ruby 是一门简单而强大的面向对象语言,拥有代码块、迭代器、元类和文本处理优势。[2] 有意思的词不只在于“面向对象”,更在于对象取向、代码块和元编程的组合。Ruby 的日常手感来自普通库代码可以读起来像一门小语言:each 接收代码块,Rails 路由读起来带有声明感,RSpec 示例读起来接近规格说明,Enumerable 方法让集合流水线贴近正在描述的领域。

也正因如此,只用 benchmark 视角会读浅这场演讲。若一门语言为了让热路径更快,把常见代码推向晦涩模式,收益会消散在维护成本里。若 VM 和解析器改善,而用户代码依旧清楚,收益会层层累积。在这个意义上,Ruby 的性能问题并非“能不能让这串指令跑得更快”,而是“实现层能不能吸收更多工作,让应用代码保留人的尺度”。

对象和代码块是社会契约

Ruby 的对象模型不只是运行时细节。它是一种社会契约,让 Rubyist 可以假定这门语言具有广泛的一致性。几乎所有东西都像对象一样行动。方法、消息发送、代码块、迭代器、模块和开放类共同铺出一个表面,让库可以保持表达力,同时仍然留在 Ruby 之内。[2] 这种表达力很强,也让实现层少了许多小而严格的语言可以享有的捷径。

因此,这支视频更适合作为维护者的优先级练习来读。若某项优化破坏了人们平常书写 Ruby 的方式,Ruby 就不能追逐所有优化。一个 Web 团队不该只因为 VM 偏爱某种机械模式,就从自然的迭代中移除代码块。gem 作者也不该只因为调度路径比编译器喜欢的形态更动态,就放弃清楚的 API。实现层可以要求用户保持一定纪律,尤其是在分配很重的代码和热循环周围,但这门语言不该把性能变成性格测试。[1][2]

这也解释了为什么 Ruby 的语法变化往往比初看时更有分量。Ruby 语法指南记录的模式匹配,让 case 表达式可以匹配结构化值并绑定其中部分内容。[5] Ruby 3.4 的 it 代码块参数,为常见的单参数代码块提供了一个轻量名称。[3] 两者都不是狭义上的“性能”功能。它们共同服务于局部推理:让程序员在源码里表达形状、流程和小变换时,少搭脚手架。一门重视人的时间的语言,必须把这部分也放进 CPU 时间所在的同一本预算里。

VM 工作应当隐身,直到需要测量

YJIT 是性能工作服务于面向人 API 的最干净例子。Ruby 3.4 的 YJIT 文档把它描述为 CRuby 内部的轻量 JIT,使用 Basic Block Versioning 惰性编译代码,并支持常见的 macOS、Linux、BSD、x86-64 和 arm64/aarch64 目标。[4] 这段描述刻意偏向实现细节,但面向用户的愿望很简单:更多真实 Ruby 代码应当变快,同时不被改写成另一种语言风格。

发布说明把 YJIT 改进与 Prism 成为默认解析器放在一起,也很重要。[3] 更快的 JIT 只是运行时故事的一部分。解析器若能成为稳定的共享基础,会影响工具、语法演进、诊断和实现一致性。Modular GC 则指向另一条路:垃圾回收可以变成更清洁的实验表面,而不是一次押在单体实现上的选择。[3] 这些项目合在一起,把性能前沿往下移到引擎室里,语言用户得到好处,却不用为了日常工作记住 VM 内部结构。

运营边界仍然存在。YJIT 自己的文档讨论了内存使用和生产调优,这提醒人们,性能功能没有魔法开关的效果。[4] 运行 Ruby 服务的团队仍该在真实工作负载下测试,观察内存余量,比较尾延迟,再决定这种改进是否值得对应的部署形态。成熟的读法落在测量上:VM 能否在代码保持惯用写法时给出速度。

兼容性也是性能功能

开源语言的性能受生态信任约束。Ruby 的价值不只在解释器。它还在 gems、C 扩展、Rails 应用、测试套件、部署惯例,以及围绕这门语言积累起来的人类习惯里。某个单独看很漂亮的破坏性优化,若撕裂了生态,或者让升级变得让人犹疑,最终就会成为净损失。

这也是 Ruby 3.4 发布说明的组合值得细看的原因。Prism 作为默认解析器、YJIT 工作、Modular GC、socket 改进和代码块语法变化,都被纳入一次正常发布,而不是另起一个“新 Ruby”分支。[3] 项目仍在尝试移动既有用户脚下的地板。这种抱负比从零开始的语言安静,却常常在生产环境里更有价值,因为团队可以沿着常规升级吸收改进。

同样的逻辑也适用于模式匹配。它加入了一种让结构化数据处理更清楚的方式,但它位于 Ruby 既有的 case 表达式之内,没有制造一门外来的子语言。[5] Ruby 最好的模式就在这里:扩展表达力,保留熟悉表面,让实现工作在底下继续推进。兼容性不只是进步的刹车。对组织来说,它也是一种性能功能,因为保留心智模型的升级,测试、教学和推出都更便宜。

工程团队该从这场演讲带走什么

实际启发并不是 Ruby 总是最快的选择。Ruby 的速度叙事应当放在 Ruby 真正优化的单位里评估:由人长期书写和维护的代码库。若团队需要极端的数值吞吐、硬实时保证或极小内存足迹,Ruby 就难以成为合适的一层。若团队需要富有表达力的应用代码、深厚的 Web 生态、容易接近的测试,以及足够多的 VM 进展来让生产成本保持合理,Ruby 仍是一套严肃的开源平台。[1][2][3][4]

因此,采用问题也变得具体。使用当前 Ruby 版本,因为许多回报来自实现层工作。拿 YJIT 和自己的工作负载做 benchmark,而不是和口号相比。在把热路径写聪明之前,先让它保持可读。把模式匹配和新的代码块便利看作澄清意图的工具,而不是强制的风格翻新。最重要的是,把 Matz 的标题保留在它完整的矛盾里:性能有分量,但只有当它服务于人们明年仍能理解的代码时才有分量。[1][3][4][5]

来源

  1. Ruby Central,《RubyConf 2022: Performance does (not) Matter by Yukihiro Matsumoto (Matz)》,YouTube 视频。
  2. Ruby,《Official Ruby FAQ》——由项目维护的 Ruby 对象模型、代码块、迭代器与语言气质概览。
  3. Ruby,《Ruby 3.4.0 Released》——官方发布说明,涵盖 Prism 成为默认解析器、YJIT 改进、Modular GC 与 it 代码块参数引用。
  4. Ruby 文档,《YJIT - Yet Another Ruby JIT》——Ruby 3.4 关于 YJIT 架构、平台支持、用法与内存指引的官方文档。
  5. Ruby 文档,《Pattern Matching》——Ruby 3.4 关于 case/in 结构化匹配与绑定的官方语法指南。
  6. Wikimedia Commons,《File:Yukihiro Matsumoto EuRuKo 2011.jpg》——Mathias Meyer 拍摄的 Matsumoto 在 EuRuKo 2011 发表主题演讲的真实照片。