(reposting from my Sun blog)
I’ve wondered before what the fastest way is to pass a hash of options to a method in Ruby.. so today I benchmarked a few methods I’ve used in the past.
I’ve seen three main ways of passing an options hash to a method and extracting the options or use default values if they weren’t passed in:
- Merge the options with a hash of defaults, then assign values to local variables (or just use the options hash directly within the method)
- Use something like var = options[:var] || ‘default’
- Use the delete method on the hash, e.g., var = options.delete(:var) || ‘default’
From what I can tell, using delete is the fastest:
require 'benchmark' def ops_delete(ops={}) a = ops.delete(:a) || 11 b = ops.delete(:b) || 22 c = ops.delete(:c) || 33 d = ops.delete(:d) || 44 end def ops_merge(ops={}) ops = {:a => 11, :b => 22, :c => 33, :d => 44}.merge(ops) a = ops[:a] b = ops[:b] c = ops[:c] d = ops[:d] end def ops_or(ops={}) a = ops[:a] || 11 b = ops[:b] || 22 c = ops[:c] || 33 d = ops[:d] || 44 end n = 100000 puts "With no option values passed" Benchmark.bmbm(7) do |x| x.report("delete:") { n.times do ops_delete; end } x.report("merge:") { n.times do ops_merge; end } x.report("ops_or:") { n.times do ops_or; end } end puts puts "With some option values passed" Benchmark.bmbm(7) do |x| x.report("delete:") { n.times do ops_delete(:a => 5, :c => 2); end } x.report("merge:") { n.times do ops_merge(:a => 5, :c => 2); end } x.report("ops_or:") { n.times do ops_or(:a => 5, :c => 2); end } end puts puts "With all option values passed" Benchmark.bmbm(7) do |x| x.report("delete:") { n.times do ops_delete(:a => 5, :b => 1, :c => 2, :d => 4); end } x.report("merge:") { n.times do ops_merge(:a => 5, :b => 1, :c => 2, :d => 4); end } x.report("ops_or:") { n.times do ops_or(:a => 5, :b => 1, :c => 2, :d => 4); end } end
Results (rehearsals omitted):
With no option values passed
user system total real
delete: 0.210000 0.000000 0.210000 ( 0.209877)
merge: 0.720000 0.000000 0.720000 ( 0.750394)
ops_or: 0.260000 0.000000 0.260000 ( 0.258416)
With some option values passed
user system total real
delete: 0.250000 0.000000 0.250000 ( 0.255536)
merge: 0.830000 0.000000 0.830000 ( 0.831358)
ops_or: 0.310000 0.000000 0.310000 ( 0.314737)
With all option values passed
user system total real
delete: 0.310000 0.000000 0.310000 ( 0.306889)
merge: 0.900000 0.000000 0.900000 ( 0.905261)
ops_or: 0.340000 0.000000 0.340000 ( 0.348061)This was done in Ruby 1.8.4 on a 2GHz MacBook Intel Core Duo with 1GB 667 MHz DDR2 SDRAM.
I know this is old and you’ve clearly defined you’re using Ruby 1.8.4, but on Ruby 1.9.1 ops_or shows up as consistently faster than delete. The merge technique is still some three times slower though, as in your results.
Thanks Peter. That’s good to hear, since using delete is not as intuitive.