Ruby vs Python vs Windows vs Linux
mar/100
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 “L” sono state installate tramite gestore di pacchetti, le RVM invece, tramite il Ruby Version Manager.
| Versione | Compilatore/Sistema/VM | Secondi |
|---|---|---|
| JRuby 1.4.0 L | 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 L | 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 L | 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 L | 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
gen/100
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
ago/095
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!
