&method(:method_name)习惯用法对Ruby的性能有害吗?

我最近遇到了&method(:method_name)语法。 (这使用Object#method方法 – RDoc链接 )例如,

 [5, 7, 8, 1].each(&method(:puts)) 

相当于

 [5, 7, 8, 1].each{|number| puts number} 

在Ruby的各种实现中,与前者相比,后者是否存在性能损失? 如果是这样,实施者是否正在努力改善其性能?

是的,它似乎对性能不利。

 def time start = Time.now yield "%.6f" % (Time.now - start) end def do_nothing(arg) end RUBY_VERSION # => "1.9.2" # small ary = *1..10 time { ary.each(&method(:do_nothing)) } # => "0.000019" time { ary.each { |arg| do_nothing arg } } # => "0.000003" # large ary = *1..10_000 time { ary.each(&method(:do_nothing)) } # => "0.002787" time { ary.each { |arg| do_nothing arg } } # => "0.001810" # huge ary = *1..10_000_000 time { ary.each(&method(:do_nothing)) } # => "37.901283" time { ary.each { |arg| do_nothing arg } } # => "1.754063" 

看来这是在JRuby中解决的:

 $ rvm use jruby Using /Users/joshuajcheek/.rvm/gems/jruby-1.6.3 $ xmpfilter f.rb def time start = Time.now yield "%.6f" % (Time.now - start) end def do_nothing(arg) end RUBY_VERSION # => "1.8.7" # small ary = *1..10 time { ary.each(&method(:do_nothing)) } # => "0.009000" time { ary.each { |arg| do_nothing arg } } # => "0.001000" # large ary = *1..10_000 time { ary.each(&method(:do_nothing)) } # => "0.043000" time { ary.each { |arg| do_nothing arg } } # => "0.055000" # huge ary = *1..10_000_000 time { ary.each(&method(:do_nothing)) } # => "0.427000" time { ary.each { |arg| do_nothing arg } } # => "0.634000" 

由于Rubinius是最先进且最积极地优化Ruby实现的,我在Rubinius邮件列表中 提出了这个问题 , 这就是Evan Phoenix所说的 :

你可以说它与块相同的假设是,我很遗憾地说,这是错误的。 有理由你没有看到Method#to_proc并且在分析中是这样的2倍:

  1. 大多数(全部?)MRI剖面仪都没有显示MRI在C中定义的方法,因此它们永远不会出现。
  2. 激活已转换为Proc的方法的机制全部在C中,因此开销在调用端也是不可见的。

你对艺术差异的看法是正确的。 此外,您认为VM可以轻松地将其优化为块是非常错误的。 Object#method不是可以被检测和优化的东西。 此外,即使使用运行时优化,仍然需要类似转义分析之类的东西,因为#method返回一个您必须在里面看到并从中提取信息的Method对象。 在调用方面,在块内联的情况下,调用的方法只能对块执行特殊操作,这是只有Rubinius的优化。

所以要回答你的问题:

  1. Rubinius是否优化了此代码? 不,不是吗? 是的,但这并不容易。
  2. 及时它可以,是的。
  3. 它应该及时,是的。

注意:他在最后一段中提到的问题是:

  1. Rubinius目前是否优化了这种无点代码?
  2. 如果没有,可以吗?
  3. 如果可以,应该吗?

看起来它们在最新的ruby 1.9.2上非常相似/相同

 # Using ruby 1.9.2-p290 require 'benchmark' Benchmark.measure do 1000.times { [5, 7, 8, 1].each(&method(:puts)) } end # => 0.020000 0.020000 0.040000 ( 0.066408) # => 0.020000 0.010000 0.030000 ( 0.075474) # => 0.020000 0.020000 0.040000 ( 0.048462) Benchmark.measure do 1000.times { [5, 7, 8, 1].each{|number| puts number} } end # => 0.020000 0.020000 0.040000 ( 0.071505) # => 0.020000 0.020000 0.040000 ( 0.062571) # => 0.010000 0.020000 0.030000 ( 0.040944) 

这是一篇很好的文章(及时):

http://www.potstuck.com/2011/08/06/ruby-symbols-instead-of-blocks/

如果仔细观察Mario的答案中的分析数字,那么由于调用Symbol#to_proc而导致额外的方法调用会受到轻微的惩罚。

只是一个猜测,但我会说不,他们可能不会很快加快速度。

至于ruby1.9.3-p327

 def time &block start = Time.now yield puts "%s : %.6f" % block.to_s, (Time.now - start)) end RUBY_VERSION # => "1.9.3-p327" # small ary = *1..10 time { ary.each(&:to_i) } # => "0.000010" time { ary.each { |arg| arg.to_i } } # => "0.000002" # large ary = *1..10_000 time { ary.each(&:to_i) } # => "0.000494" time { ary.each { |arg| arg.to_i } } # => "0.000798" # huge ary = *1..10_000_000 time { ary.each(&:to_i) } # => "0.504329" time { ary.each { |arg| arg.to_i } } # => "0.883390"