Feature #16600
openOptimized opcodes for frozen arrays and hashes literals
Description
Context¶
A somewhat common pattern when trying to optimize a tight loop is to avoid allocations from some regular idioms such as a parameter default value being an empty hash or array, e.g.
def some_hotspot(foo, options = {})
# ...
end
Is often translated in:
EMPTY_HASH = {}.freeze
private_constant :EMPTY_HASH
def some_hotspot(foo, options = EMPTY_HASH)
# ...
end
But there are many variations, such as (something || []).each ..
, etc.
A search for that pattern on GitHub returns thousands of hits, and more specifically you'll often see it in popular gems such as Rails.
Proposal¶
I believe that the parser could apply optimizations when freeze
is called on a Hash or Array literal, similar to what it does for String literals (minus the deduplication).
I implemented it as a proof of concept for [].freeze
specifically, and it's not particularly complex, and I'm confident doing the same for {}.freeze
would be just as simple.
Potential followup¶
I also went a bit farther, and did another proof of concept that reuse that opcode for non empty literal arrays. Most of the logic needed to decided wether a literal array can be directly used already exist for the duparray
opcode.
So it short opt_ary_freeze / opt_hash_freeze
could substitute duparray / duphash
if .freeze
is called on the literal, and that would save an allocation. That isn't huge, but could be useful for things such as:
%i(foo bar baz).freeze.include?(value)
Or to avoid allocating hashes and arrays in pattern matching:
case value
in { foo: 42 }.freeze
# ...
end