&method(:method_name)在Ruby中的性能是否有害?

And*_*imm 12 ruby performance

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

[5, 7, 8, 1].each(&method(:puts))
Run Code Online (Sandbox Code Playgroud)

相当于

[5, 7, 8, 1].each{|number| puts number}
Run Code Online (Sandbox Code Playgroud)

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

Jos*_*eek 18

是的,它似乎对性能有害.

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

看来这是在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"
Run Code Online (Sandbox Code Playgroud)


Jör*_*tag 14

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

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

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

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

所以要回答你的问题:

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

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

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


Mar*_*sic 5

看起来它们在最新的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)
Run Code Online (Sandbox Code Playgroud)