別の質問に対するこの回答は、
array.map(&:to_s)
よりも速い
array.map { |n| n.to_s }
最初の例では、Procに&
変わります。:to_s
2番目の例では、ブロックを使用しています。
なぜProcはそのベンチマークのブロックよりも速いのでしょうか?この手法で通訳者が実行できる最適化はありますか?
別の質問に対するこの回答は、
array.map(&:to_s)
よりも速い
array.map { |n| n.to_s }
最初の例では、Procに&
変わります。:to_s
2番目の例では、ブロックを使用しています。
なぜProcはそのベンチマークのブロックよりも速いのでしょうか?この手法で通訳者が実行できる最適化はありますか?
それは実際には「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
それ自体の中にあります。
Symbol#to_proc
他の人が言っているように、これは一般的にprocsではなく具体的にであり、ほぼ確実にルビーの実装に依存しています。以前Symbol#to_proc
はルビー自体でしたが、それの純粋なルビーの実装は、同等のブロックよりも明らかに遅くなりました。
本当の答えを得るには、そのようなベンチマークを実行している間、ルビーのプロファイルを作成する必要があります。
私がrubyのソースコードを読んだところSymbol#to_proc
、procを呼び出すと、少し特殊なものになります。procの本体は単なるC api呼び出し(rb_funcall_passing_block
)ですが、それ以外の場合は、実際のrubyコードに少し時間がかかります。実行する。
単なる推測ですが、おそらくそれは、Proc がブロックと同じように呼び出しのコンテキストを保持する必要がないためです。ブロックは、その外部で宣言された変数を知る必要がありますが、Proc は知りません。