MRI 优于 JRuby 的典型工作负载示例有哪些?
What are examples of typical workloads where MRI outperforms JRuby?
我有一个 Ruby 网络服务,最近我检查了使用 JRuby(9.1.17.0,OpenJDK 1.8)是否会相对于当前使用 MRI(2.5.0)提高性能).我预计可能是这种情况,因为性能瓶颈是为计算响应数据而执行的大量 'basic arithmetic',而 JRuby 在计算量大的基准测试中往往优于 MRI。
然而,事实并非如此:我尝试了很多 JRuby/JVM 选项的组合,但是 'steady state' 比 MRI 慢 2 倍。在重复请求约 100 次后达到稳定状态,此时 JVM 显然正在发挥其 JIT 魔力,因为相对于初始请求,性能提高了 2.5 倍。
我想了解这是预期的还是意外的行为。所以我想知道:预计 JRuby 比 MRI 慢的典型工作负载是什么? 'basic arithmetic on floats' 确实在其中吗?
(性能瓶颈在 MRI 和 JRuby 的同一个地方,使用适当的分析器确定。最初这个 post 说 JRuby 只慢了 20%,但是从那以后,我引入了一项优化,将 MRI 性能提高了近 2 倍,但几乎没有改变 JRuby 性能。我怀疑 JVM 自动执行了相同的优化,因为它基本上等于 'constant folding')
如果您在 Integer
s 上进行计算,并且 Integer
s 适合 native_word_size - 1 位,那么 YARV 将在 Fixnum
上使用本机算法。如果您在 Float
s 上进行计算,在 64 位平台上,并且您的计算适合 62 位,YARV 将在 flonums 上使用本机 FPU 算法。在任何一种情况下,它都不会比这快得多,除非你的操作是如此微不足道以至于 JVM JIT(或 JRuby 编译器)可以完全优化它们,不断折叠它们,或类似的东西。
最佳点是大于 63 位但小于 64 位的 Integer
s,JRuby 而不是 YARV 将其视为本地机器整数,Float
s 大于 62 但小于 64 位。在此范围内,JRuby 将使用本机操作,但 YARV 不会,这为 JRuby 提供了性能优势。
一般来说,YARV 在 延迟 方面优于 JRuby,尤其是启动时间。不过,这在很大程度上取决于所使用的 JVM 和环境。有些 JVM 的设计启动速度非常快(例如 IBM J9,IMO 应该是默认的桌面 JVM 而不是 Oracle HotSpot)或 Avian(实际上不是 JVM,因为它只实现了 JVM 和 JRE 的一个子集)规范,但仍然可以 运行 许多不使用任何未实现功能的重要程序,JRuby 就是其中之一。)此外,还有一些环境和配置,允许您可以在内存中保留并重新使用 JVM 和 JRuby 实例,从而减少大部分启动时间。
第二个大问题是 YARV C 扩展。 YARV 对 C 扩展有非常开放和广泛的 API。本质上,YARV C 扩展几乎可以访问 YARV 的每个私有内部实现细节。 (这显然意味着它们可以破坏 YARV 并使其崩溃。)另一方面,JVM "C extensions" 始终需要通过安全屏障。它们只能破坏调用它们的 Java 代码显式传递给它们的内存,它们永远不会破坏其他内存,更不用说 JVM 本身了。但是,这是以性能成本为代价的:从 Java 调用 C 或从后者调用 C 通常 比从 YARV 调用 C 慢,反之亦然。
YARV C 扩展甚至比它慢,因为 JRuby 本质上必须提供一个完整的复杂仿真层,仿真内部数据结构、函数和YARV 的内存布局,以便至少获得 运行 的一些 YARV C 扩展。这太慢了。期间.
请注意,这不适用于使用 Ruby FFI API 的 C 库的 Ruby 包装器。这些不依赖于 YARV 内部结构,因此不需要仿真层,并且 JRuby 有一个非常快速和优化的 Ruby FFI API 实现。不过,JVM ↔ C 桥接的成本仍然适用。
这是 YARV 更快的两大方面:代码 运行 太短而无法利用 JVM 对 long-运行ning 进程的优化,以及大量使用的代码来往于 C 的调用,尤其是 YARV C 扩展。
如果您可以在 TruffleRuby 上将您的代码发送到 运行,那将是一个有趣的实验。 TruffleRuby 可以做的优化确实令人惊叹(例如,使用大量动态元编程、反射和 Hash
查找将整个 Ruby 库折叠成一个常量)并且它可以接近和甚至打败了手动优化的 C。此外,TruffleRuby 除了 Ruby 解释器之外还包含一个 C 解释器,因此可以分析和优化 Ruby 调用 C 扩展的代码,反之亦然,甚至执行跨语言内联,这意味着在某些基准测试中,它可以比 YARV 更快地执行 Ruby 大量使用 YARV 扩展的代码!
我有一个 Ruby 网络服务,最近我检查了使用 JRuby(9.1.17.0,OpenJDK 1.8)是否会相对于当前使用 MRI(2.5.0)提高性能).我预计可能是这种情况,因为性能瓶颈是为计算响应数据而执行的大量 'basic arithmetic',而 JRuby 在计算量大的基准测试中往往优于 MRI。
然而,事实并非如此:我尝试了很多 JRuby/JVM 选项的组合,但是 'steady state' 比 MRI 慢 2 倍。在重复请求约 100 次后达到稳定状态,此时 JVM 显然正在发挥其 JIT 魔力,因为相对于初始请求,性能提高了 2.5 倍。
我想了解这是预期的还是意外的行为。所以我想知道:预计 JRuby 比 MRI 慢的典型工作负载是什么? 'basic arithmetic on floats' 确实在其中吗?
(性能瓶颈在 MRI 和 JRuby 的同一个地方,使用适当的分析器确定。最初这个 post 说 JRuby 只慢了 20%,但是从那以后,我引入了一项优化,将 MRI 性能提高了近 2 倍,但几乎没有改变 JRuby 性能。我怀疑 JVM 自动执行了相同的优化,因为它基本上等于 'constant folding')
如果您在 Integer
s 上进行计算,并且 Integer
s 适合 native_word_size - 1 位,那么 YARV 将在 Fixnum
上使用本机算法。如果您在 Float
s 上进行计算,在 64 位平台上,并且您的计算适合 62 位,YARV 将在 flonums 上使用本机 FPU 算法。在任何一种情况下,它都不会比这快得多,除非你的操作是如此微不足道以至于 JVM JIT(或 JRuby 编译器)可以完全优化它们,不断折叠它们,或类似的东西。
最佳点是大于 63 位但小于 64 位的 Integer
s,JRuby 而不是 YARV 将其视为本地机器整数,Float
s 大于 62 但小于 64 位。在此范围内,JRuby 将使用本机操作,但 YARV 不会,这为 JRuby 提供了性能优势。
一般来说,YARV 在 延迟 方面优于 JRuby,尤其是启动时间。不过,这在很大程度上取决于所使用的 JVM 和环境。有些 JVM 的设计启动速度非常快(例如 IBM J9,IMO 应该是默认的桌面 JVM 而不是 Oracle HotSpot)或 Avian(实际上不是 JVM,因为它只实现了 JVM 和 JRE 的一个子集)规范,但仍然可以 运行 许多不使用任何未实现功能的重要程序,JRuby 就是其中之一。)此外,还有一些环境和配置,允许您可以在内存中保留并重新使用 JVM 和 JRuby 实例,从而减少大部分启动时间。
第二个大问题是 YARV C 扩展。 YARV 对 C 扩展有非常开放和广泛的 API。本质上,YARV C 扩展几乎可以访问 YARV 的每个私有内部实现细节。 (这显然意味着它们可以破坏 YARV 并使其崩溃。)另一方面,JVM "C extensions" 始终需要通过安全屏障。它们只能破坏调用它们的 Java 代码显式传递给它们的内存,它们永远不会破坏其他内存,更不用说 JVM 本身了。但是,这是以性能成本为代价的:从 Java 调用 C 或从后者调用 C 通常 比从 YARV 调用 C 慢,反之亦然。
YARV C 扩展甚至比它慢,因为 JRuby 本质上必须提供一个完整的复杂仿真层,仿真内部数据结构、函数和YARV 的内存布局,以便至少获得 运行 的一些 YARV C 扩展。这太慢了。期间.
请注意,这不适用于使用 Ruby FFI API 的 C 库的 Ruby 包装器。这些不依赖于 YARV 内部结构,因此不需要仿真层,并且 JRuby 有一个非常快速和优化的 Ruby FFI API 实现。不过,JVM ↔ C 桥接的成本仍然适用。
这是 YARV 更快的两大方面:代码 运行 太短而无法利用 JVM 对 long-运行ning 进程的优化,以及大量使用的代码来往于 C 的调用,尤其是 YARV C 扩展。
如果您可以在 TruffleRuby 上将您的代码发送到 运行,那将是一个有趣的实验。 TruffleRuby 可以做的优化确实令人惊叹(例如,使用大量动态元编程、反射和 Hash
查找将整个 Ruby 库折叠成一个常量)并且它可以接近和甚至打败了手动优化的 C。此外,TruffleRuby 除了 Ruby 解释器之外还包含一个 C 解释器,因此可以分析和优化 Ruby 调用 C 扩展的代码,反之亦然,甚至执行跨语言内联,这意味着在某些基准测试中,它可以比 YARV 更快地执行 Ruby 大量使用 YARV 扩展的代码!