13

This answer on another question说

array.map(&:to_s)

array.map { |n| n.to_s }

在第一个例子中,&变成:to_s了一个 Proc。第二个示例使用块。

为什么 Proc 可能比该基准测试中的块更快?这种技术是否允许解释器进行一些优化?

4

3 回答 3

5

这并不是真正的“proc vs block”。

这是一个简单的实验(随意复制和运行):

require 'benchmark'

many = 500
array = (1..10000).to_a

proc = proc { |a| a.to_s }

Benchmark.bm do |x|
  x.report('Symbol#to_proc') { many.times { array.map(&:to_s) } }
  x.report('proc') { many.times { array.map(&proc) }  }
  x.report('block') { many.times { array.map { |a| a.to_s } }  }
end

Ruby 1.9.3p194 结果:

                user     system      total        real
Symbol#to_proc  1.170000   0.000000   1.170000 (  1.169055)
proc            1.450000   0.000000   1.450000 (  1.454216)
block           1.450000   0.000000   1.450000 (  1.448094)

如您所见,block两者proc占用的 CPU 时间几乎相同。魔力就在它的内部Symbol#to_proc

于 2012-07-14T06:22:30.450 回答
4

正如其他人所说,这是专门针对Symbol#to_proc而不是一般的procs,并且几乎可以肯定它依赖于ruby实现。之前Symbol#to_proc是 ruby​​ 本身,它的纯 ruby​​ 实现肯定比等效块慢。

要获得真正的答案,您需要在执行此类基准测试时分析 ruby​​。

我对 ruby​​ 源代码的阅读是,当你调用Symbol#to_procproc 时,你得到的有点特别:proc 的主体只是一个 C api 调用(rb_funcall_passing_block),而在其他情况下,它是实际的 ruby​​ 代码,需要更长的时间才能执行。

于 2012-07-14T10:29:47.950 回答
0

只是一个猜测,但可能是因为 Proc 不需要像块一样保持调用的上下文。该块需要知道在其外部声明的变量,Proc 不需要。

于 2012-07-14T05:43:00.307 回答