Comparing Ruby and C# Performance






4.75/5 (7 votes)
A quick test of Ruby performance vs. C#
Introduction
As I'm in the middle of learning Ruby and Ruby on Rails, I wanted to do a quick comparison of Ruby vs C#, knowing quite well that C# will outperform Ruby, but I wanted to get some idea by how much. This site indicates that Ruby is about 25 times slower on average, but I wanted to see for myself. As it turns out, Ashraff Ali Wahab had a couple days ago posted the article Eratosthenes/Sundaram/Atkins Sieve Implementation in C#, and I figured this would be a quick way to write some tests.
This is obviously not conclusive - it's a bit like comparing apples and oranges at the code level, especially since I tried to leverage the syntax of Ruby to my current level of understanding. Also, I decided to give IronRuby a try as well, however the tests were inconclusive as the program failed to complete.
Source Code
C#
Please refer to Ashraff Ali Wahab article for the C# source code.
Ruby
Brute Force Algorithm
def BruteForce(topCandidate) totalCount = 1 isPrime = true 3.step(topCandidate, 2) do |i| j=3 while j*j <= i && isPrime isPrime = false if i%j==0 j += 2 end isPrime ? totalCount += 1 : isPrime = true end totalCount end
Sieve of Eratosthenes Algorithm
def SieveOfEratosthenes(topCandidate) myBA1 = Array.new(topCandidate + 1) {true} myBA1[0] = myBA1[1] = false thisFactor = 2 while thisFactor * thisFactor <= topCandidate do mark = thisFactor + thisFactor mark.step(topCandidate+1, thisFactor) {|n| myBA1[n] = false} thisFactor += 1 while !myBA1[thisFactor] do thisFactor += 1 end end myBA1.count(true) end
Sieve of Sundaram Algorithm
def SieveOfSundaram(topCandidate) k = topCandidate / 2 myBA1 = Array.new(k + 1) {true} myBA1[0] = myBA1[k] = false for i in 1..k do denominator = (i << 1) + 1 maxVal = (k - i) / denominator i.step(maxVal+1, 1) {|n| myBA1[i + n * denominator] = false} # this version takes .20 seconds longer to run 1M iterations! # for n in i..maxVal+1 do # myBA1[i + n * denominator] = false # end end myBA1.count(true) + 1 end
"Main"
def main max = 1000000 startTime = Time.now() primes = BruteForce(max) endTime = Time.now() elapsed = endTime - startTime printf("Elapsed time for Brute Force : %f Primes = %d\n", elapsed, primes) startTime = Time.now() primes = SieveOfEratosthenes(max) endTime = Time.now() elapsed = endTime - startTime printf("Elapsed time for Sieve of Eratosthenes: %f Primes = %d\n", elapsed, primes) startTime = Time.now() primes = SieveOfSundaram(max) endTime = Time.now() elapsed = endTime - startTime printf("Elapsed time for Sieve of Sundaram : %f Primes = %d\n", elapsed, primes) end
The Results
As you can see from these screenshots:
Ruby is:
- about 5 times slower for the brute force algorithm
- about 19 times slower for the Eratosthenes and Sundaram algorithms
For my purposes, that's essentially inline with the shootout website I mentioned in the Introduction.
Sadly, the IronRuby program did not complete:
Dying on this line:
myBA1.count(true)
But the brute force algorithm was consistently almost twice as slow.
Running on a Virtual Box
I'm also running Ubuntu on Virtual Box (2GB RAM, 3 processors) and was pleased with the results:
Only about 3 times slower!
Conclusion
While not conclusive, it was a useful exercise to go through. Note particularly the commented out Ruby code:
i.step(maxVal+1, 1) {|n| myBA1[i + n * denominator] = false} # this version takes .20 seconds longer to run 1M iterations! # for n in i..maxVal+1 do # myBA1[i + n * denominator] = false # end
The "for" loop version takes almost 50% longer! That is a significant
and worthwhile discovery, and it essentially makes sense -- the step
function
is a library implementation (and I would assume therefore compiled) whereas the
for
loop I would imagine is constantly being interpreted.
Still, it's a significant difference, especially considering that the block {|n| myBA1[i + n * denominator] = false}
theoretically
is implemented as a function call.
Also, it was disappointing that the IronRuby code failed. I was hoping that something this "simple" would not have issues.
Lastly, please do not take this as a detraction to Ruby! This is an amazing language and for many purposes, performance is not the most important concern - interactions with a database and network latency (if you're thinking of Ruby on Rails) will often contribute more to the perception of performance than the language performance.
Also, there appear to be some compilers available, for example Rubinius as well as The Ludicrous JIT Compiler. The former looked much too complicated to try, and the latter, Ludicrous, I did try but was not successful with the installation. Given that the creator claims "Though still in the experimental stage, its performance is roughly on par with YARV", it doesn't seem that helpful, given that: "Probably the most exciting and visible change in Ruby 1.9 is the addition of a bytecode interpreter for Ruby. The YARV (Yet Another Ruby VM) interpreter was integrated into the Ruby project, replacing the interpreter created by Matz (aka MRI, Matz's Ruby Interpreter)." (read here).