Ruby 缺乏常量表达式优化



我希望Ruby的解析器会进行这种微不足道的优化,但似乎没有(谈到YARV实现,Ruby 1.9.x,2.0.0):

require 'benchmark'
def fib1
    a, b = 0, 1
    while b < 9999**4000
        a, b = b, a+b
    end
    puts "tdone !"
end
MAX_FIB = 9999**4000
def fib2
    a, b = 0, 1
    while b < MAX_FIB
        a, b = b, a+b
    end
    puts "tdone !"
end
if __FILE__ == $0
    Benchmark.bm do |r|
        r.report('plain') { fib1 }
        r.report('predefined constant') { fib2 }
    end
end
plain    done !
 32.810000   0.220000  33.030000 ( 33.069854)
predefined constant    done !
  0.120000   0.000000   0.120000 (  0.119303)

由于这两种方法都是相同的,除了使用预定义的常量而不是第二个常量表达式,因此 Ruby 解释器似乎在每个循环中一次又一次地计算功率常数。

Ruby 根本不做这个基本优化或只是在某些特定情况下有什么材料吗?

我很

抱歉给出另一个答案,但我不想删除或编辑我以前的答案,因为它下面的有趣讨论。

正如 Jörg W Mittag 所说,Ruby 每次都必须计算给while表达式,因为根本不可能说它是恒定的。举一个极端的例子,我想出了以下代码:

class Fixnum
  alias :original_plus :+
  @@increment = 0
  def self.increase!
    @@increment = @@increment.original_plus 1
  end
  def +(other)
    (original_plus(other)).original_plus(@@increment)
  end 
end
while 1+1 < 5
  puts Fixnum.increase!
end
puts 'done' 
# 1 
# 2
# 3
# done

尝试:

def fib1
  a, b = 0, 1
  while b < 9999**4000
    a, b = b, a+b
  end
  puts "tdone !"
end

def fib2
  a, b = 0, 1
  value = 9999**4000
  while b < value
    a, b = b, a+b
  end
  puts "tdone !"
end

不会有区别的!

原因是在第一次测试中,Ruby 在你调用的方法内部计算9999**4000而第二个测试使用的是在外部(在 Benchmark 启动之前)计算的常量,因此你的第二个结果在 '9999**4000' 计算时间更短。

更新:嗯,这两个测试之间实际上有很大的差异,Ruby 可以立即计算出 9999**4000 - 请参阅下面的@JorgWMittag评论。

我删除这个答案并不是为了让评论保持活力。

相关内容

  • 没有找到相关文章

最新更新