<feed xmlns='http://www.w3.org/2005/Atom'>
<title>ruby.git/test/ruby/test_allocation.rb, branch v4.0.4</title>
<subtitle>The Ruby Programming Language</subtitle>
<link rel='alternate' type='text/html' href='https://git.ruby-lang.org/ruby.git/'/>
<entry>
<title>Fix allocationless anonymous splat keyword argument check</title>
<updated>2025-12-09T20:44:50+00:00</updated>
<author>
<name>Jeremy Evans</name>
<email>code@jeremyevans.net</email>
</author>
<published>2025-12-03T01:34:36+00:00</published>
<link rel='alternate' type='text/html' href='https://git.ruby-lang.org/ruby.git/commit/?id=6409715212d22699bd2751a363b050a5d8b94b83'/>
<id>6409715212d22699bd2751a363b050a5d8b94b83</id>
<content type='text'>
Previously, if an argument splat and keywords are provided by
the caller, it did not check whether the method/proc accepted
keywords before avoiding the allocation. This is incorrect,
because if the method/proc does not accept keywords, the
keywords passed by the caller are added as a positional
argument, so there must be an allocation to avoid mutating
the positional splat argument.

Add a check that if the caller passes keywords, the
method/proc must accept keywords in order to optimize.
If the caller passes a keyword splat, either the
method/proc must accept keywords, or the keyword splat must
be empty in order to optimize.

If keywords are explicitly disallowed via `**nil`, the
optimization should be skipped, because the array is mutated
before the ArgumentError exception is raised.

In addition to a test for the correct behavior, add an
allocation test for a method that accepts an anonymous splat
without keywords.

Fixes [Bug #21757]
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
Previously, if an argument splat and keywords are provided by
the caller, it did not check whether the method/proc accepted
keywords before avoiding the allocation. This is incorrect,
because if the method/proc does not accept keywords, the
keywords passed by the caller are added as a positional
argument, so there must be an allocation to avoid mutating
the positional splat argument.

Add a check that if the caller passes keywords, the
method/proc must accept keywords in order to optimize.
If the caller passes a keyword splat, either the
method/proc must accept keywords, or the keyword splat must
be empty in order to optimize.

If keywords are explicitly disallowed via `**nil`, the
optimization should be skipped, because the array is mutated
before the ArgumentError exception is raised.

In addition to a test for the correct behavior, add an
allocation test for a method that accepts an anonymous splat
without keywords.

Fixes [Bug #21757]
</pre>
</div>
</content>
</entry>
<entry>
<title>Simplified assertion</title>
<updated>2025-08-26T03:16:18+00:00</updated>
<author>
<name>Hiroshi SHIBATA</name>
<email>hsbt@ruby-lang.org</email>
</author>
<published>2025-08-26T02:16:04+00:00</published>
<link rel='alternate' type='text/html' href='https://git.ruby-lang.org/ruby.git/commit/?id=b2e940a3740a558d6b5f8f7fbc5677ffa190c610'/>
<id>b2e940a3740a558d6b5f8f7fbc5677ffa190c610</id>
<content type='text'>
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
</pre>
</div>
</content>
</entry>
<entry>
<title>Revert "Omit extra allocation of Array object in Windows platform temporary"</title>
<updated>2025-08-26T02:12:38+00:00</updated>
<author>
<name>Benoit Daloze</name>
<email>eregontp@gmail.com</email>
</author>
<published>2025-08-25T20:04:41+00:00</published>
<link rel='alternate' type='text/html' href='https://git.ruby-lang.org/ruby.git/commit/?id=e31750a6690238381c4a5bf97952d57275918098'/>
<id>e31750a6690238381c4a5bf97952d57275918098</id>
<content type='text'>
This reverts commit 6ab2cd0f69ff1591db3a0011b73d3b26a9a69412.
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
This reverts commit 6ab2cd0f69ff1591db3a0011b73d3b26a9a69412.
</pre>
</div>
</content>
</entry>
<entry>
<title>Omit extra allocation of Array object in Windows platform temporary</title>
<updated>2025-08-25T08:40:11+00:00</updated>
<author>
<name>Hiroshi SHIBATA</name>
<email>hsbt@ruby-lang.org</email>
</author>
<published>2025-08-22T10:10:54+00:00</published>
<link rel='alternate' type='text/html' href='https://git.ruby-lang.org/ruby.git/commit/?id=6ab2cd0f69ff1591db3a0011b73d3b26a9a69412'/>
<id>6ab2cd0f69ff1591db3a0011b73d3b26a9a69412</id>
<content type='text'>
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
</pre>
</div>
</content>
</entry>
<entry>
<title>Avoid allocation for positional splat for literal array keyword argument</title>
<updated>2025-06-21T21:43:13+00:00</updated>
<author>
<name>Jeremy Evans</name>
<email>code@jeremyevans.net</email>
</author>
<published>2025-06-20T00:57:20+00:00</published>
<link rel='alternate' type='text/html' href='https://git.ruby-lang.org/ruby.git/commit/?id=353fa6f0bab278d9bd5bd8bd073b31f586116600'/>
<id>353fa6f0bab278d9bd5bd8bd073b31f586116600</id>
<content type='text'>
If all nodes in the array are safe, then it is safe to avoid
allocation for the positional splat:

```ruby
m(*a, kw: [:a])   # Safe
m(*a, kw: [meth]) # Unsafe
```

This avoids an unnecessary allocation in a Rails method call.
Details: https://github.com/rails/rails/pull/54949/files#r2052645431
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
If all nodes in the array are safe, then it is safe to avoid
allocation for the positional splat:

```ruby
m(*a, kw: [:a])   # Safe
m(*a, kw: [meth]) # Unsafe
```

This avoids an unnecessary allocation in a Rails method call.
Details: https://github.com/rails/rails/pull/54949/files#r2052645431
</pre>
</div>
</content>
</entry>
<entry>
<title>Fix handling of PM_CONSTANT_PATH_NODE node in keyword arguments with ARGS_SPLAT</title>
<updated>2025-06-20T23:46:24+00:00</updated>
<author>
<name>Jeremy Evans</name>
<email>code@jeremyevans.net</email>
</author>
<published>2025-06-20T00:27:11+00:00</published>
<link rel='alternate' type='text/html' href='https://git.ruby-lang.org/ruby.git/commit/?id=1d94a9e1a4351e01f851dad250ba97dad859ee70'/>
<id>1d94a9e1a4351e01f851dad250ba97dad859ee70</id>
<content type='text'>
This was handled correctly in parse.y (NODE_COLON2), but not in
prism. This wasn't caught earlier, because I only added tests for
the optimized case and not the unoptimized case. Add tests for
the unoptimized case.

In code terms:

```ruby
m(*a, kw: lvar::X)     # Does not require allocation for *a
m(*a, kw: method()::X) # Requires allocation for *a
```

This commit fixes the second case when prism is used.
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
This was handled correctly in parse.y (NODE_COLON2), but not in
prism. This wasn't caught earlier, because I only added tests for
the optimized case and not the unoptimized case. Add tests for
the unoptimized case.

In code terms:

```ruby
m(*a, kw: lvar::X)     # Does not require allocation for *a
m(*a, kw: method()::X) # Requires allocation for *a
```

This commit fixes the second case when prism is used.
</pre>
</div>
</content>
</entry>
<entry>
<title>Skip mmtk/i686 tests for a while</title>
<updated>2025-05-11T14:32:50+00:00</updated>
<author>
<name>Satoshi Tagomori</name>
<email>tagomoris@gmail.com</email>
</author>
<published>2025-05-11T00:30:16+00:00</published>
<link rel='alternate' type='text/html' href='https://git.ruby-lang.org/ruby.git/commit/?id=b132322e94460a50fd7f0d844fe73e2272207f1d'/>
<id>b132322e94460a50fd7f0d844fe73e2272207f1d</id>
<content type='text'>
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
</pre>
</div>
</content>
</entry>
<entry>
<title>Avoid allocation for anonymous positional splat with no arguments</title>
<updated>2025-03-27T20:59:03+00:00</updated>
<author>
<name>Jeremy Evans</name>
<email>code@jeremyevans.net</email>
</author>
<published>2025-01-18T16:54:28+00:00</published>
<link rel='alternate' type='text/html' href='https://git.ruby-lang.org/ruby.git/commit/?id=43683e1e9d8f7f6ed3fb4a48598190a0993f8f66'/>
<id>43683e1e9d8f7f6ed3fb4a48598190a0993f8f66</id>
<content type='text'>
Anonymous positional splats cannot be directly accessed, they can
only be passed as splats to other methods.  So if an anonymous
positional splat would be empty, you can use a shared frozen
empty array to save an allocation.

```ruby
def a(*) end
a()
```

This is similar to how anonymous empty keyword splats are optimized,
except those use `nil` instead of a shared empty frozen hash.

This updates the allocation tests to check that the array allocations
are avoided where possible.

It also makes a small change to test_iseq.rb to ensure an unfrozen
hash is passed as the value of an anonymous splat parameter.
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
Anonymous positional splats cannot be directly accessed, they can
only be passed as splats to other methods.  So if an anonymous
positional splat would be empty, you can use a shared frozen
empty array to save an allocation.

```ruby
def a(*) end
a()
```

This is similar to how anonymous empty keyword splats are optimized,
except those use `nil` instead of a shared empty frozen hash.

This updates the allocation tests to check that the array allocations
are avoided where possible.

It also makes a small change to test_iseq.rb to ensure an unfrozen
hash is passed as the value of an anonymous splat parameter.
</pre>
</div>
</content>
</entry>
<entry>
<title>Avoid array allocation for *nil, by not calling nil.to_a</title>
<updated>2025-03-27T18:17:40+00:00</updated>
<author>
<name>Jeremy Evans</name>
<email>code@jeremyevans.net</email>
</author>
<published>2025-01-18T09:40:05+00:00</published>
<link rel='alternate' type='text/html' href='https://git.ruby-lang.org/ruby.git/commit/?id=67d1dd2ebd622c27d2ae0681c544d9f5d2f5349b'/>
<id>67d1dd2ebd622c27d2ae0681c544d9f5d2f5349b</id>
<content type='text'>
The following method call:

```ruby
a(*nil)
```

A method call such as `a(*nil)` previously allocated an array, because
it calls `nil.to_a`, but I have determined this array allocation is
unnecessary.  The instructions in this case are:

```
0000 putself                                                          (   1)[Li]
0001 putnil
0002 splatarray                             false
0004 opt_send_without_block                 &lt;calldata!mid:a, argc:1, ARGS_SPLAT|FCALL&gt;
0006 leave
```

The method call uses `ARGS_SPLAT` without `ARGS_SPLAT_MUT`, so the
returned array doesn't need to be mutable.  I believe all cases where
`splatarray false` are used allow the returned object to be frozen,
since the `false` means to not duplicate the array.  The optimization
in this case is to have `splatarray false` push a shared empty frozen
array, instead of calling `nil.to_a` to return a newly allocated array.

There is a slightly backwards incompatibility with this optimization,
in that `nil.to_a` is not called.  However, I believe the new behavior
of `*nil` not calling `nil.to_a` is more consistent with how `**nil`
does not call `nil.to_hash`.  Also, so much Ruby code would break if
`nil.to_a` returned something different from the empty hash, that it's
difficult to imagine anyone actually doing that in real code, though
we have a few tests/specs for that.

I think it would be bad for consistency if `*nil` called `nil.to_a`
in some cases and not others, so this changes other cases to not
call `nil.to_a`:

For `[*nil]`, this uses `splatarray true`, which now allocates a
new array for a `nil` argument without calling `nil.to_a`.

For `[1, *nil]`, this uses `concattoarray`, which now returns
the first array if the second array is `nil`.

This updates the allocation tests to check that the array allocations
are avoided where possible.

Implements [Feature #21047]
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
The following method call:

```ruby
a(*nil)
```

A method call such as `a(*nil)` previously allocated an array, because
it calls `nil.to_a`, but I have determined this array allocation is
unnecessary.  The instructions in this case are:

```
0000 putself                                                          (   1)[Li]
0001 putnil
0002 splatarray                             false
0004 opt_send_without_block                 &lt;calldata!mid:a, argc:1, ARGS_SPLAT|FCALL&gt;
0006 leave
```

The method call uses `ARGS_SPLAT` without `ARGS_SPLAT_MUT`, so the
returned array doesn't need to be mutable.  I believe all cases where
`splatarray false` are used allow the returned object to be frozen,
since the `false` means to not duplicate the array.  The optimization
in this case is to have `splatarray false` push a shared empty frozen
array, instead of calling `nil.to_a` to return a newly allocated array.

There is a slightly backwards incompatibility with this optimization,
in that `nil.to_a` is not called.  However, I believe the new behavior
of `*nil` not calling `nil.to_a` is more consistent with how `**nil`
does not call `nil.to_hash`.  Also, so much Ruby code would break if
`nil.to_a` returned something different from the empty hash, that it's
difficult to imagine anyone actually doing that in real code, though
we have a few tests/specs for that.

I think it would be bad for consistency if `*nil` called `nil.to_a`
in some cases and not others, so this changes other cases to not
call `nil.to_a`:

For `[*nil]`, this uses `splatarray true`, which now allocates a
new array for a `nil` argument without calling `nil.to_a`.

For `[1, *nil]`, this uses `concattoarray`, which now returns
the first array if the second array is `nil`.

This updates the allocation tests to check that the array allocations
are avoided where possible.

Implements [Feature #21047]
</pre>
</div>
</content>
</entry>
<entry>
<title>Avoid hash allocation for certain proc calls</title>
<updated>2024-08-20T02:00:37+00:00</updated>
<author>
<name>Jeremy Evans</name>
<email>code@jeremyevans.net</email>
</author>
<published>2024-08-20T02:00:37+00:00</published>
<link rel='alternate' type='text/html' href='https://git.ruby-lang.org/ruby.git/commit/?id=ea7ceff82cec98d0c419e9807dcb33dcc08b56fa'/>
<id>ea7ceff82cec98d0c419e9807dcb33dcc08b56fa</id>
<content type='text'>
Previously, proc calls such as:

```ruby
proc{|| }.(**empty_hash)
proc{|b: 1| }.(**r2k_array_with_empty_hash)
```

both allocated hashes unnecessarily, due to two separate code paths.

The first call goes through CALLER_SETUP_ARG/vm_caller_setup_keyword_hash,
and is simple to fix by not duping an empty keyword hash that will be
dropped.

The second case is more involved, in setup_parameters_complex, but is
fixed the exact same way as when the ruby2_keywords hash is not empty,
by flattening the rest array to the VM stack, ignoring the last
element (the empty keyword splat).  Add a flatten_rest_array static
function to handle this case.

Update test_allocation.rb to automatically convert the method call
allocation tests to proc allocation tests, at least for the calls
that can be converted.  With the code changes, all proc call
allocation tests pass, showing that proc calls and method calls
now allocate the same number of objects.

I've audited the allocation tests, and I believe that all of the low
hanging fruit has been collected.  All remaining allocations are
either caller side:

* Positional splat + post argument
* Multiple positional splats
* Literal keywords + keyword splat
* Multiple keyword splats

Or callee side:

* Positional splat parameter
* Keyword splat parameter
* Keyword to positional argument conversion for methods that don't accept keywords
* ruby2_keywords method called with keywords

Reapplies abc04e898b627ab37fa9dd5e330f239768778d8b, which was reverted at
d56470a27c5a8a2e7aee7a76cea445c2d29c0c59, with the addition of a bug fix and
test.

Fixes [Bug #20679]</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
Previously, proc calls such as:

```ruby
proc{|| }.(**empty_hash)
proc{|b: 1| }.(**r2k_array_with_empty_hash)
```

both allocated hashes unnecessarily, due to two separate code paths.

The first call goes through CALLER_SETUP_ARG/vm_caller_setup_keyword_hash,
and is simple to fix by not duping an empty keyword hash that will be
dropped.

The second case is more involved, in setup_parameters_complex, but is
fixed the exact same way as when the ruby2_keywords hash is not empty,
by flattening the rest array to the VM stack, ignoring the last
element (the empty keyword splat).  Add a flatten_rest_array static
function to handle this case.

Update test_allocation.rb to automatically convert the method call
allocation tests to proc allocation tests, at least for the calls
that can be converted.  With the code changes, all proc call
allocation tests pass, showing that proc calls and method calls
now allocate the same number of objects.

I've audited the allocation tests, and I believe that all of the low
hanging fruit has been collected.  All remaining allocations are
either caller side:

* Positional splat + post argument
* Multiple positional splats
* Literal keywords + keyword splat
* Multiple keyword splats

Or callee side:

* Positional splat parameter
* Keyword splat parameter
* Keyword to positional argument conversion for methods that don't accept keywords
* ruby2_keywords method called with keywords

Reapplies abc04e898b627ab37fa9dd5e330f239768778d8b, which was reverted at
d56470a27c5a8a2e7aee7a76cea445c2d29c0c59, with the addition of a bug fix and
test.

Fixes [Bug #20679]</pre>
</div>
</content>
</entry>
</feed>
