可能なすべてのメソッド呼び出しは次のとおりです。
require 'benchmark/ips'
class FooBar
def name; end
end
el = FooBar.new
Benchmark.ips do |x|
x.report('plain') { el.name }
x.report('eval') { eval('el.name') }
x.report('method call') { el.method(:name).call }
x.report('send sym') { el.send(:name) }
x.report('send str') { el.send('name') }
x.compare!
end
結果は次のとおりです。
Warming up --------------------------------------
plain 236.448k i/100ms
eval 20.743k i/100ms
method call 131.408k i/100ms
send sym 205.491k i/100ms
send str 168.137k i/100ms
Calculating -------------------------------------
plain 9.150M (± 6.5%) i/s - 45.634M in 5.009566s
eval 232.303k (± 5.4%) i/s - 1.162M in 5.015430s
method call 2.602M (± 4.5%) i/s - 13.009M in 5.010535s
send sym 6.729M (± 8.6%) i/s - 33.495M in 5.016481s
send str 4.027M (± 5.7%) i/s - 20.176M in 5.027409s
Comparison:
plain: 9149514.0 i/s
send sym: 6729490.1 i/s - 1.36x slower
send str: 4026672.4 i/s - 2.27x slower
method call: 2601777.5 i/s - 3.52x slower
eval: 232302.6 i/s - 39.39x slower
普通の呼び出しが最速で、追加の割り当て、シンボル検索はなく、メソッドの検索と評価だけであると予想されます。
シンボル経由に関してsend
は、シンボルにメモリを割り当てるのがはるかに簡単なため、文字列経由よりも高速です。定義されると、メモリに長期間保存され、再割り当てはありません。
同じ理由がmethod(:name)
(1) オブジェクトにメモリを割り当てる必要がProc
ある (2) クラスでメソッドを呼び出しているため、追加のメソッド ルックアップが必要になり、これにも時間がかかります。
eval
インタープリターを実行するため、最も重いです。