在编程语言的世界中,性能始终是开发者关注的焦点之一。尽管C#通常被认为在性能上优于Ruby,但这种差距究竟有多大,一直是许多开发者心中的疑问。本文旨在通过实际的代码测试,探索Ruby与C#在执行相同任务时的性能差异。
为了进行这一比较,选择了几种不同的算法实现:暴力算法、埃拉托斯特尼筛法和桑德拉姆筛法。这些算法被广泛应用于计算素数,是测试性能的不错选择。
实验在Ubuntu操作系统上进行,使用了Virtual Box虚拟机,配置为2GB内存和3个处理器。首先参考了Ashraff Ali Wahab的文章,获取了C#的源代码。然后,尝试使用Ruby实现相同的算法,并运行测试。
以下是C#和Ruby的源代码实现:
// C#源代码请参考Ashraff Ali Wahab的文章
// Ruby暴力算法实现
def BruteForce(topCandidate)
totalCount = 1
isPrime = true
(3..step(topCandidate, 2)).each do |i|
j = 3
while j * j <= i && isPrime
isPrime = false if i % j == 0
j += 2
end
totalCount += 1 if isPrime
isPrime = true
end
totalCount
end
// Ruby埃拉托斯特尼筛法实现
def SieveOfEratosthenes(topCandidate)
myBA1 = Array.new(topCandidate + 1, true)
myBA1[0] = myBA1[1] = false
thisFactor = 2
while thisFactor * thisFactor <= topCandidate
mark = thisFactor + thisFactor
(mark..step(topCandidate, thisFactor)).each { |n| myBA1[n] = false }
thisFactor += 1 while !myBA1[thisFactor]
thisFactor += 1
end
myBA1.count(true)
end
// Ruby桑德拉姆筛法实现
def SieveOfSundaram(topCandidate)
k = topCandidate / 2
myBA1 = Array.new(k + 1, true)
myBA1[0] = myBA1[k] = false
(1..k).each do |i|
denominator = (i << 1) + 1
maxVal = (k - i) / denominator
(i..step(maxVal+1, 1)).each { |n| myBA1[i + n * denominator] = false }
end
myBA1.count(true) + 1
end
// 主程序
def main
max = 1000000
startTime = Time.now()
primes = BruteForce(max)
endTime = Time.now()
elapsed = endTime - startTime
puts "Elapsed time for Brute Force: #{elapsed} Primes = #{primes}"
startTime = Time.now()
primes = SieveOfEratosthenes(max)
endTime = Time.now()
elapsed = endTime - startTime
puts "Elapsed time for Sieve of Eratosthenes: #{elapsed} Primes = #{primes}"
startTime = Time.now()
primes = SieveOfSundaram(max)
endTime = Time.now()
elapsed = endTime - startTime
puts "Elapsed time for Sieve of Sundaram: #{elapsed} Primes = #{primes}"
end
从测试结果可以看出,Ruby在执行暴力算法时大约比C#慢5倍,在执行埃拉托斯特尼筛法和桑德拉姆筛法时大约慢19倍。这一结果与之前提到的网站数据基本一致。
遗憾的是,IronRuby的测试没有完成,程序在执行过程中失败了。
虽然这次测试并不完全,但它提供了一些有价值的见解。特别是Ruby代码中的注释部分:
# 这个版本的for循环比step函数慢了50%!
(1..maxVal+1).each do |n|
# myBA1[i + n * denominator] = false
end
这表明,尽管理论上block {|n| myBA1[i + n * denominator] = false} 是作为函数调用实现的,但step函数作为库实现(很可能是编译过的),其性能优势是显著的。
IronRuby代码的失败令人失望,原本期望这种“简单”的任务不会遇到问题。