I did some benchmarks:
require 'benchmark'
words = File.open('/usr/share/dict/words', 'r') do |file|
file.each_line.take(1_000_000).map(&:chomp)
end
Benchmark.bmbm(20) do |x|
GC.start
x.report(:map) do
words.map do |word|
word.size if word.size > 5
end.compact
end
GC.start
x.report(:each_with_object) do
words.each_with_object([]) do |word, long_sizes|
long_sizes << word.size if word.size > 5
end
end
end
Output (ruby 2.3.0):
Rehearsal --------------------------------------------------------
map 0.020000 0.000000 0.020000 ( 0.016906)
each_with_object 0.020000 0.000000 0.020000 ( 0.024695)
----------------------------------------------- total: 0.040000sec
user system total real
map 0.010000 0.000000 0.010000 ( 0.015004)
each_with_object 0.020000 0.000000 0.020000 ( 0.024183)
I cannot understand it because I thought that each_with_object
should be faster: it needs only 1 loop and 1 new object to create a new array instead of 2 loops and 2 new objects in case when we combine map
and compact
.
Any ideas?
Array#<<
needs to reallocate memory if the original memory space doesn't have enough room to hold the new item. See the implementation, especially this line
VALUE target_ary = ary_ensure_room_for_push(ary, 1);
While Array#map
doesn't have to reallocate memory from time to time because it already knows the size of the result array. See the implementation, especially
collect = rb_ary_new2(RARRAY_LEN(ary));
which allocates the same size of memory as the original array.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With