Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Ruby method call benchmark
require 'benchmark/ips'
class Runner
def self.create(obj, method_name)
Class.new(self).class_eval do
eval <<-E
def run
@obj.#{method_name}
end
E
new(obj)
end
end
def initialize(obj)
@obj = obj
end
end
class SRunner
def self.create(obj, method_name)
new(obj).instance_exec(method_name) do |mn|
eval <<-E
def run
@obj.#{mn}
end
E
self
end
end
def initialize(obj)
@obj = obj
end
end
obj = 'str'
to_s_method = obj.method(:to_s)
to_s_proc = -> { obj.to_s }
to_s_runner = Runner.create(obj, :to_s)
to_s_srunner = SRunner.create(obj, :to_s)
to_s_proxy = Class.new.class_eval { define_method(:run, &to_s_method); self }.new
to_s_sproxy = Object.new.tap {|o| o.define_singleton_method(:run, &to_s_method) }
raise unless [obj.to_s, to_s_method.call, to_s_proc.call, to_s_runner.run, to_s_srunner.run, to_s_proxy.run, to_s_sproxy.run].uniq.size == 1
# 1st stage
Benchmark.ips do |x|
x.report 'direct' do |times|
i = 0
while i < times; i += 1
obj.to_s
end
end
x.report '__send__' do |times|
i = 0
while i < times; i += 1
obj.__send__(:to_s)
end
end
x.report 'public_send' do |times|
i = 0
while i < times; i += 1
obj.public_send(:to_s)
end
end
x.report 'Method#call' do |times|
i = 0
while i < times; i += 1
to_s_method.call
end
end
x.report 'Proc#call' do |times|
i = 0
while i < times; i += 1
to_s_proc.call
end
end
x.report 'Runner#run' do |times|
i = 0
while i < times; i += 1
to_s_runner.run
end
end
x.report 'SRunner#run' do |times|
i = 0
while i < times; i += 1
to_s_srunner.run
end
end
x.report 'define_method' do |times|
i = 0
while i < times; i += 1
to_s_proxy.run
end
end
x.report 'define_s_method' do |times|
i = 0
while i < times; i += 1
to_s_sproxy.run
end
end
x.compare!
end
# 2nd stage
methods = %i[size to_i succ chomp reverse]
[2, 20, 200, 2_000, 4_000, 6_000, 8_000, 10_000, 20_000, 40_000, 60_000, 80_000].each do |n|
Benchmark.ips do |x|
x.report "direct x#{n * methods.size}" do
methods.cycle(n) do |method|
case method
when :size then obj.size
when :to_i then obj.to_i
when :succ then obj.succ
when :chomp then obj.chomp
when :reverse then obj.reverse
end
end
end
x.report "public_send x#{n * methods.size}" do
methods.cycle(n) do |method|
obj.public_send(method)
end
end
x.report "Method#call x#{n * methods.size}" do
map = methods.each_with_object({}) {|method, h| h[method] = obj.method(method) }
methods.cycle(n) do |method|
map[method].call
end
end
x.report "Proc#call x#{n * methods.size}" do
map = {
size: -> { obj.size },
to_i: -> { obj.to_i },
succ: -> { obj.succ },
chomp: -> { obj.chomp },
reverse: -> { obj.reverse },
}
methods.cycle(n) do |method|
map[method].call
end
end
x.report "Runner#run x#{n * methods.size}" do
map = methods.each_with_object({}) {|method, h| h[method] = Runner.create(obj, method) }
methods.cycle(n) do |method|
map[method].run
end
end
x.report "SRunner#run x#{n * methods.size}" do
map = methods.each_with_object({}) {|method, h| h[method] = SRunner.create(obj, method) }
methods.cycle(n) do |method|
map[method].run
end
end
x.report "define_method x#{n * methods.size}" do
map = methods.each_with_object({}) {|method, h| h[method] = Class.new.class_eval { define_method(:run, &obj.method(method)); self }.new }
methods.cycle(n) do |method|
map[method].run
end
end
x.report "define_s_method x#{n * methods.size}" do
map = methods.each_with_object({}) {|method, h| h[method] = Object.new.tap {|o| o.define_singleton_method(:run, &obj.method(method)) } }
methods.cycle(n) do |method|
map[method].run
end
end
x.compare!
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.