&方法(:method_name)是否会影响Ruby中的性能?

时间:2011-08-07 23:53:42

标签: ruby performance

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

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

相当于

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

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

5 个答案:

答案 0 :(得分:16)

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

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"

答案 1 :(得分:12)

由于Rubinius是最先进且最积极地优化Ruby实现的,asked this question上的Rubinius mailinglisthere's what Evan Phoenix had to say

  

您的假设,这可能是作为一个块是一样的,我很伤心地说,大错特错了。有理由你没有看到Method#to_proc,这样的分析是2倍:

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

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

         

    所以要回答你的问题:

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

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

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

答案 2 :(得分: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)

答案 3 :(得分:0)

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

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

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

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

答案 4 :(得分:0)

ruby​​ 1.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"