Skip to content

Instantly share code, notes, and snippets.

@arika
Created November 11, 2017 05:58
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save arika/36c1e7c908571f878ace87be50bde49a to your computer and use it in GitHub Desktop.
Save arika/36c1e7c908571f878ace87be50bde49a to your computer and use it in GitHub Desktop.
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