image by Sabri Tuzcu
Benchmarking each_with_object Against inject when building Hashes from Arrays
I read Better Hash Injection using each_with_object with interest.
I’d long known that using Ruby’s Hash#merge!
rather than Hash#merge
was much faster: merge hash in place in memory, don’t copy and assign. I’d never come across each_with_index
in the wild, at least and remembered.
What a fool I’ve been.
Rather than use code like either of these…
array_of_stuff.inject({}) do |result, element|
result[element.id] = element.value
result
end
array_of_stuff.inject({}) do |result, element|
result.merge!(element.id => element.value)
end
…it’s much more idiomatic Ruby to use each_with_object
.
array_of_stuff.each_with_object({}) do |element, result|
result[element.id] = element.value
end
I was interested to see how this idiomatic Ruby performed. I put together a little script to test the various ways of generating a Hash from an decent-sized array of simple Struct
-based objects. I used the benchmark-ips gem.
require 'benchmark/ips'
User = Struct.new(:id, :stuff)
a = Array.new(1000) { |i| User.new(i, stuff: rand(1000)) }
Benchmark.ips do |x|
x.report('assign&return') do
a.inject({}) { |memo, i| memo[i.id] = i.stuff; memo }
end
x.report('merge') do
a.inject({}) { |memo, i| memo.merge(i.id => i.stuff) }
end
x.report('merge!') do
a.inject({}) { |memo, i| memo.merge!(i.id => i.stuff) }
end
x.report('map with tuples') do
a.map { |i| [i.id, i.stuff] }.to_h
end
x.report('each_with_object') do
a.each_with_object({}) { |i, memo| memo[i.id] = i.stuff }
end
end
The results were interesting.
assign&return | 3136.7 | (±8.2%) i/s |
---|---|---|
merge | 5.9 | (±0.0%) i/s |
merge! | 1168.0 | (±28.3%) i/s |
map with tuples | 2400.8 | (±23.0%) i/s |
each_with_object | 3220.8 | (±3.3%) i/s |
Turns out the most idiomatic code is also the fastest. Followed surprisingly closely by the ‘do the simplest thing’ variant, but not by a huge amount.
PS If you’re using merge
without the !
inside loops like this… just don’t.
Last updated on October 21st, 2014