是&;method(:method\u name)习惯用法对Ruby的性能有害?

是&;method(:method\u name)习惯用法对Ruby的性能有害?,ruby,performance,Ruby,Performance,我最近遇到了方法(:method\u name)语法。(这使用了Object#methodmethod-)例如 [5, 7, 8, 1].each(&method(:puts)) 相当于 [5, 7, 8, 1].each{|number| puts number} 在Ruby的各种实现中,与前者相比,后者是否存在性能损失?如果是这样的话,实现者是否正在努力提高其性能?在最新的ruby 1.9.2上,他们看起来非常相似/相同 # Using ruby 1.9.2-p290 requ

我最近遇到了
方法(:method\u name)
语法。(这使用了
Object#method
method-)例如

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

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

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

在最新的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)

下面是一篇关于它的好文章(及时):

如果仔细看一下Mario答案中的分析数字,那么由于调用
Symbol\u proc
,额外的方法调用会受到轻微的惩罚


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

是的,这似乎对性能不利

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实现的工具,因此我选择了,并且:

我很遗憾地说,你认为它可能和一个街区一样的假设是完全错误的。您没有看到
Method#to_proc
的原因有二:

  • 大多数(全部?)MRI分析器没有显示MRI在C中定义的方法,因此它们永远不会出现
  • 激活已转换为
    Proc
    的方法的机制都是C语言,因此调用端的开销也是不可见的
  • 你关于艺术差异的观点是正确的。此外,您认为虚拟机可以轻松地将其优化为块的想法是完全错误的<代码>对象#方法不是一种可以检测和优化的方法。此外,即使使用运行时优化,仍然需要类似escape analysis的内容,因为
    #method
    返回一个
    method
    对象,您必须查看该对象的内部并从中提取信息。在调用方面,在块内联的情况下,被调用的方法只能对块执行一些特殊的操作,这是一种只有Rubinius具有的优化

    因此,要回答您的问题:

  • Rubinius优化了这个代码吗?不,可以吗?是的,但这并不容易
  • 是的,它迟早会的
  • 是的,它会及时出现的
  • 注:他在最后一段提到的问题是:

  • Rubinius目前是否优化了这种无点代码
  • 如果没有,可以吗
  • 如果可以,应该吗
  • 从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"
    

    该链接描述的是
    &:foo
    (调用
    object.foo
    ),而不是
    &方法(:foo)
    (调用
    foo(object)
    ),对此表示抱歉。显然,它们不是同一个电话。不过还是一篇不错的文章!我在MRI 1.8的ruby prof评测中看到了
    Symbol#to_proc
    (实现没有名字?),所以我不明白为什么
    Method#to_proc
    不会出现。然而,这是将符号或方法转换为某种过程的开销。我怀疑上述过程的执行速度可能不同于普通块的执行速度:请参阅C中定义的关于评测方法的链接:我怀疑perftools的非Ruby版本可能能够做到这一点(即“评测Ruby VM和C扩展”).只是想知道最后两个问题的答案是否有变化?您的
    &:method(:dou\u nothing)
    do\u nothing arg
    不太清楚您的意思。可能
    do\u nothing
    方法在第一个方法中不接收参数,但在第二个方法中作为参数接收
    arg
    。这有点不同,因为它调用的是绑定到对象的方法。