Mi sto spostando su blogger, questo è il link del mio nuovo blog.
Archive for the ‘Ruby’ Category
Leonardo: un generatore di applicazioni Rails 3.1
L’aggiornamento alla versione più recente lo trovi sul mio nuovo blog. Click here for the english version.
Ho creato un nuovo generatore per creare applicazioni rails 3.1 (che attualmente si trova in rc6) ed automatizzare le operazioni che risultano ripetitive.
Il nome della gemma è leonardo ed è suddivisa in due generatori:
- leolay: per creare il layout
- leosca: per creare la risorsa (sostituisce lo scaffold)
Perchè creare un nuovo generatore se esiste già lo scaffold di rails?
Lo scaffold è un comodo generatore e si può anche personalizzare con facilità ma è adatto per scopi didattici e la personalizzazione è limitata alle sole viste. In produzione mi serviva qualcosa di più completo e personalizzabile.
Preparazione dell’ambiente ed installazione della gemma
Creiamo un nuovo ambiente ruby ed installiamo rails 3.1:
gem install rails –pre
usiamo il –pre perchè in questo momento si trova in release candidate, toglietelo se invece è stata rilasciata
Possiamo installare la gemma semplicemente con:
gem install leonardo
…ma non è necessario e consiglio di usare il procedimento che sto per descrivere:
creiamo una nuova applicazione utilizzando il template che ho preparato, raccomandato per agevolare l’avvio e per meglio sfruttare la gemma:
rails new NewApp -m http://cloud.github.com/downloads/marcomd/Leonardo/template.rb
Se non dovesse funzionare il link potete comunque trovare il template nella root della gemma
Il template chiederà se installare determinate gemme esterne, per una prova consiglio di installarle tutte rispondendo y o premendo invio quando propone un default (es. devise)
Verrà poi eseguito un bundle install per verificare la presenza delle gemme ed in seguito verranno eseguite le varie generazioni, il tutto durerà qualche minuto circa.
La generazione del layout chiederà una conferma per sostituire il file en.yml, rispondete y. Preferisco non forzare la sostituzione per permettere altre ed eventuali esecuzioni del generatore leolay nel caso si volesse aggiornare il layout.
Al termine otterrete un applicazione “pronta per partire” potendo così orientare lo sviluppo sulla parte applicativa.
Spostiamoci nella cartella dell’applicazione ed avviamo il server
cd NewApp
rails s
indirizziamo il browser all’indirizzo http://localhost:3000 per accedere alla home
Se clicchiamo su “Sign in” verrà richiesta l’autenticazione (se l’avete inclusa), per un rapido accesso potete inserire:
email: admin@newapp.com
password: abcd1234

Vengono create tre utenze con tre ruoli diversi (nel caso avete incluso le autorizzazioni):
- admin@newapp.com
- manager@newapp.com
- user@newapp.com
Naturalmente si tratta di utenze con finalità legate allo sviluppo per cui ricordate di eliminarle prima di qualsiasi rilascio. Possiamo consultare e modificare i ruoli accedendo al file app/models/ability.rb in quanto la gestione è affidata alla gemma cancan.
Per cambiare la lingua è sufficiente inviare la nuova come valore del parametro lang, esempio:
http://localhost:3000/?lang=it
il generatore attualmente gestisce :en e :it ma aggiungere il supporto ad un’altra lingua è semplice, basta aggiungere il file yml nella cartella dove si trovano i primi due, naturalmente deve avere gli stessi tags.
Generare le risorse
Ora generiamo qualche risorsa utilizzando il generatore leosca, una sorta di scaffold personalizzato:
rails g leosca category name:string active:boolean
rails g leosca product category:references name:string description:text price:decimal
ora creiamo le due nuove tabelle anche sul database:
rake db:migrate
ed eseguiamo anche il popolamento con qualche dato che leosca ha preparato per noi:
rake db:seed

Alcune note:
- L’esportazione dei dati in csv attualmente non funziona se attiva la paginazione ajax, sto cercando un buon metodo per risolvere.
- Nelle liste vengono inseriti in automatico tutti i campi per poter applicare dei filtri, eliminiamo quelli che non ci interessano. I filtri su campi boolean non funzionano con sqlite3 a causa di un bug nel driver che non genera un sql corretto. Con SQLServer funziona alla perfezione.
- Gli oggetti relazionati mostrano un link col nome e cliccando si effettua una :show. Nel caso la tabella non abbia il campo :name viene utilizzato l’id che è possibile sostituire con il dato che più lo rappresenta.
- Solo l’operazione destroy è gestita tramite ajax. Show, edit e create attualmente sono gestite tramite http.
- Riguardo le utenze create in automatico: con i ruoli di default admin può fare tutto, manager tutte le operazioni crud, user tutte tranne la delete.
- Le etichette dei campi vengono inserite in automatico ma la traduzione è necessario farsela da se all’interno dei files config/locales/*.yml
Personalizzare il generatore
E’ sufficiente installarlo nel progetto e personalizzarlo secondo le proprie esigenze:
rails g leosca:install
si trova sotto lib ed eventuali modifiche avranno la precedenza sulla gemma
Conclusioni
Per ulteriori approfondimenti o aggiornamenti sugli sviluppi futuri è possibile consultare la homepage del progetto:
https://github.com/marcomd/Leonardo
Altri riferimenti:
http://edgeguides.rubyonrails.org/generators.html
http://railscasts.com/episodes/216-generators-in-rails-3
http://railscasts.com/episodes/218-making-generators-in-rails-3
http://railscasts.com/episodes/242-thor
http://rdoc.info/github/wycats/thor/master/Thor
http://textmate.rubyforge.org/thor/Thor/Actions.html
http://railscasts.com/episodes/265-rails-3-1-overview
Files di grandi dimensioni su Windows Server 2008 R2 64Bit
Nel precedente articolo ho esaminato la semplice lettura e scrittura di tre files con dimensioni da 330Mb a 2,6Gb, su un normalissimo PC con Windows XP. Ora vediamo cosa cambia su un server virtuale, sistema produttivo che coinvolge un numero di aziende sempre più ampio.
I linguaggi in esame sono i seguenti:
Ruby 1.8.6 p383 (2009-08-04) [i386-mingw32]
Ruby 1.8.7 p334 (2011-02-18) [i386-mingw32]
Ruby 1.9.2 p180 (2011-02-18) [i386-mingw32]
jruby 1.6.1 (ruby-1.8.7-p330) (2011-04-12) (Java HotSpot(TM) 64-Bit Server VM 1.6.0_23) [Windows Server 2008 R2-amd64-java]
IronRuby 1.1.3.0 (ruby-1.9.2) on .NET 4.0.30319.225
Python 2.7.1 32bit
Python 2.7.1 64bit
Python 3.2.0 32bit
Python 3.2.0 64bit
Php 5.3.6 vc9 unsafe thread
Lua 5.1.4 40
C# 32bit on .NET 2.0.50727.4927
C# 64bit on .NET 2.0.50727.4927
C# 32bit on .NET 4.0.30319.1
C# 64bit on .NET 4.0.30319.1
Solo python fornisce pacchetti di installazione x64 e ne ho approfittato per confrontarli con le versioni a 32 bit. Probabilmente le differenze si noteranno con operazioni matematiche rispetto la sezione IO ma questo apre la strada alla prossima comparazione.
La versione di ruby 1.8.6 è una mingw32 e non mswin32 come nel precedente test. IronRuby invece è l’ultima 1.1.3 che abbraccia il supporto a ruby 1.9.2 e non 1.8.6 come la versione del precedente test con cui, comunque, condivide lo stesso framework .net e la stessa sezione IO.
Questa volta ho anche aggiunto C# nella comparazione, ho compilato quattro versioni differenziando per piattaforma, x86 e x64, ed anche per framework, 3.5 e 4. Il framework .net 3.5 utilizza lo stesso CLR del 2.0.
Una nota di merito a IronRuby, il primo della classe che è persino davanti a C#, linguaggio compilato e con cui condivide molto. E’ vero che questo test non richiede potenza computazionale particolarmente elevata ma è certamente un risultato curioso.
Un riepilogo anche sul consumo della memoria:
| Lua 5.1.4 | 0,7mb |
| Php 5.3.6 | 2,2mb |
| Python 2.7.1 32bit | 2,5mb |
| Python 3.2.0 32bit | 3,7mb |
| Python 2.7.1 64bit | 4mb |
| Python 3.2.0 64bit | 5,5mb |
| Ruby 1.9.2p180 | 4-6mb |
| Ruby 1.8.6p383 | 4-9mb |
| Ruby 1.8.7p334 | 4-9mb |
| C# 32bit on .NET 2.0.50727.4927 | 7mb |
| C# 32bit on .NET 4.0.30319.1 | 7mb |
| C# 64bit on .NET 2.0.50727.4927 | 9mb |
| C# 64bit on .NET 4.0.30319.1 | 9mb |
| IronRuby 1.1.3.0 on .NET 4.0.30319.225 | 11mb |
| jruby 1.6.1 (JVM 64-Bit Server 1.6.0_23) | jruby 1mb + java 200mb |




Questo è il codice C# che ho compilato con Visual Studio 2010:
using System; using System.IO; namespace Split { class Program { /// <summary> /// To split a file into n output files /// </summary> /// <param name="args">Filename and records number to split</param> static void Main(string[] args) { string strInput = args[0]; string strOutput = "out_{0:000}.txt"; Int32 nrec_to_split = Convert.ToInt32(args[1]); DateTime t1 = DateTime.Now; Console.WriteLine("C# {1} Started at {0:R}, please wait...", t1, System.Environment.Version); StreamReader sr; StreamWriter sw = null; sr = new StreamReader(strInput); Int16 nsplit = 0; Int64 nrec = 0; while (sr.Peek() >= 0) { if (nrec % nrec_to_split == 0) { ++nsplit; if (sw != null) sw.Close(); sw = new StreamWriter(String.Format(strOutput, nsplit)); } sw.WriteLine(sr.ReadLine()); ++nrec; } Console.WriteLine("Ended at {0:R}, please wait...", DateTime.Now); Console.WriteLine("Elapsed time {0}", DateTime.Now - t1); } } }
Ruby, Python, Php e Lua in comparazione con files di grandi dimensioni
Vediamo come si comporta la sezione IO di alcuni tra i più popolari linguaggi script. L’esercizio consiste nel leggere sequenzialmente un grosso file di input e dividerlo in files più piccoli, in termini pratici, splittarlo.
I linguaggi in esame sono:
Ruby 1.8.6 p287 (2008-08-11) [i386-mswin32]
Ruby 1.8.7 p334 (2011-02-18) [i386-mingw32]
Ruby 1.9.2 p180 (2011-02-18) [i386-mingw32]
jruby 1.5.1 (ruby 1.8.7 patch 249) (Java HotSpot(TM) Client VM 1.6.0_14) [x86-java]
jruby 1.5.1 (ruby 1.8.7 patch 249) (Java HotSpot(TM) Client VM 1.6.0_24) [x86-java]
jruby 1.6.1 (ruby-1.8.7-p330) (Java HotSpot(TM) Client VM 1.6.0_24) [Windows XP-x86-java]
IronRuby 1.1.0.0 on .NET 4.0.30319.225
Python 2.6.2
Python 2.7.1
Python 3.2.0
Php 5.3.6 vc9 unsafe thread
Lua 5.1.4 40
Iniziamo col creare i tre files di input necessari per il test:
ruby new.rb input1.txt 185000 1799 => 330Mb
ruby new.rb input2.txt 500000 1799 => 880Mb
ruby new.rb input3.txt 1500000 1799 => 2,6Gb
Il test l’ho eseguito su un PC con cpu Intel E7300 Core2 Duo 2,66Ghz Ram 3,25Gb con Windows XP Professional 32bit, Hard Disk ST3250310AS Barracuda 7200.10 SATA 3.0Gb/s da 250Gb.
Prossimamente lo eseguirò anche su un Server Windows 2008 R2 64bit su VMWare Xeon X7460 Dual Core a 2,66Ghz e 2Gb di ram con dischi SCSI.
Prima e dopo aver creato i tre files di input ho deframmentato il disco. Se i tempi sono incostanti significa che il disco deve essere deframmentato o c’è qualcosa che rallenta il sistema come ad esempio l’antivirus che deve essere disabilitato.
Per ogni file ho eseguito sei elaborazioni e considerando le scarse prestazioni IO del sistema, ho scartato le tre peggiori. Naturalmente, prima di ogni test ho eliminato i files di output.
I grafici parlano da soli.
L’unico commento che posso fare riguarda la versione 1.9.2 di ruby che ha evidenti problemi di IO e questi risultati non sono in linea con le performance generali di questo linguaggio che, come ho potuto rilevare da altri precedenti test, sono invece molto buone.




Questi sono gli script che ho scritto per l’occasione:
# Written by Marco Mastrodonato on 19/04/2011 # Script to split a file into n output files # Example: # ruby split.rb par1 par2 # par1 => name [default => input1.txt] # par2 => record number that determines the number of output files [default => 1650] strinput = ARGV[0] || 'input1.txt' nrec_to_split = ARGV[1] ? ARGV[1].to_i : 1650 unless File.exists? strinput puts "File #{strinput} doesn't exists!" exit 1 end stroutput = "out_%03d.txt" t1= Time.now puts "Ruby #{RUBY_VERSION} #{strinput} started at #{t1}, wait please..." File.open(strinput, "r") do |f| nsplit = 0 nrec = 0 fileoutput = nil while line = f.gets if nrec % nrec_to_split == 0 nsplit += 1 fileoutput.close if fileoutput fileoutput = File.open(stroutput % nsplit, 'w') end fileoutput.write line nrec += 1 end fileoutput.close if fileoutput end puts "Ended at #{Time.now}" puts "Elapsed time #{Time.now - t1}" exit 0
# Written by Marco Mastrodonato on 19/04/2011 # Script to split a file into n output files # Example: # python split.py par1 par2 # par1 => name [default => input1.txt] # par2 => record number that determines the number of output files [default => 1650] from time import time, gmtime, strftime import sys try: strinput = sys.argv[1] except: strinput = 'input1.txt' stroutput = "out_%03d.txt" try: nrec_to_split = int(sys.argv[2]) except: nrec_to_split = 1650 t1 = time() print(sys.version) print(strftime("Started at %a, %d %b %Y %H:%M:%S +0000, wait please...", gmtime())) nrec = 0 nsplit = 0 fileinput = open(strinput, "r") for line in fileinput: if nrec % nrec_to_split == 0: try: fileoutput.close() except NameError: fileoutput = None nsplit += 1 fileoutput = open(stroutput %nsplit , "w") fileoutput.write(line) nrec += 1 fileoutput.close() fileinput.close() print(strftime("Ended at %a, %d %b %Y %H:%M:%S +0000", gmtime())) print("Elapsed time %f" %(time() - t1))
<?php // Written by Marco Mastrodonato on 19/04/2011 // Script to split a file into n output files // Example: // php split.php par1 par2 // par1 => name [default => input1.txt] // par2 => record number that determines the number of output files [default => 1650] $strinput = isset($argv[1]) ? $argv[1] : 'input1.txt'; $nrec_to_split = isset($argv[2]) ? $argv[2] : 1650; $stroutput = 'out_%03d.txt'; $t1 = microtime_float(); echo "Php ".phpversion()." started at ".date('D, d M Y H:i:s T').", wait please...\n"; $nsplit = 0; $nrec = 0; $fileinput=fopen($strinput,"r"); while(!feof($fileinput)) { if ($nrec % $nrec_to_split == 0) { ++$nsplit; if (isset($fileoutput)) fclose($fileoutput); $fileoutput = fopen(sprintf($stroutput, $nsplit), 'w'); } $buffer = fgets($fileinput); fwrite($fileoutput, $buffer); ++$nrec; } fclose ($fileinput); echo "Ended at ".date('D, d M Y H:i:s T')."\n"; echo "Elapsed time ".(microtime_float() - $t1)."\n"; function microtime_float() { list($usec, $sec) = explode(" ", microtime()); return ((float)$usec + (float)$sec); } ?>
--[[ Written by Marco Mastrodonato on 19/04/2011 Script to split a file into n output files Example: lua split.lua par1 par2 par1 => name [default => input1.txt] par2 => record number that determines the number of output files [default => 1650] --]] strinput = arg and arg[1] or "input1.txt" stroutput = "out_%03d.txt" nrec_to_split = arg and arg[2] and tonumber(arg[2]) or 1650 local t1 = os.clock() print(_VERSION .. " started at " .. os.date("%a, %d %b %Y %H:%M:%S +0000")) nsplit = 0 nrec = 0 for line in io.lines(strinput) do if nrec % nrec_to_split == 0 then if fileOut ~= nil then io.close(fileOut) end nsplit = nsplit + 1 fileOut = io.open(string.format(stroutput, nsplit) , 'w') end fileOut:write (line .. '\n') nrec = nrec + 1 end io.close(fileOut) print("Ended at " .. os.date("%a, %d %b %Y %H:%M:%S +0000")) print(string.format("Elapsed time: %.2f\n", os.clock() - t1))
Per creare i files ho usato questo semplice script ruby:
# Example: # ruby new.rb [NOME] [LINES] [RECORD SIZE] stroutput = ARGV[0] || 'input1.txt' num = ARGV[1] ? ARGV[1].to_i : 185000 size = ARGV[2] ? ARGV[2].to_i : 1799 if File.exists? stroutput puts "File #{stroutput} already exists!" exit 1 end t1= Time.now puts "Ruby #{RUBY_VERSION} #{stroutput} started at #{t1}, wait please..." line = "*" * size File.open(stroutput, "w") do |f| num.times do f.puts line end end puts "Ended at #{Time.now}" puts "Elapsed time #{Time.now - t1}" exit 0
Ruby 1.9.2 prestazioni al top
Con un precedente test, avevo riscontrato che JRuby era la massima espressione per quanto riguardava la velocità nell’eseguire codice ruby, surclassando non solo gli altri interpreti C++ e .Net ma azzardando prepotenti confronti con le ultime due incarnazioni di python. Gli script erano questi e li ho rispolverati anche per questo nuovo confronto.
Ora che Rails 3 è stato rilasciato ed uno degli interpreti consigliati è il nuovo 1.9.2, ero curioso di osservare come si comporta la release ufficiale: direi davvero niente male!
Questa volta ho ristretto il numero dei partecipanti alle sole versioni installate con RVM più un paio di versioni windows: JRuby 1.4.0 ed il più usato 1.8.7 mingw32, giusto per avere un metro di paragone.
Distribuzione Linux: Ubuntu 10.4 64bit
Windows XP 32Bit SP3
| Versione | Compilatore/Sistema | Secondi |
|---|---|---|
| Ruby 1.9.2 p0 RVM | x86_64-linux | 6,6 |
| JRuby 1.4.0 RVM | OpenJDK 64-Bit Server VM 1.6.0_18 [amd64-java] | 6,8 |
| JRuby 1.5.2 RVM | OpenJDK 64-Bit Server VM 1.6.0_18 [amd64-java] | 7,0 |
| JRuby 1.4.0 | Windows Client VM 1.6.0_15 [x86-java] | 7,0 |
| Ruby 1.9.1 p378 RVM | x86_64-linux | 8,9 |
| Ruby 1.8.7 p249 RVM | x86_64-linux | 12,2 |
| Ruby 1.8.7 p249 | i386-mingw32 | 23,9 |
Questo il log completo dell’esecuzione:
marco@d9400:~$ rvm list
rvm rubies
=> jruby-1.4.0 [ amd64-java ]
jruby-1.5.2 [ amd64-java ]
ruby-1.8.7-p249 [ x86_64 ]
ruby-1.9.1-p378 [ x86_64 ]
ruby-1.9.2-p0 [ x86_64 ]
marco@d9400:~$ java -version
java version "1.6.0_18"
OpenJDK Runtime Environment (IcedTea6 1.8.1) (6b18-1.8.1-0ubuntu1)
OpenJDK 64-Bit Server VM (build 16.0-b13, mixed mode)
marco@d9400:~$ ruby -v
jruby 1.4.0 (ruby 1.8.7 patchlevel 174) (2009-11-02 69fbfa3) (OpenJDK 64-Bit Server VM 1.6.0_18) [amd64-java]
marco@d9400:~$ ruby RubyMM186_3.rb --server --fast
Warming up...
Strings test - Elapsed 2.096
Check1: 50000
Check2: 157490
Check3: 50005
Check4: 373847
Arrays test - Elapsed 3.508
Check1: 20900
Check2: 250000
Check3: 250000
Check4: 1500000
Check5: 5749950
Numeric test - Elapsed 2.856
Check1: 6252500
Check2: 1439295494700374021157505910939096377494040420940312
Ruby Partial elapsed time 8.460
1. Starting Ruby tests...
Strings test - Elapsed 1.501
Arrays test - Elapsed 3.054
Numeric test - Elapsed 2.280
Ruby Partial elapsed time 6.835
2. Starting Ruby tests...
Strings test - Elapsed 1.431
Arrays test - Elapsed 3.091
Numeric test - Elapsed 2.249
Ruby Partial elapsed time 6.771
3. Starting Ruby tests...
Strings test - Elapsed 1.505
Arrays test - Elapsed 2.999
Numeric test - Elapsed 2.261
Ruby Partial elapsed time 6.765
-------------------------------------
Average Strings test - Elapsed 1.479
Average Arrays test - Elapsed 3.048
Average Numeric test - Elapsed 2.263
Ruby Average elapsed time 6.790
marco@d9400:~$ rvm use jruby-1.5.2
marco@d9400:~$ ruby -v
jruby 1.5.2 (ruby 1.8.7 patchlevel 249) (2010-08-20 1c5e29d) (OpenJDK 64-Bit Server VM 1.6.0_18) [amd64-java]
marco@d9400:~$ ruby RubyMM186_3.rb --server --fast
Warming up...
Strings test - Elapsed 2.383
Check1: 50000
Check2: 157490
Check3: 50005
Check4: 373847
Arrays test - Elapsed 3.561
Check1: 20900
Check2: 250000
Check3: 250000
Check4: 1500000
Check5: 5749950
Numeric test - Elapsed 3.210
Check1: 6252500
Check2: 1439295494700374021157505910939096377494040420940312
Ruby Partial elapsed time 9.154
1. Starting Ruby tests...
Strings test - Elapsed 1.479
Arrays test - Elapsed 3.092
Numeric test - Elapsed 2.516
Ruby Partial elapsed time 7.087
2. Starting Ruby tests...
Strings test - Elapsed 1.431
Arrays test - Elapsed 3.056
Numeric test - Elapsed 2.508
Ruby Partial elapsed time 6.995
3. Starting Ruby tests...
Strings test - Elapsed 1.423
Arrays test - Elapsed 3.053
Numeric test - Elapsed 2.524
Ruby Partial elapsed time 7.000
-------------------------------------
Average Strings test - Elapsed 1.444
Average Arrays test - Elapsed 3.067
Average Numeric test - Elapsed 2.516
Ruby Average elapsed time 7.027
marco@d9400:~$ rvm use ruby-1.8.7-p249
marco@d9400:~$ ruby -v
ruby 1.8.7 (2010-01-10 patchlevel 249) [x86_64-linux]
marco@d9400:~$ ruby RubyMM186_3.rb
Warming up...
Strings test - Elapsed 3.135
Check1: 50000
Check2: 157490
Check3: 50005
Check4: 373847
Arrays test - Elapsed 4.386
Check1: 20900
Check2: 250000
Check3: 250000
Check4: 1500000
Check5: 5749950
Numeric test - Elapsed 4.629
Check1: 6252500
Check2: 1439295494700374021157505910939096377494040420940312
Ruby Partial elapsed time 12.149
1. Starting Ruby tests...
Strings test - Elapsed 3.181
Arrays test - Elapsed 4.402
Numeric test - Elapsed 4.598
Ruby Partial elapsed time 12.180
2. Starting Ruby tests...
Strings test - Elapsed 3.153
Arrays test - Elapsed 4.387
Numeric test - Elapsed 4.572
Ruby Partial elapsed time 12.113
3. Starting Ruby tests...
Strings test - Elapsed 3.210
Arrays test - Elapsed 4.395
Numeric test - Elapsed 4.583
Ruby Partial elapsed time 12.189
-------------------------------------
Average Strings test - Elapsed 3.181
Average Arrays test - Elapsed 4.395
Average Numeric test - Elapsed 4.584
Ruby Average elapsed time 12.160
marco@d9400:~$ rvm use ruby-1.9.1
marco@d9400:~$ ruby -v
ruby 1.9.1p378 (2010-01-10 revision 26273) [x86_64-linux]
marco@d9400:~$ ruby RubyMM191_3.rb
Warming up...
Strings test - Elapsed 2.452
Check1: 50000
Check2: 157490
Check3: 50005
Check4: 373847
Arrays test - Elapsed 4.488
Check1: 20900
Check2: 250000
Check3: 250000
Check4: 1500000
Check5: 5749950
Numeric test - Elapsed 1.840
Check1: 6252500
Check2: 1439295494700374021157505910939096377494040420940312
Ruby Partial elapsed time 8.780
1. Starting Ruby tests...
Strings test - Elapsed 2.314
Arrays test - Elapsed 4.679
Numeric test - Elapsed 1.843
Ruby Partial elapsed time 8.836
2. Starting Ruby tests...
Strings test - Elapsed 2.319
Arrays test - Elapsed 4.691
Numeric test - Elapsed 1.843
Ruby Partial elapsed time 8.853
3. Starting Ruby tests...
Strings test - Elapsed 2.318
Arrays test - Elapsed 4.705
Numeric test - Elapsed 1.847
Ruby Partial elapsed time 8.870
-------------------------------------
Average Strings test - Elapsed 2.317
Average Arrays test - Elapsed 4.692
Average Numeric test - Elapsed 1.844
Ruby Average elapsed time 8.853
marco@d9400:~$ rvm use ruby-1.9.2
marco@d9400:~$ ruby -v
ruby 1.9.2p0 (2010-08-18 revision 29036) [x86_64-linux]
marco@d9400:~$ ruby RubyMM191_3.rb
Warming up...
Strings test - Elapsed 1.680
Check1: 50000
Check2: 157490
Check3: 50005
Check4: 373847
Arrays test - Elapsed 4.192
Check1: 20900
Check2: 250000
Check3: 250000
Check4: 1500000
Check5: 5749950
Numeric test - Elapsed 1.708
Check1: 6252500
Check2: 1439295494700374021157505910939096377494040420940312
Ruby Partial elapsed time 7.579
1. Starting Ruby tests...
Strings test - Elapsed 1.654
Arrays test - Elapsed 3.178
Numeric test - Elapsed 1.704
Ruby Partial elapsed time 6.536
2. Starting Ruby tests...
Strings test - Elapsed 1.746
Arrays test - Elapsed 3.388
Numeric test - Elapsed 1.699
Ruby Partial elapsed time 6.833
3. Starting Ruby tests...
Strings test - Elapsed 1.644
Arrays test - Elapsed 3.227
Numeric test - Elapsed 1.706
Ruby Partial elapsed time 6.577
-------------------------------------
Average Strings test - Elapsed 1.681
Average Arrays test - Elapsed 3.264
Average Numeric test - Elapsed 1.703
Ruby Average elapsed time 6.649
Ruby vs Python vs Windows vs Linux
Nel precedente articolo, ho esaminato le prestazioni di alcuni interpreti ruby e python, su un sistema windows xp. Questa volta, ho eseguito gli stessi script sotto linux: distribuzione ubuntu 9.10 2.6.31-20-generic. Le versioni APT sono state installate tramite gestore di pacchetti, le RVM invece, tramite il Ruby Version Manager e compilate dai sorgenti.
| Versione | Compilatore/Sistema/VM | Secondi |
|---|---|---|
| JRuby 1.4.0 APT | Java HotSpot(TM) 64-Bit Server VM 1.6.0_16) [amd64-java] | 6,1 |
| JRuby 1.4.0 RVM | Java HotSpot(TM) 64-Bit Server VM 1.6.0_16) [amd64-java] | 6,2 |
| Ruby 1.9.2 preview1 RVM | x86_64-linux | 6,5 |
| JRuby 1.4.0 | Windows Client VM 1.6.0_15 [x86-java] | 6,9 |
| Python 2.6.4 | Windows | 7,5 |
| Ruby 1.9.1 p129 | i386-mingw32 | 8,1 |
| Python 2.6.4 APT | x86_64-linux | 8,7 |
| Ruby 1.9.1 p378 RVM | x86_64-linux | 8,7 |
| Ruby 1.9.1 p243 RVM | x86_64-linux | 8,8 |
| Python 3.1.1 | Windows | 9,0 |
| Ruby 1.9.1 p243 APT | x86_64-linux | 9,3 |
| Ruby 1.9.1 p243 | i386-mingw32 | 9,6 |
| Ruby 1.8.7 p249 RVM | x86_64-linux | 12,2 |
| IronRuby 0.9.3.0 | Windows .NET 2.0.0.0 | 18,9 |
| Ruby 1.9.1 p376 | i386-mswin32 | 20,8 |
| Ruby 1.8.7 p174 APT | x86_64-linux | 23,0 |
| Ruby 1.8.6 p368 | i386-mingw32 | 23,3 |
| Ruby 1.8.7 p249 | i386-mingw32 | 23,9 |
| IronPython 2.6 | Windows .NET 2.0.0.0 | 256,5 |
| Jython 2.5.1 | Windows Client VM 1.6.0_15 [x86-java] | Timeout |
Ruby vs Ruby vs Python vs Python
Un altro benchmark che mette a confronto alcune recenti versioni di ruby con gli ultimi rilasci di python. Due semplici script per confrontare sintassi e prestazioni di questi moderni linguaggi.
Il sistema su cui ho eseguito la prova è un portatile Dell Inspiron 9400 con Centrino Duo, Intel T7200 4Mb Cache 2Ghz, Ram 2Gb 667Mhz. Sistema operativo Windows XP pro SP3.
Questo è il risultato del primo test, usato come riscaldamento per le vm:
| Versione | Compilatore | Secondi |
|---|---|---|
| Python 2.6.4 | 7,5 | |
| Ruby 1.9.1 p129 | i386-mingw32 | 8,2 |
| JRuby 1.4.0 | Client VM 1.6.0_15 [x86-java] | 9,0 |
| Python 3.1.1 | 9,1 | |
| Ruby 1.9.1 p243 | i386-mingw32 | 9,6 |
| IronRuby 0.9.3.0 | .NET 2.0.0.0 | 20,3 |
| Ruby 1.9.1 p376 | i386-mswin32 | 20,9 |
| Ruby 1.8.6 p368 | i386-mingw32 | 22,9 |
| IronPython 2.6 | .NET 2.0.0.0 | 225,4 |
| Jython 2.5.1 | Client VM 1.6.0_15 [x86-java] | Timeout |
Il risultato che segue invece, si riferisce alla media delle tre rilevazioni successive al riscaldamento. Le prestazioni di JRuby migliorano del 23%:
| Versione | Compilatore | Secondi |
|---|---|---|
| JRuby 1.4.0 | Client VM 1.6.0_15 [x86-java] | 6,9 |
| Python 2.6.4 | 7,5 | |
| Ruby 1.9.1 p129 | i386-mingw32 | 8,2 |
| Python 3.1.1 | 9,0 | |
| Ruby 1.9.1 p243 | i386-mingw32 | 10,0 |
| IronRuby 0.9.3.0 | .NET 2.0.0.0 | 18,9 |
| Ruby 1.9.1 p376 | i386-mswin32 | 20,6 |
| Ruby 1.8.6 p368 | i386-mingw32 | 23,2 |
| IronPython 2.6 | .NET 2.0.0.0 | 256,5 |
| Jython 2.5.1 | Client VM 1.6.0_15 [x86-java] | Timeout |
Ed ecco gli script. Ho cercato di ottimizzare le rispettive versioni e per fare ciò, ho dovuto creare due varianti per ogni linguaggio.
Ruby 1.8.6:
def strings_test(ntest) r1 = r2 = r3 = 0 xstr = "" ntest.times do #Create a string, add 'abcde1234_' until getting a str size 1000 xstr = 'abcde1234_' * 10000 #Make letters upcase xstr.upcase! #Change '1234_' with '67890 ' (space at last position) #Now the repeated string should be 'ABCDE67890 ' xstr.gsub! '1234_', '67890 ' #Cast numbers from 29 upto size/2 to string and add it to xstr variable, ciclying for every number (not add all numbers one time) 29.upto(xstr.size/2) {|n| xstr << n.to_s} #Check 1: Count 'A' char #Check 2: Count '9' char 0.upto(xstr.size-1) do |n| if xstr[n].chr() == 'A' r1+=1 elsif xstr[n].chr() == '9' r2+=1 end end #Create an array from xstr using space to split r3 += xstr.split.size end return r1, r2, r3, xstr end def arrays_test(ntest, xstr) r1 = r2 = r3 = r4 = r5 = 0 ntest.times do #Clear ar then add 5000 times this element: "I", "am", "great", null, "or", "number", 1 ar = [] 5000.times do ['I', 'am', 'great', nil, 'or', 'number', 1].each {|a| ar << a} end #...then reverse elements to obtain this order: 1, "number", "or", null, "great", "am", "I" ar.reverse! #...then, count the element with value "great" using two separate cicle #the first starting from 31 until 2955 (bounty inclused) 31.upto(2955) do |n| r1 += 1 if ar[n] == 'great' end #the second looping all the array elements ar.each {|n| r2+=1 if n == 'great'} #Loop inside and build a temporary string with index and value, without put it into a variable and only for elements <> null ar.each_index{|i| ar[i] ? "#{i} #{ar[i]}" : r3+=1 } #delete null value elements and take its size ar.compact! r4 += ar.size #then join elements with space and take its size r5 += ar.join(' ').size end return r1, r2, r3, r4, r5 end def nums_test(ntest) r1 = r2 = 0 ntest.times do #Find all prime numbers from 8 to 95 step by 3 and sum all primes got, to check the result #51.upto(307) do |n| (8..95).step(3) do |n| primes(n).each {|a| r1 += a} end #Calculate factorial numbers start from 2 to 42 r2 = 0 for n in 2..42 r2 += fac(n) end end return r1, r2 end #Primes must return an array of prime numbers def primes(n) ar = [] for x in (2..n) prime = true for y in (2..x-1) if x%y == 0 prime = false break end end ar << x if prime end return ar end def fac(n) (1..n).inject{|total, current| total * current} end # --- START --- puts "\nWarming up..." t1=t2=t3=0 time = Time.now r1, r2, r3, xstr = strings_test(5) puts "Strings test - Elapsed %.3f" % (p1=Time.now - time) puts "Check1: #{r1}" puts "Check2: #{r2}" puts "Check3: #{r3}" puts "Check4: #{xstr.size}" time = Time.now r1, r2, r3, r4, r5 = arrays_test(50, xstr) puts "Arrays test - Elapsed %.3f" % (p2=Time.now - time) puts "Check1: #{r1}" puts "Check2: #{r2}" puts "Check3: #{r3}" puts "Check4: #{r4}" puts "Check5: #{r5}" time = Time.now r1, r2 = nums_test(500) puts "Numeric test - Elapsed %.3f" % (p3=Time.now - time) puts "Check1: #{r1}" puts "Check2: #{r2}" puts "Ruby Partial elapsed time %.3f" % (p1+p2+p3) puts "\n1. Starting Ruby tests..." time = Time.now r1, r2, r3, xstr = strings_test(5) puts "Strings test - Elapsed %.3f" % (p1=Time.now - time) time = Time.now r1, r2, r3, r4, r5 = arrays_test(50, xstr) puts "Arrays test - Elapsed %.3f" % (p2=Time.now - time) time = Time.now r1, r2 = nums_test(500) puts "Numeric test - Elapsed %.3f" % (p3=Time.now - time) puts "Ruby Partial elapsed time %.3f" % (p1+p2+p3) t1+=p1;t2+=p2;t3+=p3 puts "\n2. Starting Ruby tests..." time = Time.now r1, r2, r3, xstr = strings_test(5) puts "Strings test - Elapsed %.3f" % (p1=Time.now - time) time = Time.now r1, r2, r3, r4, r5 = arrays_test(50, xstr) puts "Arrays test - Elapsed %.3f" % (p2=Time.now - time) time = Time.now r1, r2 = nums_test(500) puts "Numeric test - Elapsed %.3f" % (p3=Time.now - time) puts "Ruby Partial elapsed time %.3f" % (p1+p2+p3) t1+=p1;t2+=p2;t3+=p3 puts "\n3. Starting Ruby tests..." time = Time.now r1, r2, r3, xstr = strings_test(5) puts "Strings test - Elapsed %.3f" % (p1=Time.now - time) time = Time.now r1, r2, r3, r4, r5 = arrays_test(50, xstr) puts "Arrays test - Elapsed %.3f" % (p2=Time.now - time) time = Time.now r1, r2 = nums_test(500) puts "Numeric test - Elapsed %.3f" % (p3=Time.now - time) puts "Ruby Partial elapsed time %.3f" % (p1+p2+p3) t1+=p1;t2+=p2;t3+=p3 puts "-------------------------------------" puts "Average Strings test - Elapsed %.3f" % (t1/3) puts "Average Arrays test - Elapsed %.3f" % (t2/3) puts "Average Numeric test - Elapsed %.3f" % (t3/3) puts "\nRuby Average elapsed time %.3f" % (t1/3+t2/3+t3/3)
Ruby 1.9.1:
def strings_test(ntest) r1 = r2 = r3 = 0 xstr = "" ntest.times do #Create a string, add 'abcde1234_' until getting a str size 1000 xstr = 'abcde1234_' * 10000 #Make letters upcase xstr.upcase! #Change '1234_' with '67890 ' (space at last position) #Now the repeated string should be 'ABCDE67890 ' xstr.gsub! '1234_', '67890 ' #Cast numbers to string, from 29 up to size/2. Add it to xstr variable as well, ciclying for every number (not adding all numbers once) 29.upto(xstr.size/2) {|n| xstr << n.to_s} #Check 1: Count 'A' char #Check 2: Count '9' char 0.upto(xstr.size-1) do |n| if xstr[n] == 'A' r1+=1 elsif xstr[n] == '9' r2+=1 end end #Create an array from xstr using space to split, its size is the third check r3 += xstr.split.size end return r1, r2, r3, xstr end def arrays_test(ntest, xstr) r1 = r2 = r3 = r4 = r5 = 0 ntest.times do #Clear ar then add 5000 times this element: "I", "am", "great", null, "or", "number", 1 ar = [] 5000.times do ['I', 'am', 'great', nil, 'or', 'number', 1].each {|a| ar << a} end #...then reverse elements to obtain this order: 1, "number", "or", null, "great", "am", "I" ar.reverse! #...then, count the element with value "great" using two separate cicle #the first starting from 31 until 2955 (bounty inclused) 31.upto(2955) do |n| r1 += 1 if ar[n] == 'great' end #the second looping all the array elements ar.each {|n| r2+=1 if n == 'great'} #Loop inside and build a temporary string with index and value, without put it into a variable and only for elements <> null ar.each_index{|i| ar[i] ? "#{i} #{ar[i]}" : r3+=1 } #delete null value elements and take its size ar.compact! r4 += ar.size #then join elements with space and take its size r5 += ar.join(' ').size end return r1, r2, r3, r4, r5 end def nums_test(ntest) r1 = r2 = 0 ntest.times do #Find all prime numbers from 8 to 95 step by 3 (bounds included) and sum all primes got, to check the result #51.upto(307) do |n| (8..95).step(3) do |n| primes(n).each {|a| r1 += a} end #Calculate factorial numbers start from 2 to 42 r2 = 0 for n in 2..42 r2 += fac(n) end end return r1, r2 end #Primes must return an array of prime numbers def primes(n) ar = [] for x in (2..n) prime = true for y in (2..x-1) if x%y == 0 prime = false break end end ar << x if prime end return ar end def fac(n) (1..n).inject{|total, current| total * current} end # --- START --- puts "\nWarming up..." t1=t2=t3=0 time = Time.now r1, r2, r3, xstr = strings_test(5) puts "Strings test - Elapsed %.3f" % (p1=Time.now - time) puts "Check1: #{r1}" puts "Check2: #{r2}" puts "Check3: #{r3}" puts "Check4: #{xstr.size}" time = Time.now r1, r2, r3, r4, r5 = arrays_test(50, xstr) puts "Arrays test - Elapsed %.3f" % (p2=Time.now - time) puts "Check1: #{r1}" puts "Check2: #{r2}" puts "Check3: #{r3}" puts "Check4: #{r4}" puts "Check5: #{r5}" time = Time.now r1, r2 = nums_test(500) puts "Numeric test - Elapsed %.3f" % (p3=Time.now - time) puts "Check1: #{r1}" puts "Check2: #{r2}" puts "Ruby Partial elapsed time %.3f" % (p1+p2+p3) puts "\n1. Starting Ruby tests..." time = Time.now r1, r2, r3, xstr = strings_test(5) puts "Strings test - Elapsed %.3f" % (p1=Time.now - time) time = Time.now r1, r2, r3, r4, r5 = arrays_test(50, xstr) puts "Arrays test - Elapsed %.3f" % (p2=Time.now - time) time = Time.now r1, r2 = nums_test(500) puts "Numeric test - Elapsed %.3f" % (p3=Time.now - time) puts "Ruby Partial elapsed time %.3f" % (p1+p2+p3) t1+=p1;t2+=p2;t3+=p3 puts "\n2. Starting Ruby tests..." time = Time.now r1, r2, r3, xstr = strings_test(5) puts "Strings test - Elapsed %.3f" % (p1=Time.now - time) time = Time.now r1, r2, r3, r4, r5 = arrays_test(50, xstr) puts "Arrays test - Elapsed %.3f" % (p2=Time.now - time) time = Time.now r1, r2 = nums_test(500) puts "Numeric test - Elapsed %.3f" % (p3=Time.now - time) puts "Ruby Partial elapsed time %.3f" % (p1+p2+p3) t1+=p1;t2+=p2;t3+=p3 puts "\n3. Starting Ruby tests..." time = Time.now r1, r2, r3, xstr = strings_test(5) puts "Strings test - Elapsed %.3f" % (p1=Time.now - time) time = Time.now r1, r2, r3, r4, r5 = arrays_test(50, xstr) puts "Arrays test - Elapsed %.3f" % (p2=Time.now - time) time = Time.now r1, r2 = nums_test(500) puts "Numeric test - Elapsed %.3f" % (p3=Time.now - time) puts "Ruby Partial elapsed time %.3f" % (p1+p2+p3) t1+=p1;t2+=p2;t3+=p3 puts "-------------------------------------" puts "Average Strings test - Elapsed %.3f" % (t1/3) puts "Average Arrays test - Elapsed %.3f" % (t2/3) puts "Average Numeric test - Elapsed %.3f" % (t3/3) puts "\nRuby Average elapsed time %.3f" % (t1/3+t2/3+t3/3)
Python 2.6:
from time import time #import psyco #psyco.full() #psyco.full(memory=100) #psyco.profile(0.05, memory=100) #psyco.profile(0.2) def strings_test(ntest): r1 = r2 = r3 = 0 xstr = "" for x in xrange(ntest): #Create a string, add 'abcde1234_' until getting a xstr size 1000 xstr = 'abcde1234_' * 10000 #Make letters upcase xstr = xstr.upper() #Change '1234_' with '67890 ' (space at last position) #Now the repeated string should be 'ABCDE67890 ' xstr = xstr.replace('1234_', '67890 ') #Cast numbers to string, from 29 up to size/2. Add it to xstr variable as well, ciclying for every number (not adding all numbers once) for y in xrange(29,int(len(xstr)/2) + 1): xstr += "%s" %y #Result 1: Count 'A' char #Result 2: Count '9' char for y in xrange(0, len(xstr)): if xstr[y] == 'A': r1+=1 elif xstr[y] == '9': r2+=1 #Create an array from xstr using space to split r3 += len(xstr.split()) return r1, r2, r3, xstr #Slower than other version def multiremove(ar, what): i = 0 for el in ar: if el == what: del ar[i] i+=1 #Ugly but a bit faster def multiremove2(ar, what): todel = [] for y in xrange(0,len(ar)): if ar[y] == what: todel.append(y) todel.reverse() for y in todel: ar.pop(y) def arrays_test(ntest, xstr): r1 = r2 = r3 = r4 = r5 = 0 for x in xrange(ntest): #Clear ar then add 5000 times this element: "I", "am", "great", null, "or", "number", 1 ar = [] for y in xrange(0, 5000): ar.extend(["I", "am", "great", None, "or", "number", 1]) #...then reverse elements to obtain this order: 1, "number", "or", null, "great", "am", "I" ar.reverse() #...then, count the element with value "great" using two separate cicle #the first starting from 31 until 2955 (bounty included) for y in xrange(31,2955): if ar[y] == "great": r1 +=1 #the second looping all the array elements for y in xrange(0, len(ar)): if ar[y] == "great": r2+=1 #Loop inside and build a temporary string with index and value, without put it into a variable and only for elements <> null for y in xrange(0, len(ar)): if ar[y]: "%s %s" %(y, ar[y]) else: r3+=1 #delete null value elements and take its size multiremove2(ar, None) r4 += len(ar) #then join elements with space and take its size r5 += len(" ".join(str(n) for n in ar)) return r1, r2, r3, r4, r5 def nums_test(ntest): r1 = r2 = 0 for x in xrange(ntest): #Find all prime numbers from 8 to 95 step by 3 (bounds included) and sum all primes got, to check the result for n in xrange(8, 96, 3): for prime in primes(n): r1 += prime fac = lambda n:[1,0][n>0] or fac(n-1)*n #Calculate factorial numbers start from 2 to 42 (bounds included) r2 = 0 for n in xrange(2, 43): r2 += fac(n) return r1, r2 #Primes must return an array of prime numbers def primes(n): ar = [] for x in xrange(2, n+1): prime = True for y in xrange(2, x): if x%y == 0: prime = False break if prime: ar.append(x) return ar # --- START --- print "\nWarming up..." t1=t2=t3=0 stime = time() r1, r2, r3, xstr = strings_test(5) p1=time() - stime print "Strings test - Elapsed %.3f" % (p1) print "Check1: %s" %r1 print "Check2: %s" %r2 print "Check3: %s" %r3 print "Check4: %d" %(len(xstr)) stime = time() r1, r2, r3, r4, r5 = arrays_test(50, xstr) p2=time() - stime print "Arrays test - Elapsed %.3f" % (p2) print "Check1: %s" %r1 print "Check2: %s" %r2 print "Check3: %s" %r3 print "Check4: %s" %r4 print "Check5: %s" %r5 stime = time() r1, r2 = nums_test(500) p3=time() - stime print "Numeric test - Elapsed %.3f" % (p3) print "Check1: %s" %r1 print "Check2: %s" %r2 print "Python Partial elapsed time %.3f" % (p1+p2+p3) print "\n1. Starting Python tests..." stime = time() r1, r2, r3, xstr = strings_test(5) p1=time() - stime print "Strings test - Elapsed %.3f" % (p1) stime = time() r1, r2, r3, r4, r5 = arrays_test(50, xstr) p2=time() - stime print "Arrays test - Elapsed %.3f" % (p2) stime = time() r1, r2 = nums_test(500) p3=time() - stime print "Numeric test - Elapsed %.3f" % (p3) print "Python Partial elapsed time %.3f" % (p1+p2+p3) t1+=p1;t2+=p2;t3+=p3 print "\n2. Starting Python tests..." stime = time() r1, r2, r3, xstr = strings_test(5) p1=time() - stime print "Strings test - Elapsed %.3f" % (p1) stime = time() r1, r2, r3, r4, r5 = arrays_test(50, xstr) p2=time() - stime print "Arrays test - Elapsed %.3f" % (p2) stime = time() r1, r2 = nums_test(500) p3=time() - stime print "Numeric test - Elapsed %.3f" % (p3) print "Python Partial elapsed time %.3f" % (p1+p2+p3) t1+=p1;t2+=p2;t3+=p3 print "\n3. Starting Python tests..." stime = time() r1, r2, r3, xstr = strings_test(5) p1=time() - stime print "Strings test - Elapsed %.3f" % (p1) stime = time() r1, r2, r3, r4, r5 = arrays_test(50, xstr) p2=time() - stime print "Arrays test - Elapsed %.3f" % (p2) stime = time() r1, r2 = nums_test(500) p3=time() - stime print "Numeric test - Elapsed %.3f" % (p3) print "Python Partial elapsed time %.3f" % (p1+p2+p3) t1+=p1;t2+=p2;t3+=p3 print "-------------------------------------" print "Average Strings test - Elapsed %.3f" % (t1/3) print "Average Arrays test - Elapsed %.3f" % (t2/3) print "Average Numeric test - Elapsed %.3f" % (t3/3) print "Python Average elapsed time %.3f" % (t1/3+t2/3+t3/3)
Python 3.1:
from time import time #import psyco #psyco.full() #psyco.full(memory=100) #psyco.profile(0.05, memory=100) #psyco.profile(0.2) def strings_test(ntest): r1 = r2 = r3 = 0 xstr = "" for x in range(ntest): #Create a string, add 'abcde1234_' until getting a xstr size 1000 xstr = 'abcde1234_' * 10000 #Make letters upcase xstr = xstr.upper() #Change '1234_' with '67890 ' (space at last position) #Now the repeated string should be 'ABCDE67890 ' xstr = xstr.replace('1234_', '67890 ') #Cast numbers from 29 upto 1028 to string and add it to xstr variable, ciclying for every number (not add all numbers one time) for y in range(29,int(len(xstr)/2) + 1): xstr += "%s" %y #Result 1: Count 'A' char #Result 2: Count '9' char for y in range(0, len(xstr)): if xstr[y] == 'A': r1+=1 elif xstr[y] == '9': r2+=1 #Create an array from xstr using space to split r3 += len(xstr.split()) return r1, r2, r3, xstr #Slower than other version def multiremove(ar, what): i = 0 for el in ar: if el == what: del ar[i] i+=1 #Ugly but a bit faster def multiremove2(ar, what): todel = [] for y in range(0,len(ar)): if ar[y] == what: todel.append(y) todel.reverse() for y in todel: ar.pop(y) def arrays_test(ntest, xstr): r1 = r2 = r3 = r4 = r5 = 0 for x in range(ntest): #Clear ar then add 5000 times this element: "I", "am", "great", null, "or", "number", 1 ar = [] for y in range(0, 5000): ar.extend(["I", "am", "great", None, "or", "number", 1]) #...then reverse elements to obtain this order: 1, "number", "or", null, "great", "am", "I" ar.reverse() #...then, count the element with value "great" using two separate cicle #the first starting from 31 until 2955 (bounty included) for y in range(31,2955): if ar[y] == "great": r1 +=1 #the second looping all the array elements for y in range(0, len(ar)): if ar[y] == "great": r2+=1 #Loop inside and build a temporary string with index and value, without put it into a variable and only for elements <> null for y in range(0, len(ar)): if ar[y]: "%s %s" %(y, ar[y]) else: r3+=1 #delete null value elements and take its size multiremove2(ar, None) r4 += len(ar) #then join elements with space and take its size r5 += len(" ".join(str(n) for n in ar)) return r1, r2, r3, r4, r5 def nums_test(ntest): r1 = r2 = 0 for x in range(ntest): #Find all prime numbers from 8 to 95 step by 3 (bounds included) and sum all primes got, to check the result for n in range(8, 96, 3): for prime in primes(n): r1 += prime fac = lambda n:[1,0][n>0] or fac(n-1)*n #Calculate factorial numbers start from 2 to 42 (bounds included) r2 = 0 for n in range(2, 43): r2 += fac(n) return r1, r2 #Primes must return an array of prime numbers def primes(n): ar = [] for x in range(2, n+1): prime = True for y in range(2, x): if x%y == 0: prime = False break if prime: ar.append(x) return ar # --- START --- print("\nWarming up...") t1=t2=t3=0 stime = time() r1, r2, r3, xstr = strings_test(5) p1=time() - stime print("Strings test - Elapsed %.3f" % p1) print("Check1: %s" % r1) print("Check2: %s" % r2) print("Check3: %s" % r3) print("Check4: %d" % len(xstr)) stime = time() r1, r2, r3, r4, r5 = arrays_test(50, xstr) p2=time() - stime print("Arrays test - Elapsed %.3f" % p2) print("Check1: %s" % r1) print("Check2: %s" % r2) print("Check3: %s" % r3) print("Check4: %s" % r4) print("Check5: %s" % r5) stime = time() r1, r2 = nums_test(500) p3=time() - stime print("Numeric test - Elapsed %.3f" % p3) print("Check1: %s" % r1) print("Check2: %s" % r2) print( "Python Partial elapsed time %.3f" % (p1+p2+p3)) print("\n1. Starting Python tests...") stime = time() r1, r2, r3, xstr = strings_test(5) p1=time() - stime print("Strings test - Elapsed %.3f" % p1) stime = time() r1, r2, r3, r4, r5 = arrays_test(50, xstr) p2=time() - stime print( "Arrays test - Elapsed %.3f" % p2) stime = time() r1, r2 = nums_test(500) p3=time() - stime print("Numeric test - Elapsed %.3f" % p3) print( "Python Partial elapsed time %.3f" % (p1+p2+p3)) t1+=p1;t2+=p2;t3+=p3 print("\n2. Starting Python tests...") stime = time() r1, r2, r3, xstr = strings_test(5) p1=time() - stime print("Strings test - Elapsed %.3f" % p1) stime = time() r1, r2, r3, r4, r5 = arrays_test(50, xstr) p2=time() - stime print( "Arrays test - Elapsed %.3f" % p2) stime = time() r1, r2 = nums_test(500) p3=time() - stime print("Numeric test - Elapsed %.3f" % p3) print( "Python Partial elapsed time %.3f" % (p1+p2+p3)) t1+=p1;t2+=p2;t3+=p3 print("\n3. Starting Python tests...") stime = time() r1, r2, r3, xstr = strings_test(5) p1=time() - stime print("Strings test - Elapsed %.3f" % p1) stime = time() r1, r2, r3, r4, r5 = arrays_test(50, xstr) p2=time() - stime print( "Arrays test - Elapsed %.3f" % p2) stime = time() r1, r2 = nums_test(500) p3=time() - stime print("Numeric test - Elapsed %.3f" % p3) print( "Python Partial elapsed time %.3f" % (p1+p2+p3)) t1+=p1;t2+=p2;t3+=p3 print("-------------------------------------") print("Average Strings test - Elapsed %.3f" % (t1/3)) print("Average Arrays test - Elapsed %.3f" % (t2/3)) print("Average Numeric test - Elapsed %.3f" % (t3/3)) print("Python Average elapsed time %.3f" % (t1/3+t2/3+t3/3))
Comparazione linguaggi script per la geometria frattale
In questo articolo metterò a confronto le ultime incarnazioni di ruby, con le ultime di python, groovy, php, lua, perl e anche java, per avere un metro di paragone con un linguaggio precompilato. Vedremo, infatti, come si comportano i linguaggi di script applicati alla geometria frattale, più precisamente un algoritmo della famiglia di Mandelbrot.
Navigando, ho trovato un confronto molto interessante ma un pò datato, risale a più di due anni fa. Da allora sono cambiate un pò di cose e ne ho approfittato per fare un aggiornamento anche se non includerò tutti quei linguaggi molti dei quali semi sconosciuti. Questa è l’occasione per mettere a confronto ruby e python anche nelle loro versioni java e .net, un intenzione che avevo già da un pò di tempo.
Usare un frattale come benchmark è oltretutto comodo: se un tentativo di ottimizzazione non va a buon fine se ne ha subito l’evidenza ed essendo disegnato in tempo reale, si riesce a percepire la velocità di esecuzione. Il frattale è disegnato in ascii anche perchè l’utilizzo di librerie esterne ne avrebbe drogato l’esito.
*
*
*
*
*
***
*****
*****
***
*
*********
*************
***************
*********************
*********************
*******************
*******************
*******************
*******************
***********************
*******************
*******************
*********************
*******************
*******************
*****************
***************
*************
*********
*
***************
***********************
* ************************* *
*****************************
* ******************************* *
*********************************
***********************************
***************************************
*** ***************************************** ***
*************************************************
***********************************************
*********************************************
*********************************************
***********************************************
***********************************************
***************************************************
*************************************************
*************************************************
***************************************************
***************************************************
* *************************************************** *
***** *************************************************** *****
****** *************************************************** ******
******* *************************************************** *******
***********************************************************************
********* *************************************************** *********
****** *************************************************** ******
***** *************************************************** *****
***************************************************
***************************************************
***************************************************
***************************************************
*************************************************
*************************************************
***************************************************
***********************************************
***********************************************
*******************************************
*****************************************
*********************************************
**** ****************** ****************** ****
*** **************** **************** ***
* ************** ************** *
*********** ***********
** ***** ***** **
* * * *
Questi sono i dati del sistema:
Dell Inspiron 9400, Centrino Duo, T7200 @ 2Ghz 4Mb Cache L1, Ram 2Gb @ 667Mhz
Windows XP pro SP3
Java 6 update 15
Microsoft .NET 3.5 SP1
Questi sono i risultati prestazionali ottenuti da una media di cinque rilevazioni catturate dopo qualche tentativo (non mi sono fidato dello startup delle VM):
Linguaggio Tempo (in secondi) n più lento di java _____________________________________________________________ Java 6 update 15 0,153 Lua 5.1.4 0,815 5x Php 5.3.0 2,083 14x Python 2.6.2 2,269 15x Python 3.1.1 1,566 10x Jython 2.5.0 2,850 19x Jruby 1.3.1 2,466 16x Groovy 1.6.3 6,491 42x Ruby 1.9.1 p129 2,688 18x Ruby 1.8.6 p368 6,863 45x Ruby 1.8.6 p111 9,709 63x IronRuby 0.9.0 6,038 39x IronPyhon 2.0.2 0,978 6x Perl 5.10.0 2,722 18x
Questo è il grafico, naturalmente valori più bassi indicano una prestazione migliore

Questi sono gli script usati per generare il frattale, andavano benissimo quelli di Erik Wrenholt, mi sono limitato a qualche semplice modifica per far funzionare python 3.1 o per migliorare lievemente la già ottima leggibilità in ruby e lua.
Java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | // by Erik Wrenholt import java.util.*; class Bench1 { static int BAILOUT = 16; static int MAX_ITERATIONS = 1000; private static int iterate(float x, float y) { float cr = y-0.5f; float ci = x; float zi = 0.0f; float zr = 0.0f; int i = 0; while (true) { i++; float temp = zr * zi; float zr2 = zr * zr; float zi2 = zi * zi; zr = zr2 - zi2 + cr; zi = temp + temp + ci; if (zi2 + zr2 > BAILOUT) return i; if (i > MAX_ITERATIONS) return 0; } } public static void main(String args[]) { Date d1 = new Date(); int x,y; for (y = -39; y < 39; y++) { System.out.print("\n"); for (x = -39; x < 39; x++) { if (iterate(x/40.0f,y/40.0f) == 0) System.out.print("*"); else System.out.print(" "); } } Date d2 = new Date(); long diff = d2.getTime() - d1.getTime(); System.out.println("\nJava Elapsed " + diff/1000.0f); } } |
Lua
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | -- By Erik Wrenholt local BAILOUT = 16 local MAX_ITERATIONS = 1000 function iterate(x,y) local cr = y-0.5 local ci = x local zi = 0.0 local zr = 0.0 local i = 0 while 1 do i = i+1 local temp = zr * zi local zr2 = zr*zr local zi2 = zi*zi zr = zr2-zi2+cr zi = temp+temp+ci if (zi2+zr2 > BAILOUT) then return i end if (i > MAX_ITERATIONS) then return 0 end end end function bench1() local t = os.clock() for y = -39, 38 do for x = -39, 38 do if (iterate(x/40.0, y/40) == 0) then io.write("*") else io.write(" ") end end io.write("\n") end io.write(string.format("Time Elapsed %.3fn", os.clock() - t)) end bench1() |
Php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | <?php define("BAILOUT",16); define("MAX_ITERATIONS",1000); class Bench1 { function Bench1() { $d1 = microtime(1); for ($y = -39; $y < 39; $y++) { echo("\n"); for ($x = -39; $x < 39; $x++) { if ($this->iterate($x/40.0,$y/40.0) == 0) echo("*"); else echo(" "); } } $d2 = microtime(1); $diff = $d2 - $d1; printf("\nPHP Elapsed %0.3f", $diff); } function iterate($x,$y) { $cr = $y-0.5; $ci = $x; $zi = 0.0; $zr = 0.0; $i = 0; while (true) { $i++; $temp = $zr * $zi; $zr2 = $zr * $zr; $zi2 = $zi * $zi; $zr = $zr2 - $zi2 + $cr; $zi = $temp + $temp + $ci; if ($zi2 + $zr2 > BAILOUT) return $i; if ($i > MAX_ITERATIONS) return 0; } } } new Bench1(); ?> |
Python
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | import sys, time stdout = sys.stdout BAILOUT = 16 MAX_ITERATIONS = 1000 class Bench1: def __init__(self): print ('Rendering...') for y in range(-39, 39): stdout.write('n') for x in range(-39, 39): i = self.start(x/40.0, y/40.0) if i == 0: stdout.write('*') else: stdout.write(' ') def start(self, x, y): cr = y - 0.5 ci = x zi = zr = 0.0 i = 0 while True: i += 1 temp = zr * zi zr2 = zr * zr zi2 = zi * zi zr = zr2 - zi2 + cr zi = temp + temp + ci if zi2 + zr2 > BAILOUT: return i if i > MAX_ITERATIONS: return 0 t = time.time() Bench1() print ('\nPython Elapsed %.3f' % (time.time() - t)) |
Groovy
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | //Created By Marco Mastrodonato 22/09/2009 class Bench1{ public int BAILOUT = 16 public int MAX_ITERATIONS = 1000 def Bench1(){ println("Rendering...") for (y in -39..39){ println("") for (x in -39..39){ if (iterate(x/40.0, y/40.0) == 0){ print("*") } else { print(" ") } } } } def iterate(x,y){ float cr = y-0.5 float ci = x float zi = 0.0 float zr = 0.0 def i = 0 while(1){ i += 1 float temp = zr * zi float zr2 = zr * zr float zi2 = zi * zi zr = zr2 - zi2 + cr zi = temp + temp + ci if (zi2 + zr2 > BAILOUT){ return i } if (i > MAX_ITERATIONS){ return 0 } } } } time1 = new Date().time new Bench1() time2 = new Date().time float elapsed = (time2 - time1)/1000 println("\nGroovy Elapsed ${elapsed}") |
Ruby
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | BAILOUT = 16 MAX_ITERATIONS = 1000 class Bench1 def initialize puts "Rendering..." for y in -39..39 print "\n" for x in -39..39 i = iterate x/40.0, y/40.0 if i == 0 then print "*" else print " " end end end end def iterate(x,y) cr = y-0.5 ci = x zi = zr = 0.0 i = 0 while true i += 1 temp = zr * zi zr2 = zr * zr zi2 = zi * zi zr = zr2 - zi2 + cr zi = temp + temp + ci return i if zi2 + zr2 > BAILOUT return 0 if i > MAX_ITERATIONS end end end time = Time.now Bench1.new puts "\nRuby Elapsed %.3f" % (Time.now - time) |
Perl
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | # Ported from C to Perl by Anders Bergh <anders1@gmail.com> $BAILOUT=16; $MAX_ITERATIONS=1000; $begin = time(); sub mandelbrot { local $x = $_[0]; local $y = $_[1]; local $cr = $y - 0.5; local $ci = $x; local $zi = 0.0; local $zr = 0.0; local $i = 0; while (1) { $i = $i + 1; local $temp = $zr * $zi; local $zr2 = $zr * $zr; local $zi2 = $zi * $zi; $zr = $zr2 - $zi2 + $cr; $zi = $temp + $temp + $ci; if ($zi2 + $zr2 > $BAILOUT) { return $i; } if ($i > $MAX_ITERATIONS) { return 0; } } } for ($y = -39; $y < 39; $y++) { print("\n"); for ($x = -39; $x < 39; $x++) { $i = mandelbrot($x/40.0, $y/40.0); if ($i == 0) { print("*"); } else { print(" "); } } } print("\n"); $end = time() - $begin; printf ("Perl Elapsed %.3fn",$end); |
Commenti:
La velocità di Lua è ormai nota, solamente 5 volte più lento rispetto al codice java compilato, il miglior risultato. La sua semplicità è la sua forza, forse è proprio questo che lo rende così veloce? E’ stato adottato dalla Blizzard all’interno del gioco World of Warcraft e se l’hanno fatto loro un motivo ci sarà. Non è ad oggetti o meglio, nativamente non li supporta anche se c’è il progetto LOOP che lo estende a questo modello di programmazione.
Php e Perl non hanno bisogno di commenti.
Tra le versioni C di Ruby e Python è chiaramente avanti quest’ultimo. Il paragone equo sarebbe la Rb1.8.6 con la Py2.6.2 e la Rb1.9.1 con la Py3.1.1.
La sfida tra le versioni che sfruttalo la Java VM: Groovy, Jython e JRuby, vede in vantaggio quest’ultimo. Groovy è molto indietro come prestazioni ma il dubbio più grosso che ho è: ma a chi è destinato? Come sintassi non è male ma ruby è ancora più scorrevole e poi ha quel rake che è tanto comodo per tante cose.
Le versioni ruby e python per .Net vede incredibilmente avanti IronPython! Ma che gli hanno messo dentro, la dinamite? Sarà molto interessante esaminare il nuovo framework MVC di ASP.NET da poco arrivato alla versione 1 e che sarà incluso nel framework .Net 4, esistono progetti sia per IronRuby che per IronPython.
Se questo articolo è risultato interessante, forse potrai trovare qualcos’altro tra gli annunci del mio sponsor, si trova nella colonna a destra, grazie!
Cinque rubini nella sfida multicore
Il sistema su cui ho eseguito la prova è un portatile: Dell Inspiron 9400 con Centrino Duo, Intel T7200 4Mb Cache 2Ghz (166×12) Ram 2Gb 667Mhz. Si tratta di un sistema fisico Windows XP pro SP3.
Lo scopo: verificare lo sfruttamento di più core del processore (due nel mio caso) mettendo a confronto un processo singolo con una doppia esecuzione in contemporanea.
Per fare questo ho utilizzato il bench delle stringhe di un articolo precedente.
C’è da tener presente che nell’esecuzione del doppio processo, ognuno deve condividere la cpu con il sistema operativo (il carico è sempre al 100%) mentre nel test singolo questo non succede, un core è dedicato al test mentre l’altro alle altre faccende. Per questo motivo il sistema operativo era privo di antivirus e di altri processi/servizi pesanti.
Cominciamo con il vecchio interprete dell’oneclick installer:
Ruby 1.8.6 patch 111
C:LavoroProgettiTestBench>ruby -v
ruby 1.8.6 (2007-09-24 patchlevel 111) [i386-mswin32]
C:LavoroProgettiTestBench>ruby bench_str.rb
user system total real
Concat 1.000.000:
+ 6.641000 0.000000 6.641000 ( 6.640000)
<< 5.344000 0.000000 5.344000 ( 5.375000)
#{} 6.078000 0.000000 6.078000 ( 6.078000)
Add 100.000:
+= 3.547000 2.969000 6.516000 ( 6.579000)
<< 0.062000 0.000000 0.062000 ( 0.062000)
a = a + '.' 3.000000 3.422000 6.422000 ( 6.547000)
#{} 4.531000 1.906000 6.437000 ( 6.516000)
Other 100.000:
* 100: 0.500000 0.063000 0.563000 ( 0.562000)
capitalize: 1.719000 0.078000 1.797000 ( 1.797000)
upcase: 4.906000 0.140000 5.046000 ( 5.047000)
chomp: 0.266000 0.063000 0.329000 ( 0.328000)
include: 1.234000 0.000000 1.234000 ( 1.234000)
index: 1.235000 0.000000 1.235000 ( 1.250000)
sub: 0.875000 0.094000 0.969000 ( 0.969000)
gsub: 17.453000 0.562000 18.015000 ( 18.047000)
[x..y]: 0.547000 0.016000 0.563000 ( 0.562000)
slice: 0.562000 0.000000 0.562000 ( 0.563000)
strip: 0.156000 0.000000 0.156000 ( 0.156000)
Each: 12.282000 0.078000 12.360000 ( 12.390000)
Cast 1.000.000:
.to_i: 0.437000 0.000000 0.437000 ( 0.438000)
.to_sym: 0.531000 0.000000 0.531000 ( 0.531000)
split: 9.047000 0.140000 9.187000 ( 9.235000)
--- Total: 80.953000 9.531000 90.484000 ( 90.906000)
Singolo:
--- Total: 80.953000 9.531000 90.484000 ( 90.906000)
Multicore:
CORE1: --- Total: 87.063000 19.531000 106.594000 (107.923000) CORE2: --- Total: 91.344000 18.375000 109.719000 (110.125000)
Questo è il decremento del test doppio processo rispetto l'esecuzione singola:
Decremento del 10%
Decremento reale del 20%
Ruby 1.8.6 patch 368
C:LavoroProgettiTestBench>ruby -v
ruby 1.8.6 (2009-03-31 patchlevel 368) [i386-mingw32]
C:LavoroProgettiTestBench>ruby bench_str.rb
user system total real
Concat 1.000.000:
+ 4.828000 0.000000 4.828000 ( 4.828125)
<< 3.938000 0.000000 3.938000 ( 3.953125)
#{} 4.719000 0.016000 4.735000 ( 4.750000)
Add 100.000:
+= 3.687000 2.719000 6.406000 ( 6.468750)
<< 0.047000 0.000000 0.047000 ( 0.046875)
a = a + '.' 3.422000 3.000000 6.422000 ( 6.468750)
#{} 4.797000 1.109000 5.906000 ( 5.906250)
Other 100.000:
* 100: 0.328000 0.094000 0.422000 ( 0.421875)
capitalize: 0.890000 0.078000 0.968000 ( 1.000000)
upcase: 3.469000 0.109000 3.578000 ( 3.578125)
chomp: 0.235000 0.078000 0.313000 ( 0.312500)
include: 0.796000 0.000000 0.796000 ( 0.796875)
index: 0.797000 0.000000 0.797000 ( 0.796875)
sub: 0.750000 0.172000 0.922000 ( 0.921875)
gsub: 20.860000 0.531000 21.391000 ( 21.421875)
[x..y]: 0.437000 0.016000 0.453000 ( 0.453125)
slice: 0.438000 0.000000 0.438000 ( 0.437500)
strip: 0.109000 0.000000 0.109000 ( 0.109375)
Each: 9.078000 0.172000 9.250000 ( 9.281250)
Cast 1.000.000:
.to_i: 0.297000 0.000000 0.297000 ( 0.296875)
.to_sym: 0.344000 0.000000 0.344000 ( 0.343750)
split: 7.375000 0.109000 7.484000 ( 7.500000)
--- Total: 71.641000 8.203000 79.844000 ( 80.093750)
Singolo:
--- Total: 71.641000 8.203000 79.844000 ( 80.093750)
Multicore:
CORE1: --- Total: 80.797000 17.750000 98.547000 ( 99.093750) CORE2: --- Total: 76.500000 19.375000 95.875000 ( 97.187500)
Decremento del 9,8%
Decremento reale del 22,5%
Ruby 1.9.1
C:LavoroProgettiTestBench>ruby -v
ruby 1.9.1p129 (2009-05-12 revision 23412) [i386-mingw32]
C:LavoroProgettiTestBench>ruby bench_str.rb
user system total real
Concat 1.000.000:
+ 2.610000 0.000000 2.610000 ( 2.609375)
<< 2.125000 0.000000 2.125000 ( 2.125000)
#{} 2.796000 0.000000 2.796000 ( 2.796875)
Add 100.000:
+= 3.688000 2.875000 6.563000 ( 6.609375)
<< 0.031000 0.000000 0.031000 ( 0.031250)
a = a + '.' 3.235000 3.265000 6.500000 ( 6.578125)
#{} 5.187000 2.266000 7.453000 ( 7.546875)
Other 100.000:
* 100: 0.203000 0.125000 0.328000 ( 0.328125)
capitalize: 5.563000 0.047000 5.610000 ( 5.656250)
upcase: 0.703000 0.031000 0.734000 ( 0.734375)
chomp: 0.219000 0.063000 0.282000 ( 0.296875)
include: 0.156000 0.000000 0.156000 ( 0.156250)
index: 0.156000 0.000000 0.156000 ( 0.156250)
sub: 0.500000 0.093000 0.593000 ( 0.593750)
gsub: 8.453000 0.266000 8.719000 ( 8.718750)
[x..y]: 0.219000 0.000000 0.219000 ( 0.218750)
slice: 0.219000 0.000000 0.219000 ( 0.218750)
strip: 0.109000 0.000000 0.109000 ( 0.109375)
Each: 7.188000 0.141000 7.329000 ( 7.328125)
Cast 1.000.000:
.to_i: 0.234000 0.000000 0.234000 ( 0.234375)
.to_sym: 0.391000 0.000000 0.391000 ( 0.390625)
split: 4.609000 0.000000 4.609000 ( 4.609375)
--- Total: 48.594000 9.172000 57.766000 ( 58.046875)
Singolo:
--- Total: 48.594000 9.172000 57.766000 ( 58.046875)
Multicore:
CORE1: --- Total: 56.345000 19.219000 75.564000 ( 76.312500) CORE2: --- Total: 52.109000 20.703000 72.812000 ( 74.156250)
Decremento del 11,5%
Decremento reale del 29,6%
jRuby 1.3.1
C:LavoroProgettiTestBench>jruby -v
jruby 1.3.1 (ruby 1.8.6p287) (2009-07-24 6586) (Java HotSpot(TM) Client VM 1.6.0
_15) [x86-java]
C:LavoroProgettiTestBench>jruby bench_str.rb
user system total real
Concat 1.000.000:
+ 1.703000 0.000000 1.703000 ( 1.672000)
<< 1.375000 0.000000 1.375000 ( 1.375000)
#{} 0.625000 0.000000 0.625000 ( 0.625000)
Add 100.000:
+= 8.343000 0.000000 8.343000 ( 8.343000)
<< 0.031000 0.000000 0.031000 ( 0.031000)
a = a + '.' 8.406000 0.000000 8.406000 ( 8.406000)
#{} 18.984000 0.000000 18.984000 ( 18.984000)
Other 100.000:
* 100: 0.157000 0.000000 0.157000 ( 0.157000)
capitalize: 0.937000 0.000000 0.937000 ( 0.937000)
upcase: 1.703000 0.000000 1.703000 ( 1.703000)
chomp: 0.063000 0.000000 0.063000 ( 0.063000)
include: 0.281000 0.000000 0.281000 ( 0.281000)
index: 0.313000 0.000000 0.313000 ( 0.313000)
sub: 0.437000 0.000000 0.437000 ( 0.437000)
gsub: 4.813000 0.000000 4.813000 ( 4.813000)
[x..y]: 0.156000 0.000000 0.156000 ( 0.156000)
slice: 0.140000 0.000000 0.140000 ( 0.140000)
strip: 0.047000 0.000000 0.047000 ( 0.047000)
Each: 3.094000 0.000000 3.094000 ( 3.094000)
Cast 1.000.000:
.to_i: 0.609000 0.000000 0.609000 ( 0.609000)
.to_sym: 0.266000 0.000000 0.266000 ( 0.266000)
split: 2.203000 0.000000 2.203000 ( 2.203000)
--- Total: 54.686000 0.000000 54.686000 ( 54.655000)
Singolo:
--- Total: 54.686000 0.000000 54.686000 ( 54.655000)
Multicore:
CORE1: --- Total: 65.170000 0.000000 65.170000 ( 64.920000) CORE2: --- Total: 64.234000 0.000000 64.234000 ( 64.000000)
Decremento del 17,2%
Decremento reale del 17,9%
IronRuby 0.9.0
C:LavoroProgettiTestBench>ir -v
IronRuby 0.9.0.0 on .NET 2.0.0.0
C:LavoroProgettiTestBench>ir bench_str.rb
user system total real
Concat 1.000.000:
+ 3.562500 0.000000 3.562500 ( 3.234375)
<< 1.531250 0.000000 1.531250 ( 1.531250)
#{} 1.453125 0.015625 1.468750 ( 1.453125)
Add 100.000:
+= 54.656250 11.296875 65.953125 ( 66.171875)
<< 0.031250 0.000000 0.031250 ( 0.031250)
a = a + '.' 54.734375 11.718750 66.453125 ( 66.671875)
#{} 55.609375 10.843750 66.453125 ( 66.609375)
Other 100.000:
* 100: 0.671875 0.000000 0.671875 ( 0.671875)
capitalize: 2.593750 0.000000 2.593750 ( 2.609375)
upcase: 7.484375 0.000000 7.484375 ( 7.500000)
chomp: 0.250000 0.000000 0.250000 ( 0.250000)
include: 5.968750 0.015625 5.984375 ( 6.031250)
index: 5.968750 0.031250 6.000000 ( 6.031250)
sub: 1.781250 0.015625 1.796875 ( 1.781250)
gsub: 12.203125 0.031250 12.234375 ( 12.281250)
[x..y]: 0.406250 0.000000 0.406250 ( 0.390625)
slice: 0.343750 0.000000 0.343750 ( 0.343750)
strip: 0.078125 0.000000 0.078125 ( 0.078125)
Each: 31.546875 0.312500 31.859375 ( 32.203125)
Cast 1.000.000:
.to_i: 0.265625 0.000000 0.265625 ( 0.250000)
.to_sym: 0.406250 0.015625 0.421875 ( 0.421875)
split: 5.515625 0.031250 5.546875 ( 5.531250)
--- Total: 247.062500 34.328125 281.390625 (282.078125)
C:LavoroProgettiTestBench>
Singolo:
--- Total: 247.062500 34.328125 281.390625 (282.078125)
Multicore:
CORE1: --- Total: 315.531250 40.578125 356.109375 (359.500000) CORE2: --- Total: 300.484375 46.156250 346.640625 (350.265625)
Decremento del 24,5%
Decremento reale del 25,8%
Riepilogo
Ruby 1.8.6 patch 111
Decremento del 10%
Decremento reale del 20%
Ruby 1.8.6 patch 368
Decremento del 9,8%
Decremento reale del 22,5%
Ruby 1.9.1
Decremento del 11,5%
Decremento reale del 29,6%
jRuby 1.3.1
Decremento del 17,2%
Decremento reale del 17,9%
IronRuby 0.9.0
Decremento del 24,5%
Decremento reale del 25,8%
Conclusioni
Non so cosa sarebe successo con altri test ma in questo, jRuby è l'unico che ha un carico uniforme sui due core e lo fa costantemente quasi per tutta la durata. Solo alla fine si sposta verso un core ma con più discrezione. Ottima anche la prestazione con i due processi in contemporanea.
Le versioni 1.8.6 e 1.9.1 sono abbastanza allineate e riescono a spartire il carico solo sui test "Add". La prestazione del doppio processo non sarebbe male ma crea qualche difficoltà al sistema (almeno al mio) che ne allunga i tempi.
IronRuby ha qualche problemino con le stringhe già evidenziato in un articolo precedente. Ignorando questo, non ha sfruttato il secondo core che è rimasto scarico per tutta la durata. Col secondo core libero, mi sarei aspettato un decremento meno evidente nel test del doppio processo ma così non è stato, questo è strano!
In un prossimo articolo, mi piacerebbe ripetere il test su un server quad core.
Quattro interpreti ruby a confronto
Non si può negare che la versatilità sia una delle sue caratteristiche; un vero multipiattaforma. Ci sono interpreti per molti sistemi operativi e oltre alla versione classica in C++ abbiamo la valida alternativa di JRuby per il mondo java e infine Ironruby per il framework .NET di Microsoft, arrivato in questi ultimi giorni ad una versione abbastanza matura.
Niente male, pensiamo ai benefici che un’azienda potrebbe avere: un linguaggio semplice da imparare ma molto potente, ha un ottimo framework (uno su tutti, Rails) per sviluppare rapidamente applicazioni disponibili per esempio nella intranet e in aggiunta a tutto questo, la possibilità di usare le librerie java o .net.
In questo articolo vedremo come si comportano questi quattro interpreti:
- Ruby 1.8.6 patch 368 aggiornato al 2009-03-31
- Ruby 1.9.1 patch 129 aggiornato al 2009-05-12 revisione 23412
- jRuby 1.3.1 (ruby 1.8.6p287) 2009-06-15 Client VM 1.6.0 update 14
- IronRuby 0.9.0.0 on .NET 2.0.0.0
Lo scopo: per scoprire le differenze prestazionali principalmente nell’ambito del singolo interprete. Ci sono metodi e tecniche che portano allo stesso risultato e senza simili test difficilmente si può stabilire qual’è la pratica migliore.
Nei test che vedremo, mi sono concentrato sulla velocità di elaborazione controllando solo grossolanamente la quantità di memoria utilizzata.
Il sistema: La macchina fisica è un Windows XP Professional SP3 32bit con un Intel E7300 dual core con 3Mb di cache e 3,25Gb di ram.
Sistema fisico
I test giravano su un sistema virtualizzato con MS Virtual PC 2007, os sempre XP Pro, Java SDK 6.14, Framework .NET 3.5 SP1, processore singolo core, memoria 768Mb. Solo il test più pesante sugli hash l’ho ripetuto con 2Gb.
I benchmarks sono divisi in quattro categorie:
- Stringhe
- Numeri
- Array
- Hash
Li ho creati io e si limitano ad eseguire operazioni molto semplici. Alla fine c’è un totale ma non deve essere inteso come un indice sull’effettiva prestazione dell’interprete perchè questa cambia molto in base ai test scelti e quelli che ho utilizzati io non è detto che siano condizioni riscontrate nell’uso reale.
Le stringhe
Questo è il primo script:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 | require "benchmark" include Benchmark Benchmark.bm(21, "--- Total:") do|b| puts "Concat 1.000.000:" n=1_000_000 t1 = b.report("+") do n.times { "a "+"b "+"c "+"d "+"e "; "f "+"g" } end t1 += b.report("<<") do n.times { "a "<<"b "<<"c "<<"d "<<"e "; "f "<<"g" } end t1 += b.report("#{}") do sa="a"; sb="b"; sc="c"; sd="d"; se="e" n.times { "#{sa} #{sb} #{sc} #{sd} #{se}"; "#{sb} #{sc}" } end puts "Add 100.000:" n=100_000 t2 = b.report("+=") do a = "" n.times { a += "." } end t2 += b.report("<<") do a = "" n.times { a << "." } end t2 += b.report("a = a + '.'") do a = "" n.times { a = a + "." } end t2 += b.report("#{}") do a = "" n.times { a = "#{a}." } end puts "Other 100.000:" n=100_000 str="" t3 = b.report("* 100:") do n.times { str = " abc def ghi rn" * 100 } end t3 += b.report("capitalize:") do n.times { str.capitalize } end t3 += b.report("upcase:") do n.times { str.upcase } end t3 += b.report("chomp:") do n.times { str.chomp } end t3 += b.report("include:") do n.times { str.include?("ghi");str.include?("qwertyuiopasdfghjkl") } end t3 += b.report("index:") do n.times { str.index("ghi");str.index("qwertyuiopasdfghjkl") } end t3 += b.report("sub:") do n.times { str.sub("ghi", "GGHHII") } end t3 += b.report("gsub:") do n.times { str.gsub("ghi", "GGHHII") } end t3 += b.report("[x..y]:") do n.times {|x| str[0..2];str[5..15];str[6..26];str[10..50] } end t3 += b.report("slice:") do n.times {|x| str.slice(0..2);str.slice(5..15);str.slice(6..26);str.slice(10..50) } end t3 += b.report("strip:") do n.times {|x| " 1 2 3 4 5 ".strip } end t3 += b.report("Each:") do n.times { str.each_line{|x| x } } end puts "Cast 1.000.000:" n=1_000_000 str="abcd1234" t4 = b.report(".to_i:") do n.times { str.to_i } end t4 += b.report(".to_sym:") do n.times { str.to_sym } end n=100_000 str="abc123 " * 100 t4 += b.report("split:") do n.times { str.split } end [t1+t2+t3+t4] end |
Questi sono i risultati in ordine di interprete, dal primo al quarto:
Ruby 1.8.6
C:ProgettiRubybench>ruby bench_str.rb
user system total real
Concat 1.000.000:
+ 3.525000 0.000000 3.525000 ( 3.555325)
<< 2.864000 0.000000 2.864000 ( 2.884320)
#{} 3.565000 0.010000 3.575000 ( 3.615415)
Add 100.000:
+= 5.257000 4.306000 9.563000 ( 9.724565)
<< 0.030000 0.010000 0.040000 ( 0.040060)
a = a + '.' 5.398000 4.376000 9.774000 ( 9.884805)
#{} 5.328000 2.474000 7.802000 ( 7.901835)
Other 100.000:
* 100: 0.551000 0.380000 0.931000 ( 0.981470)
capitalize: 0.951000 0.361000 1.312000 ( 1.311965)
upcase: 2.844000 0.490000 3.334000 ( 3.365040)
chomp: 0.491000 0.301000 0.792000 ( 0.861290)
include: 0.631000 0.000000 0.631000 ( 0.630945)
index: 0.611000 0.000000 0.611000 ( 0.610915)
sub: 1.181000 0.410000 1.591000 ( 1.602400)
gsub: 19.989000 1.623000 21.612000 ( 21.842715)
[x..y]: 0.351000 0.000000 0.351000 ( 0.360540)
slice: 0.350000 0.000000 0.350000 ( 0.350525)
strip: 0.090000 0.000000 0.090000 ( 0.090135)
Each: 7.711000 0.000000 7.711000 ( 7.761625)
Cast 1.000.000:
.to_i: 0.221000 0.000000 0.221000 ( 0.220330)
.to_sym: 0.240000 0.000000 0.240000 ( 0.240360)
split: 6.229000 0.010000 6.239000 ( 6.269390)
--- Total: 68.408000 14.751000 83.159000 ( 84.105970)
Ruby 1.9.1
C:ProgettiRubybench>ruby bench_str.rb
user system total real
Concat 1.000.000:
+ 1.932000 0.000000 1.932000 ( 1.992985)
<< 1.633000 0.000000 1.633000 ( 1.672505)
#{} 2.183000 0.000000 2.183000 ( 2.213315)
Add 100.000:
+= 5.538000 4.136000 9.674000 ( 9.884805)
<< 0.030000 0.000000 0.030000 ( 0.030045)
a = a + '.' 5.368000 4.346000 9.714000 ( 9.884805)
#{} 6.529000 4.246000 10.775000 ( 11.096620)
Other 100.000:
* 100: 0.491000 0.400000 0.891000 ( 0.891335)
capitalize: 4.406000 0.501000 4.907000 ( 4.977455)
upcase: 0.671000 0.501000 1.172000 ( 1.171755)
chomp: 0.541000 0.340000 0.881000 ( 0.911365)
include: 0.110000 0.000000 0.110000 ( 0.110165)
index: 0.120000 0.000000 0.120000 ( 0.120180)
sub: 0.731000 0.371000 1.102000 ( 1.111665)
gsub: 7.361000 0.991000 8.352000 ( 8.512750)
[x..y]: 0.200000 0.010000 0.210000 ( 0.210315)
slice: 0.170000 0.000000 0.170000 ( 0.170255)
strip: 0.090000 0.000000 0.090000 ( 0.090135)
Each: 6.359000 0.020000 6.379000 ( 6.419615)
Cast 1.000.000:
.to_i: 0.171000 0.000000 0.171000 ( 0.170255)
.to_sym: 0.290000 0.000000 0.290000 ( 0.330495)
split: 3.575000 0.000000 3.575000 ( 3.595385)
--- Total: 48.499000 15.862000 64.361000 ( 65.568205)
JRuby
C:ProgettiRubybench>jruby bench_str.rb
user system total real
Concat 1.000.000:
+ 1.142000 0.000000 1.142000 ( 1.101000)
<< 0.932000 0.000000 0.932000 ( 0.942000)
#{} 0.430000 0.000000 0.430000 ( 0.430000)
Add 100.000:
+= 7.962000 0.000000 7.962000 ( 7.962000)
<< 0.020000 0.000000 0.020000 ( 0.020000)
a = a + '.' 7.542000 0.000000 7.542000 ( 7.542000)
#{} 16.324000 0.000000 16.324000 ( 16.324000)
Other 100.000:
* 100: 0.140000 0.000000 0.140000 ( 0.140000)
capitalize: 0.781000 0.000000 0.781000 ( 0.781000)
upcase: 1.382000 0.000000 1.382000 ( 1.382000)
chomp: 0.051000 0.000000 0.051000 ( 0.051000)
include: 0.240000 0.000000 0.240000 ( 0.240000)
index: 0.230000 0.000000 0.230000 ( 0.230000)
sub: 0.381000 0.000000 0.381000 ( 0.391000)
gsub: 3.816000 0.000000 3.816000 ( 3.816000)
[x..y]: 0.100000 0.000000 0.100000 ( 0.100000)
slice: 0.120000 0.000000 0.120000 ( 0.120000)
strip: 0.040000 0.000000 0.040000 ( 0.040000)
Each: 2.333000 0.000000 2.333000 ( 2.333000)
Cast 1.000.000:
.to_i: 0.470000 0.000000 0.470000 ( 0.470000)
.to_sym: 0.191000 0.000000 0.191000 ( 0.191000)
split: 1.622000 0.000000 1.622000 ( 1.622000)
--- Total: 46.249000 0.000000 46.249000 ( 46.227999)
IronRuby
C:ProgettiRubybench>ir bench_str.rb
user system total real
Concat 1.000.000:
+ 2.804032 0.070101 2.874133 ( 2.954425)
<< 1.101584 0.000000 1.101584 ( 1.101650)
#{} 1.331915 0.000000 1.331915 ( 1.342010)
Add 100.000:
+= 50.502619 4.836955 55.339574 ( 56.043940)
<< 0.040058 0.000000 0.040058 ( 0.040060)
a = a + '.' 50.662850 4.616638 55.279488 ( 56.013895)
#{} 51.173584 4.666710 55.840294 ( 56.634825)
Other 100.000:
* 100: 0.500720 0.000000 0.500720 ( 0.500750)
capitalize: 2.503600 0.010014 2.513614 ( 2.583870)
upcase: 6.078741 0.000000 6.078741 ( 6.199285)
chomp: 0.200288 0.000000 0.200288 ( 0.200300)
include: 4.216062 0.000000 4.216062 ( 4.246360)
index: 4.135947 0.000000 4.135947 ( 4.206300)
sub: 1.462102 0.000000 1.462102 ( 1.492235)
gsub: 8.692499 0.020029 8.712528 ( 8.773140)
[x..y]: 0.290418 0.000000 0.290418 ( 0.290435)
slice: 0.230331 0.010014 0.240346 ( 0.260390)
strip: 0.070101 0.000000 0.070101 ( 0.070105)
Each: 25.015971 0.050072 25.066043 ( 25.357980)
Cast 1.000.000:
.to_i: 0.220317 0.000000 0.220317 ( 0.220330)
.to_sym: 0.280403 0.000000 0.280403 ( 0.280420)
split: 4.446394 0.010014 4.456408 ( 4.516765)
--- Total: 215.960536 14.290549 230.251085 (233.329469)
Questo è il riepilogo dei totali:
Ruby 1.8.6
--- Total: 68.408000 14.751000 83.159000 ( 84.105970)
Ruby 1.9.1
--- Total: 48.499000 15.862000 64.361000 ( 65.568205)
Jruby
--- Total: 46.249000 0.000000 46.249000 ( 46.227999)
Ironruby
--- Total: 215.960536 14.290549 230.251085 (233.329469)
Commento: Per la concatenazione, in tutti i casi prevale l'operatore << mentre includere le variabili nelle stringhe con #{}, sarà forse efficiente come uso di memoria ma in quanto a prestazioni è in coda agli altri. Ironruby fatica con le stringhe, è alto infatti il divario con particolari operatori anche se basterebbe usare << per quasi annullare il divario. Un pò di fatica anche sul ciclo delle righe se confrontato con gli altri tre. Molto bene ruby 1.9.1 con prestazioni simili a Jruby ma un consumo di memoria di soli 5Mb contro 30 della versione java. Anche la versione 1.8.6 non è niente male, ha perso tempo con gsub (soprattutto rispetto a jruby!).
I numeri
Lo script:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 | require "benchmark" include Benchmark Benchmark.bm(21, "--- Total:") do|b| puts "Integer" puts "Arithmetic operations 10.000.000:" n=10_000_000 t1 = b.report("x + 2") do a = 0 n.times {|x| a = x + 2 } end t1 += b.report("x - 1") do n.times {|x| a = x - 1 } end t1 += b.report("x * 3") do n.times {|x| a = x * 3 } end t1 += b.report("x / 2") do n.times {|x| a = x / 2 } end t1 += b.report("x ** 2") do n.times {|x| a = x ** 2 } end t1 += b.report("x % 360") do n.times {|x| x % 360 } end t1 += b.report("Cast") do n.times {|x| x.to_s } n.times {|x| x.to_f } end puts "Add 10.000.000:" n=10_000_000 t2 = b.report("+=") do a = 0 n.times { a += 1 } end t2 += b.report("a = a + 1") do a = 0 n.times { a = a + 1 } end t2 += b.report("<<") do a = 0 n.times { a << 1 } end t2 += b.report(".next") do a = 0 n.times { a = a.next } end puts "Float" puts "Arithmetic operations 10.000.000:" n=10_000_000 t3 = b.report("x + 1.234567") do a = 0 n.times {|x| a = x + 1.234567 } end t3 += b.report("x - 0.135799") do a = 0 n.times {|x| a = x - 0.135799 } end t3 += b.report("x * 0.987654") do a = 0 n.times {|x| a = x * 0.987654 } end t3 += b.report("x / 1.975313") do a = 0 n.times {|x| a = x / 1.975313 } end t3 += b.report("x ** 1.987654") do a = 0 n.times {|x| a = x ** 1.987654 } end t3 += b.report("Cast") do n.times {|x| x.to_s } n.times {|x| x.to_i } end [t1+t2+t3] end |
Ruby 1.8.6
C:ProgettiRubybench>ruby bench_num.rb
user system total real
Integer
Arithmetic operations 10.000.000:
x + 2 3.154000 0.000000 3.154000 ( 3.194785)
x - 1 3.365000 0.000000 3.365000 ( 3.415115)
x * 3 3.415000 0.000000 3.415000 ( 3.495235)
x / 2 3.475000 0.000000 3.475000 ( 3.495235)
x ** 2 14.771000 0.010000 14.781000 ( 14.952395)
x % 360 2.914000 0.000000 2.914000 ( 3.004500)
Cast 11.847000 0.000000 11.847000 ( 11.987955)
Add 10.000.000:
+= 2.544000 0.000000 2.544000 ( 2.583870)
a = a + 1 2.524000 0.000000 2.524000 ( 2.533795)
<< 2.233000 0.000000 2.233000 ( 2.253375)
.next 1.963000 0.000000 1.963000 ( 1.982970)
Float
Arithmetic operations 10.000.000:
x + 1.234567 3.885000 0.000000 3.885000 ( 3.985970)
x - 0.135799 3.886000 0.000000 3.886000 ( 3.945910)
x * 0.987654 3.895000 0.000000 3.895000 ( 3.945910)
x / 1.975313 12.378000 0.010000 12.388000 ( 12.588855)
x ** 1.987654 5.057000 0.000000 5.057000 ( 5.147710)
Cast 11.106000 0.030000 11.136000 ( 11.286905)
--- Total: 92.412000 0.050000 92.462000 ( 93.800490)
Ruby 1.9.1
C:ProgettiRubybench>ruby bench_num.rb
user system total real
Integer
Arithmetic operations 10.000.000:
x + 2 0.981000 0.000000 0.981000 ( 0.991485)
x - 1 0.901000 0.000000 0.901000 ( 0.911365)
x * 3 0.962000 0.000000 0.962000 ( 0.961440)
x / 2 0.911000 0.000000 0.911000 ( 0.921380)
x ** 2 4.316000 0.000000 4.316000 ( 4.366540)
x % 360 0.881000 0.000000 0.881000 ( 0.961440)
Cast 4.727000 0.020000 4.747000 ( 5.217815)
Add 10.000.000:
+= 0.952000 0.000000 0.952000 ( 0.951425)
a = a + 1 0.901000 0.010000 0.911000 ( 0.951425)
<< 1.312000 0.000000 1.312000 ( 1.342010)
.next 1.312000 0.020000 1.332000 ( 1.402100)
Float
Arithmetic operations 10.000.000:
x + 1.234567 2.103000 0.000000 2.103000 ( 2.233345)
x - 0.135799 1.983000 0.000000 1.983000 ( 2.013015)
x * 0.987654 2.042000 0.000000 2.042000 ( 2.053075)
x / 1.975313 2.183000 0.000000 2.183000 ( 2.243360)
x ** 1.987654 3.174000 0.000000 3.174000 ( 3.184770)
Cast 4.106000 0.000000 4.106000 ( 4.146210)
--- Total: 33.747000 0.050000 33.797000 ( 34.852200)
JRuby
C:ProgettiRubybench>jruby bench_num.rb
user system total real
Integer
Arithmetic operations 10.000.000:
x + 2 2.043000 0.000000 2.043000 ( 1.863000)
x - 1 1.803000 0.000000 1.803000 ( 1.803000)
x * 3 1.923000 0.000000 1.923000 ( 1.923000)
x / 2 2.153000 0.000000 2.153000 ( 2.153000)
x ** 2 5.899000 0.000000 5.899000 ( 5.899000)
x % 360 1.803000 0.000000 1.803000 ( 1.803000)
Cast 4.527000 0.000000 4.527000 ( 4.527000)
Add 10.000.000:
+= 1.682000 0.000000 1.682000 ( 1.682000)
a = a + 1 1.602000 0.000000 1.602000 ( 1.602000)
<< 1.302000 0.000000 1.302000 ( 1.302000)
.next 1.433000 0.000000 1.433000 ( 1.433000)
Float
Arithmetic operations 10.000.000:
x + 1.234567 2.363000 0.000000 2.363000 ( 2.363000)
x - 0.135799 2.404000 0.000000 2.404000 ( 2.404000)
x * 0.987654 2.153000 0.000000 2.153000 ( 2.153000)
x / 1.975313 4.487000 0.000000 4.487000 ( 4.487000)
x ** 1.987654 5.829000 0.000000 5.829000 ( 5.839000)
Cast 4.146000 0.000000 4.146000 ( 4.156000)
--- Total: 47.552000 0.000000 47.552000 ( 47.392000)
IronRuby
C:ProgettiRubybench>ir bench_num.rb
user system total real
Integer
Arithmetic operations 10.000.000:
x + 2 3.925645 0.060086 3.985731 ( 4.056075)
x - 1 1.652376 0.000000 1.652376 ( 1.662490)
x * 3 1.912750 0.000000 1.912750 ( 1.972955)
x / 2 1.982851 0.010014 1.992866 ( 2.013015)
x ** 2 3.895602 0.010014 3.905616 ( 3.975955)
x % 360 1.672405 0.010014 1.682419 ( 1.692535)
Cast 6.248986 0.010014 6.259000 ( 6.389570)
Add 10.000.000:
+= 1.672405 0.010014 1.682419 ( 1.712565)
a = a + 1 1.742506 0.000000 1.742506 ( 1.792685)
<< 1.472117 0.040058 1.512174 ( 1.532295)
.next 1.672405 0.000000 1.672405 ( 1.672505)
Float
Arithmetic operations 10.000.000:
x + 1.234567 1.932779 0.000000 1.932779 ( 1.932895)
x - 0.135799 1.952808 0.010014 1.962822 ( 2.033045)
x * 0.987654 1.992866 0.000000 1.992866 ( 1.992985)
x / 1.975313 3.825501 0.030043 3.855544 ( 3.925880)
x ** 1.987654 3.014334 0.000000 3.014334 ( 3.034545)
Cast 5.878453 0.000000 5.878453 ( 5.928880)
--- Total: 46.446787 0.190274 46.637061 ( 47.320875)
Riepilogo totali:
Ruby 1.8.6
--- Total: 92.412000 0.050000 92.462000 ( 93.800490)
Ruby 1.9.1
--- Total: 33.747000 0.050000 33.797000 ( 34.852200)
Jruby
--- Total: 47.552000 0.000000 47.552000 ( 47.392000)
Ironruby
--- Total: 46.446787 0.190274 46.637061 ( 47.320875)
Commento: Netto miglioramento della versione 1.9.1 che, con i numeri, è tre volte più veloce della 1.8.6. Anche gli altri due non sono male.
16/08/2009: Mi sono accorto di aver inserito erroneamente il metodo shift (<<) tra gli add.
24/08/2009: Avevo invertito i risultati della 1.8.6 con quelli della 1.9.1... in effetti mi sembravano un pò strani :-p
Gli array
Lo script:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 | require "benchmark" include Benchmark Benchmark.bm(21, "Total:") do|b| n=100_000 puts "Create 100.000:" t1 = b.report("%w()") do n.times { %w(a b c d f e g h j k i l m n o p q r s t u v w y z 0 1 2 3 4 5 6 7 8 9) } end t1 += b.report("%w''") do n.times { %w"a b c d f e g h j k i l m n o p q r s t u v w y z 0 1 2 3 4 5 6 7 8 9" } end t1 += b.report("split") do n.times { "a b c d f e g h j k i l m n o p q r s t u v w y z 0 1 2 3 4 5 6 7 8 9".split(' ') } end t1 += b.report("[str]") do n.times { ["a","b","c","d","f","e","g","h","j","k","i","l","m","n","o","p","q","r","s","t","u","v","w","y","z","0","1","2","3","4","5","6","7","8","9"] } end t1 += b.report("[num]") do n.times { [0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4] } end t1 += b.report("Array.new(str)") do n.times { Array.new(35, "a") } end t1 += b.report("Array.new(num)") do n.times { Array.new(35, 0) } end t1 += b.report("Array.new(hash)") do n.times { Array.new(35, {}) } end n=100_000 puts "Add 100.000:" t2 = b.report("+= ['.']") do a = [] n.times { a += ["."] } end t2 += b.report("+= [0]") do a = [] n.times { a += [0] } end t2 += b.report("<< ['.']") do a = [] n.times { a << ["."] } end t2 += b.report("<< [0]") do a = [] n.times { a << [0] } end t2 += b.report("a = a+['.']") do a = [] n.times { a = a + ["."] } end t2 += b.report("a = a+[0]") do a = [] n.times { a = a + [0] } end n=100_000 puts "Concatenate 100.000:" t3 = b.report("[num]+") do n.times { [0,1,2,3,4]+[5,6,7,8,9]+[0,1,2,3,4,5,6,7,8,9] } end t3 += b.report("[str]+") do n.times { ["a","b","c","d","f"]+["e","g","h","j","k"]+["i","l","m","n","o","p","q","r","s","t"] } end t3 += b.report("[mix]+") do n.times { ["a","b","c","d","f"]+[5,6,7,8,9]+["i",0,"m",1,"o",2,"q",3,"s",4] } end t3 += b.report("[num]<<") do n.times { [0,1,2,3,4]<<[5,6,7,8,9]<<[0,1,2,3,4,5,6,7,8,9] } end t3 += b.report("[str]<<") do n.times { ["a","b","c","d","f"]<<["e","g","h","j","k"]<<["i","l","m","n","o","p","q","r","s","t"] } end t3 += b.report("[mix]<<") do n.times { ["a","b","c","d","f"]<<[5,6,7,8,9]<<["i",0,"m",1,"o",2,"q",3,"s",4] } end t3 += b.report("[num].concat") do n.times { [0,1,2,3,4].concat([5,6,7,8,9]).concat([0,1,2,3,4,5,6,7,8,9]) } end t3 += b.report("[str].concat") do n.times { ["a","b","c","d","f"].concat(["e","g","h","j","k"]).concat(["i","l","m","n","o","p","q","r","s","t"]) } end t3 += b.report("[mix].concat") do n.times { ["a","b","c","d","f"].concat([5,6,7,8,9]).concat(["i",0,"m",1,"o",2,"q",3,"s",4])} end t3 += b.report("union [num]") do n.times { [0,14,2,3,4,53,682] | [3,4,53,6,7,84,9] } end t3 += b.report("union [str]") do n.times { ["a","b","c","d","f"] | ["b","d","h","j","k"] } end a=Array.new(100, 0) n=1_000_000 puts "Read 1.000.000:" t4 = b.report("each:") do n.times { a.each{|x| x} } end t4 += b.report("map:") do n.times { a.map{|x| x} } end puts "Other 10.000:" n=10_000 a=["a",nil,0,"b",1,nil] * 500 t5 = b.report(".compact:") do n.times { a.compact } end t5 += b.report(".delete(nil):") do n.times { a.delete(nil)} end a=[] n.times {|x| a << x } t5 += b.report("delete:") do n.times {|x| a.delete(x) } end puts "-"*20 [t1+t2+t3+t4+t5] end |
Ruby 1.8.6
C:ProgettiRubybench>ruby bench_arr.rb
user system total real
Create 100.000:
%w() 0.300000 0.010000 0.310000 ( 0.320480)
%w'' 0.300000 0.000000 0.300000 ( 0.310465)
split 2.164000 0.010000 2.174000 ( 2.213315)
[str] 0.310000 0.000000 0.310000 ( 0.310465)
[num] 0.160000 0.030000 0.190000 ( 0.220330)
Array.new(str) 0.180000 0.010000 0.190000 ( 0.190285)
Array.new(num) 0.151000 0.030000 0.181000 ( 0.180270)
Array.new(hash) 0.270000 0.040000 0.310000 ( 0.310465)
Add 100.000:
+= ['.'] 22.472000 8.272000 30.744000 ( 31.487160)
+= [0] 20.800000 8.602000 29.402000 ( 30.004940)
<< ['.'] 0.070000 0.010000 0.080000 ( 0.090135)
<< [0] 0.070000 0.000000 0.070000 ( 0.070105)
a = a+['.'] 28.321000 7.351000 35.672000 ( 36.514690)
a = a+[0] 25.226000 7.480000 32.706000 ( 33.520205)
Concatenate 100.000:
[num]+ 0.451000 0.030000 0.481000 ( 0.490735)
[str]+ 0.581000 0.030000 0.611000 ( 0.640960)
[mix]+ 0.521000 0.030000 0.551000 ( 0.550825)
[num]<< 0.350000 0.020000 0.370000 ( 0.370555)
[str]<< 0.421000 0.031000 0.452000 ( 0.450675)
[mix]<< 0.420000 0.010000 0.430000 ( 0.430645)
[num].concat 0.341000 0.030000 0.371000 ( 0.380570)
[str].concat 0.491000 0.020000 0.511000 ( 0.510765)
[mix].concat 0.400000 0.020000 0.420000 ( 0.430645)
union [num] 1.032000 0.020000 1.052000 ( 1.051575)
union [str] 0.761000 0.080000 0.841000 ( 0.881320)
Read 1.000.000:
each: 16.934000 0.000000 16.934000 ( 17.145680)
map: 20.750000 1.181000 21.931000 ( 22.243315)
Other 10.000:
.compact: 0.401000 0.131000 0.532000 ( 0.530795)
.delete(nil): 4.246000 0.000000 4.246000 ( 4.316465)
delete: 4.937000 0.000000 4.937000 ( 5.017515)
--------------------
Total: 153.831000 33.478000 187.309000 (191.186350)
Ruby 1.9.1
C:ProgettiRubybench>ruby bench_arr.rb
user system total real
Create 100.000:
%w() 0.520000 0.000000 0.520000 ( 0.520780)
%w'' 0.541000 0.000000 0.541000 ( 0.550825)
split 0.781000 0.000000 0.781000 ( 0.791185)
[str] 0.511000 0.000000 0.511000 ( 0.520780)
[num] 0.020000 0.000000 0.020000 ( 0.020030)
Array.new(str) 0.170000 0.010000 0.180000 ( 0.190285)
Array.new(num) 0.140000 0.000000 0.140000 ( 0.140210)
Array.new(hash) 0.141000 0.030000 0.171000 ( 0.200300)
Add 100.000:
+= ['.'] 22.131000 9.553000 31.684000 ( 32.268330)
+= [0] 16.514000 8.422000 24.936000 ( 25.478160)
<< ['.'] 0.010000 0.020000 0.030000 ( 0.030045)
<< [0] 0.030000 0.000000 0.030000 ( 0.030045)
a = a+['.'] 17.856000 3.946000 21.802000 ( 22.323435)
a = a+[0] 17.024000 8.172000 25.196000 ( 25.788625)
Concatenate 100.000:
[num]+ 0.211000 0.020000 0.231000 ( 0.230345)
[str]+ 0.490000 0.000000 0.490000 ( 0.500750)
[mix]+ 0.341000 0.000000 0.341000 ( 0.340510)
[num]<< 0.140000 0.000000 0.140000 ( 0.140210)
[str]<< 0.421000 0.010000 0.431000 ( 0.460690)
[mix]<< 0.260000 0.000000 0.260000 ( 0.260390)
[num].concat 0.160000 0.000000 0.160000 ( 0.160240)
[str].concat 0.471000 0.000000 0.471000 ( 0.470705)
[mix].concat 0.320000 0.000000 0.320000 ( 0.320480)
union [num] 0.691000 0.020000 0.711000 ( 0.721080)
union [str] 0.952000 0.000000 0.952000 ( 0.961440)
Read 1.000.000:
each: 7.861000 0.000000 7.861000 ( 7.991970)
map: 13.169000 1.482000 14.651000 ( 14.822200)
Other 10.000:
.compact: 0.250000 0.130000 0.380000 ( 0.380570)
.delete(nil): 2.684000 0.000000 2.684000 ( 2.704050)
delete: 4.907000 0.000000 4.907000 ( 4.987470)
--------------------
Total: 109.717000 31.815000 141.532000 (144.306135)
JRuby
C:ProgettiRubybench>jruby bench_arr.rb
user system total real
Create 100.000:
%w() 0.251000 0.000000 0.251000 ( 0.211000)
%w'' 0.190000 0.000000 0.190000 ( 0.190000)
split 0.370000 0.000000 0.370000 ( 0.370000)
[str] 0.191000 0.000000 0.191000 ( 0.191000)
[num] 0.030000 0.000000 0.030000 ( 0.030000)
Array.new(str) 0.080000 0.000000 0.080000 ( 0.080000)
Array.new(num) 0.060000 0.000000 0.060000 ( 0.060000)
Array.new(hash) 0.100000 0.000000 0.100000 ( 0.100000)
Add 100.000:
+= ['.'] 143.074000 0.000000 143.074000 (143.074000)
+= [0] 126.099000 0.000000 126.099000 (126.099000)
<< ['.'] 0.211000 0.000000 0.211000 ( 0.211000)
<< [0] 0.090000 0.000000 0.090000 ( 0.090000)
a = a+['.'] 124.536000 0.000000 124.536000 (124.536000)
a = a+[0] 127.672000 0.000000 127.672000 (127.672000)
Concatenate 100.000:
[num]+ 0.120000 0.000000 0.120000 ( 0.120000)
[str]+ 0.180000 0.000000 0.180000 ( 0.180000)
[mix]+ 0.120000 0.000000 0.120000 ( 0.120000)
[num]<< 0.070000 0.000000 0.070000 ( 0.070000)
[str]<< 0.211000 0.000000 0.211000 ( 0.211000)
[mix]<< 0.110000 0.000000 0.110000 ( 0.110000)
[num].concat 0.100000 0.000000 0.100000 ( 0.100000)
[str].concat 0.160000 0.000000 0.160000 ( 0.160000)
[mix].concat 0.131000 0.000000 0.131000 ( 0.131000)
union [num] 0.280000 0.000000 0.280000 ( 0.280000)
union [str] 0.300000 0.000000 0.300000 ( 0.300000)
Read 1.000.000:
each: 9.364000 0.000000 9.364000 ( 9.364000)
map: 11.007000 0.000000 11.007000 ( 11.007000)
Other 10.000:
.compact: 0.411000 0.000000 0.411000 ( 0.411000)
.delete(nil): 2.904000 0.000000 2.904000 ( 2.904000)
delete: 4.687000 0.000000 4.687000 ( 4.687000)
--------------------
Total: 553.109000 0.000000 553.109000 (553.069000)
IronRuby
C:ProgettiRubybench>ir bench_arr.rb
user system total real
Create 100.000:
%w() 0.721037 0.000000 0.721037 ( 0.721080)
%w'' 0.230331 0.000000 0.230331 ( 0.250375)
split 1.482131 0.000000 1.482131 ( 1.482220)
[str] 0.240346 0.000000 0.240346 ( 0.240360)
[num] 0.100144 0.000000 0.100144 ( 0.100150)
Array.new(str) 0.150216 0.000000 0.150216 ( 0.150225)
Array.new(num) 0.130187 0.000000 0.130187 ( 0.130195)
Array.new(hash) 0.110158 0.000000 0.110158 ( 0.120180)
Add 100.000:
+= ['.'] 93.234064 9.723982 102.958046 (105.808475)
+= [0] 44.143475 3.294738 47.438213 ( 48.092030)
<< ['.'] 0.160230 0.000000 0.160230 ( 0.160240)
<< [0] 0.090130 0.010014 0.100144 ( 0.100150)
a = a+['.'] 96.779162 9.483637 106.262798 (109.644220)
a = a+[0] 43.823014 3.545098 47.368112 ( 47.921775)
Concatenate 100.000:
[num]+ 0.160230 0.000000 0.160230 ( 0.160240)
[str]+ 0.230331 0.000000 0.230331 ( 0.240360)
[mix]+ 0.180259 0.000000 0.180259 ( 0.180270)
[num]<< 0.090130 0.000000 0.090130 ( 0.090135)
[str]<< 0.180259 0.010014 0.190274 ( 0.190285)
[mix]<< 0.150216 0.000000 0.150216 ( 0.150225)
[num].concat 0.110158 0.010014 0.120173 ( 0.120180)
[str].concat 0.220317 0.000000 0.220317 ( 0.220330)
[mix].concat 0.180259 0.000000 0.180259 ( 0.180270)
union [num] 0.330475 0.000000 0.330475 ( 0.340510)
union [str] 0.410590 0.000000 0.410590 ( 0.410615)
Read 1.000.000:
each: 14.040189 0.010014 14.050203 ( 14.181240)
map: 19.848541 0.020029 19.868570 ( 20.120135)
Other 10.000:
.compact: 1.161670 0.000000 1.161670 ( 1.171755)
.delete(nil): 15.462234 0.030043 15.492277 ( 15.833715)
delete: 1.422045 0.000000 1.422045 ( 1.472205)
--------------------
Total: 335.572530 26.137584 361.710114 (369.984144)
Riepilogo totali:
Ruby 1.8.6
Total: 153.831000 33.478000 187.309000 (191.186350)
Ruby 1.9.1
Total: 109.717000 31.815000 141.532000 (144.306135)
Jruby
Total: 553.109000 0.000000 553.109000 (553.069000)
Ironruby
Total: 335.572530 26.137584 361.710114 (369.984144)
Commento: Dunque, in questo terzo test è importante spendere qualche parola. Il tempo alto di jruby ed ironruby è dovuto anche al fatto che ho voluto comparare la concatenazione += ed una versione simile, entrambi MOLTO sconvenienti per questi due interpreti, i tempi sono infatti altissimi, basterebbe utilizzare solo l'operatore << per avere risultati molto diversi, in ordine: 1.8.6=61 1.9.1=40 jruby=33 e ironruby=60 ...jruby risulterebbe il più veloce.
Un altra curiosità è l'utilizzo del metodo compact, oltre ad essere più elegante è anche più prestazionale del sistema analogo delete(nil) è anche più prestazionale con sensibili differenze in Ironruby.
Gli Hash
Lo script per testare gli Hash è il più pesante nell'utilizzo della memoria, sinceramente non era nelle mie intenzioni ma poi ho pensato di tenerlo così per esaminarne gli esiti:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | require "benchmark" include Benchmark n=1_000_000 Benchmark.bm(21, "Total:") do|b| puts "Write 1.000.000:" h = {} t1 = b.report("int => str:") do n.times {|x| h[x] = "." } end h = {} t1 += b.report("int => int:") do n.times {|x| h[x] = x } end t1 += b.report("int => [int]:") do n.times {|x| h[x] = [x] } end t1 += b.report(".to_sym => .to_s:") do n.times {|x| h[x.to_s.to_sym] = x.to_s } end puts ":sym => str 1.000.000:" t2 = b.report("Each:") do h.each {|k,v| k; v} end t2 += b.report("each_key:") do h.each_key {|k| k} end t2 += b.report("each_value:") do h.each_value {|v| v} end t2 += b.report("read by key:") do h.each {|k,v| h[k]} end h1=h t3 = b.report("5 invert:") do 5.times { h.invert } end h=h1 t3 += b.report("1000 shift:") do 1000.times { h.shift } end h=h1 t3 += b.report("1000 delete:") do 1000.times {|x| h.delete(x) } end h=h1 t3 += b.report("all delete:") do h.each_key {|k| h.delete(k)} end [t1+t2+t3] end |
Ruby 1.8.6
C:ProgettiRubybench>ruby bench_hsh.rb
user system total real
Write 1.000.000:
int => str: 0.851000 0.030000 0.881000 ( 0.881320)
int => int: 1.021000 0.090000 1.111000 ( 1.141710)
int => [int]: 0.791000 0.030000 0.821000 ( 0.841260)
.to_sym => .to_s: 6.400000 0.080000 6.480000 ( 6.509750)
:sym => str 1.000.000:
Each: 2.553000 0.090000 2.643000 ( 2.684020)
each_key: 0.481000 0.000000 0.481000 ( 0.490735)
each_value: 0.491000 0.000000 0.491000 ( 0.490735)
read by key: 2.854000 0.040000 2.894000 ( 2.904350)
5 invert: 31.185000 0.791000 31.976000 ( 32.438585)
1000 shift: 37.604000 0.010000 37.614000 ( 38.177180)
1000 delete: 0.000000 0.000000 0.000000 ( 0.000000)
all delete: 1.442000 0.010000 1.452000 ( 1.452175)
Total: 85.673000 1.171000 86.844000 ( 88.011820)
Ruby 1.9.1
C:ProgettiRubybench>ruby bench_hsh.rb
user system total real
Write 1.000.000:
int => str: 0.721000 0.100000 0.821000 ( 0.841260)
int => int: 0.590000 0.140000 0.730000 ( 0.731095)
int => [int]: 0.391000 0.020000 0.411000 ( 0.440660)
.to_sym => .to_s: 4.957000 0.320000 5.277000 ( 5.327980)
:sym => str 1.000.000:
Each: 0.691000 0.050000 0.741000 ( 0.801200)
each_key: 0.461000 0.000000 0.461000 ( 0.470705)
each_value: 0.500000 0.000000 0.500000 ( 0.520780)
read by key: 1.022000 0.011000 1.033000 ( 1.051575)
5 invert: 33.939000 1.071000 35.010000 ( 35.603325)
1000 shift: 48.800000 0.020000 48.820000 ( 49.474100)
1000 delete: 0.000000 0.000000 0.000000 ( 0.000000)
all delete: 1.432000 0.010000 1.442000 ( 1.472205)
Total: 93.504000 1.742000 95.246000 ( 96.734885)
JRuby
C:ProgettiRubybench>jruby bench_hsh.rb
user system total real
Write 1.000.000:
int => str: 2.113000 0.000000 2.113000 ( 1.933000)
int => int: 0.721000 0.000000 0.721000 ( 0.721000)
int => [int]: 1.022000 0.000000 1.022000 ( 1.022000)
.to_sym => .to_s: 34.641000 0.000000 34.641000 ( 34.641000)
:sym => str 1.000.000:
Each: 0.541000 0.000000 0.541000 ( 0.541000)
each_key: 0.191000 0.000000 0.191000 ( 0.191000)
each_value: 0.240000 0.000000 0.240000 ( 0.240000)
read by key: 0.761000 0.000000 0.761000 ( 0.761000)
5 invert: Error: Your application used more memory than the safety cap.
IronRuby
C:ProgettiRubybench>ir bench_hsh.rb
user system total real
Write 1.000.000:
int => str: 2.183139 0.030043 2.213182 ( 2.213315)
int => int: 1.211742 0.030043 1.241786 ( 1.291935)
int => [int]: 1.712462 0.010014 1.722477 ( 1.732595)
.to_sym => .to_s: 7.460728 0.120173 7.580901 ( 7.781655)
:sym => str 1.000.000:
Each: 1.111598 0.000000 1.111598 ( 1.131695)
each_key: 0.340490 0.000000 0.340490 ( 0.350525)
each_value: 0.330475 0.010014 0.340490 ( 0.350525)
read by key: 1.021469 0.000000 1.021469 ( 1.031545)
5 invert: 5.588035 0.350504 5.938539 ( 7.381055)
1000 shift: 0.020029 0.000000 0.020029 ( 0.190285)
1000 delete: 0.020029 0.000000 0.020029 ( 0.080120)
all delete: 1.031483 0.020029 1.051512 ( 1.061590)
Total: 22.031680 0.570821 22.602501 ( 24.596839)
Riepilogo totali:
Ruby 1.8.6
Total: 85.673000 1.171000 86.844000 ( 88.011820)
Ruby 1.9.1
Total: 93.504000 1.742000 95.246000 ( 96.734885)
Jruby
*** Out of memory ***
Ironruby
Total: 22.031680 0.570821 22.602501 ( 24.596839)
Commento: JRuby non ha concluso per un errore di out of memory, il processo java è arrivato ad occupare 600Mb nel test dove si effettua un'inversione degli hash, raggiungendo il limite della JVM. Il meno ingordo di memoria è stato ruby 1.8.6 con "soli" 300Mb mentre la 1.9.1 era quasi fisso su 530Mb, non aumentava, come se ci fosse un muro. Non c'era invece per Ironruby che ha superato la barriera dei 600Mb ma con risultati soprendenti, il test dell'inversione in 5 secondi, contro 33 della 1.9.1!
Rieseguengo il test con 2Gb di ram:
Ruby 1.8.6
C:ProgettiRubybench>ruby bench_hsh.rb
user system total real
Write 1.000.000:
int => str: 0.811000 0.060000 0.871000 ( 0.871252)
int => int: 1.081000 0.050000 1.131000 ( 1.141642)
int => [int]: 0.751000 0.030000 0.781000 ( 0.801152)
.to_sym => .to_s: 6.299000 0.130000 6.429000 ( 6.469302)
:sym => str 1.000.000:
Each: 2.474000 0.060000 2.534000 ( 2.553672)
each_key: 0.491000 0.000000 0.491000 ( 0.490706)
each_value: 0.500000 0.000000 0.500000 ( 0.500720)
read by key: 2.834000 0.080000 2.914000 ( 2.964263)
5 invert: 30.454000 0.992000 31.446000 ( 31.885849)
1000 shift: 38.285000 0.020000 38.305000 ( 38.765743)
1000 delete: 0.000000 0.000000 0.000000 ( 0.000000)
all delete: 1.472000 0.010000 1.482000 ( 1.512174)
Total: 85.452000 1.432000 86.884000 ( 87.956475)
Ruby 1.9.1
C:ProgettiRubybench>ruby bench_hsh.rb
user system total real
Write 1.000.000:
int => str: 0.701000 0.090000 0.791000 ( 0.790640)
int => int: 0.661000 0.100000 0.761000 ( 0.780631)
int => [int]: 0.360000 0.020000 0.380000 ( 0.390316)
.to_sym => .to_s: 4.947000 0.380000 5.327000 ( 5.524472)
:sym => str 1.000.000:
Each: 0.631000 0.041000 0.672000 ( 0.690558)
each_key: 0.451000 0.000000 0.451000 ( 0.450365)
each_value: 0.490000 0.000000 0.490000 ( 0.490397)
read by key: 0.892000 0.030000 0.922000 ( 0.940761)
5 invert: 27.760000 6.299000 34.059000 ( 34.547961)
1000 shift: 48.289000 0.020000 48.309000 ( 48.789488)
1000 delete: 0.000000 0.000000 0.000000 ( 0.000000)
all delete: 1.072000 0.370000 1.442000 ( 1.441166)
Total: 86.254000 7.350000 93.604000 ( 94.836755)
JRuby
C:ProgettiRubybench>jruby bench_hsh.rb
user system total real
Write 1.000.000:
int => str: 2.261000 0.000000 2.261000 ( 2.081000)
int => int: 0.760000 0.000000 0.760000 ( 0.760000)
int => [int]: 1.051000 0.000000 1.051000 ( 1.051000)
.to_sym => .to_s: 35.694000 0.000000 35.694000 ( 35.694000)
:sym => str 1.000.000:
Each: 0.481000 0.000000 0.481000 ( 0.481000)
each_key: 0.190000 0.000000 0.190000 ( 0.190000)
each_value: 0.190000 0.000000 0.190000 ( 0.190000)
read by key: 0.701000 0.000000 0.701000 ( 0.701000)
5 invert: Error: Your application used more memory than the safety cap.
C:ProgettiRubybench>jruby bench_hsh.rb -J-Xmx1500m
user system total real
Write 1.000.000:
int => str: 1.612000 0.000000 1.612000 ( 1.562000)
int => int: 0.842000 0.000000 0.842000 ( 0.842000)
int => [int]: 1.101000 0.000000 1.101000 ( 1.101000)
.to_sym => .to_s: 35.341000 0.000000 35.341000 ( 35.341000)
:sym => str 1.000.000:
Each: 0.431000 0.000000 0.431000 ( 0.431000)
each_key: 0.180000 0.000000 0.180000 ( 0.180000)
each_value: 0.190000 0.000000 0.190000 ( 0.200000)
read by key: 0.701000 0.000000 0.701000 ( 0.691000)
5 invert: 11.076000 0.000000 11.076000 ( 11.076000)
1000 shift: 0.000000 0.000000 0.000000 ( 0.000000)
1000 delete: 0.000000 0.000000 0.000000 ( 0.000000)
all delete: 0.912000 0.000000 0.912000 ( 0.912000)
Total: 52.385999 0.000000 52.385999 ( 52.335999)
IronRuby
C:ProgettiRubybench>ir bench_hsh.rb
user system total real
Write 1.000.000:
int => str: 2.283283 0.090130 2.373413 ( 2.363398)
int => int: 1.201728 0.030043 1.231771 ( 1.241786)
int => [int]: 1.792578 0.020029 1.812606 ( 1.862678)
.to_sym => .to_s: 7.340555 0.120173 7.460728 ( 7.590915)
:sym => str 1.000.000:
Each: 1.091570 0.000000 1.091570 ( 1.141642)
each_key: 0.310446 0.000000 0.310446 ( 0.310447)
each_value: 0.360518 0.000000 0.360518 ( 0.360518)
read by key: 1.001440 0.000000 1.001440 ( 1.001440)
5 invert: 4.816926 0.070101 4.887027 ( 4.917070)
1000 shift: 0.010014 0.000000 0.010014 ( 0.010014)
1000 delete: 0.010014 0.000000 0.010014 ( 0.010015)
all delete: 1.061526 0.000000 1.061526 ( 1.071541)
Total: 21.280600 0.330475 21.611075 ( 21.881464)
Riepilogo totali:
Ruby 1.8.6
Total: 85.452000 1.432000 86.884000 ( 87.956475)
Ruby 1.9.1
Total: 86.254000 7.350000 93.604000 ( 94.836755)
Jruby
Total: 52.385999 0.000000 52.385999 ( 52.335999)
Ironruby
Total: 21.280600 0.330475 21.611075 ( 21.881464)
Commento: JRuby si blocca al solito test e sempre con un processo di 600Mb, ho quindi alzato il cap di memoria a 1,5Gb e gli ha permesso di concludere ed anche con un buon tempo ma il processo ha raggiunto dimensioni mostruose, ha di poco superato il giga. Ruby 1.8.6 sempre il meno esoso con 300Mb e la 1.9.1 sempre 530Mb. Entrambi hanno tempi alti con l'inversione ed in particolare con il metodo shift al contrario degli altri due. Ironruby ancora il più veloce con un processo che si accorge della maggiore quantità di memoria e ne usa un altro pò arrivando ad allocare quasi 700Mb.
Le delete degli elementi è sempre stata velocissima anche se aumentavo il numero, forse non ho controllato bene che non venisse effettuata in un unica chiamata, comunque mi accontento di questo risultato.
Conclusioni:
Ogni interprete ha i propri ambiti dove eccelle, Ironruby quando lo fa, lo fa bene distanziando gli altri. Anche JRuby si è preso i suoi trionfi, forse avevo un pò più aspettative dalla versione 1.9.1 anche se credo non sia un suo demerito bensì sono stati gli altri a migliorare molto negli ultimi mesi. La versione 1.8.6 mi ha ben impressionato in questa sua nuova versione o meglio patch 368, è migliorata molto accorciando le distanze prestazionali con la nuova 1.9, pur non avendo una VM. Inoltre è la versione più equilibrata senza alti nè bassi e c'è da considerare che è ancora la versione più usata, più compatibile e con il maggior numero di gemme.
Spero che questo articolo sia risultato interessante, nel prossimo includerò un confronto con python 2.6.2 e la nuova versione 3.1.1 che mi risulta essere velocissima, hola!






