Skip to content

Instantly share code, notes, and snippets.

@NickLaMuro
Created January 4, 2018 03:12
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 NickLaMuro/b9a84ae813ffb3932ac3b293eeb5b4bc to your computer and use it in GitHub Desktop.
Save NickLaMuro/b9a84ae813ffb3932ac3b293eeb5b4bc to your computer and use it in GitHub Desktop.
Comparing ruby's `define_method` to each type of `eval` for defining methods
# requires installing "benchmark-ips" (`$ gem install benchmark-ips`)
#
# Sample result
#
# $ ruby compare_define_method_with_eval.rb
# Warming up --------------------------------------
# using_define_method 17.203k i/100ms
# using_eval 29.526k i/100ms
# using_module_eval 30.355k i/100ms
# using_class_eval 30.809k i/100ms
# Calculating -------------------------------------
# using_define_method 182.710k (± 2.8%) i/s - 928.962k in 5.088524s
# using_eval 334.404k (± 3.3%) i/s - 1.683M in 5.038579s
# using_module_eval 334.300k (± 4.7%) i/s - 1.670M in 5.005709s
# using_class_eval 343.665k (± 3.8%) i/s - 1.725M in 5.027972s
#
# Comparison:
# using_class_eval: 343664.7 i/s
# using_eval: 334403.9 i/s - same-ish: difference falls within error
# using_module_eval: 334299.7 i/s - same-ish: difference falls within error
# using_define_method: 182710.2 i/s - 1.88x slower
#
class BasePlayer
METHODS_TYPES = %w[move look kick punch].freeze
METHODS_MODS = {
:up => { :x => 0, :y => 1, :z => 0 }.freeze,
:down => { :x => 0, :y => -1, :z => 0 }.freeze,
:left => { :x => -1, :y => 0, :z => 0 }.freeze,
:right => { :x => 1, :y => 0, :z => 0 }.freeze,
:straight => { :x => 0, :y => 0, :z => 1 }.freeze,
:backwards => { :x => 0, :y => 0, :z => -1 }.freeze
}.freeze
def initialize
@x = 0
@y = 0
@z = 0
end
# Used to basically confirm that all of the implementations are doing the
# same thing when executing `#perform_all`
def perform_and_compare amount=1
perform_all amount
raise "something isn't right..." unless @x == 0 && @y == 0 && @z == 0
end
# not metaprogrammed to be consistent between the two tests... if that matters...
def perform_all amount
move_up amount
move_down amount
move_left amount
move_right amount
move_straight amount
move_backwards amount
look_up amount
look_down amount
look_left amount
look_right amount
look_straight amount
look_backwards amount
kick_up amount
kick_down amount
kick_left amount
kick_right amount
kick_straight amount
kick_backwards amount
punch_up amount
punch_down amount
punch_left amount
punch_right amount
punch_straight amount
punch_backwards amount
end
end
class UsingDefineMethod < BasePlayer
METHODS_TYPES.each do |type|
METHODS_MODS.keys.each do |dir|
define_method "#{type}_#{dir}" do |amount=1|
@x += METHODS_MODS[dir][:x] * amount
@y += METHODS_MODS[dir][:y] * amount
@z += METHODS_MODS[dir][:z] * amount
end
end
end
end
class UsingEval < BasePlayer
METHODS_TYPES.each do |type|
METHODS_MODS.keys.each do |dir|
eval <<-CODE
def #{type}_#{dir} amount=1
@x += #{METHODS_MODS[dir][:x]} * amount
@y += #{METHODS_MODS[dir][:y]} * amount
@z += #{METHODS_MODS[dir][:z]} * amount
end
CODE
end
end
end
class UsingModuleEval < BasePlayer
METHODS_TYPES.each do |type|
METHODS_MODS.keys.each do |dir|
module_eval <<-CODE
def #{type}_#{dir} amount=1
@x += #{METHODS_MODS[dir][:x]} * amount
@y += #{METHODS_MODS[dir][:y]} * amount
@z += #{METHODS_MODS[dir][:z]} * amount
end
CODE
end
end
end
class UsingClassEval < BasePlayer
METHODS_TYPES.each do |type|
METHODS_MODS.keys.each do |dir|
class_eval <<-CODE
def #{type}_#{dir} amount=1
@x += #{METHODS_MODS[dir][:x]} * amount
@y += #{METHODS_MODS[dir][:y]} * amount
@z += #{METHODS_MODS[dir][:z]} * amount
end
CODE
end
end
end
require 'benchmark/ips'
using_define_method = UsingDefineMethod.new
using_eval = UsingEval.new
using_module_eval = UsingModuleEval.new
using_class_eval = UsingClassEval.new
Benchmark.ips do |bench|
bench.report("using_define_method") { using_define_method.perform_and_compare 5 }
bench.report("using_eval") { using_eval.perform_and_compare 5 }
bench.report("using_module_eval") { using_module_eval.perform_and_compare 5 }
bench.report("using_class_eval") { using_class_eval.perform_and_compare 5 }
bench.compare!
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment