Created
January 4, 2018 03:12
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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