Ruby 2.6.0 发布
Ruby 2.6.0 正式版已经发布,引入了许多新功能和性能改进,最值得关注的有亮点:
- 新的 JIT 编译器
- RubyVM::AbstractSyntaxTree 模块
JIT [实验]
Ruby 2.6 引入了 JIT (Just-in-time) 编译器实现。
JIT 编译器旨在提高 Ruby 性能。与其他语言的普通 JIT 编译器不同,Ruby 的 JIT 编译器以一种独特的方式进行 JIT 编译,它先将 Ruby 编译成 C 代码,然后通过生成通用的 C 编译器过程 (compiler process) 来生成原生机器码。详情可查阅 Vladimir Makarov 的 MJIT 组织。
要启用 JIT 编译器,在命令行或 $RUBYOPT 环境变量中指定 --jit 参数即可。
在一个名叫 Optcarrot 的 CPU 密集计算基准测试中,Ruby 2.6 与 Ruby 2.5 相比,性能提高了 1.7 倍。不过目前仍然处于试验阶段,详见 Ruby 2.6 JIT - 进程与未来。
请保持对 Ruby 新时代性能的关注。
RubyVM::AbstractSyntaxTree [实验]
Ruby 2.6 引入了 RubyVM::AbstractSyntaxTree 模块,此模块向后兼容性不做保证。
此模块提供一个 parse 方法,传入 Ruby 代码字符串,返回 AST(抽象语法树)节点。而 parse_file 方法则接受一个 Ruby 代码文件作为参数,返回 AST 节点。
同时引入了 RubyVM::AbstractSyntaxTree::Node 类,你可以从 Node 对象中获取位置信息和子节点。此功能尚处于实验性质。
其它新特性
- Kernel#yield_self 添加新别名 then。[Feature #14594]
- 常量名现在可以以非 ASCII 大写字母开头。[Feature #13770]
- 无限范围 [Feature #12912]
引入了无限范围 (1..)。这个范围没有终点,以下是使用场景的举例。
ary[1..] # 等价于 ary[1..-1] 而不需要魔法 -1 (1..).each {|index| ... } # 从 1 开始无限循环 ary.zip(1..) {|elem, index| ... } # ary.each.with_index(1) { ... }
- 新增 Enumerable#chain 与 Enumerator#+ [Feature #15144]
- 为 Proc 和 Method 新增了函数构造操作符 << 与 >>。 [Feature #6284]
f = proc{|x| x + 2} g = proc{|x| x * 3} (f << g).call(3) # -> 11; identical to f(g(3)) (f >> g).call(3) # -> 15; identical to g(f(3))
- 新增 Binding#source_location。[Feature #14230]
此方法以一个二元组数组 FILE 和 LINE 的形式返回 binding 的源代码路径。传统上,这可以通过执行 eval("[__FILE__, __LINE__]", binding) 来获得相同的数据。但我们计划改变这一行为让 Kernel#eval 忽略 binding 的源代码路径 [Bug #4352]。所以,用户需要通过新加入的方法来替代之前的 Kernel#eval。
增加 :exception 选项,以让 Kernel.#system 抛出错误而不是返回 false。[Feature #14386]
新增 oneshot 模式 [Feature #15022]
- 此模式检查「每一行代码是否都至少被执行一次」,而不是「每行代码被执行了几次」。每行代码的 hook 至多被调用一次,并会在调用后将 hook 标识移除。换句话说,移除后的代码运行将没有额外的性能开销。
- 为 Coverage.start 方法新增 :oneshot_lines 关键字参数。
- 为 Coverage.result 方法新增 :stop 和 :clear 关键字参数。如果 clear 被设置为 true,它会清空计数器。如果 stop 被设置为 true,它会禁用覆盖测量。
- 新增 Coverage.line_stub,其为从源代码新建代码覆盖存根(stub)提供了一个简单的帮助函数。
- FileUtils#cp_lr。[Feature #4189]
性能提升
- 由于移除了对 $SAFE 临时赋值的支持,提升 Proc#call 的速度。[Feature #14318]
- 通过 lc_fizzbuzz 多次使用 Proc#call 的 benchmark 我们测量到了 1.4 倍性能提升 [Bug #10212]。
- 提升了当 block 是代码块参数时 block.call 的性能。[Feature #14330]
通过与 Ruby 2.5 中引入的提升代码块传递的性能的方法结合,Ruby 2.6 进一步提升了传递代码块调用时的性能。通过 micro-benchmark 测试有 2.6 倍性能提升。[Feature #14045]
- 引入了瞬态堆 (theap)。 [Bug #14858] [Feature #14989]
瞬态堆是用于管理指向特定类(Array、Hash、Object 和 Struct)短生命周期内存对象的堆。例如,创建小而短生命周期的哈希对象的速度提升到了 2 倍快。根据 rdoc benchmark,我们观察到了 6% 到 7% 的性能提升。
- 协程采用了原生实现(arm32、arm64、ppc64le、win32、win64、x86、amd64)显著提升了 Fiber 的性能。 [Feature #14739]
Fiber.yield 与 Fiber#resume 方法在 64 位 Linux 上提升了 5 倍性能。对于使用 Fiber 密集的程序,约有最高 5% 的性能提升。
其它自 2.5 以来值得注意的新特性
- $SAFE 成为了进程全局状态,我们可以再次将其设为 0。[Feature #14250]
- 不再建议将 safe_level 参数传递给 ERB.new 的行为。trim_mode 和 eoutvar 参数被转换成了关键词参数。[Feature #14256]
- 升级支持的 Unicode 版本至 11。我们计划在未来 Ruby 2.6 的小更新中升级至 12 和 12.1。其将引入新的日本年号。
- 合并 RubyGems 3.0.1,--ri 和 --rdoc 选项已被移除。请使用 --document 和 --no-document 选项来替代他们。
- 合并 Bundler 作为默认 gem 安装。
- 不含 rescue 的 else 现在会引起语法错误。实验性
更多详情见 NEWS 或 提交日志 以查看详情。
随着这些变动,自 Ruby 2.5.0 已发生了 6437 个文件变更,231471 行新增(+),98498 行删除(-)!
圣诞快乐!享受你 Ruby 2.6 的编程之旅吧!