2009-11-24 8 views
5

J'essaye de faire une implémentation rapide/efficace de Mandelbrot dans Ruby. Il y a très longtemps, une façon de l'accélérer était d'utiliser des entiers à virgule fixe au lieu de flottants. J'ai donc fait le benchmark suivant, en comparant le flottant et l'élévation d'entier à un carré, en utilisant la multiplication ou l'opérande carré **.multiplication rapide/rapide entier en rubis?

require 'benchmark' 

Benchmark.bmbm(10) do |x| 
    x.report("float-multip") do 
    for z in 0..100000 
     zf = z.to_f 
     y = zf*zf 
    end 
    end 

    x.report("float-square") do 
    for z in 0..100000 
     zf = z.to_f 
     y = zf**2 
    end 
    end 

    x.report("int-multip") do 
    zo = 0 
    for zi in 0..100000 
     y2 = zo*zo 
     zo += 1 
    end 
    end 

    x.report("int-multip") do 
    for zi in 0..100000 
     y2 = zi**2 
    end 
    end 
end 

et cela génère la sortie suivante:

Rehearsal ------------------------------------------------ 
float-multip 0.125000 0.000000 0.125000 ( 0.125000) 
float-square 0.125000 0.000000 0.125000 ( 0.125000) 
int-multip  0.250000 0.000000 0.250000 ( 0.250000) 
int-multip  0.282000 0.000000 0.282000 ( 0.282000) 
--------------------------------------- total: 0.782000sec 

        user  system  total  real 
float-multip 0.110000 0.000000 0.110000 ( 0.110000) 
float-square 0.125000 0.000000 0.125000 ( 0.125000) 
int-multip  0.219000 0.016000 0.235000 ( 0.235000) 
int-multip  0.265000 0.015000 0.280000 ( 0.282000) 

qui montre clairement la multiplication Fixnum est presque deux fois plus lent que virgule flottante.

J'ai deux questions:

  • Quelqu'un peut-il expliquer cela? Une raison que je peux imaginer est que la multiplication de Fixnum est plus lente à cause de la vérification interne si elle doit ou non être convertie en Bignum.
  • d'autre part est-ce qu'une multiplication d'entier rapide pour ruby?
+0

affectation pourrait être vous ralentir par quelques millièmes ou centièmes de seconde. (IE, la portée non utilisée qui doit être surveillée et nettoyée dans les tests, qui n'est pas utilisée comme 'y'.) –

Répondre

3

1.8.6 est juste plus lent dans ce domaine 1.8. .7 fait un peu mieux et 1.9.1 encore mieux. Je ne saurais dire pourquoi, mais RVM est d'accord avec vous et Pavel que 1.8.6 est étrangement lent.

 
1.8.6: 
Rehearsal ------------------------------------------------ 
float-multip 0.140000 0.000000 0.140000 ( 0.141560) 
float-square 0.150000 0.000000 0.150000 ( 0.146286) 
int-multip  0.220000 0.000000 0.220000 ( 0.223255) 
int-multip  0.180000 0.000000 0.180000 ( 0.183850) 
--------------------------------------- total: 0.690000sec 

1.8.7: 
Rehearsal ------------------------------------------------ 
float-multip 0.090000 0.000000 0.090000 ( 0.092346) 
float-square 0.080000 0.000000 0.080000 ( 0.080335) 
int-multip  0.070000 0.000000 0.070000 ( 0.068012) 
int-multip  0.080000 0.000000 0.080000 ( 0.081713) 
--------------------------------------- total: 0.320000sec 

1.9.1: 
Rehearsal ------------------------------------------------ 
float-multip 0.070000 0.000000 0.070000 ( 0.065532) 
float-square 0.080000 0.000000 0.080000 ( 0.081620) 
int-multip  0.060000 0.000000 0.060000 ( 0.065371) 
int-multip  0.070000 0.000000 0.070000 ( 0.065761) 
--------------------------------------- total: 0.280000sec 
+0

Wow super. J'ai regardé des benchmarks toute la journée. Je vais essayer de passer à 1.9.1 dès que possible :) – nathanvda

+0

1.9.1 est toujours instable et a quelques changements importants dans le code. C'est la branche pré-2.0 donc vous pouvez trouver beaucoup de différences intéressantes. Je ne sais pas si cela compte ou non pour votre cas, mais si vous faites le code de production 1.9.1 n'est peut-être pas le meilleur. Academia d'autre part ... :) –

0

Je ne peux pas expliquer vos tables. Mais je peux expliquer le mien (rubis 1.8.7):

    user  system  total  real 
float-multip 0.600000 0.000000 0.600000 ( 0.612311) 
float-square 0.650000 0.000000 0.650000 ( 0.649399) 
int-multip  0.450000 0.010000 0.460000 ( 0.457004) 
int-multip  0.690000 0.000000 0.690000 ( 0.692879) 

Oups. La multiplication d'entier bat les virgules flottantes.

Étant donné que votre processeur est 5 fois plus lent que le mien (j'ai augmenté le nombre de répétitions de votre test en dix fois), il doit y avoir quelque chose qui ne vous inquiète pas.

** opération probablement utilisé arithmétique à virgule flottante (exp (x * ln (2)), il est aussi lent que d'autres opérations à virgule flottante.

+0

Concernant :) Je cours ruby ​​1.8.6 sous Windows, alors peut-être que cela explique les choses. Va essayer dans Virtual Box :) – nathanvda

+0

En effet, le carré entier plus lent attire un peu l'œil. Pour JRuby, le carré entier est en réalité plus lent que les carrés flottants. –

5

deux choses viennent à l'esprit. Vous ne spécifiez pas l'implémentation de Ruby que vous utilisez Comme vous exécutez Ruby 1.8.6 sous Windows, je vais supposer que vous utilisez l'IRM installée via le programme d'installation Windows One-Click

C'est une sorte de pire scénario:

  1. IRM est la plus lente de toutes les implémentations Ruby
  2. IRM sur Windows est encore plus lent que l'IRM sous Linux ou OSX
  3. Le One-Click Installer utilise les binaires précompilés de Ruby-Lang.Org, qui sont compilés avec Microsoft Visual C++ 6.0 à partir de 1996, et sont donc encore plus lent que MRI sur Windows compilé avec Microsoft Visual C++ 10.0 ou GCC 4.x ou même GCC 3.x.

est ici quelques conseils que vous pouvez essayer d'améliorer les performances:

  • utiliser le projet RubyInstaller, qui utilise les interprètes compilés avec GCC 3.x au lieu de MSVC6,
  • recompiler peut-être l'interprète vous-même (ce n'est pas si difficile avec les Rakefiles fournis par le projet RubyInstaller) avec GCC 4.x et/ou différentes options d'optimisation (RubyInstaller est compilé avec des options d'optimisation modérées et pour les CPU 386 génériques),
  • utiliser une version plus récente o f IRM que 1.8.6,
  • utiliser une autre mise en œuvre de Ruby:

    • YARV est nettement plus rapide que l'IRM (malheureusement, il met en œuvre que Ruby 1.9, de sorte que vous pourriez avoir à changer votre code),
    • JRuby est nettement plus rapide que YARV dans un grand nombre de scénarios, et il met en œuvre à la fois Ruby 1.8 et Ruby 1.9 (il a également une option -fast de ligne de commande, ce qui est légèrement incompatible avec Ruby, mais améliore la performance, y compris le rendement arithmétique) et
    • IronRuby pourrait également être plus rapide que YA RV, en fonction de la charge de travail.

Dans ces deux derniers cas, vous pourriez vouloir réviser vos repères un peu. Les deux finissent par compiler le code Ruby en code machine natif, mais cela peut prendre un certain temps. JRuby, par exemple, se compile en un bytecode JVM après qu'une méthode a été exécutée 20 fois et HotSpot Server compile le bytecode JVM en code machine natif après qu'il a été exécuté 20000 fois. En outre, la compilation elle-même prend du temps, donc le programme doit fonctionner un certain temps pour récupérer ce coût grâce à des performances améliorées. En particulier, Charles Oliver Nutter, l'un des principaux développeurs de JRuby, a déclaré que, selon la charge de travail, JRuby pourrait prendre jusqu'à 5-15 secondes pour atteindre sa pleine vitesse. Vos benchmarks sont environ 100x trop rapides (voici une phrase que vous n'entendez pas tous les jours ...).

+0

Ouais, je lis aussi dans ce post: http://antoniocangiano.com/2009/08/10/how-much-faster-is-ruby-on-linux/ ce rubis sur Linux est également sensiblement plus rapide (70% pour ruby ​​1.9.1 et 100% pour ruby ​​1.8.6). Donc, ce que je vais essayer de faire est de déplacer tout mon code à 1.9.1, j'espère que la plupart de mes gemmes/plugins sont compatibles. Il y a deux compatibilités à s'inquiéter: ruby ​​1.9.1 d'une part, et l'autre plate-forme binaire de l'installateur ruby. Mais je pense que cela en vaudra la peine. D'un autre côté, je vais le tester sur Linux dans une boîte virtuelle sur ma machine, et peut-être que ce sera encore plus rapide;) – nathanvda