MRI Ruby和jRuby之间的性能差异

在做一些基准测试以回答有关连接数组的最快方法的问题时,我很惊讶当我使用jRuby进行相同的基准测试时,测试速度要慢得多。

这是否意味着关于jRuby比MRI Ruby更快的旧的慢板已经消失了? 或者这是关于如何在jRuby中处理数组?

这里的基准测试结果和MRI Ruby 2.3.0和jRuby 9.1.2.0两者都运行在64位Windows 7盒子上,所有4个处理器忙于50-60%,内存使用±5.5GB。 必须使用参数-J-Xmx1500M启动jRuby以提供足够的堆空间。 由于堆栈级别太深,我不得不用推送删除测试,并且还删除了最慢的方法,以免使测试时间过长。 使用Jave运行时:1.7.0_21

 require 'Benchmark' N = 100 class Array def concat_all self.reduce([], :+) end end # small arrays a = (1..10).to_a b = (11..20).to_a c = (21..30).to_a Benchmark.bm do |r| r.report('plus ') { N.times { a + b + c }} r.report('concat ') { N.times { [].concat(a).concat(b).concat(c) }} r.report('splash ') { N.times {[*a, *b, *c]} } r.report('concat_all ') { N.times { [a, b, c].concat_all }} r.report('flat_map ') { N.times {[a, b, c].flat_map(&:itself)} } end #large arrays a = (1..10_000_000).to_a b = (10_000_001..20_000_000).to_a c = (20_000_001..30_000_000).to_a Benchmark.bm do |r| r.report('plus ') { N.times { a + b + c }} r.report('concat ') { N.times { [].concat(a).concat(b).concat(c) }} r.report('splash ') { N.times {[*a, *b, *c]} } r.report('concat_all ') { N.times { [a, b, c].concat_all }} r.report('flat_map ') { N.times {[a, b, c].flat_map(&:itself)} } end 

这个问题与使用的不同方法无关,请参阅原始问题。 在这两种情况下,MRI都快7倍! 有人能解释我为什么? 我也很好奇其他实现如何,比如RBX(Rubinius)

 C:\Users\...>d:\jruby\bin\jruby -J-Xmx1500M concat3.rb user system total real plus 0.000000 0.000000 0.000000 ( 0.000946) concat 0.000000 0.000000 0.000000 ( 0.001436) splash 0.000000 0.000000 0.000000 ( 0.001456) concat_all 0.000000 0.000000 0.000000 ( 0.002177) flat_map 0.010000 0.000000 0.010000 ( 0.003179) user system total real plus 140.166000 0.000000 140.166000 (140.158687) concat 143.475000 0.000000 143.475000 (143.473786) splash 139.408000 0.000000 139.408000 (139.406671) concat_all 144.475000 0.000000 144.475000 (144.474436) flat_map143.519000 0.000000 143.519000 (143.517636) C:\Users\...>ruby concat3.rb user system total real plus 0.000000 0.000000 0.000000 ( 0.000074) concat 0.000000 0.000000 0.000000 ( 0.000065) splash 0.000000 0.000000 0.000000 ( 0.000098) concat_all 0.000000 0.000000 0.000000 ( 0.000141) flat_map 0.000000 0.000000 0.000000 ( 0.000122) user system total real plus 15.226000 6.723000 21.949000 ( 21.958854) concat 11.700000 9.142000 20.842000 ( 20.928087) splash 21.247000 12.589000 33.836000 ( 33.933170) concat_all 14.508000 8.315000 22.823000 ( 22.871641) flat_map 11.170000 8.923000 20.093000 ( 20.170945) 

一般规则是(如评论中所述)JRuby / JVM需要预热。

通常bmbm是合适的,虽然应该增加TIMES=1000 (至少对于小arrays情况),1.5G可能还不足以实现JRuby的最佳性能(注意到从-Xmx2g到-Xmx3g的数量发生了相当大的变化) 。 这是结果:

 ruby 2.3.1p112 (2016-04-26 revision 54768) [x86_64-linux] $ ruby concat3.rb Rehearsal ----------------------------------------------- plus 0.000000 0.000000 0.000000 ( 0.000076) concat 0.000000 0.000000 0.000000 ( 0.000070) splash 0.000000 0.000000 0.000000 ( 0.000099) concat_all 0.000000 0.000000 0.000000 ( 0.000136) flat_map 0.000000 0.000000 0.000000 ( 0.000138) -------------------------------------- total: 0.000000sec user system total real plus 0.000000 0.000000 0.000000 ( 0.000051) concat 0.000000 0.000000 0.000000 ( 0.000059) splash 0.000000 0.000000 0.000000 ( 0.000083) concat_all 0.000000 0.000000 0.000000 ( 0.000120) flat_map 0.000000 0.000000 0.000000 ( 0.000173) Rehearsal ----------------------------------------------- plus 43.040000 3.320000 46.360000 ( 46.351004) concat 15.080000 3.870000 18.950000 ( 19.228059) splash 49.680000 4.820000 54.500000 ( 54.587707) concat_all 51.840000 5.260000 57.100000 ( 57.114867) flat_map 17.380000 5.340000 22.720000 ( 22.716987) ------------------------------------ total: 199.630000sec user system total real plus 42.880000 3.600000 46.480000 ( 46.506013) concat 17.230000 5.290000 22.520000 ( 22.890809) splash 60.300000 7.480000 67.780000 ( 67.878534) concat_all 54.910000 6.480000 61.390000 ( 61.404383) flat_map 17.310000 5.570000 22.880000 ( 23.223789) 

 jruby 9.1.6.0 (2.3.1) 2016-11-09 0150a76 Java HotSpot(TM) 64-Bit Server VM 25.112-b15 on 1.8.0_112-b15 +jit [linux-x86_64] $ jruby -J-Xmx3g concat3.rb Rehearsal ----------------------------------------------- plus 0.010000 0.000000 0.010000 ( 0.001445) concat 0.000000 0.000000 0.000000 ( 0.002534) splash 0.000000 0.000000 0.000000 ( 0.001791) concat_all 0.000000 0.000000 0.000000 ( 0.002513) flat_map 0.010000 0.000000 0.010000 ( 0.007088) -------------------------------------- total: 0.020000sec user system total real plus 0.010000 0.000000 0.010000 ( 0.002700) concat 0.000000 0.000000 0.000000 ( 0.001085) splash 0.000000 0.000000 0.000000 ( 0.001569) concat_all 0.000000 0.000000 0.000000 ( 0.003052) flat_map 0.000000 0.000000 0.000000 ( 0.002252) Rehearsal ----------------------------------------------- plus 32.410000 0.670000 33.080000 ( 17.385688) concat 18.610000 0.060000 18.670000 ( 11.206419) splash 57.770000 0.330000 58.100000 ( 25.366032) concat_all 19.100000 0.030000 19.130000 ( 13.747319) flat_map 16.160000 0.040000 16.200000 ( 10.534130) ------------------------------------ total: 145.180000sec user system total real plus 16.060000 0.040000 16.100000 ( 11.737483) concat 15.950000 0.030000 15.980000 ( 10.480468) splash 47.870000 0.130000 48.000000 ( 22.668069) concat_all 19.150000 0.030000 19.180000 ( 13.934314) flat_map 16.850000 0.020000 16.870000 ( 10.862716) 

……所以看起来相反 – MRI 2.3比JRuby 9.1慢2-5倍

 cat concat3.rb require 'benchmark' N = (ENV['TIMES'] || 100).to_i class Array def concat_all self.reduce([], :+) end end # small arrays a = (1..10).to_a b = (11..20).to_a c = (21..30).to_a Benchmark.bmbm do |r| r.report('plus ') { N.times { a + b + c }} r.report('concat ') { N.times { [].concat(a).concat(b).concat(c) }} r.report('splash ') { N.times {[*a, *b, *c]} } r.report('concat_all ') { N.times { [a, b, c].concat_all }} r.report('flat_map ') { N.times {[a, b, c].flat_map(&:itself)} } end #large arrays a = (1..10_000_000).to_a b = (10_000_001..20_000_000).to_a c = (20_000_001..30_000_000).to_a Benchmark.bmbm do |r| r.report('plus ') { N.times { a + b + c }} r.report('concat ') { N.times { [].concat(a).concat(b).concat(c) }} r.report('splash ') { N.times {[*a, *b, *c]} } r.report('concat_all ') { N.times { [a, b, c].concat_all }} r.report('flat_map ') { N.times {[a, b, c].flat_map(&:itself)} } end 

我从这些评论和答案以及我之后做过的测试中学到了什么……

  • 操作系统可能会有所作为,我会在不同的情况下喜欢更多的答案,所以在这里我只是在猜测
  • 最快的方法在运行时,MRI或jRuby,64位,JRE之间有所不同,因此声称该方法比其他方法更难,在我的系统上,plus方法几乎在所有情况下都是最快但我没有使用Java HotSpot就像kares一样
  • 在64位jRuby中,你可以指定比32位(我的系统上的1.5G)更高的堆,在64位我可以使用比我有内存更多的堆(某个地方有错误吗?)
  • 更高的堆使用像我使用的巨大arrays那样的大量内存来加速操作
  • 使用最新的Java运行时,速度更好
  • jRuby需要预热,一个方法需要在编译之前运行多次,所以使用带有不同重复值的.bm和.bmbm来查找边距
  • 有时MRI更快但是使用正确的参数和热身jRuby在我的系统上的速度是这个特定测试的3到3.5倍

最后,加上JVM使得MRI更适合短临时脚本,jRuby更适合过程饥饿,更长时间运行的流程以及经常重复的方法,因此jRuby更适合运行服务器和服务。

我所看到的确认:为长期或重复的过程做自己的基准测试。 与早期版本相比,这两种实现都在速度方面取得了很大的进步,让我们不要忘记:Ruby可能会慢一些,但开发人员会更快,如果你将一些额外硬件的成本与一些额外的开发人员进行比较……

感谢所有评论者和凯伦的专业知识。

编辑

出于好奇,我还在rubinius 3.69 (2.3.1 a57071c6 2016-11-17 3.8.0) [x86_64-linux-gnu]容器中使用Rubinius进行测试(我在Windows上), rubinius 3.69 (2.3.1 a57071c6 2016-11-17 3.8.0) [x86_64-linux-gnu]只有concat和flat_map是与MRI相提并论,我想知道这些方法是否在C中,其余的是纯Ruby。

 Rehearsal ----------------------------------------------- plus 0.000000 0.000000 0.000000 ( 0.000742) concat 0.000000 0.000000 0.000000 ( 0.000093) splash 0.000000 0.000000 0.000000 ( 0.000619) concat_all 0.000000 0.000000 0.000000 ( 0.001357) flat_map 0.000000 0.000000 0.000000 ( 0.001536) -------------------------------------- total: 0.000000sec user system total real plus 0.000000 0.000000 0.000000 ( 0.000589) concat 0.000000 0.000000 0.000000 ( 0.000084) splash 0.000000 0.000000 0.000000 ( 0.000596) concat_all 0.000000 0.000000 0.000000 ( 0.001679) flat_map 0.000000 0.000000 0.000000 ( 0.001568) Rehearsal ----------------------------------------------- plus 68.770000 63.320000 132.090000 (265.589506) concat 20.300000 2.810000 23.110000 ( 23.662007) splash 79.310000 74.090000 153.400000 (305.013934) concat_all 83.130000 100.580000 183.710000 (378.988638) flat_map 20.680000 0.960000 21.640000 ( 21.769550) ------------------------------------ total: 513.950000sec user system total real plus 65.310000 70.300000 135.610000 (273.799215) concat 20.050000 0.610000 20.660000 ( 21.163930) splash 79.360000 80.000000 159.360000 (316.366122) concat_all 84.980000 99.880000 184.860000 (383.870653) flat_map 20.940000 1.760000 22.700000 ( 22.760643)