From: jean.boussier@... Date: 2020-01-29T20:48:53+00:00 Subject: [ruby-core:97025] [Ruby master Feature#16600] Optimized opcodes for frozen arrays and hashes literals Issue #16600 has been updated by byroot (Jean Boussier). Someone pointed to me that https://bugs.ruby-lang.org/issues/15393 was somewhat related. ---------------------------------------- Feature #16600: Optimized opcodes for frozen arrays and hashes literals https://bugs.ruby-lang.org/issues/16600#change-84114 * 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: