Issue #16600 has been updated by byroot (Jean Boussier).


> This is not a literal hash, so unrelated at all.

My bad, I totally misread the disassembly output.

----------------------------------------
Feature #16600: Optimized opcodes for frozen arrays and hashes literals
https://bugs.ruby-lang.org/issues/16600#change-84117

* Author: byroot (Jean Boussier)
* Status: Open
* Priority: Normal
* Assignee: 
* Target version: 
----------------------------------------
### 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.

```ruby
def some_hotspot(foo, options = {})
  # ...
end
```

Is often translated in:

```ruby
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](https://github.com/search?q=%22EMPTY_HASH+%3D+%7B%7D.freeze%22+language%3ARuby&type=Code), and more specifically you'll often [see it in popular gems such as Rails](https://github.com/rails/rails/blob/57c9932ea3ca0d6ee297d9e9169e9635f8aa3369/actionpack/lib/action_controller/metal/strong_parameters.rb#L1008-L1009).

### 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](https://github.com/Shopify/ruby/commit/011a9b9e17d9968918d7db324dfdcf49c1b21238), 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](https://github.com/Shopify/ruby/commit/bec3189e0f3d1a60d58638ce6c0cda1f3fdf5a7b). 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:

```ruby
%i(foo bar baz).freeze.include?(value)
```

Or to avoid allocating hashes and arrays in pattern matching:

```ruby
case value
in { foo: 42 }.freeze
  # ...
end
```




-- 
https://bugs.ruby-lang.org/

Unsubscribe: <mailto:ruby-core-request / ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-core>