Actions
Bug #20608
closedHash#find always allocates each iterated pair
Description
Hey there!
Recently I ran into this sharp edge in Hash#find
:
puts RUBY_DESCRIPTION
def allocated_now = GC.stat(:total_allocated_objects)
dummy_hash = 10_000.times.each_with_index.to_h
before_allocs = allocated_now
dummy_hash.any? { |k, v| }
puts "Allocated #{allocated_now - before_allocs} for #{File.read(__FILE__).lines[__LINE__-2]}"
before_allocs = allocated_now
dummy_hash.each { |k, v| }
puts "Allocated #{allocated_now - before_allocs} for #{File.read(__FILE__).lines[__LINE__-2]}"
before_allocs = allocated_now
dummy_hash.find { |k, v| }
puts "Allocated #{allocated_now - before_allocs} for #{File.read(__FILE__).lines[__LINE__-2]}"
before_allocs = allocated_now
dummy_hash.any? { |k| }
puts "Allocated #{allocated_now - before_allocs} for #{File.read(__FILE__).lines[__LINE__-2]}"
Result:
ruby 3.4.0preview1 (2024-05-16 master 9d69619623) [x86_64-linux]
Allocated 0 for dummy_hash.any? { |k, v| }
Allocated 0 for dummy_hash.each { |k, v| }
Allocated 10002 for dummy_hash.find { |k, v| }
Allocated 10000 for dummy_hash.any? { |k| }
That is, while Hash#any?
, Hash#each
, etc avoid doing any allocations during iteration, Hash#find
does not hit the rb_block_pair_yield_optimizable
=> each_pair_i_fast
fast path, and so is massively costly compared to the others.
This is very surprising, as I'd expect a find
to have a comparable cost to any?
(and I ended up redoing some code to avoid find).
Also while experimenting a bit, it was surprising to me that the allocation optimization only kicks in when |k, v|
are declared, and thus .any? { |k| }
is also more expensive.
Actions
Like0
Like0Like0Like1Like0Like0