我最近遇到了&method(:method_name)
语法。 (这使用Object#method
方法 - RDoc link)例如,
[5, 7, 8, 1].each(&method(:puts))
相当于
[5, 7, 8, 1].each{|number| puts number}
与Ruby的各种实现中的前者相比,后者是否存在性能损失?如果是这样,实施者是否正致力于提高其绩效?
答案 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 mailinglist和here's what Evan Phoenix had to say:
您的假设,这可能是作为一个块是一样的,我很伤心地说,大错特错了。有理由你没有看到
Method#to_proc
,这样的分析是2倍:
- 大多数(全部?)MRI剖面仪没有显示MRI在C中定义的方法,因此它们永远不会出现。
- 激活已转换为
醇>Proc
的方法的机制全部都在C中,因此开销在调用端也是不可见的。关于艺术差异的观点是正确的。另外,您认为VM可以轻松地将其优化为块是非常错误的。
Object#method
不会被检测到并优化掉。此外,即使使用运行时优化,仍然需要类似转义分析之类的内容,因为#method
会返回您必须在其中查看并从中提取信息的Method
对象。在调用方面,在块内联的情况下,调用的方法只能对块执行特殊操作,这是仅Rubinius具有的优化。所以要回答你的问题:
- Rubinius是否优化了此代码?不,不是吗?是的,但这并不容易。
- 及时可以,是的。
- 及时应该,是的。
醇>
注意:他在最后一段中提到的问题是:
- Rubinius目前是否优化了这种无点代码?
- 如果没有,可以吗?
- 如果可以,应该吗?
醇>
答案 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"