我有一个Ruby,最近我检查了使用JRuby (9.1.17.0,OpenJDK 1.8)是否会提高相对于目前使用的MRI (2.5.0)的性能。我预计会出现这种情况,因为性能瓶颈是用于计算响应数据的大量“基本算法”,而JRuby在计算量大的基准测试方面往往优于MRI。
然而,事实并非如此:我尝试了许多JRuby/JVM选项的组合,但是“稳态”比MRI慢2倍。稳定状态是在重复请求~ 100次之后实现的,JVM显然在执行其JIT魔术,因为性能比初始请求提高了2.5倍。
我想知道这是预期的还是意外的行为。因此,我想知道:在哪些典型的工作负载上,JRuby可能比MRI慢?而“浮子上的基本算术”真的在其中吗?
(性能瓶颈在MRI和JRuby中处于同一位置,使用适当的剖析器来确定。最初,这篇文章说JRuby只慢了20%,但后来我引入了一种优化,使JRuby的性能提高了近2倍,但几乎没有改变JRuby的性能。我怀疑JVM自动执行相同的优化,因为它基本上相当于“常量折叠”)
发布于 2018-05-05 20:08:15
如果您正在Integer
s上进行计算,并且Integer
的fit在native_word_size - 1位上,则YARV将在Fixnum
s上使用本机算法。如果您在Float
上进行计算,则在64位平台上进行计算,并且您的计算适合62位,YARV将在flonum上使用本地FPU算法。在这两种情况下,除非您的操作如此琐碎,以致于JIT (或JRuby编译器)能够完全优化它们,不断折叠它们,或者类似的东西,否则它不会变得更快。
热点是大于63位但小于64位的Integer
s,它被JRuby视为本地机器整数,而不是YARV,对于大于62位但小于64位的Float
也是如此。在这个范围内,JRuby将使用本地操作,但YARV不会使用,这给JRuby带来了性能优势。
一般来说,YARV在延迟(特别是启动时间)方面优于JRuby。但是,这在很大程度上取决于所使用的JVM和环境。有些JVM被设计为非常快地启动(例如IBM J9,IMO应该是默认的桌面JVM,而不是Oracle )或Avian (实际上不是JVM,因为它只实现JVM和JRE规范的一个子集,但是可以运行许多不使用任何非实现功能的重要程序,JRuby就是其中之一)。此外,还有一些环境和配置,允许您在内存中保留和重用JVM和JRuby实例,从而减少了大部分启动时间。
第二个较大的是YARV扩展。YARV为C扩展提供了一个非常开放和广泛的API。本质上,YARV C扩展可以访问YARV的几乎所有内部实现细节。(这显然意味着它们会破坏和破坏YARV。)另一方面,JVM "C扩展“总是需要通过一个安全屏障。它们只能破坏调用它们的Java代码显式传递给它们的内存,它们永远不能损坏其他内存,更不用说JVM本身了。但是,这需要付出性能代价:从Java调用C或从Java调用C通常比从YARV调用C慢,反之亦然。
YARV扩展甚至比这慢,因为JRuby本质上必须提供一个完整的复杂仿真层,模拟YARV的内部数据结构、函数和内存布局,以便至少运行一些YARV扩展。这太慢了。句号。
注意,这不适用于使用Ruby的C库的Ruby包装器。它们不依赖于YARV内部,因此不需要仿真层,而且JRuby有一个非常快速和优化的Ruby实现。但是,JVM↔C桥接的成本仍然适用。
这是YARV速度更快的两大特点:运行时间太短而无法利用JVM对长期运行进程的优化的代码,以及大量使用对C的调用,特别是YARV C扩展的代码。
如果您可以让代码在TruffleRuby上运行,那将是一个有趣的实验。TruffleRuby所能做的优化确实令人惊叹(例如,使用大量动态元编程、反射和Hash
查找将整个Ruby库折叠成一个常数),它可以接近甚至击败手工优化的C。此外,TruffleRuby除了一个Ruby解释器之外,还包含一个C解释器,从而可以分析和优化调用C扩展的Ruby代码,反之亦然,甚至执行跨语言内联,这意味着在某些基准中,它可以比YARV更快地执行Ruby代码,从而大大提高YARV扩展的使用速度!
https://stackoverflow.com/questions/50190091
复制相似问题