2016-11-10 2 views
4

Tout en faisant quelques benchmarking pour répondre à this question sur le moyen le plus rapide de concaténer les tableaux, j'ai été surpris que lorsque j'ai fait les mêmes tests avec jRuby les tests étaient beaucoup plus lent. Est-ce que cela signifie que le vieil adagio à propos de jRuby étant plus rapide que MRI Ruby est parti? Ou s'agit-il de la façon dont les tableaux sont traités dans jRuby?Différence de performance entre MRI Ruby et jRuby

Voici le cas-test et les résultats en MRI Ruby 2.3.0 et jRuby 9.1.2.0 Les deux fonctionnent sur un boîtier Windows 7 64 bits, les 4 processeurs occupés pour 50-60%, la mémoire utilisée ± 5,5 Go. Le jRuby a dû être démarré avec le paramètre -J-Xmx1500M pour fournir suffisamment d'espace de tas. J'ai dû supprimer le test avec push à cause du niveau de la pile trop profond et j'ai également retiré les méthodes les plus lentes pour ne pas faire les tests trop longtemps. runtime occasion 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 

Cette question ne concerne pas les différentes méthodes utilisées, voir la question initiale pour cela. Dans les deux cas, l'IRM est 7 fois plus rapide! Quelqu'un peut-il m'expliquer pourquoi? Je suis aussi curieux de la façon dont d'autres implémentations faire, comme 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) 
+0

sur mon système (OS X, JRuby 9.1.6.0, 2.3.1 IRM), les tableaux « petits » tableaux sont plus rapides sur l'IRM alors que les « grands » sont 2 -4x plus rapide dans JRuby. Cela est dû à l'utilisation du processeur: l'IRM n'utilise qu'un seul cœur et JRuby fait tourner mon ventilateur. Je ne sais pas pourquoi les résultats sont si différents. – Stefan

+0

J'ai trouvé le coupable: '-J-Xmx1500M'. Je n'ai pas utilisé cette option au début, donc la taille de tas maximum par défaut de Java a été utilisée ('4096M' sur mon système) et cela a très bien fonctionné. Si je fournis cette option, abaissant ainsi la valeur à «1500M», j'obtiens des résultats douloureusement lents. – Stefan

+0

Comme avec presque toutes les questions de benchmarking «Pourquoi Java est lent», je soupçonne que vous ne faites pas correctement l'analyse comparative. Par exemple, JRuby compile seulement le code Ruby au bytecode JVM après qu'une méthode ait été exécutée 20 fois (je pense). Et HotSpot ne compile que le bytecode JVM au code machine natif après qu'une méthode ait été exécutée plusieurs * fois * (IIRC, le seuil par défaut pour le compilateur C1 est 20000). Aucune de vos méthodes n'est exécutée même à distance assez souvent pour être compilé, elles seront toujours interprétées. Par exemple, 'concat_all' est seulement exécuté' N' fois, donc 'N' devrait être ... –

Répondre

3

règle générale est (comme mentionné dans les commentaires) qui a besoin JRuby/JVM warm-up.

habituellement bmbm est bon ajustement, bien que TIMES=1000 devrait être augmentée (au moins pour les petits cas de tableau), également 1.5G peut-être pas assez pour une performance optimale de JRuby (remarqué un changement considérable du nombre allant de -Xmx2g à - Xmx3g). voici les résultats:

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) 

... il semble que le contraire - IRM 2.3 obtient 2-5x plus lent que JRuby 9,1

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 
+0

pourriez-vous s'il vous plaît ajouter rubinius à vos repères? :-) – nurettin

1

Ce que j'ai appris de ces commentaires et réponses et les tests que j'ai fait par la suite ..

  • le système d'exploitation fait probablement une différence, je l'aurais aimé plus de réponses dans différentes situations alors voici que je suis juste deviner
  • la méthode la plus rapide diffère entre l'exécution, l'IRM ou JRuby, 32 de 64 bits, JRE, donc des réclamations que cette méthode est Beter que autre est difficile, sur mon sysrtem la méthode la plus rapide, plus était en presque toutes les circonstances, mais j'utiliser Java HotSpot plaisaient comme kares
  • 64 bits JRuby vous pouvez spécifier un tas beaucoup plus élevé que dans 32 bits (1.5G sur mon système), en 64 bits, je peux utiliser plus de tas que j'ai mémoire (un bug quelque part?)
  • tas plus d'accélérer les opérations en utilisant beaucoup de mémoire comme les énormes tableaux que j'ai utilisé
  • utiliser la dernière exécution Java, la vitesse est meilleure
  • JRuby a besoin d'un warm-up, une des méthodes doit exécuter un certain nombre de fois avant compilé, utilisez donc .bm et .BMBM avec des valeurs de répétition différentes pour trouver cette marge
  • Parfois, l'IRM est plus rapide, mais avec les bons paramètres et warm-up JRuby était de 3 à 3,5 fois plus vite sur mon système pour ce particulier essai

Le dernier, ensemble Avec le chargement de la JVM, l'IRM est meilleure pour les courts scripts ad hoc, jRuby mieux pour les processus affamés, les processus plus longs avec des méthodes répétées souvent, donc jRuby serait meilleur pour l'exécution de serveurs et de services.

Ce que j'ai vu confirmé: faites vos propres repères pour les processus longs ou répétés. Les deux implémentations ont fait de grandes améliorations de vitesse par rapport aux versions précédentes, n'oublions pas: Ruby peut être un coureur plus lent mais un développeur plus rapide et si vous comparez le coût de matériel supplémentaire à quelques développeurs supplémentaires ...

Merci à tous les commentateurs et karen pour leur expertise.

EDIT

Par curiosité je lance le test aussi avec Rubinius dans un récipient de docker (je suis sous Windows), rubinius 3.69 (2.3.1 a57071c6 2016-11-17 3.8.0) [x86_64-linux-gnu] Seulement concat et flat_map sont à égalité avec l'IRM, je me demande si ces méthodes sont C et le reste dans le plus pur 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) 
+0

Oui, il est important de mentionner ce que vous utilisez JVM. les gens assument HotSpot quand vous dites simplement Java. c'est toujours génial de connaître la version aussi, parfois (rare mais quand même) il pourrait y avoir des améliorations dans une version Java particulière. évidemment, utilisez la dernière version de JRuby (9.1.2 avait des bogues - utilisez plus récent 9.1.x si possible). permettre plus de tas que vous avez de la mémoire n'est pas un bug - certain genre de sous-optimale (JVM pourrait imprimer un avertissement au moins) ... puisque vous pourriez encore avoir permuter :) – kares