Ruby 3.3.0 现已正式发布。新版本添加了一个名为 Prism 的新解析器,使用 Lrama 作为解析器生成器,添加了一个名为 RJIT 的新纯 Ruby JIT 编译器,以及许多性能改进,尤其是 YJIT。
具体更新内容如下:
Prism
- 引入 Prism 解析器作为默认 gem
- Prism 是一个可移植、容错且可维护的 Ruby 语言递归下降解析器
- Prism 已做好生产准备并积极维护,用户可以使用它代替 Ripper
- 具体介绍可查看 Prism 的文档。
- Prism 既是一个由 CRuby 内部使用的 C 库,又是一个可供任何需要解析 Ruby 代码的工具使用的 Ruby gem
- Prism API 中值得注意的方法有:
Prism.parse(source)
返回 AST 作为解析结果对象的一部分Prism.parse_comments(source)
返回 commentsPrism.parse_success?(source)
如果没有错误,则返回 true
- 现在可以使用
ruby --parser=prism
或RUBYOPT="--parser=prism"
尝试使用 Prism 编译器。值得注意的是,该 flag 仅用于调试。
使用 Lrama 代替 Bison
- 用 Lrama LALR 解析器生成器替换 Bison [Feature #19637]
- 更多详情可参阅Ruby Parser 的未来愿景
- 为了便于维护,Lrama 内部解析器被替换为 Racc 生成的 LR 解析器
- 支持参数化规则
(?, *, +)
,将在 Ruby parse.y 中使用
YJIT
- 相对 Ruby 3.2 的主要性能改进
- 对 splat 和 rest 参数的支持已得到改进。
- 为虚拟机的堆栈操作分配了寄存器。
- 编译更多带有可选参数的调用。异常处理程序也被编译。
- 不支持的调用类型和复态调用点不再退出到解释器。
- Rails
#blank?
和specialized#present?
等基本方法都是内联的。 - 对
Integer#*
、Integer#!=
、String#!=
、String#getbyte
、Kernel#block_given?
、Kernel#is_a?
、Kernel#instance_of?
和Module#===
进行了特别优化。 - 编译速度现在比 Ruby 3.2 稍快。
- 现在比 Optcarrot 上的解释器快 3 倍以上
- 与 Ruby 3.2 相比显着提高了内存使用率
- 编译代码的元数据占用内存更少。
- 当应用程序的 ISEQ 超过 40,000 个时,
--yjit-call-threshold
会自动从 30 提高到 120。 - 添加
--yjit-cold-threshold
以跳过编译 cold ISEQ。 - 在 Arm64 上生成更紧凑的代码。
- Code GC 现在默认禁用
--yjit-exec-mem-size
被视为新代码编译停止的硬限制。- 不会因 Code GC 导致性能突然下降。使用 Pitchfork 重新分叉的服务器上有更好的 copy-on-write 行为 。
- 如果需要,仍然可以使用
--yjit-code-gc
启用 code GC
- 添加
RubyVM::YJIT.enable
以便在运行时启用 YJIT- 无需修改命令行参数或环境变量即可启动 YJIT。Rails 7.2 将使用此方法默认启用 YJIT 。
- 这也可用于仅在应用程序完成启动后启用 YJIT。如果想在启动时禁用 YJIT 的同时使用其他 YJIT 选项,可以使用
--yjit-disable
。
- 默认情况下提供更多 YJIT stats
- 现在默认情况下还提供了
yjit_alloc_size
和更多与元数据相关的统计信息。 - 由
--yjit-stats
生成的ratio_in_yjit
stat 现在可在发行版中使用,不再需要特殊的 stats 或 dev 版本才能访问大多数统计信息。
- 现在默认情况下还提供了
- 添加更多 profiling 功能
- 添加
--yjit-perf
以方便使用 Linux perf 进行分析。 --yjit-trace-exits
现在支持使用--yjit-trace-exits-sample-rate=N
进行采样
- 添加
- 更全面的测试和多个错误修复
RJIT
- 引入了纯 Ruby JIT 编译器 RJIT 并取代了 MJIT。
- RJIT 仅支持 Unix 平台上的 x86-64 架构。
- 与 MJIT 不同,它在运行时不需要 C 编译器。
- RJIT 的存在仅用于实验目的。
- 建议用户在生产环境中继续使用 YJIT。
- 如果对为开发 JIT for Ruby 感兴趣,可查看 k0kubun 在 RubyKaigi 第 3 天的演示。
M:N 线程调度器
- 引入了 M:N 线程调度程序。[Feature#19842]
- M 个 Ruby 线程由 N 个本机线程(操作系统线程)管理,因此减少了线程创建和管理成本。
- 它可能会破坏 C 扩展兼容性,因此默认情况下会在主 Ractor 上禁用 M:N 线程调度程序。
RUBY_MN_THREADS=1
环境变量可在主 Ractor 上启用 M:N 线程。- M:N 线程始终在非主 Ractor 上启用。
RUBY_MAX_CPU=n
环境变量用于设置N
(本地线程的最大数量)的最大值。默认值为 8。- 由于每个 Ractor 只能同时运行一个 Ruby 线程,因此将使用的本地线程数是
RUBY_MAX_CPU
中指定的线程数和正在运行的 Ractor 数量中较小的一个。因此,单 Ractor 应用程序(大多数应用程序)将只使用 1 个本地线程。 - 为了支持阻塞操作,可以使用
N
个以上的本地线程。
- 由于每个 Ractor 只能同时运行一个 Ruby 线程,因此将使用的本地线程数是
更多详情可查看官方公告。