Skip to content

Instantly share code, notes, and snippets.

@jodosha
Last active August 29, 2015 13:57
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 jodosha/9387124 to your computer and use it in GitHub Desktop.
Save jodosha/9387124 to your computer and use it in GitHub Desktop.
Ruby's benchmark for regular expressions: Hash#find vs multiple case statement
#!/usr/bin/env ruby
require 'benchmark'
TIMES = (ENV['TIMES'] || 1_000_000).to_i
ENDPOINT = ->(env) { [200, {}, ['Hello, World!']] }
ROUTES = {
# '/articles(.:format)'
/\A\/articles(?-mix:[\.]*(?<format>[a-z0-9_]*))\z/ => ENDPOINT,
# '/accounts/:account_id/people/:id'
/\A\/accounts\/(?-mix:(?<account_id>[a-z0-9_]+))\/people\/(?-mix:(?<id>[a-z0-9_]+))\z/ => ENDPOINT,
# '/*var'
/\A\/(?-mix:(?<var>(.*?)))\z/ => ENDPOINT,
# '/(:var)'
/\A\/(?-mix:(?<var>[a-z0-9_]*))\z/ => ENDPOINT,
# '/:var'
/\A\/(?-mix:(?<var>[a-z0-9_]+))\z/ => ENDPOINT,
# '/:var' (constrainted)
/\A\/(?-mix:(?<var>(?-mix:[0-9]+)))\z/ => ENDPOINT,
# '/:foo'
/\A\/(?-mix:(?<foo>[a-z0-9_]+))\z/ => ENDPOINT,
# '/:foo' (constrainted)
/\A\/(?-mix:(?<foo>(?-mix:[0-9]+)))\z/ => ENDPOINT,
# '/:bar'
/\A\/(?-mix:(?<bar>[a-z0-9_]+))\z/ => ENDPOINT,
# '/:bar' (constrainted)
/\A\/(?-mix:(?<bar>(?-mix:[0-9]+)))\z/ => ENDPOINT,
# '/test/:test-*variable.:format'
/\A\/test\/(?-mix:(?<test>[a-z0-9_]+))-(?-mix:(?<variable>(.*?)))(?-mix:(?<format>[a-z0-9_]+))\z/ => ENDPOINT
}.freeze
FIRST = '/articles'.freeze
SECOND = '/accounts/1/people/23'.freeze
LAST = '/test/one-two/three/four/five.six'.freeze
class IterativeRouteSet
def initialize(routes)
@routes = routes
end
def call(path)
@routes.find do |key,_|
key.match(path)
end
end
end
class CompiledRouteSet
def initialize(routes)
@routes = routes
compile
end
private
def compile
statements = @routes.map do |route, _|
"when #{ route.inspect } then #{ route.inspect }"
end.join "\n"
instance_eval %{
def call(path)
key = case path
#{ statements }
end
@routes[key]
end
}
end
end
iterative = IterativeRouteSet.new(ROUTES)
compiled = CompiledRouteSet.new(ROUTES)
Benchmark.bm(30) do |bm|
bm.report 'Hash#find on first element' do
TIMES.times do
raise unless iterative.call(FIRST)
end
end
bm.report 'case on first element' do
TIMES.times do
raise unless compiled.call(FIRST)
end
end
bm.report 'Hash#find on second element' do
TIMES.times do
raise unless iterative.call(SECOND)
end
end
bm.report 'case on second element' do
TIMES.times do
raise unless compiled.call(SECOND)
end
end
bm.report 'Hash#find on last element' do
TIMES.times do
raise unless iterative.call(LAST)
end
end
bm.report 'case on last element' do
TIMES.times do
raise unless compiled.call(LAST)
end
end
end
__END__
user system total real
Hash#find on first element 2.110000 0.100000 2.210000 ( 2.207421)
case on first element 1.880000 0.090000 1.970000 ( 1.980411)
Hash#find on second element 2.910000 0.080000 2.990000 ( 2.987846)
case on second element 2.350000 0.100000 2.450000 ( 2.448161)
Hash#find on last element 4.080000 0.070000 4.150000 ( 4.151123)
case on last element 3.370000 0.090000 3.460000 ( 3.462316)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment