Skip to content
Browse files

Updated internal Hash benchmarks to use IPS.

These benchmarks are generalized over the Hash class so they can be used
to experiment with alternate Hash implementations.
  • Loading branch information...
1 parent dcd36e3 commit 3b03c1471e599a519ca111243cb32549fcce17b1 Brian Ford committed Jul 25, 2011
View
22 benchmark/rubinius/hash/bench_create.rb
@@ -0,0 +1,22 @@
+require 'benchmark'
+require 'benchmark/ips'
+
+require File.expand_path('../setup', __FILE__)
+
+Benchmark.ips do |x|
+ x.report "new" do |t|
+ i = 0
+ while i < t
+ h = new_hash
+ i += 1
+ end
+ end
+
+ x.report "allocate" do |t|
+ i = 0
+ while i < t
+ h = hash_class.allocate
+ i += 1
+ end
+ end
+end
View
87 benchmark/rubinius/hash/bench_delete.rb
@@ -0,0 +1,87 @@
+require 'benchmark'
+require 'benchmark/ips'
+
+require File.expand_path('../setup', __FILE__)
+
+def hash_delete_missing(t, h, k)
+ i = 0
+ while i < t
+ h.delete k
+ i += 1
+ end
+end
+
+def hash_delete(t, h, k)
+ i = 0
+ while i < t
+ h.delete k
+ # add the key back so we actually measure deleting it
+ h[k] = 1
+ i += 1
+ end
+end
+
+def hash_delete_all(t, h, keys)
+ i = 0
+ while i < t
+ hd = h.dup
+ j = 0
+ n = keys.size
+ while j < n
+ hd.delete keys[j]
+ j += 1
+ end
+ i += 1
+ end
+end
+
+h_sym = {}
+h_str = {}
+sym = {}
+str = {}
+
+[10, 100, 1000, 10000].each do |n|
+ h_sym[n] = new_sym_hash n
+ h_str[n] = new_str_hash n
+ sym[n] = SYM_KEYS[0, n]
+ str[n] = STR_KEYS[0, n]
+end
+
+Benchmark.ips do |x|
+ mk_sym = :"key#{SYM_KEYS.size+1}"
+ ek_sym = SYM_KEYS[0]
+
+ x.report("delete sym missing 10") { |t| hash_delete_missing t, h_sym[10], mk_sym }
+ x.report("delete sym missing 100") { |t| hash_delete_missing t, h_sym[100], mk_sym }
+ x.report("delete sym missing 1000") { |t| hash_delete_missing t, h_sym[1000], mk_sym }
+ x.report("delete sym missing 10000") { |t| hash_delete_missing t, h_sym[10000], mk_sym }
+
+ x.report("delete sym existing 10") { |t| hash_delete t, h_sym[10], ek_sym }
+ x.report("delete sym existing 100") { |t| hash_delete t, h_sym[100], ek_sym }
+ x.report("delete sym existing 1000") { |t| hash_delete t, h_sym[1000], ek_sym }
+ x.report("delete sym existing 10000") { |t| hash_delete t, h_sym[10000], ek_sym }
+
+ x.report("delete sym all 10") { |t| hash_delete_all t, h_sym[10], sym[10] }
+ x.report("delete sym all 100") { |t| hash_delete_all t, h_sym[100], sym[100] }
+ x.report("delete sym all 1000") { |t| hash_delete_all t, h_sym[1000], sym[1000] }
+ x.report("delete sym all 10000") { |t| hash_delete_all t, h_sym[10000], sym[10000] }
+
+ mk_str = STR_KEYS.first.dup
+ mk_str << mk_str.reverse
+ ek_str = STR_KEYS[0]
+
+ x.report("delete str missing 10") { |t| hash_delete_missing t, h_str[10], mk_str }
+ x.report("delete str missing 100") { |t| hash_delete_missing t, h_str[100], mk_str }
+ x.report("delete str missing 1000") { |t| hash_delete_missing t, h_str[1000], mk_str }
+ x.report("delete str missing 10000") { |t| hash_delete_missing t, h_str[10000], mk_str }
+
+ x.report("delete str existing 10") { |t| hash_delete t, h_str[10], ek_str }
+ x.report("delete str existing 100") { |t| hash_delete t, h_str[100], ek_str }
+ x.report("delete str existing 1000") { |t| hash_delete t, h_str[1000], ek_str }
+ x.report("delete str existing 10000") { |t| hash_delete t, h_str[10000], ek_str }
+
+ x.report("delete str all 10") { |t| hash_delete_all t, h_str[10], str[10] }
+ x.report("delete str all 100") { |t| hash_delete_all t, h_str[100], str[100] }
+ x.report("delete str all 1000") { |t| hash_delete_all t, h_str[1000], str[1000] }
+ x.report("delete str all 10000") { |t| hash_delete_all t, h_str[10000], str[10000] }
+end
View
27 benchmark/rubinius/hash/bench_each.rb
@@ -0,0 +1,27 @@
+require 'benchmark'
+require 'benchmark/ips'
+
+require File.expand_path('../setup', __FILE__)
+
+Benchmark.ips do |x|
+ [10, 10000].each do |n|
+ h = new_sym_hash n
+ key = value = nil
+
+ x.report "each sym #{n}" do |t|
+ i = 0
+ while i < t
+ h.each { |k,v| key = k; value = v }
+ i += 1
+ end
+ end
+
+ x.report "each_pair sym #{n}" do |t|
+ i = 0
+ while i < t
+ h.each_pair { |k,v| key = k; value = v }
+ i += 1
+ end
+ end
+ end
+end
View
47 benchmark/rubinius/hash/bench_insert.rb
@@ -0,0 +1,47 @@
+require 'benchmark'
+require 'benchmark/ips'
+
+require File.expand_path('../setup', __FILE__)
+
+def hash_insert(t, a)
+ i = 0
+ while i < t
+ h = new_hash
+ a.each { |k| h[k] = 1 }
+ i += 1
+ end
+end
+
+def hash_insert_existing(t, h, a)
+ i = 0
+ while i < t
+ a.each { |k| h[k] = 1 }
+ i += 1
+ end
+end
+
+sym = {}
+str = {}
+
+[10, 100, 1000, 10000].each do |n|
+ sym[n] = SYM_KEYS[0, n]
+ str[n] = STR_KEYS[0, n]
+end
+
+Benchmark.ips do |x|
+ x.report("insert sym 10") { |t| hash_insert t, sym[10] }
+ x.report("insert sym 100") { |t| hash_insert t, sym[100] }
+ x.report("insert sym 1000") { |t| hash_insert t, sym[1000] }
+ x.report("insert sym 10000") { |t| hash_insert t, sym[10000] }
+
+ x.report("insert sym existing 10") { |t| hash_insert_existing t, new_hash, sym[10] }
+ x.report("insert sym existing 10000") { |t| hash_insert_existing t, new_hash, sym[10000] }
+
+ x.report("insert str 10") { |t| hash_insert t, str[10] }
+ x.report("insert str 100") { |t| hash_insert t, str[100] }
+ x.report("insert str 1000") { |t| hash_insert t, str[1000] }
+ x.report("insert str 10000") { |t| hash_insert t, str[10000] }
+
+ x.report("insert str existing 10") { |t| hash_insert_existing t, new_hash, str[10] }
+ x.report("insert str existing 10000") { |t| hash_insert_existing t, new_hash, str[10000] }
+end
View
78 benchmark/rubinius/hash/bench_lookup.rb
@@ -0,0 +1,78 @@
+require 'benchmark'
+require 'benchmark/ips'
+
+require File.expand_path('../setup', __FILE__)
+
+def hash_lookup(t, h, k)
+ i = 0
+ while i < t
+ value = h[k]
+ i += 1
+ end
+ value
+end
+
+def hash_lookup_all(t, h, keys)
+ i = 0
+ while i < t
+ j = 0
+ n = keys.size
+ while j < n
+ value = h[keys[j]]
+ j += 1
+ end
+ i += 1
+ end
+ value
+end
+
+h_sym = {}
+h_str = {}
+sym = {}
+str = {}
+
+[10, 100, 1000, 10000].each do |n|
+ h_sym[n] = new_sym_hash n
+ h_str[n] = new_str_hash n
+ sym[n] = SYM_KEYS[0, n]
+ str[n] = STR_KEYS[0, n]
+end
+
+Benchmark.ips do |x|
+ mk_sym = :"key#{SYM_KEYS.size+1}"
+ ek_sym = SYM_KEYS[0]
+
+ x.report("lookup sym missing 10") { |t| hash_lookup t, h_sym[10], mk_sym }
+ x.report("lookup sym missing 100") { |t| hash_lookup t, h_sym[100], mk_sym }
+ x.report("lookup sym missing 1000") { |t| hash_lookup t, h_sym[1000], mk_sym }
+ x.report("lookup sym missing 10000") { |t| hash_lookup t, h_sym[10000], mk_sym }
+
+ x.report("lookup sym existing 10") { |t| hash_lookup t, h_sym[10], ek_sym }
+ x.report("lookup sym existing 100") { |t| hash_lookup t, h_sym[100], ek_sym }
+ x.report("lookup sym existing 1000") { |t| hash_lookup t, h_sym[1000], ek_sym }
+ x.report("lookup sym existing 10000") { |t| hash_lookup t, h_sym[10000], ek_sym }
+
+ x.report("lookup sym all 10") { |t| hash_lookup t, h_sym[10], sym[10] }
+ x.report("lookup sym all 100") { |t| hash_lookup t, h_sym[100], sym[100] }
+ x.report("lookup sym all 1000") { |t| hash_lookup t, h_sym[1000], sym[1000] }
+ x.report("lookup sym all 10000") { |t| hash_lookup t, h_sym[10000], sym[10000] }
+
+ mk_str = STR_KEYS.first.dup
+ mk_str << mk_str.reverse
+ ek_str = STR_KEYS[0]
+
+ x.report("lookup str missing 10") { |t| hash_lookup t, h_str[10], mk_str }
+ x.report("lookup str missing 100") { |t| hash_lookup t, h_str[100], mk_str }
+ x.report("lookup str missing 1000") { |t| hash_lookup t, h_str[1000], mk_str }
+ x.report("lookup str missing 10000") { |t| hash_lookup t, h_str[10000], mk_str }
+
+ x.report("lookup str existing 10") { |t| hash_lookup t, h_str[10], ek_str }
+ x.report("lookup str existing 100") { |t| hash_lookup t, h_str[100], ek_str }
+ x.report("lookup str existing 1000") { |t| hash_lookup t, h_str[1000], ek_str }
+ x.report("lookup str existing 10000") { |t| hash_lookup t, h_str[10000], ek_str }
+
+ x.report("lookup str all 10") { |t| hash_lookup t, h_str[10], str[10] }
+ x.report("lookup str all 100") { |t| hash_lookup t, h_str[100], str[100] }
+ x.report("lookup str all 1000") { |t| hash_lookup t, h_str[1000], str[1000] }
+ x.report("lookup str all 10000") { |t| hash_lookup t, h_str[10000], str[10000] }
+end
View
43 benchmark/rubinius/hash/bench_select.rb
@@ -0,0 +1,43 @@
+require 'benchmark'
+require 'benchmark/ips'
+
+require File.expand_path('../setup', __FILE__)
+
+# Note: in 1.8, Hash#select returns an Array while in 1.9 it returns a Hash, so
+# be sure that this benchmark is comparing apples to apples.
+
+def hash_select(t, h, bool, mod)
+ i = 0
+ while i < t
+ h.select { |k,v| bool ^= mod }
+ i += 1
+ end
+end
+
+h_sym = {}
+h_str = {}
+
+[10, 10000].each do |n|
+ h_sym[n] = new_sym_hash n
+ h_str[n] = new_str_hash n
+end
+
+Benchmark.ips do |x|
+ x.report("select sym all 10") { |t| hash_select t, h_sym[10], true, false }
+ x.report("select sym all 10000") { |t| hash_select t, h_sym[10000], true, false }
+
+ x.report("select sym none 10") { |t| hash_select t, h_sym[10], false, false }
+ x.report("select sym none 10000") { |t| hash_select t, h_sym[10000], false, false }
+
+ x.report("select sym half 10") { |t| hash_select t, h_sym[10], true, true }
+ x.report("select sym half 10000") { |t| hash_select t, h_sym[10000], true, true }
+
+ x.report("select str all 10") { |t| hash_select t, h_str[10], true, false }
+ x.report("select str all 10000") { |t| hash_select t, h_str[10000], true, false }
+
+ x.report("select str none 10") { |t| hash_select t, h_str[10], false, false }
+ x.report("select str none 10000") { |t| hash_select t, h_str[10000], false, false }
+
+ x.report("select str half 10") { |t| hash_select t, h_str[10], true, true }
+ x.report("select str half 10000") { |t| hash_select t, h_str[10000], true, true }
+end
View
13 benchmark/rubinius/hash/bm_balanced_get_set.rb
@@ -1,13 +0,0 @@
-require File.dirname(__FILE__) + '/setup'
-
-sizes_and_iterations.each do |max, iterations|
- Bench.run iterations do |n|
- n.times do
- h = new_hash
- (0...max).each do
- h[get_key(max)] = 0
- a = h[get_key(max)]
- end
- end
- end
-end
View
5 benchmark/rubinius/hash/bm_create.rb
@@ -1,5 +0,0 @@
-require File.dirname(__FILE__) + '/setup'
-
-Bench.run [1_000_000] do |n|
- n.times { h = new_hash }
-end
View
17 benchmark/rubinius/hash/bm_each.rb
@@ -1,17 +0,0 @@
-require File.dirname(__FILE__) + '/setup'
-
-sizes_and_iterations.each do |max, iterations|
- h = new_hash
- (0...max).each do
- h[get_key(max)] = 0
- end
- key, value = nil, nil
-
- Bench.run iterations do |n|
- (n*8).times do
- h.each { |k, v| key = k; value = v }
- end
- end
-
- [key, value]
-end
View
17 benchmark/rubinius/hash/bm_each_pair.rb
@@ -1,17 +0,0 @@
-require File.dirname(__FILE__) + '/setup'
-
-sizes_and_iterations.each do |max, iterations|
- h = new_hash
- (0...max).each do
- h[get_key(max)] = 0
- end
- key, value = nil, nil
-
- Bench.run iterations do |n|
- (n*8).times do
- h.each_pair { |k, v| key = k; value = v }
- end
- end
-
- [key, value]
-end
View
17 benchmark/rubinius/hash/bm_each_single.rb
@@ -1,17 +0,0 @@
-require File.dirname(__FILE__) + '/setup'
-
-sizes_and_iterations.each do |max, iterations|
- h = new_hash
- (0...max).each do
- h[get_key(max)] = 0
- end
- entry = nil
-
- Bench.run iterations do |n|
- (n*8).times do
- h.each { |e| entry = e }
- end
- end
-
- entry
-end
View
16 benchmark/rubinius/hash/bm_get_existing.rb
@@ -1,16 +0,0 @@
-require File.dirname(__FILE__) + '/setup'
-
-sizes_and_iterations.each do |max, iterations|
- h = new_hash
- (0...max).each do
- h[get_key(max)] = 0
- end
-
- Bench.run iterations do |n|
- n.times do
- (0...max).each do
- a = h[get_key(max)]
- end
- end
- end
-end
View
16 benchmark/rubinius/hash/bm_get_missing.rb
@@ -1,16 +0,0 @@
-require File.dirname(__FILE__) + '/setup'
-
-sizes_and_iterations.each do |max, iterations|
- h = new_hash
- (0...max).each do
- h[get_key(max)] = 0
- end
-
- Bench.run iterations do |n|
- n.times do
- (0...max).each do
- a = h[get_missing_key(max)]
- end
- end
- end
-end
View
16 benchmark/rubinius/hash/bm_get_mixed.rb
@@ -1,16 +0,0 @@
-require File.dirname(__FILE__) + '/setup'
-
-sizes_and_iterations.each do |max, iterations|
- h = new_hash
- (0...max).each do
- h[get_key(max)] = 0
- end
-
- Bench.run iterations do |n|
- n.times do
- (0...max).each do
- a = h[get_key(max * 2)]
- end
- end
- end
-end
View
12 benchmark/rubinius/hash/bm_insert.rb
@@ -1,12 +0,0 @@
-require File.dirname(__FILE__) + '/setup'
-
-sizes_and_iterations.each do |max, iterations|
- Bench.run iterations do |n|
- n.times do
- h = new_hash
- (0...max).each do
- h[get_key] = 0
- end
- end
- end
-end
View
22 benchmark/rubinius/hash/bm_proportional_get_set.rb
@@ -1,22 +0,0 @@
-require File.dirname(__FILE__) + '/setup'
-
-# The proportion of #[]= : #[] is 1:5 based on Hash instances
-# at the 98% from running Hash spy while rdoc'ing Rdoc. Below
-# 75%, the ratio was close to 1:1, which is covered by the
-# balanced get/set benchmark. See bm_proportional_set_get.rb
-
-sizes_and_iterations.each do |max, iterations|
- Bench.run iterations do |n|
- n.times do
- h = new_hash
- (0...max).each do
- a = h[get_key(max)]
- a = h[get_key(max)]
- h[get_key(max)] = 0
- a = h[get_key(max)]
- a = h[get_key(max)]
- a = h[get_key(max)]
- end
- end
- end
-end
View
22 benchmark/rubinius/hash/bm_proportional_get_set_delete.rb
@@ -1,22 +0,0 @@
-require File.dirname(__FILE__) + '/setup'
-
-# See the other bm_proportional_xxx benchmarks.
-
-sizes_and_iterations.each do |max, iterations|
- Bench.run iterations do |n|
- n.times do
- h = new_hash
- (0...max).each do
- a = h[get_key(max)]
- a = h[get_key(max)]
- key = get_key(max)
- h[key] = 0
- a = h[get_key(max)]
- h[get_key(max)] = 0
- a = h[get_key(max)]
- a = h[get_key(max)]
- h.delete key
- end
- end
- end
-end
View
22 benchmark/rubinius/hash/bm_proportional_set_get.rb
@@ -1,22 +0,0 @@
-require File.dirname(__FILE__) + '/setup'
-
-# The proportion of #[]= : #[] is 5:3 based on Hash instances
-# above 95% from running Hash spy while running 'rails -h'.
-
-sizes_and_iterations.each do |max, iterations|
- Bench.run iterations do |n|
- n.times do
- h = new_hash
- (0...max).each do
- h[get_key(max)] = 0
- h[get_key(max)] = 0
- a = h[get_key(max)]
- h[get_key(max)] = 0
- a = h[get_key(max)]
- h[get_key(max)] = 0
- a = h[get_key(max)]
- h[get_key(max)] = 0
- end
- end
- end
-end
View
22 benchmark/rubinius/hash/bm_select.rb
@@ -1,22 +0,0 @@
-require File.dirname(__FILE__) + '/setup'
-
-# We use a single fixed key since the iteration
-# will need to visit every entry anyway and this
-# removes possible variations due to the size of
-# the returned array.
-sizes_and_iterations.each do |max, iterations|
- h = new_hash
- (0...max).each do
- h[get_key(max)] = 0
- end
- selected = nil
- key = get_key(max)
-
- Bench.run iterations do |n|
- (n*8).times do
- selected = h.select { |k, v| k == key }
- end
- end
-
- selected
-end
View
65 benchmark/rubinius/hash/setup.rb
@@ -1,55 +1,32 @@
-unless Kernel.method_defined? :const_lookup
- module Kernel
- # Implements rb_path2name. Based on code from wycats
- def const_lookup(name)
- names = name.split '::'
- names.shift if names.first.empty?
- names.inject(Object) do |m, n|
- m.const_defined?(n) ? m.const_get(n) : m.const_missing(n)
- end
- end
+# Returns a new instance of the Hash class being benchmarked. Define before
+# loading if using a class besides Hash.
+unless Object.public_method_defined? :new_hash
+ def hash_class
+ Hash
end
-end
-
-MAX_KEYS = (ENV['MAX_KEY'] || 20_000).to_i
-HALF_MAX_KEYS = MAX_KEYS / 2
-
-HASH_CLASS = const_lookup(ENV['HASH_CLASS'] || "Hash")
-
-HASH_KEYS = Array.new MAX_KEYS
-(0..MAX_KEYS).each do |i|
- HASH_KEYS[i] = "key#{i}".to_sym
+ def new_hash
+ Hash.new
+ end
end
+# Repeatable sequence of random numbers
srand(1)
+STRING = "rdnqsp uxq\nhnokjirs\nb c6rlh|4c@jcb av8\nPvunszwijhy lz kdgy7hlKlR nzqxg\ndqldeg nm-yg vmnb mk gdrn x"
+N = STRING.size
-# Returns a random key in the range 0...max. This ensures that
-# if all 0...max keys have been set, requesting a key will be
-# guaranteed to match an entry.
-def get_key(max = HALF_MAX_KEYS)
- HASH_KEYS[rand(max) % MAX_KEYS]
-end
-
-# Returns a random key that would not be returned by #get_key
-# for use in benchmarking missing key lookup.
-def get_missing_key(max = HALF_MAX_KEYS)
- HASH_KEYS[HALF_MAX_KEYS + (rand(max) % HALF_MAX_KEYS)]
-end
+SYM_KEYS = Array.new(10000) { |i| :"key#{i}" }
+STR_KEYS = Array.new(10000) { |i| s = STRING[rand(N/4)..rand(N/2)]; 4.times { s << rand(128) }; s }
-# Returns of the maximum sizes of Hash instances to construct
-# and the number of iterations to run the benchmark.
-def sizes_and_iterations
- [ [5, [20_000]],
- [10, [10_000]],
- [50, [1500]],
- [100, [1000]],
- [1000, [100]],
- [10_000, [10]]]
+def new_sym_hash(n)
+ h = new_hash
+ n.times { |i| h[SYM_KEYS[i]] = i }
+ h
end
-# Returns a new instance of the Hash class being benchmarked.
-def new_hash
- HASH_CLASS.new
+def new_str_hash(n)
+ h = new_hash
+ n.times { |i| h[STR_KEYS[i]] = i }
+ h
end

0 comments on commit 3b03c14

Please sign in to comment.
Something went wrong with that request. Please try again.