It cannot be denied that versatility is one of its features, a true multi-platform. There are interpreters for many operating systems and in addition to the classic C version we have JRuby, a viable alternative for the java world and we also have Ironruby for the Microsoft’s .NET, arrived in recent days at a fairly mature version.
Not bad! Think about the benefits that a company may have: a language easy to learn but very powerful, has an excellent framework (one in all, Rails) to quickly develop applications for intranets and in addition to all this, the chance to use the java’s or .net’s libraries.
In this article we will read about these four interpreters:
- Ruby 1.8.6 patch 368 updated at 2009-03-31
- Ruby 1.9.1 patch 129 updated at 2009-05-12 revision 23412
- jRuby 1.3.1 (ruby 1.8.6p287) 2009-06-15 Client VM 1.6.0 update 14
- IronRuby 0.9.0.0 on .NET 2.0.0.0
The Purpose: To discover the main differences in performance of the individual interpreter. There are methods and techniques that lead to the same result, without such tests can be difficult to determine which is the best practice.
In tests that we will see, I have focused on the processing speed and only roughly the amount of memory used.
The system: The physical machine is a Windows XP Professional SP3 32bit with an Intel E7300 dual core with 3Mb of cache, and 3.25 Gb of ram.
physical machine
The tests running on a virtualized system with MS Virtual PC 2007, Windows XP Pro, Java SDK 6:14,. NET Framework 3.5 SP1, single-core processor, 768MB memory. Only the heaviest test (hash) was repeated with 2Gb.
Benchmarks are divided into four categories:
- Strings
- Numbers
- Array
- Hash
These tests that I created, are limited to perform very simple operations. At the bottom there is a total, but should not be construed as an index to the actual interpreter’s performance because it changes a lot according to the tests selected and those that I used, is not said that are conditions encountered in actual use.
Strings
This is the first 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 |
These results are sorted by interpreter, from first to quarter:
Ruby 1.8.6
C:>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:>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:>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:>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)
This is the summary:
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)
Comments: For the concatenation, in all cases prevails, the operator << instead, include the variables in strings with # (), it may be as efficient use of memory but as a performance is at the bottom. Ironruby fatigue with the strings, the gap is huge with some operators, would enough to use << to almost cancel the gap. A little effort on the string's cycle, if compared with the other three. Very good ruby 1.9.1 with performance similar to Jruby but a memory consumption of only 5Mb against 30Mb used by java. The version 1.8.6 is not so bad, he lost time with gsub (especially compared to jruby).
Numbers
The 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:>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)
Ruby 1.9.1
C:>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)
JRuby
C:>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:>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)
Summary:
Ruby 1.8.6
--- Total: 33.747000 0.050000 33.797000 ( 34.852200)
Ruby 1.9.1
--- Total: 92.412000 0.050000 92.462000 ( 93.800490)
Jruby
--- Total: 47.552000 0.000000 47.552000 ( 47.392000)
Ironruby
--- Total: 46.446787 0.190274 46.637061 ( 47.320875)
Comments: Marked improvement in the version 1.9.1 which with numbers is three times faster than 1.8.6. The other two are not bad too.
16/08/2009: Only now i see that i mistakenly put shift method (<<) under adding methods.
24/08/2009: I had inverted the 1.8.6's results with those of 1.9.1 ...actually it seemed a bit strange! :-p
Array
The 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:>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:>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:>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:>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)
Summary:
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)
Comments: So, in this third test is important to say a few words. Time high of jruby and ironruby is also due to the fact that I wanted to compare the concatenation += with a similar version. Both are very inappropriate for these two interpreters, times are very high and would enough to use operator << to get very different results, in order: 1.8.6 = 61 1.9.1 = 40 jruby = 33 = 60 and ironruby... jruby would be the fastest.
Another curiosity is the use of compact method, besides being more elegant is even faster than delete (nil) with sensitive differences in Ironruby.
Hash
The script to test the Hash is the heaviest as memory's stress test. Frankly, it wasn't in my intentions, but then I thought to hold it for examining the outcomes:
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:>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:>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:>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:>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)
Summary:
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)
Comments: JRuby has not concluded the test to an error of "out of memory", the java process has come to occupy 600Mb in the test where you make an {}.invert, reaching the limit of the JVM. The least greedy of memory was ruby 1.8.6 with "only" 300Mb while 1.9.1 was almost fixed at 530Mb, not increased, as if there was a wall. There was not, instead, for Ironruby that has exceeded the barrier of 600Mb but with great results, the inversion executed in 5 seconds, compared with 33 of 1.9.1!
The same test with 2Gb of ram:
Ruby 1.8.6
C:>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:>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:>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:>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:>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)
Summary:
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)
Comments: JRuby hangs again and always with a process of 600Mb, so I increased the cap to 1.5 Gb memory allowing it to finish even with a good result but the process has reached monstrous size, has passed 1Gb. Ruby 1.8.6 again the least greedy with 300Mb and again 530Mb for the 1.9.1. Both have high time with the invert method and in particular with the shift, contrary for the two others. Ironruby still the fastest with a process that detects an huge amount of memory, coming to allocate almost 700Mb.
Deleting the elements has always been fast even if they increased the number, maybe I did not check well that were not performed in a single call, however, I shall from this result.
Conclusions:
Each interpreter has its areas where it excels, Ironruby when he does, he does well, outdistanced the others. JRuby also has taken its triumphs, maybe I had a little more expectations from version 1.9.1 although I believe it is not a demerit but were the others to improve much in recent months. I was very impressed by the version 1.8.6 with this "new" patch 368 with, or better, a different compiled (mingw32), that much improved performance by shortening the distances with the new 1.9, while not having a VM. It is also the most balanced without high or low and to consider that there is still the most used, and more compatible with the largest number of buds.
I hope this article was interesting, the next will include a comparison with also python 2.6.2 and the newest 3.1.1 which is very very fast. Hola!
English
Italiano