2009-11-24 25 views
5

Estoy tratando de hacer una implementación de Mandelbrot rápida/eficiente en Ruby. Hace mucho tiempo, una forma de acelerarlo era usar enteros de punto fijo en lugar de flotadores.multiplicación de enteros rápida/rápida en ruby?

Así que hice el siguiente punto de referencia, comparando float y enteros elevando a un cuadrado, usando multiplicación o cuadrado ** operando.

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 

y esto genera la salida siguiente:

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) 

que muestra claramente el la multiplicación Fixnum es casi dos veces tan lento como punto flotante.

tengo dos preguntas:

  • Puede alguien explicar esto? Una razón que puedo imaginar es que la multiplicación de Fixnum es más lenta debido a la verificación interna de si necesita convertirse o no en Bignum.
  • ¿hay una segunda multiplicación de enteros rápidos para ruby?
+0

Asignación ¿podría estar desacelerándose por unas pocas milésimas o centésimas de segundo. (IE, el alcance no utilizado que debe ser monitoreado y limpiado en las pruebas, que no se usa como 'y'.) –

Respuesta

3

1.8.6 es sólo más lento en esta área. 1.8 0.7 hace un poco mejor y 1.9.1 hace aún mejor. no podría decir por qué, pero RVM está de acuerdo con usted y Pavel 1.8.6 que es extrañamente lento.

 
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 genial. He estado buscando puntos de referencia todo el día. Trataré de moverme a 1.9.1 lo antes posible :) – nathanvda

+0

1.9.1 sigue siendo inestable y tiene algunos cambios importantes en el código. Es la rama anterior a 2.0, por lo que puede encontrar muchas diferencias interesantes. No estoy seguro si importa o no para su caso, pero si está haciendo el código de producción 1.9.1 puede no ser el mejor. Academia, por otro lado ... :) –

0

No puedo explicar sus tablas. Pero puedo explicar el mío (ruby 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) 

Whoops. La multiplicación de enteros supera a los de coma flotante.

Dado que su procesador es 5 veces más lento que el mío (aumenté el número de repeticiones en su punto de referencia en diez veces), debe haber algo que no esté relacionado con el rubí.

** operación probablemente utiliza aritmética de punto flotante (exp (x * ln (2)), por lo que es tan lento como otras operaciones de punto flotante.

+0

Acerca de :) Estoy ejecutando ruby ​​1.8.6 en Windows, así que tal vez eso explica las cosas. Voy a probar en Virtual Box :) – nathanvda

+0

De hecho, el entero más lento es algo llamativo. Para JRuby, el cuadrado entero es en realidad más lento que los cuadrados flotantes. –

5

un par de cosas vienen a la mente. No especifica qué implementación de Ruby está utilizando. Dado que ejecuta Ruby 1.8.6 en Windows, supongo que está utilizando MRI instalado mediante el instalador de Windows One-Click.

Esta es una especie de peor de los casos:

  1. RM es el más lento de todos los implementaciones de Ruby
  2. resonancia magnética en Windows es aún más lento que la RM en Linux o OSX
  3. El El instalador One-Click usa los archivos binarios compilados previamente de Ruby-Lang.Org, compilados con Microsoft Visual C++ 6.0 desde 1996, y por lo tanto son incluso más lento que MRI en Windows compilado con Microsoft Visual C++ 10.0 o GCC 4.x o incluso GCC 3.x.

Aquí hay un par de consejos que usted podría tratar de mejorar el rendimiento:

  • utilizar el proyecto RubyInstaller, que utiliza intérpretes compilados con GCC 3.x en lugar de MSVC6,
  • tal vez volver a compilar el intérprete usted mismo (no es tan difícil con los Rakefiles proporcionados por el proyecto RubyInstaller) con GCC 4.x y/o diferentes opciones de optimización (RubyInstaller se compila con opciones moderadas de optimización y para CPU genéricas 386),
  • utilice una versión más nueva o f RMN que 1.8.6,
  • utilizar una implementación diferente de Ruby:

    • YARV es significativamente más rápida que la RM (por desgracia, sólo se implementa Ruby 1.9, por lo que puede que tenga que cambiar su código),
    • JRuby es significativamente más rápido que YARV en muchos escenarios, y se implementa tanto de Ruby 1.8 y Ruby 1.9 (que también tiene una opción de -fast de comandos, que es ligeramente incompatible con Ruby, pero mejora el rendimiento, incluyendo el rendimiento aritmética) y
    • IronRuby también podría ser más rápido que YA RV, dependiendo de la carga de trabajo.

En los dos últimos casos, es posible que desee revisar sus puntos de referencia un poco. Ambos eventualmente pueden compilar el código de Ruby en el código máquina nativo, pero podría llevar un tiempo. JRuby compila, por ejemplo, el bytecode de JVM después de que se haya ejecutado un método 20 veces y HotSpot Server compila el bytecode de JVM en el código máquina nativo después de haber ejecutado 20000 veces. Además, la compilación en sí requiere tiempo, por lo que el programa necesita ejecutarse un tiempo para recuperar ese costo mediante un mejor rendimiento.

En particular, Charles Oliver Nutter, uno de los desarrolladores principales de JRuby, dijo que dependiendo de la carga de trabajo, JRuby podría tomar hasta 5-15 segundos para alcanzar la velocidad máxima. Sus puntos de referencia son 100 veces más rápidos (aquí hay una oración que no escucha todos los días ...).

+0

Sí, también leí en esta publicación: http://antoniocangiano.com/2009/08/10/how-much-faster-is-ruby-on-linux/ that ruby en Linux también es sustancialmente más rápido (70% para ruby ​​1.9.1 y 100% para ruby ​​1.8.6). Entonces, lo que intentaré hacer es mover todo mi código a 1.9.1, espero que la mayoría de mis gems/plugins sean compatibles. Hay dos compatibilidades de las que preocuparse: ruby ​​1.9.1, por un lado, y la otra plataforma binaria del instalador de ruby. Pero creo que valdrá la pena el esfuerzo. Por otro lado, lo probaré en linux en una caja virtual en mi máquina, y tal vez eso sea aún más rápido;) – nathanvda

Cuestiones relacionadas