2009-11-24 21 views
5

Sto cercando di realizzare un'implementazione Mandelbrot rapida ed efficiente in Ruby. Molto tempo fa, un modo per velocizzarlo era usare numeri interi fissi invece di float.moltiplicazione rapida/rapida di interi in ruby?

Così ho fatto il seguente benchmark, confrontando float e intero innalzandolo a un quadrato, usando l'operando moltiplicato o quadrato **.

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 

e questo genera il seguente output:

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) 

che mostra chiaramente la moltiplicazione Fixnum è quasi due volte più lento virgola mobile.

Ho due domande:

  • Qualcuno può spiegare questo? Una ragione che posso immaginare è che la moltiplicazione di Fixnum è più lenta a causa del controllo interno, indipendentemente dal fatto che debba essere convertita o meno in un Bignum.
  • in secondo luogo c'è una moltiplicazione rapida per rubino?
+0

assegnazione potresti essere rallentando di pochi millesimi o centesimi di secondo. (IE, l'ambito inutilizzato che deve essere monitorato e pulito nei test, che non viene usato come 'y'.) –

risposta

3

1.8.6 è solo più lento in questa zona. 1.8 0,7 fa un po 'meglio e 1.9.1 fa ancora meglio. non ho potuto dire perché, ma rvm d'accordo con te e Pavel che 1.8.6 è stranamente 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 fantastico. Ho guardato i benchmark tutto il giorno. Proverò a passare al 1.9.1 appena possibile :) – nathanvda

+0

1.9.1 è ancora instabile e presenta alcune modifiche significative al codice. È il ramo pre-2.0, quindi potresti trovare molte differenze interessanti. Non sono sicuro che sia importante o meno per il tuo caso, ma se stai facendo il codice di produzione 1.9.1 potrebbe non essere il migliore. Academia d'altra parte ... :) –

0

Non riesco a spiegare le vostre tabelle. Ma posso spiegare il mio (rubino 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 moltiplicazione intera batte quella in virgola mobile.

Poiché il processore è 5 volte più lento del mio (ho aumentato il numero di ripetizioni nel benchmark in dieci volte), ci deve essere qualcosa che non sia interessato al ruby.

** operazione probabilmente usato in virgola mobile (exp (x * ln (2)), quindi è lento come altre operazioni in virgola mobile.

+0

Riguardo :) Sto eseguendo Ruby 1.8.6 su Windows, quindi forse questo spiega le cose. Andando a provare in Virtual Box :) – nathanvda

+0

In effetti, il numero intero più lento è un po 'come lo sguardo. Per JRuby il quadrato intero è in realtà più lento dei quadrati fluttuanti. –

5

un paio di cose mi vengono in mente. Non si specifica quale implementazione di Ruby si sta utilizzando Da quando si esegue Ruby 1.8.6 su Windows, presumo che si stia utilizzando la risonanza magnetica installata tramite il programma di installazione con un clic su Windows.

Questa è una specie di scenario peggiore:

  1. RM è il più lento di tutti gli adempimenti di Ruby
  2. MRI su Windows è anche più lento di risonanza magnetica su Linux o OSX
  3. Il Il programma di installazione One-Click utilizza i binari precompilati da Ruby-Lang.Org, che sono stati compilati con Microsoft Visual C++ 6.0 dal 1996 e quindi sono ancora più lenti di rispetto a MRI su Windows compilato con Microsoft Visual C++ 10.0 o GCC 4.x o persino GCC 3.x.

Ecco un paio di suggerimenti che si potrebbe provare a migliorare le prestazioni:

  • utilizzare il progetto RubyInstaller, che utilizza gli interpreti compilato con GCC 3.x, invece di MSVC6,
  • forse ricompilare l'interprete te stesso (non è così difficile con i Rakefiles forniti dal progetto RubyInstaller) con GCC 4.xe/o diverse opzioni di ottimizzazione (RubyInstaller è compilato con opzioni di ottimizzazione moderate e per CPU 386 generiche),
  • usa una versione più recente o f MRI di 1.8.6,
  • utilizzare una diversa implementazione di Ruby:

    • YARV è significativamente più veloce di risonanza magnetica (purtroppo, si implementa solo Ruby 1.9, quindi potrebbe essere necessario modificare il codice),
    • JRuby è significativamente più veloce di YARV in un sacco di scenari, e implementa sia Ruby 1.8 e Ruby 1.9 (ha anche un'opzione -fast di comando, che è leggermente incompatibile con Ruby, ma migliora le prestazioni, comprese le prestazioni aritmetica) e
    • IronRuby potrebbe anche essere più veloce di YA RV, a seconda del carico di lavoro.

Negli ultimi due casi si potrebbe desiderare di rivedere i vostri punti di riferimento un po '. Entrambi alla fine possono compilare il codice Ruby al codice macchina nativo, ma potrebbe volerci un po 'di tempo. JRuby, ad esempio, compila il bytecode JVM dopo che un metodo è stato eseguito 20 volte e HotSpot Server compila il codice bytecode JVM al codice macchina nativo dopo aver eseguito 20000 volte. Inoltre, la compilazione richiede tempo, quindi il programma deve eseguire un po 'di tempo per recuperare quel costo attraverso prestazioni migliorate.

In particolare, Charles Oliver Nutter, uno degli sviluppatori principali di JRuby, ha affermato che, a seconda del carico di lavoro, JRuby potrebbe impiegare fino a 5-15 secondi per raggiungere la massima velocità. I tuoi benchmark sono circa 100x troppo veloci (ecco una frase che non senti ogni giorno ...).

+0

Sì, ho anche letto in questo post: http://antoniocangiano.com/2009/08/10/how-much-faster-is-ruby-on-linux/ che ruby su Linux è anche sostanzialmente più veloce (70% per Ruby 1.9.1 e 100% per Ruby 1.8.6). Quindi quello che cercherò di fare è spostare tutto il mio codice in 1.9.1, sperare che la maggior parte dei miei gemme/plugin siano compatibili. Ci sono due compatibilità di cui preoccuparsi: ruby ​​1.9.1 da un lato e l'altra piattaforma binaria dal programma di installazione di ruby. Ma penso che ne varrà la pena. D'altra parte lo testerò su Linux in una scatola virtuale sulla mia macchina, e forse sarà ancora più veloce;) – nathanvda