Ruby vs Python vs Windows vs Linux

5
mar/10
0

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

Win vs Linux

Win vs Linux

Ruby vs Ruby vs Python vs Python

24
gen/10
0

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

Benchmark senza warm up

Benchmark senza warm up

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

Rilevazioni dopo warm up

Rilevazioni dopo warm up

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

23
ago/09
5

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

Chart

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

19
ago/09
0

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)
Ruby 1.8.6 patch 111

Ruby 1.8.6 patch 111

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)
Ruby 1.8.6 patch 368

Ruby 1.8.6 patch 368

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)
Ruby 1.9.1 patch 129

Ruby 1.9.1 patch 129

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)
jRuby 1.3.1 => 1.8.6 p287

jRuby 1.3.1 => 1.8.6 p287

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>
IronRuby 0.9.0

IronRuby 0.9.0

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

6
ago/09
1

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:

  1. Ruby 1.8.6 patch 368 aggiornato al 2009-03-31
  2. Ruby 1.9.1 patch 129 aggiornato al 2009-05-12 revisione 23412
  3. jRuby 1.3.1 (ruby 1.8.6p287) 2009-06-15 Client VM 1.6.0 update 14
  4. 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

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!