Ruby缺少常量表达式优化?

Dav*_*ric 6 ruby optimization benchmarking

我希望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)
Run Code Online (Sandbox Code Playgroud)

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

是否有一些材料为什么Ruby根本不进行这种基本优化或只是在某些特定情况下呢?

Bro*_*tse 0

尝试:

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
Run Code Online (Sandbox Code Playgroud)

不会有什么区别!

原因是,在您的第一个测试中,Ruby9999**4000在您调用的方法内部进行计算,而第二个测试使用在外部计算的常量(在基准测试启动之前),因此您的第二个结果对于“9999**4000”计算时间较短。

更新:嗯,这两个测试之间实际上存在巨大差异,并且 ruby​​ 立即计算出 9999**4000 - 请参阅下面的 @JorgWMittag 评论。

我不会删除这个答案以保持评论的活力。

  • 因为弄清楚在循环的每次迭代期间是否修改“Fixnum#**”来执行其他操作相当于解决停止问题。 (4认同)
  • 超载不是问题。问题是 Ruby 允许重新定义内置运算符(或者换句话说,事实上*没有*没有内置运算符)。静态地确定“1+1”的结果是根本不可能的,因为“Fixnum#+”可能在运行时被重新定义。我认为 Python 不允许在运行时重新定义 `int.__add__` 的含义。 (2认同)