|
require 'benchmark'
|
|
|
|
# A basic version of a dsl that registers key-value pairs onto a Class. When
|
|
# looking up a key-value pair, Classes that use the dsl will check the
|
|
# registry of each ancestor in order, as if looking up a method.
|
|
#
|
|
# Call cache_ancestors! to cache the ancestry and speedup lookup.
|
|
module Dsl
|
|
def self.extended(base)
|
|
base.registry ||= {}
|
|
base.cache = nil
|
|
end
|
|
|
|
def inherited(base)
|
|
base.registry ||= {}
|
|
base.cache = nil
|
|
end
|
|
|
|
attr_accessor :registry
|
|
attr_accessor :cache
|
|
|
|
def set(key, value)
|
|
registry[key] = value
|
|
end
|
|
|
|
def value(key)
|
|
each_ancestor do |ancestor|
|
|
if ancestor.registry.has_key?(key)
|
|
return ancestor.registry[key]
|
|
end
|
|
end
|
|
end
|
|
|
|
def cache_ancestors!
|
|
@cache = ancestors
|
|
end
|
|
|
|
def each_ancestor
|
|
(cache || ancestors).each {|ancestor| yield(ancestor)}
|
|
end
|
|
end
|
|
|
|
# Now the benchmarks, first without caching (ie regenerate ancestors each
|
|
# time), then with caching to model performance with an each_ancestor method.
|
|
puts "Benchmark without cache"
|
|
|
|
class A
|
|
extend Dsl
|
|
set(:one, 1)
|
|
end
|
|
|
|
class B < A
|
|
set(:two, 2)
|
|
end
|
|
|
|
class C < B
|
|
set(:three, 3)
|
|
end
|
|
|
|
Benchmark.bm(20) do |x|
|
|
n = 100000
|
|
|
|
x.report "A.value(:one)" do
|
|
n.times { A.value(:one) }
|
|
end
|
|
|
|
x.report "B.value(:two)" do
|
|
n.times { B.value(:two) }
|
|
end
|
|
|
|
x.report "C.value(:three)" do
|
|
n.times { C.value(:three) }
|
|
end
|
|
end
|
|
|
|
puts
|
|
puts "Benchmark with cache"
|
|
A.cache_ancestors!
|
|
B.cache_ancestors!
|
|
C.cache_ancestors!
|
|
|
|
Benchmark.bm(20) do |x|
|
|
n = 100000
|
|
|
|
x.report "A.value(:one)" do
|
|
n.times { A.value(:one) }
|
|
end
|
|
|
|
x.report "B.value(:two)" do
|
|
n.times { B.value(:two) }
|
|
end
|
|
|
|
x.report "C.value(:three)" do
|
|
n.times { C.value(:three) }
|
|
end
|
|
|
|
x.report "Array.new" do
|
|
n.times { Array.new }
|
|
end
|
|
end
|
|
|