From: "jeremyevans0 (Jeremy Evans) via ruby-core" Date: 2025-04-20T10:46:24+00:00 Subject: [ruby-core:121693] [Ruby Feature#21274] Show performance warnings for easily avoidable unnecessary implicit splat allocations Issue #21274 has been updated by jeremyevans0 (Jeremy Evans). I used this feature to find the following unnecessary allocations in the standard library and a couple popular gems, which I fixed or submitted pull requests to fix: * https://github.com/ruby/optparse/pull/97 * https://github.com/ruby/pp/pull/41 * https://github.com/rubygems/rubygems/pull/8640 * https://github.com/rails/rails/pull/54949 * https://github.com/jeremyevans/sequel/commit/3081e843e00088a24834634d195f3fccb71b3f2e ---------------------------------------- Feature #21274: Show performance warnings for easily avoidable unnecessary implicit splat allocations https://bugs.ruby-lang.org/issues/21274#change-112744 * Author: jeremyevans0 (Jeremy Evans) * Status: Open ---------------------------------------- In Ruby 3.4, I made many changes to reduce implicit allocations (mostly in method calling). There are still a few cases where Ruby must allocate an array for a positional splat, or a hash for a keyword splat. Some of these allocations are unavoidable, but in other cases, while Ruby cannot avoid the allocation, it is easy for a user to make a small change to their code to avoid the allocation. One example of this is when Ruby allocates to avoid an evaluation order issue. For example: ```ruby def kw = nil ary = [] m(*ary, kw:) ``` Ruby allocates an array for `*ary`, even though it does not need to, because `kw` is a method call, and the method call could potentially modify `ary` (it doesn't in this example, but Ruby's compiler cannot assume that, as the method may be overridden later). It is simple to avoid the allocation by using a local variable: ```ruby def kw = nil ary = [] kw = self.kw m(*ary, kw:) ``` To make it easier for users to find and avoid these unnecessary implicit allocations, I would like to add a performance warning in cases where Ruby allocates solely to avoid an evaluation order issue. I've submitted a pull request to implement this: https://github.com/ruby/ruby/pull/13135 The current warning messages in the pull request are quite verbose: ``` $ ruby -W:performance -e 'def kw; {} end; a = []; p(*a, **kw)' -e: warning: This method call implicitly allocates a potentially unnecessary array for the positional splat, because a keyword, keyword splat, or block pass expression could cause an evaluation order issue if an array is not allocated for the positional splat. You can avoid this allocation by assigning the related keyword, keyword splat, or block pass expression to a local variable and using that local variable. $ ruby -W:performance -e 'def b; ->{} end; h = {}; p(**h, &b)' -e: warning: This method call implicitly allocates a potentially unnecessary hash for the keyword splat, because the block pass expression could cause an evaluation order issue if a hash is not allocated for the keyword splat. You can avoid this allocation by assigning the block pass expression to a local variable, and using that local variable. ``` It may be desirable to shorten these messages, so I would appreciate suggestions. -- https://bugs.ruby-lang.org/ ______________________________________________ ruby-core mailing list -- ruby-core@ml.ruby-lang.org To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org ruby-core info -- https://ml.ruby-lang.org/mailman3/lists/ruby-core.ml.ruby-lang.org/