为什么这个CoffeeScript比这个Ruby脚本更快?

我正在解决一个问题,要求我find所有在4,000,000以下的所有偶数斐波那契数的总和,我注意到下面的CoffeeScript执行得比下面的Ruby快。

CoffeeScript的

sum = 0 f = (n) -> if n == 0 then return 0 else if n == 1 then return 1 else return f(n-1) + f(n-2) console.log "Starting..." for i in [1..4000000] ff = f(i) break if ff >= 4000000 sum += ff if ff % 2 == 0 console.log ".." + ff console.log "Sum:" + sum 

ruby

 sum = 0 def F(n) if n.eql? 0 return 0 elsif n.eql? 1 return 1 else return F(n-1) + F(n-2) end end puts "Starting..." 4000000.times do |num| the_num = F(num) break if the_num >= 4000000 sum += the_num if the_num % 2 == 0 puts ".." + the_num.to_s end puts "Sum:" + sum.to_s 

Ruby脚本花费了将近6-8秒才能find400万以下的所有斐波纳契数字,而NodeJS执行coffeescript的转换需要大约0.2秒的时间。 为什么是这样??

 $ ruby --version ruby 2.1.0p0 (2013-12-25 revision 44422) [x86_64-darwin12.0] $ node --version v0.10.25 

让我们用ruby-prof来描述它:

 # so_21908065.rb sum = 0 def F(n) # using == instead of eql? speeds up from ~ 7s to ~ 2s if n == 0 return 0 elsif n == 1 return 1 else return F(n-1) + F(n-2) end end puts "Starting..." 1.upto(4000000) do |num| the_num = F(num) break if the_num >= 4000000 sum += the_num if the_num % 2 == 0 puts ".." + the_num.to_s end puts "Sum:" + sum.to_s 

 $ ruby-prof so_21908065.rb %self total self wait child calls name 16.61 27.102 27.102 0.000 0.000 87403761 Fixnum#== 9.57 15.615 15.615 0.000 0.000 48315562 Fixnum#- 4.92 8.031 8.031 0.000 0.000 24157792 Fixnum#+ 0.00 0.000 0.000 0.000 0.000 70 IO#write 0.00 163.123 0.000 0.000 163.123 1 Integer#upto 0.00 163.122 0.000 0.000 163.122 48315596 *Object#F 0.00 0.000 0.000 0.000 0.000 35 IO#puts 0.00 0.000 0.000 0.000 0.000 34 Fixnum#to_s 0.00 0.001 0.000 0.000 0.000 35 Kernel#puts 0.00 0.000 0.000 0.000 0.000 34 String#+ 0.00 163.123 0.000 0.000 163.123 2 Global#[No method] 0.00 0.000 0.000 0.000 0.000 34 Fixnum#>= 0.00 0.000 0.000 0.000 0.000 2 IO#set_encoding 0.00 0.000 0.000 0.000 0.000 33 Fixnum#% 0.00 0.000 0.000 0.000 0.000 1 Module#method_added * indicates recursively called methods 

所以主要的罪魁祸首是Fixnum#==Fixnum#-Fixnum#+

正如@HolgerJust指出的那样,由于JIT优化,可能JRuby执行的“温暖”会快得多。